diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/CREDITS linux/CREDITS
--- linux.orig/CREDITS	Tue Jan 22 17:51:22 2002
+++ linux/CREDITS	Mon Feb  4 17:38:23 2002
@@ -525,6 +525,16 @@
 S: Bellevue, Washington 98007
 S: USA
 
+N: Christopher L. Cheney
+E: ccheney@debian.org
+E: ccheney@cheney.cx
+W: http://www.cheney.cx
+P: 1024D/8E384AF2 2D31 1927 87D7 1F24 9FF9  1BC5 D106 5AB3 8E38 4AF2
+D: Vista Imaging usb webcam driver
+S: 314 Prince of Wales
+S: Conroe, TX 77304
+S: USA
+
 N: Stuart Cheshire
 E: cheshire@cs.stanford.edu
 D: Author of Starmode Radio IP (STRIP) driver
@@ -1203,10 +1213,10 @@
 S: Germany
 
 N: Christoph Hellwig
-E: hch@caldera.de
 E: hch@infradead.org
 D: misc driver & makefile hacking
 D: freevxfs driver
+D: sysvfs maintainer
 S: Triftstraße 26
 S: 38644 Goslar
 S: Germany
@@ -1265,6 +1275,13 @@
 D: bug toaster (A1 sauce makes all the difference)
 D: Random linux hacker
 
+N: Tim Hockin
+E: thockin@hockin.org
+W: http://www.hockin.org/~thockin
+D: Natsemi ethernet
+D: Cobalt Networks (x86) support
+D: This-and-That
+
 N: Dirk Hohndel
 E: hohndel@suse.de
 D: The XFree86[tm] Project
@@ -1951,9 +1968,10 @@
 S: Germany 
 
 N: Mark W. McClelland
-E: mwm@i.am
+E: mmcclell@bigfoot.com
 E: mark@alpha.dyndns.org
 W: http://alpha.dyndns.org/ov511/
+P: 1024D/357375CC 317C 58AC 1B39 2AB0 AB96  EB38 0B6F 731F 3573 75CC
 D: OV511 driver
 S: (address available on request)
 S: USA
@@ -2633,6 +2651,16 @@
 D: Linux-Support, -Mailbox, -Stammtisch
 D: several improvements to system programs
 S: Oldenburg
+S: Germany
+
+N: Robert Schwebel
+E: robert@schwebel.de
+W: http://www.schwebel.de
+D: Embedded hacker and book author,
+D: AMD Elan support for Linux
+S: Pengutronix
+S: Braunschweiger Strasse 79
+S: 31134 Hildesheim
 S: Germany
 
 N: Darren Senn
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/Configure.help linux/Documentation/Configure.help
--- linux.orig/Documentation/Configure.help	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/Configure.help	Wed Feb 13 16:34:07 2002
@@ -2446,6 +2446,14 @@
   If you want to compile it as a module, say M here and read
   <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+AH/ESP match support
+CONFIG_IP_NF_MATCH_AH_ESP
+  These two match extensions (`ah' and `esp') allow you to match a
+  range of SPIs inside AH or ESP headers of IPSec packets.
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
 TOS match support
 CONFIG_IP_NF_MATCH_TOS
   TOS matching allows you to match packets based on the Type Of
@@ -2613,6 +2621,19 @@
   If you want to compile it as a module, say M here and read
   <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+ULOG target support
+CONFIG_IP_NF_TARGET_ULOG
+  This option adds a `ULOG' target, which allows you to create rules in
+  any iptables table. The packet is passed to a userspace logging
+  daemon using netlink multicast sockets; unlike the LOG target
+  which can only be viewed through syslog.
+
+  The apropriate userspace logging daemon (ulogd) may be obtained from
+  http://www.gnumonks.org/projects/ulogd
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt.  If unsure, say `N'.
+
 LOG target support
 CONFIG_IP_NF_TARGET_LOG
   This option adds a `LOG' target, which allows you to create rules in
@@ -2669,6 +2690,23 @@
   If you want to compile it as a module, say M here and read
   <file:Documentation/modules.txt>.  If unsure, say `N'.
 
+IPV6 queue handler (EXPERIMENTAL)
+CONFIG_IP6_NF_QUEUE
+
+  This option adds a queue handler to the kernel for IPv6
+  packets which lets us to receive the filtered packets
+  with QUEUE target using libiptc as we can do with
+  the IPv4 now.
+
+  (C) Fernando Anton 2001
+  IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
+  Universidad Carlos III de Madrid
+  Universidad Politecnica de Alcala de Henares
+  email: fanton@it.uc3m.es
+
+  If you want to compile it as a module, say M here and read
+  Documentation/modules.txt. If unsure, say `N'.
+
 Owner match support
 CONFIG_IP6_NF_MATCH_OWNER
   Packet owner matching allows you to match locally-generated packets
@@ -3205,6 +3243,16 @@
   You should say Y here if you use XFree86 3.3.6 or 4.x and want to
   use GLX or DRI.  If unsure, say N.
 
+Intel 460GX support
+CONFIG_AGP_I460
+  This option gives you AGP support for the Intel 460GX chipset.  This
+  chipset, the first to support Intel Itanium processors, is new and
+  this option is correspondingly a little experimental.
+
+  If you don't have a 460GX based machine (such as BigSur) with an AGP 
+  slot then this option isn't going to do you much good.  If you're
+  dying to do Direct Rendering on IA-64, this is what you're looking for.
+
 Intel I810/I815 DC100/I810e support
 CONFIG_AGP_I810
   This option gives you AGP support for the Xserver on the Intel 810
@@ -3813,6 +3861,7 @@
    - "Pentium-4" for the Intel Pentium 4.
    - "K6" for the AMD K6, K6-II and K6-III (aka K6-3D).
    - "Athlon" for the AMD K7 family (Athlon/Duron/Thunderbird).
+   - "Elan" for the AMD Elan family (Elan SC400/SC410).
    - "Crusoe" for the Transmeta Crusoe series.
    - "Winchip-C6" for original IDT Winchip.
    - "Winchip-2" for IDT Winchip 2.
@@ -4069,6 +4118,19 @@
   module will be called rivafb.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+Trident Blade/Image support
+CONFIG_FB_TRIDENT
+  This driver is supposed to support graphics boards with the
+  Trident CyberXXXX/Image/CyberBlade chips mostly found in laptops
+  but also on some motherboards.Read <file:Documentation/fb/tridentfb.txt>
+
+  Say Y if you have such a graphics board.
+
+  The driver is also available as a module ( = code which can be
+  inserted and removed from the running kernel whenever you want). The
+  module will be called rivafb.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 ATI Mach64 display support
 CONFIG_FB_ATY
   This driver supports graphics boards with the ATI Mach64 chips.
@@ -4350,11 +4412,9 @@
 
 Matrox unified accelerated driver
 CONFIG_FB_MATROX
-  Say Y here if you have a Matrox Millennium, Matrox Millennium II,
-  Matrox Mystique, Matrox Mystique 220, Matrox Productiva G100, Matrox
-  Mystique G200, Matrox Millennium G200, Matrox Marvel G200 video,
-  Matrox G400 or G450 card in your box. At this time, support for the
-  G100 is untested and support for G450 is highly experimental.
+  Say Y here if you have a Matrox Millennium, Millennium II, Mystique,
+  Mystique 220, Productiva G100, Mystique G200, Millennium G200,
+  Marvel G200 video, G400, G450, or G550 card in your box.
 
   This driver is also available as a module ( = code which can be
   inserted and removed from the running kernel whenever you want).
@@ -4381,12 +4441,12 @@
   packed pixel and 32 bpp packed pixel. You can also use font widths
   different from 8.
 
-Matrox G100/G200/G400/G450 support
+Matrox G100/G200/G400/G450/G550 support
 CONFIG_FB_MATROX_G100
-  Say Y here if you have a Matrox G100, G200, G400 or G450 based
-  video card. If you select "Advanced lowlevel driver options", you
-  should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp packed
-  pixel and 32 bpp packed pixel. You can also use font widths
+  Say Y here if you have a Matrox G100, G200, G400, G450, or G550
+  based video card. If you select "Advanced lowlevel driver options",
+  you should check 8 bpp packed pixel, 16 bpp packed pixel, 24 bpp
+  packed pixel and 32 bpp packed pixel. You can also use font widths
   different from 8.
 
   If you need support for G400 secondary head, you must first say Y to
@@ -4394,6 +4454,9 @@
   section, and then to "Matrox I2C support" and "G400 second head
   support" here in the framebuffer section.
 
+  If you need support for G450 or G550 secondary head, say Y to
+  "Matrox G450/G550 second head support" below.
+
 Matrox I2C support
 CONFIG_FB_MATROX_I2C
   This drivers creates I2C buses which are needed for accessing the
@@ -4440,7 +4503,7 @@
 Matrox G450 second head support
 CONFIG_FB_MATROX_G450
   Say Y or M here if you want to use a secondary head (meaning two
-  monitors in parallel) on G450.
+  monitors in parallel) on G450 or G550.
 
   If you compile it as module, two modules are created,
   matroxfb_crtc2.o and matroxfb_g450.o. Both modules are needed if you
@@ -4452,6 +4515,9 @@
   primary and secondary head outputs.  Secondary head driver always
   start in 640x480 resolution and you must use fbset to change it.
 
+  Note on most G550 cards the analog output is the secondary head,
+  so you will need to say Y here to use it.
+
   Also do not forget that second head supports only 16 and 32 bpp
   packed pixels, so it is a good idea to compile them into the kernel
   too. You can use only some font widths, as the driver uses generic
@@ -10798,6 +10864,15 @@
   More specific information and updates are available from
   <http://www.scyld.com/network/natsemi.html>.
 
+NatSemi workaround for high errors
+CONFIG_NATSEMI_CABLE_MAGIC
+  Some systems see lots of errors with NatSemi ethernet controllers
+  on certain cables.  If you are seeing lots of errors, try turning
+  this option on.  Some boards have incorrect values for supporting
+  resistors that can cause this change to break.  If you turn this
+  option on and your network suddenly stops working, turn this
+  option off.
+
 SK_G16 support
 CONFIG_SK_G16
   If you have a network (Ethernet) card of this type, say Y and read
@@ -12908,6 +12983,17 @@
   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
+CONFIG_USB_SERIAL_IPAQ
+  Say Y here if you want to connect to your Compaq iPAQ running
+  Windows CE 3.0 using a USB autosync cable. For information on using
+  the driver, read <file:Documentation/usb/usb-serial.txt>.
+
+  This code 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 ipaq.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
 USB IR Dongle Serial Driver
 CONFIG_USB_SERIAL_IR
   Say Y here if you want to enable simple serial support for USB IrDA
@@ -13117,6 +13203,22 @@
   The module will be called io_edgeport.o.  If you want to compile it
   as a module, say M here and read <file:Documentation/modules.txt>.
 
+USB PalmConnect (and other KL5KUSB105-based) Single Port Serial Driver
+CONFIG_USB_SERIAL_KLSI
+  Say Y here if you want to use a KL5KUSB105 - based single port
+  serial adapter. The most widely known -- and currently the only
+  tested -- device in this category is the PalmConnect USB Serial
+  adapter sold by Palm Inc. for use with their Palm III and Palm V
+  series PDAs.
+
+  Please read <file:Documentation/usb/usb-serial.txt> for more
+  information.
+
+  This code 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 kl5kusb105.o. If you want to compile it as
+  a module, say M here and read <file:Documentation/modules.txt>.
+
 USB Serial Converter verbose debug
 CONFIG_USB_SERIAL_DEBUG
   Say Y here if you want verbose debug messages from the USB Serial
@@ -13223,6 +13325,54 @@
   The module will be called pwc.o.  If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+USB SE401 Camera support
+CONFIG_USB_SE401
+  Say Y here if you want to connect this type of camera to your
+  computer's USB port. See <file:Documentation/usb/se401.txt> for more
+  information and for a list of supported cameras.
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code 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 se401.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+USB STV680 (Pencam) Camera support
+CONFIG_USB_STV680
+  Say Y here if you want to connect this type of camera to your
+  computer's USB port. This includes the Pencam line of cameras.
+  See <file:Documentation/usb/stv680.txt> for more information and for
+  a list of supported cameras.
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code 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 stv680.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+Vicam
+CONFIG_USB_VICAM
+  Say Y here if you have 3com homeconnect camera (vicam).
+
+  This driver uses the Video For Linux API. You must say Y or M to
+  "Video For Linux" (under Multimedia Devices) to use this driver.
+  Information on this API and pointers to "v4l" programs may be found
+  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+  This code 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 vicam.o. If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt>.
+
+
 Pegasus/Pegasus II based USB-Ethernet device support
 CONFIG_USB_PEGASUS
   Say Y here if you know you have Pegasus or Pegasus II based adapter.
@@ -14812,6 +14962,18 @@
   Say Y here if you would like to be able to read the hard disk
   partition table format used by SGI machines.
 
+Intel EFI GUID partition support
+CONFIG_EFI_PARTITION
+  Say Y here if you would like to use hard disks under Linux which
+  were partitioned using EFI GPT.  Presently only useful on the
+  IA-64 platform.
+
+/dev/guid support (EXPERIMENTAL)
+CONFIG_DEVFS_GUID
+  Say Y here if you would like to access disks and partitions by
+  their Globally Unique Identifiers (GUIDs) which will appear as
+  symbolic links in /dev/guid.
+
 Ultrix partition table support
 CONFIG_ULTRIX_PARTITION
   Say Y here if you would like to be able to read the hard disk
@@ -15086,7 +15248,7 @@
   Currently, the valid values are:
   big5, cp437, cp737, cp775, cp850, cp852, cp855, cp857, cp860, cp861,
   cp862, cp863, cp864, cp865, cp866, cp869, cp874, cp932, cp936,
-  cp949, cp950, cp1251, cp1255, euc-jp, euc-kr, gb2312, iso8859-1,
+  cp949, cp950, cp1250, cp1251, cp1255, euc-jp, euc-kr, gb2312, iso8859-1,
   iso8859-2, iso8859-3, iso8859-4, iso8859-5, iso8859-6, iso8859-7,
   iso8859-8, iso8859-9, iso8859-13, iso8859-14, iso8859-15,
   koi8-r, koi8-ru, koi8-u, sjis, tis-620, utf8.
@@ -15326,6 +15488,16 @@
   say Y here if you want to include the DOS codepage for Traditional
   Chinese(Big5).
 
+Central European (Codepage 1250)
+CONFIG_NLS_CODEPAGE_1250
+  If you want to display filenames with native language characters
+  from the Microsoft FAT file system family or from JOLIET CDROMs
+  correctly on the screen, you need to include the appropriate
+  input/output character sets. Say Y here for the Windows CP-1250
+  character set, which works for most Latin-written Slavic and Central
+  European languages: Czech, German, Hungarian, Polish, Rumanian, Croatian,
+  Slovak, Slovene.
+
 NLS ISO 8859-1 (Latin 1; Western European Languages)
 CONFIG_NLS_ISO8859_1
   If you want to display filenames with native language characters
@@ -15596,13 +15768,10 @@
 
 Support for PMU-based PowerMacs
 CONFIG_ADB_PMU
-  On the PowerBook 3400 and 2400, the PMU is a 6805 microprocessor
-  core whose primary function is to control battery charging and
-  system power.  The PMU also controls the ADB (Apple Desktop Bus)
-  which connects to the keyboard and mouse, as well as the
-  non-volatile RAM and the RTC (real time clock) chip.  Say Y to
-  enable support for this device; you should do so if your machine
-  is one of these PowerBooks.
+  This provides support for PMU based Power Macintosh systems.  This
+  includes all PowerBooks and all AGP-based machines.
+  
+  If unsure say Y.
 
 Include MacIO ADB driver
 CONFIG_ADB_MACIO
@@ -17025,22 +17194,6 @@
   will issue the hlt instruction if nothing is to be done, thereby
   sending the processor to sleep and saving power.
 
-USB SE401 Camera support
-CONFIG_USB_SE401
-  Say Y here if you want to connect this type of camera to your
-  computer's USB port. See <file:Documentation/usb/se401.txt> for more
-  information and for a list of supported cameras.
-
-  This driver uses the Video For Linux API. You must say Y or M to
-  "Video For Linux" (under Multimedia Devices) to use this driver.
-  Information on this API and pointers to "v4l" programs may be found
-  on the WWW at <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
-
-  This code 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 se401.o. If you want to compile it as a
-  module, say M here and read <file:Documentation/modules.txt>.
-
 ACPI support
 CONFIG_ACPI
   ACPI/OSPM support for Linux is currently under development. As such,
@@ -20409,7 +20562,7 @@
   <file:Documentation/modules.txt>.
 
 PowerMac DMA sound support
-CONFIG_DMASOUND_AWACS
+CONFIG_DMASOUND_PMAC
   If you want to use the internal audio of your PowerMac in Linux,
   answer Y to this question. This will provide a Sun-like /dev/audio,
   compatible with the Linux/i386 sound system. Otherwise, say N.
@@ -20669,6 +20822,14 @@
   have it autoloaded. The act of removing the module shuts down the
   sound hardware for more power savings.
 
+APM emulation
+CONFIG_PMAC_APM_EMU
+  This driver provides an emulated /dev/apm_bios and /proc/apm. The
+  first one is mostly intended for XFree to sleep & wakeup properly,
+  the second ones provides some battery informations to allow existing
+  APM utilities to work. It provides less useful informations than
+  tools specifically designed for PowerBooks or /proc/pmu/battery_x
+
 Backlight control for LCD screens
 CONFIG_PMAC_BACKLIGHT
   Say Y here to build in code to manage the LCD backlight on a
@@ -20747,7 +20908,7 @@
     End of life: mid 2001 (?)
     URL: <http://www.tz-mikroelektronik.de/ServiceModule/index.html>
 
-  HERMES:
+  HERMES_PRO:
     Hermes-Pro ISDN/LAN router with integrated 8 x hub
     Manufacturer: Multidata Gesellschaft für Datentechnik und Informatik
       <http://www.multidata.de/>
@@ -21615,24 +21776,18 @@
   If you want to use the produced kernel to IPL directly from a
   device, you have to merge a bootsector specific to the device
   into the first bytes of the kernel. You will have to select the
-  IPL device.
+  IPL device on another question, that pops up, when you select
+  CONFIG_IPL.
 
-# Choice: s390_ipl
-IPL from a S/390 tape unit
+IPL from a /390 tape unit
 CONFIG_IPL_TAPE
   Select this option if you want to IPL the image from a Tape.
 
 IPL from a virtual card reader emulated by VM/ESA
-CONFIG_IPL_RDR_VM
-  Select this option if you are running under VM/ESA and want
+CONFIG_IPL_VM
+  Select this option if you are running under VM/ESA and want 
   to IPL the image from the emulated card reader.
 
-IPL from a real card reader
-CONFIG_IPL_RDR
-  Select this option if you want to IPL the image from a real
-  card reader. Maybe you still got one and want to try. We didn't
-  test.
-
 Support for DASD hard disks
 CONFIG_DASD
   Enable this option if you want to access DASDs directly utilizing
@@ -23628,12 +23783,23 @@
 
   HP-simulator   For the HP simulator
                  (<http://software.hp.com/ia64linux/>).
-  SN1-simulator  For the SGI SN1 simulator.
+  SN1            For SGI SN1 Platforms.
+  SN2            For SGI SN2 Platforms.
   DIG-compliant  For DIG ("Developer's Interface Guide") compliant
                  system.
 
   If you don't know what to do, choose "generic".
 
+CONFIG_IA64_SGI_SN_SIM
+  Build a kernel that runs on both the SGI simulator AND on hardware.
+  There is a very slight performance penalty on hardware for including this
+  option.
+
+CONFIG_IA64_SGI_SN_DEBUG
+  This enables addition debug code that helps isolate
+  platform/kernel bugs. There is a small but measurable performance
+  degradation when this option is enabled.
+
 # Choice: pagesize
 Kernel page size
 CONFIG_IA64_PAGE_SIZE_4KB
@@ -23651,53 +23817,13 @@
 
   If you don't know what to do, choose 8KB.
 
-Enable Itanium A-step specific code
-CONFIG_ITANIUM_ASTEP_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with an A-step CPU.  You have an A-step CPU if the "revision" field
-  in /proc/cpuinfo is 0.
-
 Enable Itanium B-step specific code
 CONFIG_ITANIUM_BSTEP_SPECIFIC
   Select this option to build a kernel for an Itanium prototype system
-  with a B-step CPU.  You have a B-step CPU if the "revision" field in
-  /proc/cpuinfo has a value in the range from 1 to 4.
-
-Enable Itanium B0-step specific code
-CONFIG_ITANIUM_B0_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with a B0-step CPU.  You have a B0-step CPU if the "revision" field
-  in /proc/cpuinfo is 1.
-
-Enable Itanium C-step specific code
-CONFIG_ITANIUM_CSTEP_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with a C-step CPU.  You have a C-step CPU if the "revision" field in
-  /proc/cpuinfo is in the range of 5 to 8.
-
-Enable Itanium B1-step specific code
-CONFIG_ITANIUM_B1_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with a B1-step CPU.  You have a B1-step CPU if the "revision" field
-  in /proc/cpuinfo is 2.
-
-Enable Itanium B2-step specific code
-CONFIG_ITANIUM_B2_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with a B2-step CPU.  You have a B2-step CPU if the "revision" field
-  in /proc/cpuinfo is 3.
-
-Enable Itanium C0-step specific code
-CONFIG_ITANIUM_C0_SPECIFIC
-  Select this option to build a kernel for an Itanium prototype system
-  with a C0-step CPU.  You have a C0-step CPU if the "revision" field
-  in /proc/cpuinfo is 5.
-
-Force interrupt redirection
-CONFIG_IA64_HAVE_IRQREDIR
-  Select this option if you know that your system has the ability to
-  redirect interrupts to different CPUs.  Select N here if you're
-  unsure.
+  with a B-step CPU.  Only B3 step CPUs are supported.  You have a B3-step
+  CPU if the "revision" field in /proc/cpuinfo is equal to 4.  If the
+  "revision" field shows a number bigger than 4, you do not have to turn
+  on this option.
 
 Enable IA-64 Machine Check Abort
 CONFIG_IA64_MCA
@@ -23766,6 +23892,15 @@
   To use this option, you have to check that the "/proc file system
   support" (CONFIG_PROC_FS) is enabled, too.
 
+/proc/efi/vars support
+CONFIG_EFI_VARS
+  If you say Y here, you are able to get EFI (Extensible Firmware
+  Interface) variable information in /proc/efi/vars.  You may read,
+  write, create, and destroy EFI variables through this interface.
+
+  To use this option, you have to check that the "/proc file system
+  support" (CONFIG_PROC_FS) is enabled, too.
+
 Kernel support for IA-32 emulation
 CONFIG_IA32_SUPPORT
   IA64 processors can run IA32 (that is, x86) binaries by emulating
@@ -24236,6 +24371,16 @@
   If you are using GDB for remote debugging over a serial port and
   would like kernel messages to be formatted into GDB $O packets so
   that GDB prints them as program output, say 'Y'.
+
+802.1Q VLAN Support
+CONFIG_VLAN_8021Q
+  Select this and you will be able to create 802.1Q VLAN interfaces on your
+  ethernet interfaces.  802.1Q VLAN supports almost everything a regular
+  ethernet interface does, including firewalling, bridging, and of course
+  IP traffic.  You will need the 'vconfig' tool from the VLAN project in
+  order to effectively use VLANs.  See the VLAN web page for more
+  information:  http://www.candelatech.com/~greear/vlan.html  If unsure,
+  you can safely say 'N'.
 
 #
 # A couple of things I keep forgetting:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/DMA-mapping.txt linux/Documentation/DMA-mapping.txt
--- linux.orig/Documentation/DMA-mapping.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/DMA-mapping.txt	Wed Jan 16 20:56:50 2002
@@ -689,6 +689,97 @@
 This is possible with the DAC interfaces purely because they are
 not translated in any way.
 
+		Optimizing Unmap State Space Consumption
+
+On many platforms, pci_unmap_{single,page}() is simply a nop.
+Therefore, keeping track of the mapping address and length is a waste
+of space.  Instead of filling your drivers up with ifdefs and the like
+to "work around" this (which would defeat the whole purpose of a
+portable API) the following facilities are provided.
+
+Actually, instead of describing the macros one by one, we'll
+transform some example code.
+
+1) Use DECLARE_PCI_UNMAP_{ADDR,LEN} in state saving structures.
+   Example, before:
+
+	struct ring_state {
+		struct sk_buff *skb;
+		dma_addr_t mapping;
+		__u32 len;
+	};
+
+   after:
+
+	struct ring_state {
+		struct sk_buff *skb;
+		DECLARE_PCI_UNMAP_ADDR(mapping)
+		DECLARE_PCI_UNMAP_LEN(len)
+	};
+
+   NOTE: DO NOT put a semicolon at the end of the DECLARE_*()
+         macro.
+
+2) Use pci_unmap_{addr,len}_set to set these values.
+   Example, before:
+
+	ringp->mapping = FOO;
+	ringp->len = BAR;
+
+   after:
+
+	pci_unmap_addr_set(ringp, mapping, FOO);
+	pci_unmap_len_set(ringp, len, BAR);
+
+3) Use pci_unmap_{addr,len} to access these values.
+   Example, before:
+
+	pci_unmap_single(pdev, ringp->mapping, ringp->len,
+			 PCI_DMA_FROMDEVICE);
+
+   after:
+
+	pci_unmap_single(pdev,
+			 pci_unmap_addr(ringp, mapping),
+			 pci_unmap_len(ringp, len),
+			 PCI_DMA_FROMDEVICE);
+
+It really should be self-explanatory.  We treat the ADDR and LEN
+seperately, because it is possible for an implementation to only
+need the address in order to perform the unmap operation.
+
+			Platform Issues
+
+If you are just writing drivers for Linux and do not maintain
+an architecture port for the kernel, you can safely skip down
+to "Closing".
+
+1) Struct scatterlist requirements.
+
+   Struct scatterlist must contain, at a minimum, the following
+   members:
+
+	char *address;
+	struct page *page;
+	unsigned int offset;
+	unsigned int length;
+
+   The "address" member will disappear in 2.5.x
+
+   This means that your pci_{map,unmap}_sg() and all other
+   interfaces dealing with scatterlists must be able to cope
+   properly with page being non NULL.
+
+   A scatterlist is in one of two states.  The base address is
+   either specified by "address" or by a "page+offset" pair.
+   If "address" is NULL, then "page+offset" is being used.
+   If "page" is NULL, then "address" is being used.
+
+   In 2.5.x, all scatterlists will use "page+offset".  But during
+   2.4.x we still have to support the old method.
+
+2) More to come...
+
 			   Closing
 
 This document, and the API itself, would not be in it's current
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/DocBook/kernel-hacking.tmpl linux/Documentation/DocBook/kernel-hacking.tmpl
--- linux.orig/Documentation/DocBook/kernel-hacking.tmpl	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/DocBook/kernel-hacking.tmpl	Fri Dec 21 19:45:49 2001
@@ -18,8 +18,8 @@
   </authorgroup>
 
   <copyright>
-   <year>2000</year>
-   <holder>Paul Russell</holder>
+   <year>2001</year>
+   <holder>Rusty Russell</holder>
   </copyright>
 
   <legalnotice>
@@ -651,6 +651,29 @@
    </para> 
   </sect1>
  
+  <sect1 id="routines-endian">
+   <title><function>cpu_to_be32()</function>/<function>be32_to_cpu()</function>/<function>cpu_to_le32()</function>/<function>le32_to_cpu()</function>
+     <filename class=headerfile>include/asm/byteorder.h</filename> 
+   </title>
+
+   <para>
+    The <function>cpu_to_be32()</function> family (where the "32" can
+    be replaced by 64 or 16, and the "be" can be replaced by "le") are
+    the general way to do endian conversions in the kernel: they
+    return the converted value.  All variations supply the reverse as
+    well: <function>be32_to_cpu()</function>, etc.
+   </para>
+
+   <para>
+    There are two major variations of these functions: the pointer
+    variation, such as <function>cpu_to_be32p()</function>, which take
+    a pointer to the given type, and return the converted value.  The
+    other variation is the "in-situ" family, such as
+    <function>cpu_to_be32s()</function>, which convert value referred
+    to by the pointer, and return void.
+   </para> 
+  </sect1>
+
   <sect1 id="routines-local-irqs">
    <title><function>local_irq_save()</function>/<function>local_irq_restore()</function>
     <filename class=headerfile>include/asm/system.h</filename>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/fb/tridentfb.txt linux/Documentation/fb/tridentfb.txt
--- linux.orig/Documentation/fb/tridentfb.txt	Thu Jan  1 00:00:00 1970
+++ linux/Documentation/fb/tridentfb.txt	Thu Feb  7 18:19:01 2002
@@ -0,0 +1,49 @@
+Tridentfb is a framebuffer driver for some Trident chip based cards.
+
+The following list of chips is thought to be supported although not all are
+tested:
+
+those from the Image series with Cyber in their names - accelerated
+those with Blade in their names (Blade3D,CyberBlade...) - accelerated
+the newer CyberBladeXP family  - nonaccelerated
+
+Only PCI/AGP based cards are supported, none of the older Tridents.
+
+How to use it?
+==============
+Just do your usual console work :)
+
+When booting you can pass the following parameters
+==================================================
+
+noaccel - turns off acceleration (when it doesn't work for your card)
+accel - force text acceleration (for boards which by default are noacceled)
+
+fp	- use flat panel related stuff
+crt 	- assume monitor is present instead of fp
+
+center 	- for flat panels and resolutions smaller than native size center the
+	  image, otherwise use
+stretch
+
+memsize - integer value in Kb, use if your card's memory size is misdetected.
+	  look at the driver output to see what it says when initializing.
+memdiff - integer value in Kb,should be nonzero if your card reports
+	  more memory than it actually has.For instance mine is 192K less than
+	  detection says in all three BIOS selectable situations 2M, 4M, 8M.
+	  Only use if your video memory is taken from main memory hence of
+	  configurable size.Otherwise use memsize.
+	  If in some modes which barely fit the memory you see garbage at the bottom
+	  this might help by not letting change to that mode anymore.
+
+nativex - the width in pixels of the flat panel.If you know it (usually 1024
+	  800 or 1280) and it is not what the driver seems to detect use it.
+
+bpp  - bits per pixel (8,16 or 32)
+mode - a mode name like 800x600 (as described in Documentation/fb/modedb.txt)
+
+Using insane values for the above parameters will probably result in driver
+misbehaviour so take care(for instance memsize=12345678 or memdiff=23784 or
+nativex=93)
+
+Contact: jani@astechnix.ro
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/filesystems/devfs/ChangeLog linux/Documentation/filesystems/devfs/ChangeLog
--- linux.orig/Documentation/filesystems/devfs/ChangeLog	Fri Dec 21 17:41:53 2001
+++ linux/Documentation/filesystems/devfs/ChangeLog	Mon Jan 21 20:27:04 2002
@@ -1845,3 +1845,38 @@
 - Improved debugging messages
 
 - Fixed unregister bugs in drivers/md/lvm-fs.c
+===============================================================================
+Changes for patch v199.6
+
+- Corrected (made useful) debugging message in <unregister>
+
+- Moved <kmem_cache_create> in <mount_devfs_fs> to <init_devfs_fs>
+
+- Fixed drivers/md/lvm-fs.c to create "lvm" entry
+
+- Added magic number to guard against scribbling drivers
+
+- Only return old entry in <devfs_mk_dir> if a directory
+
+- Defined macros for error and debug messages
+
+- Updated README from master HTML file
+===============================================================================
+Changes for patch v199.7
+
+- Unregister /dev/root symlink prior to creating second one (for
+  initrd)
+
+- Added support for multiple Compaq cpqarray controllers
+
+- Fixed (rare, old) race in <devfs_lookup>
+===============================================================================
+Changes for patch v199.8
+
+- Fixed deadlock bug in <devfs_d_revalidate_wait>
+
+- Tag VFS deletable in <devfs_mk_symlink> if handle ignored
+
+- Updated README from master HTML file
+
+- Fixed kdev_none macro in include/linux/kdev_t.h
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/filesystems/devfs/README linux/Documentation/filesystems/devfs/README
--- linux.orig/Documentation/filesystems/devfs/README	Fri Dec 21 17:41:53 2001
+++ linux/Documentation/filesystems/devfs/README	Mon Jan 21 20:27:04 2002
@@ -3,7 +3,16 @@
 
 Linux Devfs (Device File System) FAQ
 Richard Gooch
-13-DEC-2001
+20-JAN-2002
+
+
+Document languages:
+
+
+
+
+
+
 
 -----------------------------------------------------------------------------
 
@@ -68,6 +77,8 @@
 Alternatives to devfs
 What I don't like about devfs
 How to report bugs
+Strange kernel messages
+Compilation problems with devfsd
 
 
 Other resources
@@ -836,8 +847,8 @@
 will now work as before.
 
 Hopefully for most people devfs will have enough support so that they
-can mount devfs directly over /dev without loosing most functionality
-(i.e. loosing access to various devices). As of 22-JAN-1998 (devfs
+can mount devfs directly over /dev without losing most functionality
+(i.e. losing access to various devices). As of 22-JAN-1998 (devfs
 patch version 10) I am now running this way. All the devices I have
 are available in devfs, so I don't lose anything.
 
@@ -1475,6 +1486,8 @@
 Alternatives to devfs
 What I don't like about devfs
 How to report bugs
+Strange kernel messages
+Compilation problems with devfsd
 
 
 
@@ -1675,7 +1688,7 @@
 minor limitation
 
 
-simplying increasing the device number size is insufficient. Apart
+simply increasing the device number size is insufficient. Apart
 from causing a lot of pain, it doesn't solve the management issues
 of a /dev with thousands or more device nodes
 
@@ -1790,6 +1803,65 @@
 
 http://www.chiark.greenend.org.uk/~sgtatham/bugs.html.
 
+
+Strange kernel messages
+
+You may see devfs-related messages in your kernel logs. Below are some
+messages and what they mean (and what you should do about them, if
+anything).
+
+
+
+devfs_register(fred): could not append to parent, err: -17
+
+You need to check what the error code means, but usually 17 means
+EEXIST. This means that a driver attempted to create an entry
+fred in a directory, but there already was an entry with that
+name. This is often caused by flawed boot scripts which untar a bunch
+of inodes into /dev, as a way to restore permissions. This
+message is harmless, as the device nodes will still
+provide access to the driver (unless you use the devfs=only
+boot option, which is only for dedicated souls:-). If you want to get
+rid of these annoying messages, upgrade to devfsd-v1.3.20 and use the
+recommended RESTORE directive to restore permissions.
+
+
+devfs_mk_dir(bill): using old entry in dir: c1808724 ""
+
+This is similar to the message above, except that a driver attempted
+to create a directory named bill, and the parent directory
+has an entry with the same name. In this case, to ensure that drivers
+continue to work properly, the old entry is re-used and given to the
+driver. In 2.5 kernels, the driver is given a NULL entry, and thus,
+under rare circumstances, may not create the require device nodes.
+The solution is the same as above.
+
+
+
+
+
+Compilation problems with devfsd
+
+Usually, you can compile devfsd just by typing in
+make in the source directory, followed by a make
+install (as root). Sometimes, you may have problems, particularly
+on broken configurations.
+
+
+
+error messages relating to DEVFSD_NOTIFY_DELETE
+
+This happened because you have an ancient set of kernel headers
+installed in /usr/include/linux or /usr/src/linux.
+Install kernel 2.4.10 or later. You may need to pass the
+KERNEL_DIR variable to make (if you did not install
+the new kernel sources as /usr/src/linux), or you may copy
+the devfs_fs.h file in the kernel source tree into
+/usr/include/linux.
+
+
+
+
 -----------------------------------------------------------------------------
 
 
@@ -1844,9 +1916,25 @@
 
 
 
+The document master (in English) by rgooch@atnf.csiro.au is
+available at
+
+http://www.atnf.csiro.au/~rgooch/linux/docs/devfs.html
+
+
+
 A Korean translation by viatoris@nownuri.net is available at
 
-http://home.nownuri.net/~viatoris/devfs/devfs.html 
+http://home.nownuri.net/~viatoris/devfs/devfs.html
+
+A newer version is under construcation at
+
+http://viatoris.new21.org/devfs/devfs.html
 
 
 
+
+-----------------------------------------------------------------------------
+Most flags courtesy of ITA's 
+Flags of All Countries
+used with permission. 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/i386/boot.txt linux/Documentation/i386/boot.txt
--- linux.orig/Documentation/i386/boot.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/i386/boot.txt	Tue Jan 15 21:34:43 2002
@@ -2,7 +2,7 @@
 		     ----------------------------
 
 		    H. Peter Anvin <hpa@zytor.com>
-			Last update 2000-10-29
+			Last update 2002-01-01
 
 On the i386 platform, the Linux kernel uses a rather complicated boot
 convention.  This has evolved partially due to historical aspects, as
@@ -25,12 +25,15 @@
 Protocol 2.01:	(Kernel 1.3.76) Added a heap overrun warning.
 
 Protocol 2.02:	(Kernel 2.4.0-test3-pre3) New command line protocol.
-		Lower the conventional memory ceiling.  No overwrite
+		Lower the conventional memory ceiling.	No overwrite
 		of the traditional setup area, thus making booting
 		safe for systems which use the EBDA from SMM or 32-bit
 		BIOS entry points.  zImage deprecated but still
 		supported.
 
+Protocol 2.03:	(Kernel 2.4.18-pre1) Explicitly makes the highest possible
+		initrd address available to the bootloader.
+
 
 **** MEMORY LAYOUT
 
@@ -45,7 +48,7 @@
 098000	+------------------------+	
 	|  Kernel setup		 |	The kernel real-mode code.
 090200	+------------------------+
-	|  Kernel boot sector    |	The kernel legacy boot sector.
+	|  Kernel boot sector	 |	The kernel legacy boot sector.
 090000	+------------------------+
 	|  Protected-mode kernel |	The bulk of the kernel image.
 010000	+------------------------+
@@ -62,16 +65,16 @@
 When using bzImage, the protected-mode kernel was relocated to
 0x100000 ("high memory"), and the kernel real-mode block (boot sector,
 setup, and stack/heap) was made relocatable to any address between
-0x10000 and end of low memory.  Unfortunately, in protocols 2.00 and
+0x10000 and end of low memory.	Unfortunately, in protocols 2.00 and
 2.01 the command line is still required to live in the 0x9XXXX memory
 range, and that memory range is still overwritten by the early kernel.
-The 2.02 protocol fixes that.
+The 2.02 protocol resolves that problem.
 
 It is desirable to keep the "memory ceiling" -- the highest point in
 low memory touched by the boot loader -- as low as possible, since
 some newer BIOSes have begun to allocate some rather large amounts of
 memory, called the Extended BIOS Data Area, near the top of low
-memory.  The boot loader should use the "INT 12h" BIOS call to verify
+memory.	 The boot loader should use the "INT 12h" BIOS call to verify
 how much low memory is available.
 
 Unfortunately, if INT 12h reports that the amount of memory is too
@@ -112,7 +115,8 @@
 0202/4	2.00+	header		Magic signature "HdrS"
 0206/2	2.00+	version		Boot protocol version supported
 0208/4	2.00+	realmode_swtch	Boot loader hook (see below)
-020C/4	2.00+	start_sys	Points to kernel version string
+020C/2	2.00+	start_sys	The load-low segment (0x1000) (obsolete)
+020E/2	2.00+	kernel_version	Pointer to kernel version string
 0210/1	2.00+	type_of_loader	Boot loader identifier
 0211/1	2.00+	loadflags	Boot protocol option flags
 0212/2	2.00+	setup_move_size	Move to high memory size (used with hooks)
@@ -123,6 +127,7 @@
 0224/2	2.01+	heap_end_ptr	Free memory after setup end
 0226/2	N/A	pad1		Unused
 0228/4	2.02+	cmd_line_ptr	32-bit pointer to the kernel command line
+022C/4	2.03+	initrd_addr_max	Highest legal initrd address
 
 For backwards compatibility, if the setup_sects field contains 0, the
 real value is 4.
@@ -140,6 +145,15 @@
 setting fields in the header, you must make sure only to set fields
 supported by the protocol version in use.
 
+The "kernel_version" field, if set to a nonzero value, contains a
+pointer to a null-terminated human-readable kernel version number
+string, less 0x200.  This can be used to display the kernel version to
+the user.  This value should be less than (0x200*setup_sects).  For
+example, if this value is set to 0x1c00, the kernel version number
+string can be found at offset 0x1e00 in the kernel file.  This is a
+valid value if and only if the "setup_sects" field contains the value
+14 or higher.
+
 Most boot loaders will simply load the kernel at its target address
 directly.  Such boot loaders do not need to worry about filling in
 most of the fields in the header.  The following fields should be
@@ -160,6 +174,9 @@
 	3  SYSLINUX
 	4  EtherBoot
 
+	Please contact <hpa@zytor.com> if you need a bootloader ID
+	value assigned.
+
   loadflags, heap_end_ptr:
 	If the protocol version is 2.01 or higher, enter the
 	offset limit of the setup heap into heap_end_ptr and set the
@@ -180,9 +197,9 @@
 
 	The initrd should typically be located as high in memory as
 	possible, as it may otherwise get overwritten by the early
-	kernel initialization sequence.  However, it must never be
-	located above address 0x3C000000 if you want all kernels to
-	read it.
+	kernel initialization sequence.	 However, it must never be
+	located above the address specified in the initrd_addr_max
+	field.	The initrd should be at least 4K page aligned.
 
   cmd_line_ptr:
 	If the protocol version is 2.02 or higher, this is a 32-bit
@@ -192,7 +209,15 @@
 	command line, in which case you can point this to an empty
 	string (or better yet, to the string "auto".)  If this field
 	is left at zero, the kernel will assume that your boot loader
-	does not support the 2.02 protocol.
+	does not support the 2.02+ protocol.
+
+  ramdisk_max:
+	The maximum address that may be occupied by the initrd
+	contents.  For boot protocols 2.02 or earlier, this field is
+	not present, and the maximum address is 0x37FFFFFF.  (This
+	address is defined as the address of the highest safe byte, so
+	if your ramdisk is exactly 131072 bytes long and this field is
+	0x37FFFFFF, you can start your ramdisk at 0x37FE0000.)
 
 
 **** THE KERNEL COMMAND LINE
@@ -254,14 +279,14 @@
 		if ( protocol >= 0x0202 ) {
 			cmd_line_ptr = base_ptr + 0x9000;
 		} else {
-			cmd_line_magic  = 0xA33F;
+			cmd_line_magic	= 0xA33F;
 			cmd_line_offset = 0x9000;
 			setup_move_size = 0x9100;
 		}
 	} else {
 		/* Very old kernel */
 
-		cmd_line_magic  = 0xA33F;
+		cmd_line_magic	= 0xA33F;
 		cmd_line_offset = 0x9000;
 
 		/* A very old kernel MUST have its real-mode code
@@ -411,4 +436,3 @@
 
 	After completing your hook, you should jump to the address
 	that was in this field before your boot loader overwrote it.
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/kbuild/config-language.txt linux/Documentation/kbuild/config-language.txt
--- linux.orig/Documentation/kbuild/config-language.txt	Sat Aug  5 01:38:44 2000
+++ linux/Documentation/kbuild/config-language.txt	Mon Feb  4 16:58:00 2002
@@ -30,7 +30,7 @@
     scripts/Configure   make config, make oldconfig
     scripts/Menuconfig  make menuconfig
     scripts/tkparse     make xconfig
-    mconfig             (in development)
+    mconfig             ftp.kernel.org/pub/linux/kernel/people/hch/mconfig/
 
 'Configure' is a bash script which interprets Config.in files by sourcing
 them.  Some of the Config Language commands are native bash commands;
@@ -52,9 +52,6 @@
 into an internal syntax tree and then hands the syntax tree to one of
 several user-interface front ends.
 
-This document describes the behaviour of all four interpreters, even though
-mconfig has not been released at the time of writing.
-
 
 
 === Statements
@@ -489,7 +486,7 @@
 Configure:  implemented
 Menuconfig: implemented
 XConfig:    implemented
-mconfig:    not implemented
+mconfig:    implemented
 
 Example:
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/kernel-parameters.txt linux/Documentation/kernel-parameters.txt
--- linux.orig/Documentation/kernel-parameters.txt	Wed Jun 20 18:21:33 2001
+++ linux/Documentation/kernel-parameters.txt	Wed Jan 23 20:55:51 2002
@@ -17,6 +17,7 @@
 	CD	Appropriate CD support is enabled.
 	DEVFS   devfs support is enabled. 
 	DRM	Direct Rendering Management support is enabled. 
+	EFI	EFI Partitioning (GPT) is enabled
 	EIDE	EIDE/ATAPI support is enabled.
 	FB	The frame buffer device is enabled.
 	HW	Appropriate hardware is enabled.
@@ -211,6 +212,9 @@
 	gc_3=		[HW,JOY]
  
 	gdth=		[HW,SCSI]
+
+	gpt             [EFI] Forces disk with valid GPT signature but
+			invalid Protective MBR to be treated as GPT.
 
 	gscd=		[HW,CD]
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/networking/8139too.txt linux/Documentation/networking/8139too.txt
--- linux.orig/Documentation/networking/8139too.txt	Fri Nov  9 21:45:35 2001
+++ linux/Documentation/networking/8139too.txt	Wed Feb  6 20:25:20 2002
@@ -96,7 +96,10 @@
 KTI KF-230TX
 KTI KF-230TX/2
 Lantech FastNet TX
+Ovislink Fast Ethernet
+Planet ENW-9504 (V.4) 10/100
 SMC EZNET 10/100
+UNEX NexNIC ND012C
 
 (please add your adapter model to this list)
 
@@ -181,11 +184,18 @@
 Change History
 --------------
 
+Version 0.9.23 - January 16, 2002
+
+* New, compile-time conditional for testing better RX reset
+* Only account specific RX errors if rx_status is !OK
+
+
 Version 0.9.22 - November 8, 2001
 
 * Additional retries before aborting Tx
 * Do not write other TxConfig bits when writing clear-abort bit.
 * Ack TxErr intr status after each Tx abort, too.
+* Fix oops in interface restart
 
 
 Version 0.9.21 - November 1, 2001
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/networking/dl2k.txt linux/Documentation/networking/dl2k.txt
--- linux.orig/Documentation/networking/dl2k.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/networking/dl2k.txt	Mon Jan 21 16:59:09 2002
@@ -1,7 +1,7 @@
 
     D-Link DL2000-based Gigabit Ethernet Adapter Installation
     for Linux
-    Nov 12, 2001
+    Jan 02, 2002
 
 Contents
 ========
@@ -182,7 +182,7 @@
 mtu=packet_size			- Specifies the maximum packet size. default
 				  is 1500.
 
-media=xxxxxxxxx			- Specifies the media type the NIC operates at.
+media=media_type		- Specifies the media type the NIC operates at.
 				  autosense	Autosensing active media.
 				  10mbps_hd	10Mbps half duplex.
 				  10mbps_fd	10Mbps full duplex.
@@ -195,28 +195,41 @@
 				  2		10Mbps full duplex.
 				  3		100Mbps half duplex.
 				  4		100Mbps full duplex.
-				  5          	1000Mbps full duplex.
-				  6          	1000Mbps half duplex.
+				  5          	1000Mbps half duplex.
+				  6          	1000Mbps full duplex.
 
 				  By default, the NIC operates at autosense.
 				  Note that only 1000mbps_fd and 1000mbps_hd
 				  types are available for fiber adapter.
 
-vlan=x				- Specifies the VLAN ID. If vlan=0, the
+vlan=[0|1]			- Specifies the VLAN ID. If vlan=0, the
 				  Virtual Local Area Network (VLAN) function is
 				  disable.
 
-jumbo=x				- Specifies the jumbo frame support. If jumbo=1,
+jumbo=[0|1]			- Specifies the jumbo frame support. If jumbo=1,
 				  the NIC accept jumbo frames. By default, this
 				  function is disabled.
 				  Jumbo frame usually improve the performance
 				  int gigabit.
 				  
-int_count			- Rx frame count each interrupt.
-int_timeout			- Rx DMA wait time for an interrupt. Proper 
-				  values of int_count and int_timeout bring 
-				  a conspicuous performance in the fast machine. 
-				  Ex. int_count=5 and int_timeout=750 
+rx_coalesce=n			- Rx frame count each interrupt.
+rx_timeout=n			- Rx DMA wait time for an interrupt. Proper 
+				  values of rx_coalesce and rx_timeout bring 
+				  a conspicuous performance in the fast machine.
+				  Ex. rx_coalesce=5 and rx_timeout=750 
+
+tx_coalesce=n			- Tx transmit count each TxComp interrupt.
+				  Setting value larger than 1 will improve 
+				  performance, but this is possible to lower 
+				  stability in slow UP machines. By default, 
+				  tx_coalesce=1. (dl2k)
+				  
+tx_flow=[1|0]			- Specifies the Tx flow control. If tx_flow=1, 
+				  the Tx flow control enable.
+				  
+rx_flow=[1|0]			- Specifies the Rx flow control. If rx_flow=1, 
+				  the Rx flow control enable.
+
 
 Configuration Script Sample
 ===========================
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/networking/ip-sysctl.txt linux/Documentation/networking/ip-sysctl.txt
--- linux.orig/Documentation/networking/ip-sysctl.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/networking/ip-sysctl.txt	Mon Jan 14 16:24:07 2002
@@ -309,13 +309,20 @@
 	ICMP ECHO requests sent to it or just those to broadcast/multicast
 	addresses, respectively.
 
-icmp_destunreach_rate - INTEGER
-icmp_paramprob_rate - INTEGER
-icmp_timeexceed_rate - INTEGER
-icmp_echoreply_rate - INTEGER (not enabled per default)
-	Limit the maximal rates for sending ICMP packets to specific targets.
+icmp_ratelimit - INTEGER
+	Limit the maximal rates for sending ICMP packets whose type matches
+	icmp_ratemask (see below) to specific targets.
 	0 to disable any limiting, otherwise the maximal rate in jiffies(1)
-	See the source for more information.
+	Default: 1
+
+icmp_ratemask - INTEGER
+	Mask made of ICMP types for which rates are being limited.
+	Default: 6168
+	Note: 6168 = 0x1818 = 1<<ICMP_DEST_UNREACH + 1<<ICMP_SOURCE_QUENCH +
+	      1<<ICMP_TIME_EXCEEDED + 1<<ICMP_PARAMETERPROB, which means
+	      dest unreachable (3), source quench (4), time exceeded (11)
+	      and parameter problem (12) ICMP packets are rate limited
+	      (check values in icmp.h)
 
 icmp_ignore_bogus_error_responses - BOOLEAN
 	Some routers violate RFC 1122 by sending bogus responses to broadcast
@@ -511,4 +518,4 @@
 Pekka Savola
 pekkas@netcore.fi
 
-$Id: ip-sysctl.txt,v 1.19 2001/05/16 17:11:04 davem Exp $
+$Id: ip-sysctl.txt,v 1.19.2.1 2001/12/13 08:59:27 davem Exp $
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/s390/Debugging390.txt linux/Documentation/s390/Debugging390.txt
--- linux.orig/Documentation/s390/Debugging390.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/s390/Debugging390.txt	Fri Dec 21 16:25:30 2001
@@ -237,9 +237,10 @@
 
 
 On 390 our limitations & strengths make us slightly different.
-For backward compatibility we are only allowed use 31 bits (2GB)
-of our 32 bit addresses,however, we use entirely separate address 
-spaces for the user & kernel.
+For backward compatibility ( because of the psw address hi bit which
+indicates whether we are in 31 or 64 bit mode ) we are only allowed 
+use 31 bits (2GB) of our 32 bit addresses. However, 
+we use entirely separate address  spaces for the user & kernel.
 
 This means we can support 2GB of non Extended RAM on s/390, & more
 with the Extended memory managment swap device & 
@@ -2123,6 +2124,12 @@
 now do
 p/x (*(**$sp+56))&0x7fffffff
 & so on.
+
+Another good trick to look at addresses on the stack if you've somehow lost 
+the backchain is.
+x/500xa $sp
+This displays anything the name of any known functions above the stack pointer
+for 500 bytes.
 
 Disassembling instructions without debug info
 ---------------------------------------------
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/scsi-generic.txt linux/Documentation/scsi-generic.txt
--- linux.orig/Documentation/scsi-generic.txt	Tue May  1 23:04:56 2001
+++ linux/Documentation/scsi-generic.txt	Mon Feb  4 18:11:15 2002
@@ -1,793 +1,101 @@
-            Notes on Linux's SG driver version 2.1.39
-            -----------------------------------------
-                                                        20010329
-
+            Notes on Linux SCSI Generic (sg) driver
+            ---------------------------------------
+                                                        20020126
 Introduction
 ============
 The SCSI Generic driver (sg) is one of the four "high level" SCSI device
 drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg
 is more generalized (but lower level) than its siblings and tends to be
 used on SCSI devices that don't fit into the already serviced categories.
-Thus sg is used for scanners, cd writers and reading audio cds digitally
+Thus sg is used for scanners, CD writers and reading audio CDs digitally
 amongst other things.
 
-These are notes on the Linux SCSI generic packet device driver (sg)
-describing version 2.1.39 . The original driver was written by Lawrence
-Foard and remained in place with minimal changes since circa 1992.
-Version 2 of this driver remains backward compatible (binary and
-source **) with the original. It adds scatter gather, command queuing,
-per file descriptor sequencing, asynchronous notification and better
-error reporting.
-
-This is an abridged version of the sg documentation that is targeted
-at the linux/Documentation directory. The full document can be found
-at http://www.torque.net/sg/p/scsi-generic_long.txt .
-
-The Linux 2.4 series kernels have now been released. Lk 2.4 contains
-an upgraded "version 3" sg driver which is described in a supplementary
-document at http://www.torque.net/sg/p/scsi-generic_v3.txt .
-
-The interface and usage of the original sg driver have been documented
-by Heiko Eissfeldt in a HOWTO called SCSI-Programming-HOWTO. My copy
-of the document is version 1.5 dated 7th May 1996. It can found at
-ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO-SCSI-Programming-HOWTO .
-A copy of this document can be found at:
-http://www.torque.net/sg/p/original/HOWTO-SCSI-Programming-HOWTO.txt .
-
-** It is possible to write applications that perform differently
-depending on whether they are using the original or this version of
-the sg device driver. The author is not aware of any useful
-pre-existing applications that have problems with version 2.
-
-
-Architecture
-============
-The SCSI generic packet device driver (sg) is a character based device.
-It is one of the four high level device driver in the SCSI sub-system;
-the others are sd (for direct-access devices - disks), st (for tapes)
-and sr (for data cdroms). Sd and sr are block devices while st (like sg)
-is a character device.
-
-The unifying layer of the SCSI sub-system is the so-called mid-level.
-Below that are the "low level" drivers which are the drivers for the
-various adapters supported by Linux. Also at this level are pseudo
-adapter drivers such as ide-scsi which converts the SCSI protocol to
-ATAPI (which are similar to one another) for use by IDE devices.
-
-Since sg is a character device it supports the traditional Unix
-system calls of open(), close(), read(), write() and ioctl(). Two other
-related system calls: poll() and fcntl() are added to this list and
-how they interact with the sg device driver is documented later.
-
-An SG device is accessed by write()ing SCSI commands plus any associated
-outgoing data to it; the resulting status codes and any incoming data are
-then obtained by a read() call. The device can be opened O_NONBLOCK
-(non-blocking) and poll() used to monitor its progress. The device may be
-opened O_EXCL which excludes other "sg" users from this device (but not
-"sd", "st" or "sr" users). The buffer given to the write() call is made
-up as follows:
-        - struct sg_header image (see below)
-        - scsi command (6, 10 or 12 bytes long)
-        - data to be written to the device (if any)
-
-The buffer received from the corresponding read() call contains:
-        - struct sg_header image (check status/errors + sense_buffer)
-        - data read back from device (if any)
-
-The given SCSI command has its LUN field overwritten by the LUN value of
-the associated sg device that has been open()ed.
-
-SCSI commands are only attempted once (i.e. there are no internal
-retries). If appropriate (e.g. a SCSI READ) the data buffer is copied back
-to user space irrespective of the values of the various SCSI related
-error/status codes. [Some adapters that use an old error interface in
-the SCSI mid level ignore the retry count and retry certain errors.]
-
-
-sg_header
-=========
-This is the name of the control structure that conveys information
-about the length of data to be read/written by the associated SCSI
-command. It also conveys error and status information from the
-read() call. An instance of this structure is the first thing that
-is placed in the data buffers of both write() and read().
-
-In its original form it looked like this:
-struct sg_header {
-    int pack_len;
-    int reply_len;
-    int pack_id;
-    int result;
-    unsigned int twelve_byte:1;
-    unsigned int other_flags:31;
-    unsigned char sense_buffer[16];
-}; /* this structure is 36 bytes long */
-
-The 'pack_len' is bizarre and ends up having the 'reply_len' put in it
-(perhaps it had a use at some stage). Even though it looks like an
-input variable, it is not read by sg internally (only written).
-
-The 'reply_len' is the length of the data the corresponding read()
-will/should request (including the sg_header).
-
-The 'pack_id' is not acted upon by the sg device driver but is conveyed
-back to the corresponding read() so it can be used for sequencing by an
-application.
-
-The 'result' is also bizarre, turning certain types of host codes to 0 (no
-error), EBUSY or EIO. With better error reporting now available, the
-'result' is best ignored.
-
-The 'twelve_byte' field overrides the internal SCSI command length detection
-algorithm for group 6 and 7 commands (ie when 1st byte >= 0xc0) and forces
-a command length of 12 bytes.
-The command length detection algorithm is as follows:
-Group:  0    1    2    3    4    5    6    7
-Length: 6   10   10   12   12   12   10   10
-
-'other_flags' was originally documented as "not used" but some current
-applications assume it has 0 placed in it.
-
-The 'sense_buffer' is the first 16 bytes of SCSI sense buffer that is
-returned when the target returns a SCSI status code of CHECK_CONDITION
-or COMMAND_TERMINATED [or (driver_status & DRIVER_SENSE) is true]. This
-buffer should be at least 18 bytes long and arguably 32 bytes; unfortunately
-this is unlikely to happen in the 2.2.x series of kernels.
-
-
-The new sg_header offered in this driver is:
-#define SG_MAX_SENSE 16
-struct sg_header
-{
-    int pack_len;    /* [o] reply_len (ie useless) ignored as input */
-    int reply_len;   /* [i] max length of expected reply (inc. sg_header) */
-    int pack_id;     /* [io] id number of packet (use ints >= 0) */
-    int result;      /* [o] 0==ok, else (+ve) Unix errno (best ignored) */
-    unsigned int twelve_byte:1;
-        /* [i] Force 12 byte command length for group 6 & 7 commands  */
-    unsigned int target_status:5;   /* [o] scsi status from target */
-    unsigned int host_status:8;     /* [o] host status (see "DID" codes) */
-    unsigned int driver_status:8;   /* [o] driver status+suggestion */
-    unsigned int other_flags:10;    /* unused */
-    unsigned char sense_buffer[SG_MAX_SENSE]; /* [o] Output in 3 cases:
-           when target_status is CHECK_CONDITION or
-           when target_status is COMMAND_TERMINATED or
-           when (driver_status & DRIVER_SENSE) is true. */
-};      /* This structure is 36 bytes long on i386 */
-
-Firstly the new header is binary compatible with the original. This is
-important for keeping existing apps working without recompilation.
-
-Only those elements (or fields) that are new or in some way different
-from the original are documented below.
-
-'pack_id' becomes input to a read() when ioctl(sg_fd, SG_SET_FORCE_PACK_ID,
-&one) is active. A 'pack_id' of -1 is interpreted as fetch the oldest
-waiting packet; any other value will cause the read() to wait (or yield
-EAGAIN) until a packet with that 'pack_id' becomes available. In all cases
-the value of 'pack_id' available after a read() is the value given to that
-variable in the prior, corresponding write().
-
-The SCSI command length can now be given directly using the SG_NEXT_CMD_LEN
-ioctl().
-
-The 'target_status' field is always output and is the (masked and shifted
-1 bit right) SCSI status code from the target device. The allowable
-values are (found in <scsi/scsi.h>):
-/* N.B. 1 bit offset from usual SCSI status values */
-#define GOOD                 0x00
-#define CHECK_CONDITION      0x01
-#define CONDITION_GOOD       0x02
-#define BUSY                 0x04
-#define INTERMEDIATE_GOOD    0x08
-#define INTERMEDIATE_C_GOOD  0x0a
-#define RESERVATION_CONFLICT 0x0c
-#define COMMAND_TERMINATED   0x11
-#define QUEUE_FULL           0x14
-When the 'target_status' is CHECK_CONDITION or COMMAND_TERMINATED the
-'sense_buffer' is output. Note that when (driver_status & DRIVER_SENSE)
-is true then the 'sense_buffer' is also output (this seems to occur when
-the ide-scsi emulation is used). When the 'sense_buffer' is output the
-SCSI Sense Key can be found at (sense_buffer[2] & 0x0f) .
-
-The 'host_status' field is always output and has the following values
-whose "defines" are not visible outside the kernel. A copy of these
-defines can be found in sg_err.h (see the utilities section):
-#define DID_OK          0x00 /* NO error                                */
-#define DID_NO_CONNECT  0x01 /* Couldn't connect before timeout period  */
-#define DID_BUS_BUSY    0x02 /* BUS stayed busy through time out period */
-#define DID_TIME_OUT    0x03 /* TIMED OUT for other reason              */
-#define DID_BAD_TARGET  0x04 /* BAD target, device not responding?      */
-#define DID_ABORT       0x05 /* Told to abort for some other reason     */
-#define DID_PARITY      0x06 /* Parity error                            */
-#define DID_ERROR       0x07 /* Internal error [DMA underrun on aic7xxx]*/
-#define DID_RESET       0x08 /* Reset by somebody.                      */
-#define DID_BAD_INTR    0x09 /* Got an interrupt we weren't expecting.  */
-#define DID_PASSTHROUGH 0x0a /* Force command past mid-layer            */
-#define DID_SOFT_ERROR  0x0b /* The low level driver wants a retry      */
-
-The 'driver_status' field is always output. When ('driver_status' &
-DRIVER_SENSE) is true the 'sense_buffer' is also output. A copy of these
-defines can be found in sg_err.h (see the utilities section):
-#define DRIVER_OK           0x00 /* Typically no suggestion */
-#define DRIVER_BUSY         0x01
-#define DRIVER_SOFT         0x02
-#define DRIVER_MEDIA        0x03
-#define DRIVER_ERROR        0x04
-#define DRIVER_INVALID      0x05
-#define DRIVER_TIMEOUT      0x06
-#define DRIVER_HARD         0x07
-#define DRIVER_SENSE        0x08 /* Implies sense_buffer output */
-/* above status 'or'ed with one of the following suggestions */
-#define SUGGEST_RETRY       0x10
-#define SUGGEST_ABORT       0x20
-#define SUGGEST_REMAP       0x30
-#define SUGGEST_DIE         0x40
-#define SUGGEST_SENSE       0x80
-
-'other_flags' still remains as a 10 bit field (reduced from 31 bits), so
-code that places 0 in it will still be happy. It is not used.
-
-
-System Calls
-============
-What follows are descriptions of the characteristics of the standard
-Unix operating system calls when applied to a SCSI generic device
-using this version of the device driver.
-
-open(const char * filename, int flags)
---------------------------------------
-The filename should be an 'sg' device such as
-/dev/sg[0,1,2,...]
-/dev/sg[a-z]             <<< now deprecated >>>
-or a symbolic link to one of these. [Devfs has its own sub-directory for
-sg devices with entries like: /dev/scsi/host1/bus2/target3/lun4/generic .]
-It seems as though SCSI devices are allocated to sg minor numbers in the
-same order as they appear in 'cat /proc/scsi/scsi'. Sg is a "character"
-based Linux device driver.  This means it has an open/close/read/write/ioctl
-type interface.
-
-Flags can be either O_RDONLY or O_RDWR or-ed with either
-O_EXCL          waits for other opens on sg device to be closed before
-                proceeding. If O_NONBLOCK is set then yields EBUSY when
-                someone else has the sg device open. The combination of
-                O_RDONLY and O_EXCL is disallowed.
-O_NONBLOCK      Sets non-blocking mode. Calls that would otherwise block
-                yield EAGAIN (eg read() ) or EBUSY (eg open() ).
-
-The original version of sg did not allow the O_RDONLY (yielding a EACCES
-error). This version allows it for accessing ioctls (e.g. doing an sg
-device scan with the SG_GET_SCSI_ID ioctl) but write()s will not be
-allowed. These flags are found in <fcntl.h> .
-
-By default, sequencing is per file descriptor in this version of sg. This
-means, for example that 2 processes can independently manipulate the same
-sg device at the same time. This may or may not make sense depending on
-the application: 2 processes (logically) reading from the same direct access
-device (ie a disk) is ok while running 2 instances of cd writing software
-on the same device at the same time probably wouldn't be a good idea. The
-previous version of sg supported only per device sequencing and this can
-still be selected with the SG_SET_MERGE_FD,1 ioctl().
-
-The driver will attempt to reserve SG_DEF_RESERVED_SIZE bytes (32KBytes in
-the current sg.h) on open(). The size of this reserved buffer can
-subsequently be modified with the SG_SET_RESERVED_SIZE ioctl(). In both
-cases these are requests subject to various dynamic constraints. The actual
-amount of memory obtained can be found by the SG_GET_RESERVED_SIZE ioctl().
-The reserved buffer will be used if:
-    -  it is not already in use (eg when command queuing is in use)
-    -  a write() does not call for a buffer size larger than the
-       reserved size.
-
-Returns a file descriptor if >= 0 , otherwise -1 implies an error.
-
-Error codes (value in 'errno' after -1 returned):
-EACCES          Either the user doesn't have appropriate permissions on
-                'filename' or attempted to use both O_RDONLY and O_EXCL
-EBUSY           O_NONBLOCK set and some user of this sg device has O_EXCL
-                set while someone is already using this device
-EINTR           while waiting for an "exclusive" lock to clear, a signal
-                is received, just try again ...
-ENODEV          sg not compiled into kernel or the kernel cannot find the
-                sg module (or it can't initialize itself (low memory??))
-ENOENT          given filename not found
-ENOMEM          An attempt to get memory to store this open's context
-                failed (this was _not_ a request to reserve DMA memory)
-ENXIO           either there is no attached device corresponding to given
-                filename or scsi sub-system is currently processing some
-                error (eg doing a device reset) or the sg driver/module
-                removed or corrupted
-
-
-write(int sg_fd, const void * buffer, size_t count)
----------------------------------------------------
-Even though sg is a character-based device driver it sends and receives
-packets to/from the associated scsi device. Write() is used to send a
-packet containing 2 mandatory parts and 1 optional part. The mandatory
-parts are:
-  - a control block (an instance of struct sg_header)
-  - a SCSI command (6, 10 or 12 bytes long)
-The optional part is:
-  - outgoing data (eg if a SCSI write command is being sent)
-These should appear as one contiguous string in the buffer given to
-write() in the above order with no pad characters.
-
-If a write() accepts this packet then at some later time the user should
-call a read() to get the result of the SCSI command. The previous sg
-driver enforced a strict write()/read()/write()/read() regime so that a
-second write() would block until first read() was finished. This sg
-driver relaxes that condition and thereby allows command queuing
-(limit is SG_MAX_QUEUE (16) outstanding packets per file descriptor).
-However, for backward compatibility, command queuing is turned off
-by default (#define SG_DEF_COMMAND_Q 0 in sg.h). This can be changed
-via the SG_SET_COMMAND_Q ioctl() [or by recompiling after changing
-the above define to 1].
-
-In this sg driver a write() should return more or less immediately.
-
-Returns number of bytes written if > 0 , otherwise -1 implies an error.
-
-Error codes (value in 'errno' after -1 returned):
-EACCES          opened with RD_ONLY flag
-EAGAIN          SCSI mid-level out of command blocks (rare), try again.
-                This is more likely to happen when queuing commands,
-                so wait a bit (eg usleep(10000) ) before trying again
-EDOM            a) command queuing off: a packet is already queued
-                b) command queuing on: too many packets queued
-                   (SG_MAX_QUEUE exceeded)
-EFAULT          'buffer' for 'count' bytes is an invalid memory range
-EIO             a) incoming buffer too short. It should be at least
-                   (6 + sizeof(struct sg_header))==42 bytes long
-                b) SCSI command length given in SG_NEXT_CMD_LEN too long
-                c) reply_len negative
-ENOMEM          can't get memory for DMA. Take evasive action ...
-ENXIO           either scsi sub-system is currently processing some error
-                (eg doing a device reset) or the sg driver/module removed
-                or corrupted
-
-
-read(int sg_fd, void * buffer, size_t count)
---------------------------------------------
-Read() is used to receive a packet containing 1 mandatory part and 1
-optional part. The mandatory part is:
-  - a control block (an instance of struct sg_header)
-The optional part is:
-  - incoming data (eg if a SCSI read command was sent by earlier write() )
-The buffer given to a read() and its corresponding count should be
-sufficient to accommodate this packet to avoid truncation. Truncation occurs
-if count < sg_header::replylen .
-
-By default, read() will return the oldest packet queued up. If the
-SG_SET_FORCE_PACK_ID,1 ioctl() is active then read() will attempt to
-fetch the packet whose pack_id (given earlier to write()) matches the
-sg_header::pack_id given to this read(). If not available it will either
-wait or yield EAGAIN. As a special case, -1 in sg_header::pack_id given
-to read() will match the oldest packet.
-
-Returns number of bytes read if > 0 , otherwise -1 implies an error.
-Unfortunately the return value in the non-error case is simply the
-same as the count argument. It is not the actual number of bytes
-DMA-ed by the SCSI device. This driver is currently unable to provide
-such an underrun indication.
-
-If the SCSI device reports an error then a REQUEST SENSE is automatically
-done and the output is placed in the sense_buffer array which is in the
-control block. This action is sometimes called "auto-sense".
-
-Error codes (value in 'errno' after -1 returned):
-EAGAIN          either no waiting packet or requested packet is not
-                available while O_NONBLOCK flag was set
-EFAULT          'buffer' for 'count' bytes is an invalid memory range
-EINTR           while waiting for a packet, a signal is received, just
-                try again ...
-EIO             if the 'count' given to read() is < sizeof(struct sg_header)
-                and the 'result' element in sg_header is non-zero. Not a
-                recommended error reporting technique
-ENXIO           either scsi sub-system is currently processing some error
-                (eg doing a device reset) or the sg driver/module removed
-                or corrupted
-
-
-close(int sg_fd)
-----------------
-Preferably a close() should be done after all issued write()s have had
-their corresponding read() calls completed. Unfortunately this is not
-always possible. The semantics of close() in Unix are to return more
-or less immediately (ie not wait on any event) so the driver needs to
-arrange for an orderly cleanup of those packets that are still "in
-flight".
-
-A process that has an open file descriptor to an sg device may be aborted
-(eg by a kill signal). In this case, the kernel automatically calls close
-(which is called 'sg_release()' in the version 2 driver) to facilitate
-the cleanup mentioned above.
-
-A problem persists in version 2.1.39 if the sg driver is a module and is
-removed while packets are still "in flight".
-
-Returns 0 if successful, otherwise -1 implies an error.
-
-Error codes (value in 'errno' after -1 returned):
-ENXIO           sg driver/module removed or corrupted
-
-ioctl(int sg_fd, int command, ...)  [sg specific]
--------------------------------------------------
-Ken Thompson (or perhaps some other Unix luminary) described ioctl() as
-the "garbage bin of Unix". This driver compounds the situation by adding
-more ...
-If a ioctl command is not recognized by sg (and the various lower levels
-that it may pass the command on to) then the error EINVAL occurs. If an
-invalid address is given (in the 3rd argument) then the error EFAULT occurs.
-
-Those commands with an appended "+" are new in version 2.
-
-Those commands with an appended "W" are only accessible from file
-descriptors opened with O_RDWR. They will yield EACCES otherwise.
-
-SG_GET_TIMEOUT:
-Ignores its 3rd argument and _returns_ the timeout value (which will be
->= 0 ). The unit of this timeout is "jiffies" which are currently 10
-millisecond intervals on i386 (less on an alpha). Linux supplies
-a manifest constant HZ which is the number of "jiffies" in 1 second.
-
-SG_SET_TIMEOUT:
-Assumes 3rd argument points to an int containing the new timeout value
-for this file descriptor. The unit is a "jiffy". Packets that are
-already "in flight" will not be affected. The default value is set
-on open() and is SG_DEFAULT_TIMEOUT (defined in sg.h). This default is
-currently 1 minute and may not be long enough for formats. Negative
-values will yield an EIO error.
-
-SG_EMULATED_HOST:
-Assumes 3rd argument points to an int and outputs a flag indicating
-whether the host (adapter) is connected to a real SCSI bus or is an
-emulated one (eg ide-scsi device driver). A value of 1 means emulated
-while 0 is not.
-
-SG_SET_TRANSFORM  W:
-Only is meaningful when SG_EMULATED host has yielded 1 (i.e. the low-level
-is the ide-scsi device driver); otherwise an EINVAL error occurs. The
-default state is to _not_ transform SCSI commands to the corresponding
-ATAPI commands but pass them straight through as is. [Only certain classes
-of SCSI commands need to be transformed to their ATAPI equivalents.]
-The third argument is interpreted as an integer. When it is non-zero then
-a flag is set inside the ide-scsi driver that transforms subsequent
-commands sent to this driver. When zero is passed as the 3rd argument to
-this ioctl then the flag within the ide-scsi driver is cleared and
-subsequent commands are not transformed. Beware, this state will affect
-all devices (and hence all related sg file descriptors) associated with
-this ide-scsi "bus".
-
-SG_GET_TRANSFORM:
-Third argument is ignored. Only is meaningful when SG_EMULATED host has
-yielded 1 (ie the low-level is the ide-scsi device driver); otherwise
-an EINVAL error occurs. Returns 0 to indicate _not_ transforming SCSI
-to ATAPI commands (default). Returns 1 when it is transforming them.
-
-SG_SET_FORCE_LOW_DMA +:
-Assumes 3rd argument points to an int containing 0 or 1. 0 (default)
-means sg decides whether to use memory above 16 Mbyte level (on i386)
-based on the host adapter being used by this SCSI device. Typically
-PCI SCSI adapters will indicate they can DMA to the whole 32 bit address
-space.
-If 1 is given then the host adapter is overridden and only memory below
-the 16MB level is used for DMA. A requirement for this should be
-extremely rare. If the "reserved" buffer allocated on open() is not in
-use then it will be de-allocated and re-allocated under the 16MB level
-(and the latter operation could fail yielding ENOMEM).
-Only the current file descriptor is affected.
-
-SG_GET_LOW_DMA +:
-Assumes 3rd argument points to an int and places 0 or 1 in it. 0
-indicates the whole 32 bit address space is being used for DMA transfers
-on this file descriptor. 1 indicates the memory below the 16MB level
-(on i386) is being used (and this may be the case because the host
-adapters setting has been overridden by SG_SET_FORCE_LOW_DMA,1 .
-
-SG_GET_SCSI_ID +:
-Assumes 3rd argument is pointing to an object of type Sg_scsi_id (see
-sg.h) and populates it. That structure contains ints for host_no,
-channel, scsi_id, lun, scsi_type, allowable commands per lun and
-queue_depth. Most of this information is available from other sources
-(eg SCSI_IOCTL_GET_IDLUN and SCSI_IOCTL_GET_BUS_NUMBER) but tends to be
-awkward to collect.
-Allowable commands per lun and queue_depth give an insight to the
-command queuing capabilities of the adapters and the device. The latter
-overrides the former (logically) and the former is only of interest
-if it is equal to queue_depth which probably indicates the device
-does not support queueing commands (e.g. most scanners).
-
-SG_SET_FORCE_PACK_ID +:
-Assumes 3rd argument is pointing to an int. 0 (default) instructs read()
-to return the oldest (written) packet if multiple packets are
-waiting to be read (when command queuing is being used).
-1 instructs read() to view the sg_header::pack_id as input and return the
-oldest packet matching that pack_id or wait until it arrives (or yield
-EAGAIN if O_NONBLOCK is in force). As a special case the pack_id of -1
-given to read() in the mode will match the oldest packet.
-Only the current file descriptor is affected by this command.
-
-SG_GET_PACK_ID +:
-Assumes 3rd argument points to an int and places the pack_id of the
-oldest (written) packet in it. If no packet is waiting to be read then
-yields -1.
-
-SG_GET_NUM_WAITING +:
-Assumes 3rd argument points to an int and places the number of packets
-waiting to be read in it.
-
-SG_GET_SG_TABLESIZE +:
-Assumes 3rd argument points to an int and places the maximum number of
-scatter gather elements supported by the host adapter. 0 indicates that
-the adapter does support scatter gather.
-
-SG_SET_RESERVED_SIZE +W:
-Assumes 3rd argument is pointing to an int. That value will be used to
-request a new reserved buffer of that size. The previous reserved buffer
-is freed (if it is not in use; if it was in use -EBUSY is returned).
-A new reserved buffer is then allocated and its actual size can be found by
-calling the SG_GET_RESERVED_SIZE ioctl(). The reserved buffer is then used
-for DMA purposes by subsequent write() commands if it is not already in
-use and if the write() is not calling for a buffer size larger than that
-reserved. The reserved buffer may well be a series of kernel buffers if the
-adapter supports scatter-gather. Large buffers can be requested (eg 1 MB).
-
-SG_GET_RESERVED_SIZE +:
-Assumes 3rd argument points to an int and places the size in bytes of
-the reserved buffer from open() or the most recent SG_SET_RESERVED_SIZE
-ioctl() call on this fd.  The result can be 0 if memory is very tight. In
-this case it may not be wise to attempt something like burning a CD on
-this file descriptor.
-
-SG_SET_MERGE_FD +W:
-Assumes 3rd argument is pointing to an int. 0 (the default) causes all
-subsequent sequencing to be per file descriptor. 1 causes all subsequent
-sequencing to be per device. If this command tries to change the current
-state and there is one or more _other_ file descriptors using this sg
-device then an EBUSY error occurs. Per device sequencing was the original
-semantics and allowed, for example different processes to "share" the
-device, one perhaps write()ing with the other one read()ing. This command
-is supplied if anyone needs those semantics. Per file descriptor
-sequencing, perhaps with the use of the O_EXCL flag, seems more sensible.
-
-SG_GET_MERGE_FD +:
-Assumes 3rd argument points to an int and places 0 or 1 in it. 0 implies
-sequencing is per file descriptor. 1 implies sequencing is per device
-(original sg driver's semantics).
-
-SG_SET_COMMAND_Q +:
-Assumes 3rd argument is pointing to an int. 0 (current default, set by
-SG_DEF_COMMAND_Q in sg.h) disables command queuing. Attempts to write()
-a packet while one is already queued will result in a EDOM error.
-1 turns command queuing on.
-Changing the queuing state only affects write()s done after the change.
-Only the current file descriptor is affected by this command.
-
-SG_GET_COMMAND_Q +:
-Assumes 3rd argument points to an int and places 0 or 1 in it. 0 implies
-that command queuing is off on this file descriptor. 1 implies command
-queuing is on.
-
-SG_SET_UNDERRUN_FLAG +:
-Assumes 3rd argument is pointing to an int. 0 (current default, set by
-SG_DEF_UNDERRUN_FLAG in sg.h) requests underruns be ignored. 1 requests
-that underruns be flagged. [The only low level driver that acts on this
-at the moment is the aic7xxx which yields a DID_ERROR error on underrun.]
-Only the current file descriptor is affected by this command (unless
-"per device" sequencing has been selected).
-
-SG_GET_UNDERRUN_FLAG +:
-Assumes 3rd argument points to an int and places 0 or 1 in it. 0 implies
-that underruns are not being reported. 1 implies that underruns are being
-reported (see SG_SET_UNDERRUN_FLAG for more details).
-
-SG_NEXT_CMD_LEN +:
-Assumes 3rd argument is pointing to an int. The value of the int (if > 0)
-will be used as the SCSI command length of the next SCSI command sent to
-a write() on this fd. After that write() the SCSI command length logic is
-reset to use automatic length detection (ie depending on SCSI command group
-and the 'twelve_byte' field). If the current SCSI command length maximum of
-12 is exceeded then the affected write() will yield an EDOM error.
-Giving this ioctl() a value of 0 will set automatic length detection for
-the next write(). N.B. Only the following write() on this fd is affected by
-this ioctl().
-
-SG_GET_VERSION_NUM +:
-Assumes 3rd argument points to an int. The version number is then placed
-in that int. A sg version such as 2.1.36 will yield "20136" from this ioctl.
-
-SG_SCSI_RESET +:
-Assumes 3rd argument points to an int. Unfortunately doesn't currently
-do much (may in the future after other issues are resolved). Yields an
-EBUSY error if the SCSI bus or the associated device is being reset
-when this ioctl() is called, otherwise returns 0.
-N.B. In some recent distributions there is a patch to the SCSI mid level
-code that activates this ioctl. Check your distribution.
-
-SG_SET_DEBUG +:
-Assumes 3rd argument is pointing to an int. 0 (default) turns debugging
-off. Values > 0 cause the SCSI sense buffer to be decoded and output
-to the console/log when a SCSI device error occurs. Values > 8 cause
-the current sg device driver's state to be output to the console/log
-(this is a "one off" effect).
-If you need a _lot_ of the SCSI sub-system debug information (mainly from
-the mid-level) then try 'echo "scsi dump 0" > /proc/scsi/scsi' and lots of
-debug will appear in your console/log.
-
-
-poll(struct pollfd * udfds, unsigned int nfds, int timeout_ms)
---------------------------------------------------------------
-This is a native call in Linux 2.2 but most of its capabilities are available
-through the older select() call. Given a choice poll() should probably be
-used. Typically poll() is used when a sg scsi device is open()ed O_NONBLOCK
-for polling; and optionally with asynchronous notification as well using
-the fcntl() system call (below) and the SIGPOLL (aka SIGIO) signal.
-Only if something drastically is wrong (eg file handle gone stale) will
-POLLERR ever be set. POLLPRI, POLLHUP and POLLNVAL are never set.
-POLLIN is set when there is one or more packets waiting to be read.
-When POLLIN is set it implies that a read() will not block (nor yield
-EAGAIN in non-blocking mode) but return a packet immediately.
-POLLOUT (aka POLLWRNORM) is set when write() is able to accept a packet
-(ie will _not_ yield an EDOM error). The setting of POLLOUT is affected
-by the SG_SET_COMMAND_Q state: if the state is on then POLLOUT will remain
-set until the number of queued packets reaches SG_MAX_QUEUE, if the
-state is off then POLLOUT is only set when no packets are queued.
-Note that a packet can be queued after write()ing but not available to be
-read(); this typically happens when a SCSI read command is issued while
-the data is being retrieved.
-Poll() is per file descriptor unless SG_SET_MERGE_FD is set in which case
-it is per device.
-
-
-fcntl(int sg_fd, int cmd) or fcntl(int sg_fd, int cmd, long arg)
-----------------------------------------------------------------
-There are several uses for this system call in association with a sg
-file descriptor. The following pseudo code shows code that is useful for
-scanning the sg devices, taking care not to be caught in a wait for
-an O_EXCL lock by another process, and when the appropriate device is
-found, switching to normal blocked io. A working example of this logic
-is in the sg_scan utility program.
-
-open("/dev/sg0", O_RDONLY | O_NONBLOCK)
-/* check device, EBUSY means some other process has O_EXCL lock on it */
-/* when the device you want is found then ... */
-flags = fcntl(sg_fd, F_GETFL)
-fcntl(sg_fd, F_SETFL, flags & (~ O_NONBLOCK))
-/* since, for simple apps, it is easier to use normal blocked io */
-
-
-Some work has to be done in Linux to set up for asynchronous notification.
-This is a non-blocking mode of operation in which, when the driver receives
-data back from a device so that a read() can be done, it sends a SIGPOLL
-(aka SIGIO) signal to the owning process. A working example of this logic
-is in the sg_poll test program.
-
-sigemptyset(&sig_set)
-sigaddset(&sig_set, SIGPOLL)
-sigaction(SIGPOLL, &s_action, 0)
-fcntl(sg_fd, F_SETOWN, getpid())
-flags = fcntl(sg_fd, F_GETFL);
-fcntl(sg_fd, F_SETFL, flags | O_ASYNC)
-
-
-Utility and Test Programs
-=========================
-See the README file in the sg_utils<date>.tgz tarball. Look on the
-http://www.torque.net/sg website for the latest version.
-
-Briefly, that tarball contains the following utilities:
-sg_dd512        'dd' like program that assumes 512 byte blocks size
-sg_dd2048       'dd' like program that assumes 2048 byte blocks size
-sg_dd2352       'dd' like program that assumes 2352 byte blocks size
-sgq_dd512       like 'sg_dd512' but does command queuing on "if"
-sgp_dd          probably the most flexible 'dd' variant. It uses POSIX
-                threads, block size set by "bs=..." plus other options.
-sg_scan         outputs information (optionally Inquiry) on SCSI devices
-sg_rbuf         tests SCSI bus transfer speed (without physical IO)
-sg_whoami       outputs info (optionally capacity) of given SCSI device
-sginfo          outputs "mode" information about SCSI devices (it is a
-                  re-port of the scsiinfo program onto the sg interface)
-
-It also contains the following test programs:
-sg_debug        outputs sg driver state to console/log file
-sg_poll         tests asynchronous notification
-sg_runt_ex      example run time selection program for application authors
-sg_simple1      example program first time users
-sg_simple2      like sg_simple1 but with more primitive error processing
-sg_inquiry      does a SCSI Inquiry command (from original HOWTO)
-sg_tst_med      checks presence of media (from original HOWTO)
-
-There are also 2 source files (sg_err.[hc]) for outputting and categorizing
-SCSI 2 errors and warnings. This code is used by most of the above
-utility and test programs.
-
-The following programs: sg_dd512, sg_dd2048, sg_dd2352, sg_scan, sg_runt_ex,
-sg_rbuf, sg_tst_med, sg_inquiry and sginfo, can be compiled either for this
-new sg driver _or_ the original sg driver (in 2.0 or 2.2 series kernels).
-sg_runt_ex can be run on 2.0, 2.2 or 2.3 series kernels even if it is
-compiled on a different series (eg compiled on 2.0, run on 2.2).
-
-
-Header files
-============
-User applications need to find the correct "sg.h" header file matching
-their kernel in order to write code using the sg device driver. This is
-sometimes more difficult than it should be. The correct "sg.h" will usually
-be found at /usr/src/linux/include/scsi/sg.h . Another important header
-file is "scsi.h" which will be in the same directory.
-
-When "#include <scsi/sg.h>" is written in an application then this refers
-to the file /usr/include/scsi/sg.h . A problem sometimes arises because
-the files in the /usr/include/scsi directory are controlled by the GNU
-library people who maintain glibc. Unfortunately these 2 versions of
-the sg.h header file are not always in sync. [This was the case in Redhat
-6.0 and 6.1 .] Glibc 2.1.3 and later versions should get this right.
-
-If this is a problem, the user may need to copy sg.h (and scsi.h) from
-the kernel source includes to /usr/include scsi. If the user can change
-the effected source code then another approach is to rely on the fact that
-/usr/src/linux is a symbolic link to /usr/src/linux/include/linux and
-change the sg.h include to look like:
-    #include <linux/../scsi/sg.h>
-This solution is used by the author of cdparanoia (Monty) in his application.
-
-[Former scsi generic documents suggested adding a symbolic link to
-bypass this problem but that is not popular with the glibc maintainers.
-I would like to thank Andreas Jaeger <aj@suse.de> for his contributions
-on this subject.]
-
-
-Extra information in scsi-generic_long.txt
-==========================================
-This document is an abridged form of a more comprehensive document called
-scsi-generic_long.txt (see www.torque.net/sg/p/scsi-generic_long.txt).
-
-The longer document contains additional sections on:
-   - memory issues
-   - ioctl()s in common with sd, st + sr
-   - distinguishing the original from the new driver
-   - SG_BIG_BUFF and friends
-   - shortcomings
-   - future directions
-   - an appendix with some SCSI 2 information in it
-
-
-References
-==========
-http://www.t10.org      Very important site for SCSI related information.
-                        Contains SCSI 2 and 3 draft standards.
-http://www.andante.org/scsi.html
-                        This is Eric Youngdale's site. Eric is primarily
-                        responsible for the Linux SCSI architecture and
-                        its mid-level implementation.
-http://www.kernel.dk    Jens Axboe's site for Linux cdrom matters including
-                        the SCSI "sr" driver.
-http://www.torque.net/sg
-                        My site with sg related information.
-newsgroup:linux-scsi@vger.kernel.org
-                        Newsgroup for Linux related SCSI matters
-/usr/src/linux/MAINTAINERS
-                        This is a file in the Linux kernel source that
-                        contains up to date information about who maintains
-                        what and where information can be found. Links to
-                        SCSI adapter information are also here.
-
-
-Conclusion
-==========
-The SCSI generic packet device driver attempts to make as few assumptions
-as possible about the device it is connected to while giving applications
-using it as much flexibility as possible on the SCSI command level. Sg
-needs to hide the "messy" kernel related details while protecting
-the integrity of the kernel against sg "abuse". Some of these aims are
-contradictory and some compromises need to be made. For example: should
-a sg based application be able to reset a SCSI bus when that could cause
-collateral damage to a disk holding the root file system? There is no
-easy answer to this and many other related questions.
-
-If you have any suggestion about sg (or improving (the accuracy of) this
-document) please contact me.
+Rather than document the driver's interface here, version information
+is provided plus pointers (i.e. URLs) where to find documentation
+and examples.
+
+
+Major versions of the sg driver
+===============================
+There are three major versions of sg found in the linux kernel (lk):
+      - sg version 1 (original) from 1992 to early 1999 (lk 2.2.5) . 
+	It is based in the sg_header interface structure.
+      - sg version 2 from lk 2.2.6 in the 2.2 series. It is based on
+	an extended version of the sg_header interface structure.
+      - sg version 3 found in the lk 2.4 series (and the lk 2.5 series).
+	It adds the sg_io_hdr interface structure.
+
+
+Sg driver documentation
+=======================
+The most recent documentation of the sg driver is kept at the Linux
+Documentation Project's (LDP) site: 
+http://www.linuxdoc.org/HOWTO/SCSI-Generic-HOWTO
+This describes the sg version 3 driver found in the lk 2.4 series.
+The LDP renders documents in single and multiple page HTML, postscript
+and pdf. This document can also be found at:
+http://www.torque.net/sg/p/sg_v3_ho.html
+
+Documentation for the version 2 sg driver found in the lk 2.2 series can
+be found at http://www.torque.net/sg/p/scsi-generic.txt . A larger version
+is at:  http://www.torque.net/sg/p/scsi-generic_long.txt .
+
+The original documentation for the sg driver (prior to lk 2.2.6) can be
+found at http://www.torque.net/sg/p/original/SCSI-Programming-HOWTO.txt
+and in the LDP archives.
+
+A changelog with brief notes can be found in the
+/usr/src/linux/include/scsi/sg.h file. Note that the glibc maintainers copy 
+and edit this file (removing its changelog for example) before placing it 
+in /usr/include/scsi/sg.h . Driver debugging information and other notes 
+can be found at the top of the /usr/src/linux/drivers/scsi/sg.c file.
+
+A more general description of the Linux SCSI subsystem of which sg is a 
+part can be found at http://www.linuxdoc.org/HOWTO/SCSI-2.4-HOWTO .
+
+
+Example code and utilities
+==========================
+There are two packages of sg utilities:
+  - sg3_utils   for the sg version 3 driver found in lk 2.4
+  - sg_utils    for the sg version 2 (and original) driver found in lk 2.2
+                and earlier
+Both packages will work in the lk 2.4 series however sg3_utils offers more
+capabilities. They can be found at: http://www.torque.net/sg and 
+freshmeat.net
+
+Another approach is to look at the applications that use the sg driver.
+These include cdrecord, cdparanoia, SANE and cdrdao.
+
+
+Mapping of Linux kernel versions to sg driver versions
+======================================================
+Here is a list of linux kernels in the 2.4 series that had new version
+of the sg driver:
+      lk 2.4.0 : sg version 3.1.17
+      lk 2.4.7 : sg version 3.1.19 
+      lk 2.4.10 : sg version 3.1.20  **
+      lk 2.4.17 : sg version 3.1.22 
+
+** There were 3 changes to sg version 3.1.20 by third parties in the
+   next six linux kernel versions.
+
+For reference here is a list of linux kernels in the 2.2 series that had 
+new version of the sg driver:
+      lk 2.2.0 : original sg version [with no version number]
+      lk 2.2.6 : sg version 2.1.31
+      lk 2.2.8 : sg version 2.1.32
+      lk 2.2.10 : sg version 2.1.34 [SG_GET_VERSION_NUM ioctl first appeared]
+      lk 2.2.14 : sg version 2.1.36
+      lk 2.2.16 : sg version 2.1.38
+      lk 2.2.17 : sg version 2.1.39
+      lk 2.2.20 : sg version 2.1.40
+
+The lk 2.5 development series has recently commenced and it currently
+contains sg version 3.5.23 which is functionally equivalent to sg
+version 3.1.22 found in lk 2.4.17 .
 
 
 Douglas Gilbert
+26th January 2002
 dgilbert@interlog.com
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/scsi.txt linux/Documentation/scsi.txt
--- linux.orig/Documentation/scsi.txt	Tue Apr 28 21:22:04 1998
+++ linux/Documentation/scsi.txt	Mon Feb  4 18:11:15 2002
@@ -1,30 +1,44 @@
+SCSI subsystem documentation
+============================
+The Linux Documentation Project (LDP) maintains a document describing
+the SCSI subsystem in the Linux kernel (lk) 2.4 series. See:
+http://www.linuxdoc.org/HOWTO/SCSI-2.4-HOWTO . The LDP has single
+and multiple page HTML renderings as well as postscript and pdf.
+It can also be found at http://www.torque.net/scsi/SCSI-2.4-HOWTO .
 
-	The scsi support in the linux kernel can be modularized in a
-number of different ways depending upon the needs of the end user.  To
-understand  your options, we should first define a few terms.
-
-	The scsi-core contains the core of scsi support.  Without it
-you can do nothing with any of the other scsi drivers.  The scsi core
-support can be a module (scsi_mod.o), or it can be built into the kernel.
-If the core is a module, it must be the first scsi module loaded, and
-if you unload the modules, it will have to be the last one unloaded.
-
-	The individual upper and lower level drivers can be loaded in any
-order once the scsi core is present in the kernel (either compiled in
-or loaded as a module).  The disk driver (sd_mod.o), cdrom driver (sr_mod.o),
-tape driver (st.o) and scsi generics driver (sg.o) represent the upper level
-drivers to support the various assorted devices which can be controlled.
-You can for example load the tape driver to use the tape drive, and then
-unload it once you have no further need for the driver (and release the
-associated memory).
-
-	The lower level drivers are the ones that support the
-individual cards that are supported for the hardware platform that you
-are running under.  Examples are aha1542.o to drive Adaptec 1542
-cards.  Rather than list the drivers which *can* be modularized, it is
-easier to list the ones which cannot, since the list only contains a
-few entries.  The drivers which have NOT been modularized are:
 
-	NCR5380 boards of one kind or another including PAS16,
-		Trantor T128/128F/228, 
+Notes on using modules in the SCSI subsystem
+============================================
+The scsi support in the linux kernel can be modularized in a number of 
+different ways depending upon the needs of the end user.  To understand
+your options, we should first define a few terms.
+
+The scsi-core (also known as the "mid level") contains the core of scsi 
+support.  Without it you can do nothing with any of the other scsi drivers.
+The scsi core support can be a module (scsi_mod.o), or it can be built into
+the kernel. If the core is a module, it must be the first scsi module 
+loaded, and if you unload the modules, it will have to be the last one 
+unloaded.  In practice the modprobe and rmmod commands (and "autoclean")
+will enforce the correct ordering of loading and unloading modules in
+the SCSI subsystem.
+
+The individual upper and lower level drivers can be loaded in any order 
+once the scsi core is present in the kernel (either compiled in or loaded
+as a module).  The disk driver (sd_mod.o), cdrom driver (sr_mod.o),
+tape driver ** (st.o) and scsi generics driver (sg.o) represent the upper 
+level drivers to support the various assorted devices which can be 
+controlled.  You can for example load the tape driver to use the tape drive, 
+and then unload it once you have no further need for the driver (and release
+the associated memory).
+
+The lower level drivers are the ones that support the individual cards that
+are supported for the hardware platform that you are running under. Those
+individual cards are often called Host Bus Adapters (HBAs). For example the
+aic7xxx.o driver is used to control all recent SCSI controller cards from 
+Adaptec. Almost all lower level drivers can be built either as modules or 
+built into the kernel.
+
+
+** There is a variant of the st driver for controlling OnStream tape
+   devices. Its module name is osst.o .
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/sonypi.txt linux/Documentation/sonypi.txt
--- linux.orig/Documentation/sonypi.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/sonypi.txt	Mon Jan  7 13:03:04 2002
@@ -25,7 +25,8 @@
 can be downloaded at: <http://www.alcove-labs.org/en/software/sonypi/>
 
 This driver supports also some ioctl commands for setting the LCD screen
-brightness (some more commands may be added in the future).
+brightness and querying the batteries charge information (some more 
+commands may be added in the future).
 
 This driver can also be used to set the camera controls on Picturebook series
 (brightness, contrast etc), and is used by the video4linux driver for the 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/sound/AD1816 linux/Documentation/sound/AD1816
--- linux.orig/Documentation/sound/AD1816	Wed Sep 27 20:53:52 2000
+++ linux/Documentation/sound/AD1816	Mon Feb  4 17:38:23 2002
@@ -80,5 +80,5 @@
   tek@rbg.informatik.tu-darmstadt.de
 
 Thorsten Knabe <tek@rbg.informatik.tu-darmstadt.de>
-Christoph Hellwig <hch@caldera.de>
+Christoph Hellwig <hch@infradead.org>
 	Last modified: 2000/09/20
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/sound/AudioExcelDSP16 linux/Documentation/sound/AudioExcelDSP16
--- linux.orig/Documentation/sound/AudioExcelDSP16	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/sound/AudioExcelDSP16	Mon Jan 14 18:53:53 2002
@@ -2,7 +2,7 @@
 ------
 
 Informations about Audio Excel DSP 16 driver can be found in the source
-file lowlevel/aedsp16.c
+file aedsp16.c
 Please, read the head of the source before using it. It contain useful
 informations.
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/sound/NEWS linux/Documentation/sound/NEWS
--- linux.orig/Documentation/sound/NEWS	Wed Sep 27 20:53:52 2000
+++ linux/Documentation/sound/NEWS	Mon Feb  4 17:38:23 2002
@@ -1,6 +1,6 @@
 Linux 2.4 Sound Changes
 2000-September-25
-Christoph Hellwig, <hch@caldera.de>
+Christoph Hellwig, <hch@infradead.org>
 
 
 
@@ -9,9 +9,6 @@
 The Linux 2.4 Kernel does have reliable in-kernel isapnp support.
 Some drivers (sb.o, ad1816.o awe_wave.o) do now support automatically
 detecting and configuring isapnp devices.
-If you have a not yet supported isapnp soundcard, mail me the content
-of '/proc/isapnp' on your system and some information about your card
-and its driver(s) so I can try to get isapnp working for it.
 
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/usb/ov511.txt linux/Documentation/usb/ov511.txt
--- linux.orig/Documentation/usb/ov511.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/usb/ov511.txt	Wed Jan  9 15:42:02 2002
@@ -8,11 +8,11 @@
 INTRODUCTION:
 
 This is a driver for the OV511, a USB-only chip used in many "webcam" devices.
-Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work. It 
+Any camera using the OV511/OV511+ and the OV6620/OV7610/20/20AE should work.
+Video capture devices that use the Philips SAA7111A decoder also work. It 
 supports streaming and capture of color or monochrome video via the Video4Linux
-API. Most V4L apps are compatible with it, but a few video-conferencing programs
-do not work yet. The following resolutions are supported: 640x480, 448x336,
-384x288, 352x288, and 320x240.
+API. Most V4L apps are compatible with it. Most resolutions with a width and
+height that are a multiple of 8 are supported.
 
 If you need more information, please visit the OV511 homepage at the above URL.
 
@@ -27,22 +27,25 @@
 
 HOW TO USE IT:
 
+Note: These are simplified instructions. For complete instructions see:
+	http://alpha.dyndns.org/ov511/install.html
+
 You must have first compiled USB support, support for your specific USB host
 controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend
-making them modules.)
+making them modules.) Make sure "Enforce bandwidth allocation" is NOT enabled.
 
-Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX):
+Next, (as root):
 
-	insmod usb/usbcore.o
-	insmod usb/usb-uhci.o  <OR>  insmod usb/ohci-hcd.o
-	insmod misc/videodev.o
-	insmod usb/ov511.o
+	modprobe usbcore
+	modprobe usb-uhci  <OR>  modprobe usb-ohci
+	modprobe videodev
+	modprobe ov511
 
 If it is not already there (it usually is), create the video device:
 
-	mknod /dev/video c 81 0
+	mknod /dev/video0 c 81 0
 
-Sometimes /dev/video is a symlink to /dev/video0
+Optionally, symlink /dev/video to /dev/video0
 
 You will have to set permissions on this device to allow you to read/write
 from it:
@@ -55,39 +58,40 @@
 	
 [Using vidcat:]
 
-	vidcat -s 640x480 > test.jpg
+	vidcat -s 640x480 -p c > test.jpg
 	xview test.jpg
 	
 [Using xawtv:]
 
-You must make some modifications to the source and compile it before you use it.
-(Note: this may not be applicable to versions other than 3.06)
-
-In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next,
-in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv
-directory:
+From the main xawtv directory:
 
 	make clean
 	./configure
 	make
 	make install
 
-Now you should be able to run xawtv. Right click for the options dialog. If
-you get a scrambled image it is likely that you made a mistake in Xawtv.ad.
-Try setting the size to 320x240 if all else fails.
+Now you should be able to run xawtv. Right click for the options dialog. 
 
 MODULE PARAMETERS:
 
   You can set these with:  insmod ov511 NAME=VALUE
   There is currently no way to set these on a per-camera basis.
 
-  NAME: autoadjust
-  TYPE: integer (boolean)
+  NAME: autobright
+  TYPE: integer (Boolean)
   DEFAULT: 1
-  DESC: The camera normally adjusts exposure, gain, and hue automatically. This
-        can be set to 0 to disable this automatic adjustment. Note that there is
-        currently no way to set these parameters manually once autoadjust is
-        disabled.
+  DESC: Brightness is normally under automatic control and can't be set
+        manually by the video app. Set to 0 for manual control.
+
+  NAME: autogain
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Gain Control enable. This feature is not yet implemented.
+
+  NAME: autoexp
+  TYPE: integer (Boolean)
+  DEFAULT: 1
+  DESC: Auto Exposure Control enable. This feature is not yet implemented.
 
   NAME: debug
   TYPE: integer (0-6)
@@ -102,49 +106,23 @@
           5=highly repetitive mesgs
 
   NAME: fix_rgb_offset
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Some people have reported that the blue component of the image is one
         or so lines higher than the red component. This is only apparent in 
         images with white objects on black backgrounds at 640x480. Setting this
-        to 1 will realign the color planes correctly. NOTE: This is still
-        experimental and very buggy. You will likely need a fast (500 MHz) CPU.
+        to 1 will realign the color planes correctly. NOTE: You will likely
+        need a fast (500 MHz) CPU.
 
   NAME: snapshot
-  TYPE: integer (boolean)
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot
-        button is pressed. Note that this does not yet work with most apps,
-        including xawtv and vidcat. NOTE: See the section "TODO" for more info.
-
-  NAME: sensor
-  TYPE: integer ([0, 1, 3])
-  DEFAULT: [varies]
-  DESC: If you know that your camera sensor is not detected correctly, set this
-        parameter. This is a global option for all attached OV511 cameras. You
-        will probably never need to set this, but if you do, valid values are:
-        	0 for OV7620
-        	1 for OV7620AE
-        	3 for OV7610
-
-  NAME: i2c_detect_tries
-  TYPE: integer (don't set it insanely high!)
-  DEFAULT: 5
-  DESC: This is the number of times the driver will try to sync and detect the
-        internal i2c bus (which connects the OV511 and sensor). If you are
-        getting intermittent detection failures ("Failed to read sensor ID...")
-        you should increase this by a modest amount. If setting it to 20 or so
-        doesn't fix things, look elsewhere for the cause of the problem.
-
-  NAME: aperture
-  TYPE: integer (0 - 15)
-  DEFAULT: [varies by sensor]
-  DESC: For legal values, see the OV7610/7620 specs under register Common F.
-        This setting affects the upper nybble of that reg (bits 4-7). This is
-        for if you want to play with the camera's pixel saturation.
+  DESC: Set to 1 to enable snapshot mode. read()/VIDIOCSYNC will block until
+	the snapshot button is pressed. Note: enabling this mode disables
+	/proc/video/ov511/<minor#>/button
 
-  NAME: force_rgb
-  TYPE: integer (boolean)
+  NAME: force_rgb	(Deprecated; may be removed in the future)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: Force image to be read in RGB instead of BGR. This option allow
         programs that expect RGB data (e.g. gqcam) to work with this driver. If
@@ -169,60 +147,179 @@
         both OV511 and OV511+ cameras, trial-and-error may be necessary for
         finding the optimum setting.
 
-  NAME: retry_sync
-  TYPE: boolean
+  NAME: compress
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Set this to 1 to turn on the camera's compression engine. This can
+        potentially increase the frame rate at the expense of quality, if you
+        have a fast CPU. You must load the proper compression module for your
+        camera before starting your application (ov511_decomp or ov518_decomp).
+
+  NAME: testpat
+  TYPE: integer (Boolean)
   DEFAULT: 0
-  DESC: Prevent apps from timing out if frame is not done in time. This is
-        useful if you are having problems with Xawtv getting "stuck" on a frame
-        when your system is under heavy load.
+  DESC: This configures the camera's sensor to transmit a colored test-pattern
+        instead of an image. This does not work correctly yet.
 
-  NAME: sensor_gbr
-  TYPE: boolean
+  NAME: sensor_gbr (*** TEMPORARILY DISABLED ***)
+  TYPE: integer (Boolean)
   DEFAULT: 0
   DESC: This makes the sensor output GBR422 instead of YUV420. This saves the
         driver the trouble of converting YUV to RGB, but it currently does not
         work very well (the colors are not quite right)
 
+  NAME: dumppix
+  TYPE: integer (0-2)
+  DEFAULT: 0
+  DESC: Dumps raw pixel data and skips post-processing and format conversion.
+	It is for debugging purposes only. Options are:
+		0: Disable (default)
+		1: Dump raw data from camera, excluding headers and trailers
+		2: Dumps data exactly as received from camera
+
+  NAME: led
+  TYPE: integer (0-2)
+  DEFAULT: 1 (Always on)
+  DESC: Controls whether the LED (the little light) on the front of the camera
+	is always off (0), always on (1), or only on when driver is open (2).
+	This is only supported with the OV511+ chipset, and even then only on
+	some cameras (ones that actually have the LED wired to the control pin,
+	and not just hardwired to be on all the time).
+
+  NAME: dump_bridge
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the bridge (OV511[+] or OV518[+]) register values to the system
+	log. Only useful for serious debugging/development purposes.
+
+  NAME: dump_sensor
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Dumps the sensor register values to the system log. Only useful for
+	serious debugging/development purposes.
+
+  NAME: printph
+  TYPE: integer (Boolean)
+  DEFAULT: 0
+  DESC: Setting this to 1 will dump the first 12 bytes of each isoc frame. This
+	is only useful if you are trying to debug problems with the isoc data
+	stream (i.e.: camera initializes, but vidcat hangs until Ctrl-C). Be
+	warned that this dumps a large number of messages to your kernel log.
+
+  NAME: phy, phuv, pvy, pvuv, qhy, qhuv, qvy, qvuv
+  TYPE: integer (0-63 for phy and phuv, 0-255 for rest)
+  DEFAULT: OV511 default values
+  DESC: These are registers 70h - 77h of the OV511, which control the
+	prediction ranges and quantization thresholds of the compressor, for
+	the Y and UV channels in the horizontal and vertical directions. See
+	the OV511 or OV511+ data sheet for more detailed descriptions. These
+	normally do not need to be changed.
+
+  NAME: lightfreq
+  TYPE: integer (0, 50, or 60)
+  DEFAULT: 0 (use sensor default)
+  DESC: Sets the sensor to match your lighting frequency. This can reduce the
+	appearance of "banding", i.e. horizontal lines or waves of light and
+	dark that are often caused by artificial lighting. Valid values are:
+		0 - Use default (depends on sensor, most likely 60 Hz)
+		50 - For European and Asian 50 Hz power
+		60 - For American 60 Hz power
+
+  NAME: bandingfilter
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Enables the sensor´s banding filter exposure algorithm. This reduces
+	or stabilizes the "banding" caused by some artificial light sources
+	(especially fluorescent). You might have to set lightfreq correctly for
+	this to work right. As an added bonus, this sometimes makes it
+	possible to capture your monitor´s output.
+
+  NAME: fastset
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Allows picture settings (brightness, contrast, color, and hue) to take
+	effect immediately, even in the middle of a frame. This reduces the
+	time to change settings, but can ruin frames during the change. Only
+	affects OmniVision sensors.
+
+  NAME: force_palette
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Forces the palette (color format) to a specific value. If an
+	application requests a different palette, it will be rejected, thereby
+	forcing it to try others until it succeeds. This is useful for forcing
+	greyscale mode with a color camera, for example. Supported modes are:
+		0                           (Allows all the following formats)
+		1   VIDEO_PALETTE_GREY      (Linear greyscale)
+		3   VIDEO_PALETTE_RGB565    (565 16 bit RGB)
+		4   VIDEO_PALETTE_RGB24     (24bit RGB)
+		7   VIDEO_PALETTE_YUV422    (YUV422 capture)
+		8   VIDEO_PALETTE_YUYV      (YUV422 capture; same as 7)
+		10  VIDEO_PALETTE_YUV420    (YUV 4:2:0 Planar)
+		13  VIDEO_PALETTE_YUV422P   (YUV 4:2:2 Planar)
+		15  VIDEO_PALETTE_YUV420P   (YUV 4:2:0 Planar, same as 10)
+
+  NAME: tuner
+  TYPE: integer
+  DEFAULT: -1 (autodetect)
+  DESC: This sets the exact type of the tuner module in a device. This is set
+	automatically based on the custom ID of the OV511 device. In cases
+	where this fails, you can override this auto-detection. Please see
+	linux/drivers/media/video/tuner.h for a complete list.
+
+  NAME: backlight
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Setting this flag changes the exposure algorithm for OmniVision sensors
+	such that objects in the camera's view (i.e. your head) can be clearly
+	seen when they are illuminated from behind. It reduces or eliminates
+	the sensor's auto-exposure function, so it should only be used when
+	needed. Additionally, it is only supported with the OV6620 and OV7620.
+
+  NAME: unit_video
+  TYPE: Up to 16 comma-separated integers
+  DEFAULT: 0,0,0... (automatically assign the next available minor(s))
+  DESC: You can specify up to 16 minor numbers to be assigned to ov511 devices.
+	For example, "unit_video=1,3" will make the driver use /dev/video1 and
+	/dev/video3 for the first two devices it detects. Additional devices
+	will be assigned automatically starting at the first available device
+	node (/dev/video0 in this case). Note that you cannot specify 0 as a
+	minor number. This feature requires kernel version 2.4.5 or higher.
+
+  NAME: remove_zeros
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (do not skip any incoming data)
+  DESC: Setting this to 1 will remove zero-padding from incoming data. This
+	will compensate for the blocks of corruption that can appear when the
+	camera cannot keep up with the speed of the USB bus (eg. at low frame
+	resolutions). This feature is always enabled when compression is on.
+ 
 WORKING FEATURES:
- o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240
- o RGB24, RGB565, YUV420, YUV422, YUYV, and YUV422P color
- o Monochrome
+ o Color streaming/capture at most widths and heights that are multiples of 8.
+ o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color
+ o Monochrome (use force_palette=1 to enable)
  o Setting/getting of saturation, contrast, brightness, and hue (only some of
    them work the OV7620 and OV7620AE)
  o /proc status reporting
+ o SAA7111A video capture support at 320x240 and 640x480
+ o Compression support
 
 EXPERIMENTAL FEATURES:
- o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and
-   corrupted frames. If you have a very fast CPU, you can try it.
- o Snapshot mode (only works with some read() based apps; see below for more)
- o OV6620 sensor support
- o GBR422 parsing
- o 160x120
-
-TODO:
- o Fix the noise / grainy image problem.
- o Get compression working. It would be a nice addition as it improves
-   frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works,
-   so we can't really work on that yet. Please kindly inform OmniVision that you
-   would like them to release their specifications to the Linux community.
- o YUV422
- o Fix fixFrameRGBoffset(). It is not stable yet with streaming video.
- o V4L2 support (Probably not until it goes into the kernel)
- o Get rid of the memory management functions (put them in videodev.c??)
- o Setting of contrast and brightness not working with 7620/7620AE
- o Driver/camera state save/restore for when USB supports suspend/resume
- o Unstable on SMP systems
- o OV7620/OV6620 experience frame corruption with moving objects
- o OV6620 is too dark
- o 176x144 support
- o Driver sometimes hangs upon close() with OHCI
- o The image should always be written properly to the mmap'ed buffer as long as
-   the requested image size is at least the minimum size. This will likely
-   require a rewrite of all the parsing code.
+ o OV6630 sensor support
+ o Banding filter
+ o SMP compatibility
+
+TO-DO:
+ o V4L2 support (This will be done after the next kernel patch release)
+ o Setting of hue not working with OV7620
+ o Setting of contrast and hue not working with OV7620AE
+ o OV8600 sensor support (Not used in anything yet)
+ o OV518/OV518+ support (all that's needed is the decompressor)
+ o cams >= 3 not working
 
 HOW TO CONTACT ME:
 
-You can email me at mwm@i.am . Please prefix the subject line
+You can email me at mmcclell@bigfoot.com . Please prefix the subject line
 with "OV511: " so that I am certain to notice your message.
 
 CREDITS:
@@ -232,3 +329,4 @@
 and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and
 image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio
 Matsuoka for their work as well.
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/usb/stv680.txt linux/Documentation/usb/stv680.txt
--- linux.orig/Documentation/usb/stv680.txt	Thu Jan  1 00:00:00 1970
+++ linux/Documentation/usb/stv680.txt	Wed Dec 26 15:32:52 2001
@@ -0,0 +1,55 @@
+Linux driver for STV0680 based USB cameras
+
+Copyright, 2001, Kevin Sisson
+
+
+INTRODUCTION:
+
+STMicroelectronics produces the STV0680B chip, which comes in two 
+types, -001 and -003. The -003 version allows the recording and downloading 
+of sound clips from the camera, and allows a flash attachment. Otherwise, 
+it uses the same commands as the -001 version. Both versions support a 
+variety of SDRAM sizes and sensors, allowing for a maximum of 26 VGA or 20 
+CIF pictures. The STV0680 supports either a serial or a usb interface, and 
+video is possible through the usb interface.
+
+The following cameras are known to work with this driver, although any 
+camera with Vendor/Product codes of 0553/0202 should work:
+
+Aiptek Pencam (various models)
+Nisis QuickPix 2
+Radio Shack 'Kid's digital camera' (#60-1207)
+At least one Trust Spycam model
+Several other European brand models
+
+WHAT YOU NEED:
+
+-	USB support
+-	VIDEO4LINUX support
+
+More information about USB support for linux can be found at:
+http://www.linux-usb.org
+
+
+MODULE OPTIONS:
+
+When the driver is compiled as a module, you can set a "swapRGB=1"
+option, if necessary, for those applications that require it 
+(such as xawtv). However, the driver should detect and set this 
+automatically, so this option should not normally be used.
+
+
+KNOWN PROBLEMS:
+
+The driver seems to work better with the usb-ohci than the usb-uhci host 
+controller driver. 
+
+HELP:
+
+The latest info on this driver can be found at:
+http://personal.clt.bellsouth.net/~kjsisson or at
+http://stv0680-usb.sourceforge.net
+
+Any questions to me can be send to:  kjsisson@bellsouth.net
+
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Documentation/usb/usb-serial.txt linux/Documentation/usb/usb-serial.txt
--- linux.orig/Documentation/usb/usb-serial.txt	Tue Jan 22 17:51:22 2002
+++ linux/Documentation/usb/usb-serial.txt	Wed Dec 26 15:09:46 2001
@@ -95,6 +95,66 @@
   Kroah-Hartman at greg@kroah.com
 
 
+Compaq iPAQ driver
+
+  This driver can be used to connect to Compaq iPAQ PDAs running
+  Windows CE 3.0 using a USB autosync cable. It has been tested only on
+  the Compaq H3135. It should work with the H3600 and later models too.
+  It may work with other CE based handhelds as well.
+
+  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
+  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.
+
+  The driver works intermittently with the usb-uhci driver but quite
+  reliably with the uhci driver. Make sure you have the right driver
+  loaded - usb-uhci is often the default.
+
+  You must setup hotplug to invoke pppd as soon as the iPAQ is connected.
+  A ppp script like the one below may be used:
+
+	#!/bin/bash
+
+	MYIP=linux.box.ip
+	REMOTEIP=ipaq.ip
+	MYDNS=my.dns.server
+	killall -9 pppd
+	/usr/sbin/pppd /dev/ttyUSB0 \
+	connect "/usr/sbin/chat -v TIMEOUT 60 CLIENT 'CLIENTSERVER\c'" \
+	nocrtscts local debug passive $MYIP:$REMOTEIP ms-dns $MYDNS noauth \
+	proxyarp
+
+  You must also download and install asyncd from http://synce.sourceforge.net
+  This is required to emulate keep-alive packets which are exchanged by
+  ActiveSync and the iPAQ.
+
+  On connecting the cable, you should see the usual "Device Connected",
+  "User Authenticated" messages flash by on your iPAQ. Once connected,
+  you can use Win CE programs like ftpView, Pocket Outlook from the iPAQ
+  and other synce utilities from the Linux side. Remember to enable IP
+  forwarding. 
+
+  To use Pocket IE, follow the instructions given at
+  http://www.tekguru.co.uk/EM500/usbtonet.htm to achieve the same thing
+  on Win98. Omit the proxy server part; Linux is quite capable of forwarding
+  packets unlike Win98. Another modification is required at least for the
+  iPAQ - disable autosync by going to the Start/Settings/Connections menu
+  and unchecking the "Automatically synchronize ..." box. Go to
+  Start/Programs/Connections, connect the cable and select "usbdial" (or
+  whatever you named your new USB connection). You should finally wind
+  up with a "Connected to usbdial" window with status shown as connected.
+  Now start up PIE and browse away.
+
+  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.
+
+  For any questions or problems with the driver, please contact Ganesh
+  Varadarajan <ganesh@veritas.com>
+
+
 Keyspan PDA Serial Adapter
 
   Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly
@@ -301,6 +361,32 @@
   For any questions or problems with this driver, please contact Greg
   Kroah-Hartman at greg@kroah.com
   
+
+KL5KUSB105 chipset / PalmConnect USB single-port adapter
+  
+Current status:
+  The driver was put together by looking at the usb bus transactions
+  done by Palm's driver under Windows, so a lot of functionality is
+  still missing.  Notably, serial ioctls are sometimes faked or not yet
+  implemented.  Support for finding out about DSR and CTS line status is
+  however implemented (though not nicely), so your favorite autopilot(1)
+  and pilot-manager -daemon calls will work.  Baud rates up to 115200
+  are supported, but handshaking (software or hardware) is not, which is
+  why it is wise to cut down on the rate used is wise for large
+  transfers until this is settled.
+  
+Options supported:
+  If this driver is compiled as a module you can pass the following
+  options to it:
+  debug			- extra verbose debugging info
+  			  (default: 0; nonzero enables)
+  use_lowlatency	- use low_latency flag to speed up tty layer
+			  when reading from from the device.
+			  (default: 0; nonzero enables)
+
+  See http://www.uuhaus.de/linux/palmconnect.html for up-to-date
+  information on this driver.
+
 
 Generic Serial driver
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/MAINTAINERS linux/MAINTAINERS
--- linux.orig/MAINTAINERS	Tue Jan 22 17:51:22 2002
+++ linux/MAINTAINERS	Tue Feb  5 17:18:39 2002
@@ -582,7 +582,7 @@
 
 FREEVXFS FILESYSTEM
 P:	Christoph Hellwig
-M:	hch@caldera.de
+M:	hch@infradead.org
 W:	ftp://ftp.openlinux.org/pub/people/hch/vxfs
 S:	Maintained
 
@@ -1022,6 +1022,11 @@
 M:	andrewtv@usa.net
 S:	Maintained
 
+NATSEMI ETHERNET DRIVER (DP8381x)
+P: 	Tim Hockin
+M:	thockin@hockin.org
+S:	Maintained
+
 NCP FILESYSTEM
 P:	Petr Vandrovec
 M:	vandrove@vc.cvut.cz
@@ -1045,9 +1050,10 @@
 M:	jamesm@intercode.com.au
 P:	Harald Welte
 M:	laforge@gnumonks.org
-W:	http://netfilter.samba.org
-W:	http://netfilter.kernelnotes.org
-W:	http://netfilter.filewatcher.org
+P:	Jozsef Kadlecsik
+M:	kadlec@blackhole.kfki.hu
+W:	http://www.netfilter.org/
+W:	http://www.iptables.org/
 L:	netfilter@lists.samba.org
 S:	Supported
 
@@ -1145,8 +1151,8 @@
 S:	Maintained
 
 OPL3-SA2, SA3, and SAx DRIVER
-P:	Scott Murray
-M:	scott@spiteful.org
+P:	Zwane Mwaikambo
+M:	zwane@commfireservices.com
 L:	linux-sound@vger.kernel.org
 S:	Maintained
 
@@ -1172,9 +1178,9 @@
 
 PERSONALITY HANDLING
 P:	Christoph Hellwig
-M:	hch@caldera.de
+M:	hch@infradead.org
 L:	linux-abi-devel@lists.sourceforge.net
-S:	Supported
+S:	Maintained
 
 PCI ID DATABASE
 P:	Jens Maurer
@@ -1255,6 +1261,12 @@
 W:	http://www.alarsen.net/linux/qnx4fs/
 S:	Maintained
 
+RADEON FRAMEBUFFER DISPLAY DRIVER
+P:	Ani Joshi
+M:	ajoshi@shell.unixbox.com
+L:      linux-fbdev-devel@lists.sourceforge.net
+S:      Maintained
+
 RAGE128 FRAMEBUFFER DISPLAY DRIVER
 P:	Ani Joshi
 M:	ajoshi@shell.unixbox.com
@@ -1462,7 +1474,7 @@
 
 SYSV FILESYSTEM
 P:	Christoph Hellwig
-M:	hch@caldera.de
+M:	hch@infradead.org
 S:	Maintained
 
 TLAN NETWORK DRIVER
@@ -1606,7 +1618,7 @@
 
 USB OV511 DRIVER
 P:     Mark McClelland
-M:     mwm@i.am
+M:     mmcclell@bigfoot.com
 L:     linux-usb-users@lists.sourceforge.net
 L:     linux-usb-devel@lists.sourceforge.net
 W:     http://alpha.dyndns.org/ov511/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Makefile linux/Makefile
--- linux.orig/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/Makefile	Wed Feb 13 19:37:55 2002
@@ -1,12 +1,12 @@
 VERSION = 2
 PATCHLEVEL = 4
-SUBLEVEL = 17
-EXTRAVERSION =
+SUBLEVEL = 18
+EXTRAVERSION = -rc1
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
-KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//")
+KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
 
 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
 	  else if [ -x /bin/bash ]; then echo /bin/bash; \
@@ -137,7 +137,8 @@
 	drivers/net/net.o \
 	drivers/media/media.o
 DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
-DRIVERS-$(CONFIG_DRM) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o
+DRIVERS-$(CONFIG_DRM_OLD) += drivers/char/drm-4.0/drm.o
 DRIVERS-$(CONFIG_NUBUS) += drivers/nubus/nubus.a
 DRIVERS-$(CONFIG_ISDN) += drivers/isdn/isdn.a
 DRIVERS-$(CONFIG_NET_FC) += drivers/net/fc/fc.o
@@ -204,7 +205,7 @@
 	drivers/scsi/aic7xxx/aicasm/aicasm_scan.c \
 	drivers/scsi/aic7xxx/aicasm/y.tab.h \
 	drivers/scsi/aic7xxx/aicasm/aicasm \
-	drivers/scsi/53c700-mem.c \
+	drivers/scsi/53c700_d.h \
 	net/khttpd/make_times_h \
 	net/khttpd/times.h \
 	submenu*
@@ -242,14 +243,14 @@
 
 include arch/$(ARCH)/Makefile
 
-export	CPPFLAGS CFLAGS AFLAGS
+export	CPPFLAGS CFLAGS CFLAGS_KERNEL AFLAGS AFLAGS_KERNEL
 
 export	NETWORKS DRIVERS LIBS HEAD LDFLAGS LINKFLAGS MAKEBOOT ASFLAGS
 
 .S.s:
-	$(CPP) $(AFLAGS) -traditional -o $*.s $<
+	$(CPP) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -o $*.s $<
 .S.o:
-	$(CC) $(AFLAGS) -traditional -c -o $*.o $<
+	$(CC) $(AFLAGS) $(AFLAGS_KERNEL) -traditional -c -o $*.o $<
 
 Version: dummy
 	@rm -f include/linux/compile.h
@@ -329,11 +330,13 @@
 	@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
 	@mv -f .ver $@
 
+comma	:= ,
+
 init/version.o: init/version.c include/linux/compile.h include/config/MARKER
-	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -c -o init/version.o init/version.c
+	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) -DUTS_MACHINE='"$(ARCH)"' -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o init/version.o init/version.c
 
 init/main.o: init/main.c include/config/MARKER
-	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -c -o $*.o $<
+	$(CC) $(CFLAGS) $(CFLAGS_KERNEL) $(PROFILING) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c -o $*.o $<
 
 fs lib mm ipc kernel drivers net: dummy
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" $(subst $@, _dir_$@, $@)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/Rules.make linux/Rules.make
--- linux.orig/Rules.make	Tue Jan 22 17:51:22 2002
+++ linux/Rules.make	Thu Jan 10 20:08:20 2002
@@ -31,6 +31,8 @@
 unexport subdir-n
 unexport subdir-
 
+comma	:= ,
+
 #
 # Get things started.
 #
@@ -54,7 +56,7 @@
 	$(CPP) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) $< > $@
 
 %.o: %.c
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -c -o $@ $<
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -c -o $@ $<
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@))),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@))))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
@@ -270,7 +272,7 @@
 
 ifneq "$(strip $(export-objs))" ""
 $(export-objs): $(export-objs:.o=.c) $(TOPDIR)/include/linux/modversions.h
-	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_$@) -DEXPORT_SYMTAB -c $(@:.o=.c)
 	@ ( \
 	    echo 'ifeq ($(strip $(subst $(comma),:,$(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$@) -DEXPORT_SYMTAB)),$$(strip $$(subst $$(comma),:,$$(CFLAGS) $$(EXTRA_CFLAGS) $$(CFLAGS_$@) -DEXPORT_SYMTAB)))' ; \
 	    echo 'FILES_FLAGS_UP_TO_DATE += $@' ; \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/alpha_ksyms.c linux/arch/alpha/kernel/alpha_ksyms.c
--- linux.orig/arch/alpha/kernel/alpha_ksyms.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/alpha_ksyms.c	Mon Jan  7 14:04:12 2002
@@ -109,6 +109,7 @@
 EXPORT_SYMBOL(strrchr);
 EXPORT_SYMBOL(memcmp);
 EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(memscan);
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memset);
 EXPORT_SYMBOL(__memsetw);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/pci-noop.c linux/arch/alpha/kernel/pci-noop.c
--- linux.orig/arch/alpha/kernel/pci-noop.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/pci-noop.c	Mon Jan  7 14:18:34 2002
@@ -104,21 +104,23 @@
 }
 /* stubs for the routines in pci_iommu.c */
 void *
-pci_alloc_consistent(struct pci_dev *pdev, long size, dma_addr_t *dma_addrp)
+pci_alloc_consistent(struct pci_dev *pdev, size_t size, dma_addr_t *dma_addrp)
 {
+	return (void *)0;
 }
 void
-pci_free_consistent(struct pci_dev *pdev, long size, void *cpu_addr,
+pci_free_consistent(struct pci_dev *pdev, size_t size, void *cpu_addr,
 		    dma_addr_t dma_addr)
 {
 }
 dma_addr_t
-pci_map_single(struct pci_dev *pdev, void *cpu_addr, long size,
+pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size,
 	       int direction)
 {
+	return (dma_addr_t)0;
 }
 void
-pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, long size,
+pci_unmap_single(struct pci_dev *pdev, dma_addr_t dma_addr, size_t size,
 		 int direction)
 {
 }
@@ -126,6 +128,7 @@
 pci_map_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
 	   int direction)
 {
+	return 0;
 }
 void
 pci_unmap_sg(struct pci_dev *pdev, struct scatterlist *sg, int nents,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/pci_iommu.c linux/arch/alpha/kernel/pci_iommu.c
--- linux.orig/arch/alpha/kernel/pci_iommu.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/pci_iommu.c	Wed Jan  9 15:42:02 2002
@@ -30,6 +30,10 @@
 #define DEBUG_NODIRECT 0
 #define DEBUG_FORCEDAC 0
 
+/* Most Alphas support 32-bit ISA DMA. Exceptions are XL, Ruffian and
+   Nautilus (see asm/dma.h for details). */
+#define ISA_DMA_MASK	(MAX_DMA_ADDRESS - IDENT_ADDR - 1 < 0xffffffff ? \
+			 MAX_DMA_ADDRESS - IDENT_ADDR - 1 : 0xffffffff)
 
 static inline unsigned long
 mk_iommu_pte(unsigned long paddr)
@@ -181,7 +185,7 @@
 		 int dac_allowed)
 {
 	struct pci_controller *hose = pdev ? pdev->sysdata : pci_isa_hose;
-	dma_addr_t max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+	dma_addr_t max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 	struct pci_iommu_arena *arena;
 	long npages, dma_ofs, i;
 	unsigned long paddr;
@@ -220,7 +224,7 @@
 	}
 		
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	npages = calc_npages((paddr & ~PAGE_MASK) + size);
@@ -247,20 +251,27 @@
 dma_addr_t
 pci_map_single(struct pci_dev *pdev, void *cpu_addr, size_t size, int dir)
 {
+	int dac_allowed; 
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, cpu_addr, size,
-				pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, cpu_addr, size, dac_allowed);
 }
 
 dma_addr_t
 pci_map_page(struct pci_dev *pdev, struct page *page, unsigned long offset,
 	     size_t size, int dir)
 {
+	int dac_allowed;
+
 	if (dir == PCI_DMA_NONE)
 		BUG();
-	return pci_map_single_1(pdev, (char *)page_address(page) + offset,
-			        size, pdev ? (pdev->dma_mask >> 32) != 0 : 0);
+
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0; 
+	return pci_map_single_1(pdev, (char *)page_address(page) + offset, 
+				size, dac_allowed);
 }
 
 /* Unmap a single streaming mode DMA translation.  The DMA_ADDR and
@@ -558,7 +569,7 @@
 	if (direction == PCI_DMA_NONE)
 		BUG();
 
-	dac_allowed = ((pdev->dma_mask >> 32) != 0);
+	dac_allowed = pdev ? pci_dac_dma_supported(pdev, pdev->dma_mask) : 0;
 
 	/* Fast path single entry scatterlists.  */
 	if (nents == 1) {
@@ -578,9 +589,9 @@
 	/* Second, figure out where we're going to map things.  */
 	if (alpha_mv.mv_pci_tbi) {
 		hose = pdev ? pdev->sysdata : pci_isa_hose;
-		max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+		max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 		arena = hose->sg_pci;
-		if (!arena || arena->dma_base + arena->size > max_dma)
+		if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 			arena = hose->sg_isa;
 	} else {
 		max_dma = -1;
@@ -641,9 +652,9 @@
 		return;
 
 	hose = pdev ? pdev->sysdata : pci_isa_hose;
-	max_dma = pdev ? pdev->dma_mask : 0x00ffffff;
+	max_dma = pdev ? pdev->dma_mask : ISA_DMA_MASK;
 	arena = hose->sg_pci;
-	if (!arena || arena->dma_base + arena->size > max_dma)
+	if (!arena || arena->dma_base + arena->size - 1 > max_dma)
 		arena = hose->sg_isa;
 
 	fbeg = -1, fend = 0;
@@ -710,11 +721,10 @@
 	struct pci_iommu_arena *arena;
 
 	/* If there exists a direct map, and the mask fits either
-	   MAX_DMA_ADDRESS defined such that GFP_DMA does something
-	   useful, or the total system memory as shifted by the
-	   map base.  */
+	   the entire direct mapped space or the total system memory as
+	   shifted by the map base */
 	if (__direct_map_size != 0
-	    && (__direct_map_base + MAX_DMA_ADDRESS-IDENT_ADDR-1 <= mask
+	    && (__direct_map_base + __direct_map_size - 1 <= mask
 		|| __direct_map_base + (max_low_pfn<<PAGE_SHIFT)-1 <= mask))
 		return 1;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/proto.h linux/arch/alpha/kernel/proto.h
--- linux.orig/arch/alpha/kernel/proto.h	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/proto.h	Mon Jan 14 16:31:16 2002
@@ -106,7 +106,7 @@
 extern void SMC93x_Init(void);
 
 /* smc37c669.c */
-extern void SMC669_Init(int);
+extern int SMC669_Init(int);
 
 /* es1888.c */
 extern void es1888_init(void);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/smc37c669.c linux/arch/alpha/kernel/smc37c669.c
--- linux.orig/arch/alpha/kernel/smc37c669.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/smc37c669.c	Mon Jan 14 16:31:16 2002
@@ -2528,7 +2528,7 @@
  *
  * RETURNS:
  *
- *      Nothing
+ *      1 if the chip found, 0 otherwise
  *
  * ARGUMENTS:
  *
@@ -2539,7 +2539,7 @@
  *      None
  *
  */
-void __init SMC669_Init ( int index )
+int __init SMC669_Init ( int index )
 {
     SMC37c669_CONFIG_REGS *SMC_base;
     unsigned long flags;
@@ -2602,11 +2602,13 @@
 	__restore_flags(flags);
         printk( "SMC37c669 Super I/O Controller found @ 0x%lx\n",
 		(unsigned long) SMC_base );
+	return 1;
     }
     else {
 	__restore_flags(flags);
 #if SMC_DEBUG
         printk( "No SMC37c669 Super I/O Controller found\n" );
 #endif
+	return 0;
     }
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/sys_miata.c linux/arch/alpha/kernel/sys_miata.c
--- linux.orig/arch/alpha/kernel/sys_miata.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/kernel/sys_miata.c	Mon Jan 14 16:31:16 2002
@@ -230,7 +230,15 @@
 miata_init_pci(void)
 {
 	cia_init_pci();
-	SMC669_Init(0); /* it might be a GL (fails harmlessly if not) */
+	/* The PYXIS has data corruption problem with scatter/gather
+	   burst DMA reads crossing 8K boundary. It had been fixed
+	   with off-chip logic on all PYXIS systems except first
+	   MIATAs, so disable SG DMA on such machines. */
+	if (!SMC669_Init(0)) {	/* MIATA GL has SMC37c669 Super I/O */
+		alpha_mv.mv_pci_tbi = NULL; 
+		printk(KERN_INFO "pci: pyxis 8K boundary dma bug - "
+				 "sg dma disabled\n");
+	}
 	es1888_init();
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/kernel/time.c linux/arch/alpha/kernel/time.c
--- linux.orig/arch/alpha/kernel/time.c	Sat Nov 17 19:47:44 2001
+++ linux/arch/alpha/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -379,8 +379,6 @@
 	/* Startup the timer source. */
 	alpha_mv.init_rtc();
 
-	do_get_fast_time = do_gettimeofday;
-
 	/*
 	 * If we had wanted SRM console printk echoing early, undo it now.
 	 *
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/alpha/lib/dec_and_lock.c linux/arch/alpha/lib/dec_and_lock.c
--- linux.orig/arch/alpha/lib/dec_and_lock.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/alpha/lib/dec_and_lock.c	Mon Jan  7 14:18:34 2002
@@ -27,6 +27,7 @@
 	br	$atomic_dec_and_lock_1..ng	\n\
 	.subsection 2				\n\
 4:	br	1b				\n\
+	.previous				\n\
 	.end atomic_dec_and_lock");
 
 static int __attribute__((unused))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/arm/kernel/entry-common.S linux/arch/arm/kernel/entry-common.S
--- linux.orig/arch/arm/kernel/entry-common.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/arm/kernel/entry-common.S	Thu Jan 10 20:08:20 2002
@@ -22,12 +22,10 @@
  * Our do_softirq out of line code.  See include/asm-arm/softirq.h for
  * the calling assembly.
  */
-	.section ".text.lock","ax"
 ENTRY(__do_softirq)
 	stmfd	sp!, {r0 - r3, ip, lr}
 	bl	do_softirq
 	ldmfd	sp!, {r0 - r3, ip, pc}
-	.previous
 
 	.align	5
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/arm/kernel/semaphore.c linux/arch/arm/kernel/semaphore.c
--- linux.orig/arch/arm/kernel/semaphore.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/arm/kernel/semaphore.c	Thu Jan 10 20:08:20 2002
@@ -177,8 +177,7 @@
  * value in some cases..
  */
 #ifdef CONFIG_CPU_26
-asm("	.section	.text.lock, \"ax\"
-	.align	5
+asm("	.align	5
 	.globl	__down_failed
 __down_failed:
 	stmfd	sp!, {r0 - r3, lr}
@@ -212,13 +211,11 @@
 	bl	__up
 	ldmfd	sp!, {r0 - r3, pc}^
 
-	.previous
 	");
 
 #else
 /* 32 bit version */
-asm("	.section	.text.lock, \"ax\"
-	.align	5
+asm("	.align	5
 	.globl	__down_failed
 __down_failed:
 	stmfd	sp!, {r0 - r3, lr}
@@ -252,7 +249,6 @@
 	bl	__up
 	ldmfd	sp!, {r0 - r3, pc}
 
-	.previous
 	");
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/arm/vmlinux-armo.lds.in linux/arch/arm/vmlinux-armo.lds.in
--- linux.orig/arch/arm/vmlinux-armo.lds.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/arm/vmlinux-armo.lds.in	Thu Jan 10 20:08:20 2002
@@ -48,7 +48,6 @@
 			*(.text)
 			*(.fixup)
 			*(.gnu.warning)
-			*(.text.lock)	/* out-of-line lock text */
 			*(.rodata)
 			*(.rodata.*)
 			*(.glue_7)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/arm/vmlinux-armv.lds.in linux/arch/arm/vmlinux-armv.lds.in
--- linux.orig/arch/arm/vmlinux-armv.lds.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/arm/vmlinux-armv.lds.in	Thu Jan 10 20:08:20 2002
@@ -43,7 +43,6 @@
 			*(.text)
 			*(.fixup)
 			*(.gnu.warning)
-			*(.text.lock)	/* out-of-line lock text */
 			*(.rodata)
 			*(.rodata.*)
 			*(.glue_7)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/Makefile linux/arch/cris/Makefile
--- linux.orig/arch/cris/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/Makefile	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.22 2001/10/01 14:42:38 bjornw Exp $
+# $Id: Makefile,v 1.26 2001/11/16 17:42:17 pkj Exp $
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -37,10 +37,9 @@
 
 OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S
 
-# normally, gcc on a linux box adds __linux__ but we do it "manually"
-# -mlinux enables -march=v10, -fno-underscores among others
+# -mlinux enables -march=v10, -fno-underscores, -D__linux__ among others
 
-CFLAGS := $(CFLAGS) -mlinux -fno-strict-aliasing -pipe -D__linux__
+CFLAGS := $(CFLAGS) -mlinux -pipe
 
 ifdef CONFIG_ETRAX_KGDB
 CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/boot/Makefile linux/arch/cris/boot/Makefile
--- linux.orig/arch/cris/boot/Makefile	Fri Feb  9 00:32:44 2001
+++ linux/arch/cris/boot/Makefile	Tue Feb  5 17:02:59 2002
@@ -8,6 +8,8 @@
 	@$(MAKE) -C compressed vmlinuz
 
 dep:
+	@$(MAKE) -C compressed depend
+	@$(MAKE) -C rescue depend
 
 clean:
 	rm -f zImage tools/build compressed/vmlinux.out
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/boot/compressed/Makefile linux/arch/cris/boot/compressed/Makefile
--- linux.orig/arch/cris/boot/compressed/Makefile	Tue May  1 23:04:56 2001
+++ linux/arch/cris/boot/compressed/Makefile	Tue Feb  5 17:02:59 2002
@@ -4,6 +4,13 @@
 # create a compressed vmlinux image from the original vmlinux files and romfs
 #
 
+ifndef TOPDIR
+TOPDIR = ../../../..
+HPATH = $(TOPDIR)/include
+export HPATH
+endif
+
+
 CC = gcc-cris -melf -I $(TOPDIR)/include
 CFLAGS = -O2
 LD = ld-cris
@@ -37,4 +44,10 @@
 
 clean:
 	rm -f piggy.img vmlinuz vmlinuz.o
+
+depend:
+	$(CC) -M *.S *.c > .depend
+
+-include .depend
+
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/boot/rescue/Makefile linux/arch/cris/boot/rescue/Makefile
--- linux.orig/arch/cris/boot/rescue/Makefile	Mon Oct  8 18:43:54 2001
+++ linux/arch/cris/boot/rescue/Makefile	Tue Feb  5 17:02:59 2002
@@ -3,6 +3,8 @@
 #
 ifndef TOPDIR
 TOPDIR = ../../../..
+HPATH = $(TOPDIR)/include
+export HPATH
 endif
 CC = gcc-cris -mlinux -I $(TOPDIR)/include
 CFLAGS = -O2
@@ -35,7 +37,7 @@
 	dd if=kimagerescue_tmp.bin of=kimagerescue.bin bs=1 count=784
 	rm ktr.bin tmp2423 kimagerescue_tmp.bin
 
-head.o: head.S
+head.o: head.S 
 	$(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
 
 testrescue.o: testrescue.S
@@ -52,3 +54,8 @@
 modules:
 
 modules-install:
+
+depend:
+	$(CC) -M *.S > .depend
+
+-include .depend
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/boot/rescue/head.S linux/arch/cris/boot/rescue/head.S
--- linux.orig/arch/cris/boot/rescue/head.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/boot/rescue/head.S	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.8 2001/10/03 17:15:15 bjornw Exp $
+/* $Id: head.S,v 1.10 2001/11/08 15:10:16 starvik Exp $
  * 
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
@@ -114,7 +114,7 @@
 
 #define NOP_DI 0xf025050f
 #define RAM_INIT_MAGIC 0x56902387
-		
+
 	.text
 	
 	;; This is the entry point of the rescue code
@@ -144,7 +144,13 @@
 jumptarget:	
 	.dword	0xffffffff	; can be overwritten later to insert new code
 	
-no_newjump:	
+no_newjump:
+#ifdef CONFIG_ETRAX_ETHERNET		
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d 0x3, $r0    ; enable = on, phy = mii_clk
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+	
 	;; We need to setup the bus registers before we start using the DRAM
 #include "../../lib/dram_init.S"
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/config.in linux/arch/cris/config.in
--- linux.orig/arch/cris/config.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/config.in	Tue Jan  8 16:00:08 2002
@@ -35,6 +35,9 @@
 bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
 
 bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
+	bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+fi
 
 endmenu
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/axisflashmap.c linux/arch/cris/drivers/axisflashmap.c
--- linux.orig/arch/cris/drivers/axisflashmap.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/axisflashmap.c	Tue Jan  8 16:00:08 2002
@@ -11,6 +11,14 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.17  2001/11/12 19:42:38  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.16  2001/11/08 11:18:58  jonashg
+ * Always read from uncached address to avoid problems with flushing
+ * cachelines after write and MTD-erase. No performance loss have been
+ * seen yet.
+ *
  * Revision 1.15  2001/10/19 12:41:04  jonashg
  * Name of probe has changed in MTD.
  *
@@ -121,7 +129,7 @@
 static void flash_copy_from(struct map_info *map, void *to,
 			    unsigned long from, ssize_t len)
 {
-	memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len);
+	memcpy(to, (void *)(FLASH_UNCACHED_ADDR + from), len);
 }
 
 static void flash_write8(struct map_info *map, __u8 d, unsigned long adr)
@@ -237,7 +245,7 @@
 	int use_default_ptable = 1; /* Until proven otherwise */
 	const char *pmsg = "  /dev/flash%d at 0x%x, size 0x%x\n";
 
-	printk(KERN_NOTICE "Axis flash mapping: %x at %x\n",
+	printk(KERN_NOTICE "Axis flash mapping: %x at %lx\n",
 	       WINDOW_SIZE, FLASH_CACHED_ADDR);
 
 #ifdef CONFIG_MTD_CFI
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/ethernet.c linux/arch/cris/drivers/ethernet.c
--- linux.orig/arch/cris/drivers/ethernet.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/ethernet.c	Tue Feb  5 17:02:59 2002
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.18 2001/10/03 14:40:43 jonashg Exp $
+/* $Id: ethernet.c,v 1.22 2002/01/30 07:48:22 matsfg Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,6 +7,24 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
+ * Revision 1.22  2002/01/30 07:48:22  matsfg
+ * Initiate R_NETWORK_TR_CTRL
+ *
+ * Revision 1.21  2001/11/23 11:54:49  starvik
+ * Added IFF_PROMISC and IFF_ALLMULTI handling in set_multicast_list
+ * Removed compiler warnings
+ *
+ * Revision 1.20  2001/11/12 19:26:00  pkj
+ * * Corrected e100_negotiate() to not assign half to current_duplex when
+ *   it was supposed to compare them...
+ * * Cleaned up failure handling in e100_open().
+ * * Fixed compiler warnings.
+ *
+ * Revision 1.19  2001/11/09 07:43:09  starvik
+ * Added full duplex support
+ * Added ioctl to set speed and duplex
+ * Clear LED timer only runs when LED is lit
+ *
  * Revision 1.18  2001/10/03 14:40:43  jonashg
  * Update rx_bytes counter.
  *
@@ -104,6 +122,7 @@
 #include <asm/dma.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
+#include <asm/ethernet.h>
 
 //#define ETHDEBUG
 #define D(x)
@@ -120,7 +139,7 @@
 
 static struct sockaddr default_mac = {
 	0,
-        { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+	{ 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
 };
 
 /* Information that need to be kept for each board. */
@@ -136,6 +155,14 @@
 };
 
 
+/* Duplex settings */
+enum duplex
+{
+	half,
+	full,
+	autoneg
+};
+
 /* Dma descriptors etc. */
 
 #define RX_BUF_SIZE 32768
@@ -148,9 +175,18 @@
 /* 
 ** MDIO constants.
 */
-#define MDIO_BASE_STATUS_REG   0x1
-#define MDIO_BASE_CONTROL_REG  0x0
-#define MDIO_LINK_UP_MASK      0x4
+#define MDIO_BASE_STATUS_REG                0x1
+#define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_BC_NEGOTIATE                0x0200
+#define MDIO_BC_FULL_DUPLEX_MASK         0x0100
+#define MDIO_BC_AUTO_NEG_MASK            0x1000
+#define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_ADVERTISMENT_REG               0x4
+#define MDIO_ADVERT_100_FD                0x100
+#define MDIO_ADVERT_100_HD                0x080
+#define MDIO_ADVERT_10_FD                 0x040
+#define MDIO_ADVERT_10_HD                 0x020
+#define MDIO_LINK_UP_MASK                   0x4
 #define MDIO_START                          0x1
 #define MDIO_READ                           0x2
 #define MDIO_WRITE                          0x1
@@ -158,6 +194,7 @@
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
+#define MDIO_FULL_DUPLEX_IND                0x1
 #define MDIO_SPEED                          0x2
 #define MDIO_PHYS_ADDR                      0x0
 
@@ -165,18 +202,25 @@
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
 #define NET_FLASH_PAUSE                (HZ/100) /* 10 ms */
 #define NET_LINK_UP_CHECK_INTERVAL       (2*HZ) /* 2 s   */
+#define NET_DUPLEX_CHECK_INTERVAL        (2*HZ) /* 2 s   */
 
 #define NO_NETWORK_ACTIVITY 0
 #define NETWORK_ACTIVITY    1
 
 #define RX_DESC_BUF_SIZE   256
 #define NBR_OF_RX_DESC     (RX_BUF_SIZE / \
-			    RX_DESC_BUF_SIZE)
+                            RX_DESC_BUF_SIZE)
 
 #define GET_BIT(bit,val)   (((val) >> (bit)) & 0x01)
 
+/* Define some macros to access ETRAX 100 registers */
+#define SETF(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_FIELD(##reg##, field, val)
+#define SETS(var, reg, field, val) var = (var & ~IO_MASK(##reg##, field)) | \
+					  IO_STATE(##reg##, field, val)
+
 static etrax_dma_descr *myNextRxDesc;  /* Points to the next descriptor to
-					  to be processed */
+                                          to be processed */
 static etrax_dma_descr *myLastRxDesc;  /* The last processed descriptor */
 static etrax_dma_descr *myPrevRxDesc;  /* The descriptor right before myNextRxDesc */
 
@@ -187,13 +231,21 @@
 
 static struct sk_buff *tx_skb;
 
+static unsigned int network_rec_config_shadow = 0;
+
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
-static int current_speed;
+static int current_speed; /* Speed read from tranceiver */
+static int current_speed_selection; /* Speed selected by user */
 static int led_next_time;
 static int led_active;
 
+/* Duplex */
+static struct timer_list duplex_timer;
+static int full_duplex;
+static enum duplex current_duplex;
+
 /* Index to functions, as function prototypes. */
 
 static int etrax_ethernet_init(struct net_device *dev);
@@ -206,6 +258,8 @@
 static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 static void e100_rx(struct net_device *dev);
 static int e100_close(struct net_device *dev);
+static int e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static void e100_tx_timeout(struct net_device *dev);
 static struct net_device_stats *e100_get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void e100_hardware_send_packet(char *buf, int length);
@@ -213,6 +267,11 @@
 static void update_tx_stats(struct net_device_stats *);
 
 static void e100_check_speed(unsigned long dummy);
+static void e100_set_speed(unsigned long speed);
+static void e100_check_duplex(unsigned long dummy);
+static void e100_set_duplex(enum duplex);
+static void e100_negotiate(void);
+
 static unsigned short e100_get_mdio_reg(unsigned char reg_num);
 static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
 static void e100_send_mdio_bit(unsigned char bit);
@@ -278,6 +337,8 @@
 	dev->get_stats          = e100_get_stats;
 	dev->set_multicast_list = set_multicast_list;
 	dev->set_mac_address    = e100_set_mac_address;
+	dev->do_ioctl           = e100_ioctl;
+	dev->tx_timeout         = e100_tx_timeout;
 
 	/* set the default MAC address */
 
@@ -287,7 +348,7 @@
 
 	/* Initialise receive descriptors */
 
-	for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+	for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
 		RxDescList[i].ctrl   = 0;
 		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
 		RxDescList[i].next   = virt_to_phys(&RxDescList[i + 1]);
@@ -313,12 +374,18 @@
 	/* Initialize speed indicator stuff. */
 
 	current_speed = 10;
+	current_speed_selection = 0; /* Auto */
 	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
 	speed_timer.function = e100_check_speed;
 	add_timer(&speed_timer);
+        
 	clear_led_timer.function = e100_clear_network_leds;
-	clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
+        
+	full_duplex = 0;
+	current_duplex = autoneg;
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;		
+	duplex_timer.function = e100_check_duplex;
+	add_timer(&duplex_timer);
 
 	return 0;
 }
@@ -335,7 +402,7 @@
 
 	/* remember it */
 
-        memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
 
 	/* Write it to the hardware.
 	 * Note the way the address is wrapped:
@@ -409,21 +476,21 @@
 
 	if (request_irq(NETWORK_DMA_RX_IRQ_NBR, e100rx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit0;
 	}
 
 	/* allocate the irq corresponding to the transmitting DMA */
 
 	if (request_irq(NETWORK_DMA_TX_IRQ_NBR, e100tx_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit1;
 	}
 
 	/* allocate the irq corresponding to the network errors etc */
 
 	if (request_irq(NETWORK_STATUS_IRQ_NBR, e100nw_interrupt, 0,
 			cardname, (void *)dev)) {
-		goto grace_exit;
+		goto grace_exit2;
 	}
 
 	/*
@@ -431,18 +498,12 @@
 	 * and clean up on failure.
 	 */
 
-	if(request_dma(NETWORK_TX_DMA_NBR, cardname)) {
-		goto grace_exit;
+	if (request_dma(NETWORK_TX_DMA_NBR, cardname)) {
+		goto grace_exit3;
 	}
 
-	if(request_dma(NETWORK_RX_DMA_NBR, cardname)) {
-	grace_exit:
-		/* this will cause some 'trying to free free irq' but what the heck... */
-		free_dma(NETWORK_TX_DMA_NBR);
-		free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
-		free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
-		return -EAGAIN;
+	if (request_dma(NETWORK_RX_DMA_NBR, cardname)) {
+		goto grace_exit4;
 	}
 
 	/* give the HW an idea of what MAC address we want */
@@ -459,15 +520,25 @@
 
 	*R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */
 #else
-	*R_NETWORK_REC_CONFIG =
-		IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) |
-		IO_STATE(R_NETWORK_REC_CONFIG, ma0,       enable);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, broadcast, receive);
+	SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, ma0, enable);
+	SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+	*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 #endif
 
 	*R_NETWORK_GEN_CONFIG =
 		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
 		IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
 
+        *R_NETWORK_TR_CTRL = 
+                IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr) |
+                IO_STATE(R_NETWORK_TR_CTRL, delay, none) |
+                IO_STATE(R_NETWORK_TR_CTRL, cancel, dont) |
+                IO_STATE(R_NETWORK_TR_CTRL, cd, enable) |
+                IO_STATE(R_NETWORK_TR_CTRL, retry, enable) |
+                IO_STATE(R_NETWORK_TR_CTRL, pad, enable) |
+                IO_STATE(R_NETWORK_TR_CTRL, crc, enable);
+
 	save_flags(flags);
 	cli();
 
@@ -507,6 +578,17 @@
 	netif_start_queue(dev);
 
 	return 0;
+
+grace_exit4:
+	free_dma(NETWORK_TX_DMA_NBR);
+grace_exit3:
+	free_irq(NETWORK_STATUS_IRQ_NBR, (void *)dev);
+grace_exit2:
+	free_irq(NETWORK_DMA_TX_IRQ_NBR, (void *)dev);
+grace_exit1:
+	free_irq(NETWORK_DMA_RX_IRQ_NBR, (void *)dev);
+grace_exit0:
+	return -EAGAIN;
 }
 
 
@@ -532,10 +614,119 @@
 	add_timer(&speed_timer);
 }
 
+static void
+e100_negotiate(void)
+{
+	unsigned short cmd;
+	unsigned short data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	int bitCounter;
+
+	/* Discard old speed and duplex settings */
+	data &= ~(MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+	          MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD);
+  
+	switch (current_speed_selection) {
+		case 10 :
+			if (current_duplex == full)
+				data |= MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_10_HD |  MDIO_ADVERT_10_FD;
+			break;
+
+		case 100 :
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD;
+			else
+				data |= MDIO_ADVERT_100_HD |  MDIO_ADVERT_100_FD;
+			break;
+
+		case 0 : /* Auto */
+			 if (current_duplex == full)
+				data |= MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD;
+			else if (current_duplex == half)
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_10_HD;
+			else
+				data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+			break;
+
+		default : /* assume autoneg speed and duplex */
+			data |= MDIO_ADVERT_100_HD | MDIO_ADVERT_100_FD | 
+			        MDIO_ADVERT_10_FD | MDIO_ADVERT_10_HD;
+	}
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_ADVERTISMENT_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}
+
+	/* Renegotiate with link partner */
+	data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
+	data |= MDIO_BC_NEGOTIATE;
+
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	      (MDIO_BASE_CONTROL_REG<< 2);
+
+	e100_send_mdio_cmd(cmd, 1);
+
+	/* Data... */
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
+		e100_send_mdio_bit(GET_BIT(bitCounter, data));
+	}  
+}
+
+static void
+e100_set_speed(unsigned long speed)
+{
+	current_speed_selection = speed;
+	e100_negotiate();
+}
+
+static void
+e100_check_duplex(unsigned long dummy)
+{
+	unsigned long data;
+
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+        
+	if (data & MDIO_FULL_DUPLEX_IND) {
+		if (!full_duplex) { /* Duplex changed to full? */
+			full_duplex = 1;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	} else { /* half */
+		if (full_duplex) { /* Duplex changed to half? */
+			full_duplex = 0;
+			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+		}
+	}
+
+	/* Reinitialize the timer. */
+	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;
+	add_timer(&duplex_timer);
+}
+
+static void 
+e100_set_duplex(enum duplex new_duplex)
+{
+	current_duplex = new_duplex;
+	e100_negotiate();
+}
+
+
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
 {
-	unsigned long flags;
 	unsigned short cmd;    /* Data to be sent on MDIO port */
 	unsigned short data;   /* Data read from MDIO */
 	int bitCounter;
@@ -549,7 +740,7 @@
 	data = 0;
 	
 	/* Data... */
-	for(bitCounter=15; bitCounter>=0 ; bitCounter--) {
+	for (bitCounter=15; bitCounter>=0 ; bitCounter--) {
 		data |= (e100_receive_mdio_bit() << bitCounter);
 	}
 
@@ -563,14 +754,14 @@
 	unsigned char data = 0x2;
 	
 	/* Preamble */
-	for(bitCounter = 31; bitCounter>= 0; bitCounter--)
+	for (bitCounter = 31; bitCounter>= 0; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE));
 
-	for(bitCounter = 15; bitCounter >= 2; bitCounter--)
+	for (bitCounter = 15; bitCounter >= 2; bitCounter--)
 		e100_send_mdio_bit(GET_BIT(bitCounter, cmd));
 
 	/* Turnaround */
-	for(bitCounter = 1; bitCounter >= 0 ; bitCounter--)
+	for (bitCounter = 1; bitCounter >= 0 ; bitCounter--)
 		if (write_cmd)
 			e100_send_mdio_bit(GET_BIT(bitCounter, data));
 		else
@@ -606,7 +797,6 @@
 static void 
 e100_reset_tranceiver(void)
 {
-	unsigned long flags;
 	unsigned short cmd;
 	unsigned short data;
 	int bitCounter;
@@ -619,7 +809,7 @@
 	
 	data |= 0x8000;
 	
-	for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
+	for (bitCounter = 15; bitCounter >= 0 ; bitCounter--) {
 		e100_send_mdio_bit(GET_BIT(bitCounter, data));
 	}
 }
@@ -706,15 +896,14 @@
 	struct net_device *dev = (struct net_device *)dev_id;
 	unsigned long irqbits = *R_IRQ_MASK2_RD;
  
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
-
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
 		/* acknowledge the eop interrupt */
 
 		*R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do);
 
 		/* check if one or more complete packets were indeed received */
 
-		while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
+		while (*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) {
 			/* Take out the buffer and give it to the OS, then
 			 * allocate a new buffer to put a packet in.
 			 */
@@ -747,8 +936,7 @@
 	struct net_local *np = (struct net_local *)dev->priv;
 
 	/* check for a dma0_eop interrupt */
-	if(irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
-		
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) { 
 		/* This protects us from concurrent execution of
 		 * our dev->hard_start_xmit function above.
 		 */
@@ -759,7 +947,7 @@
 
 		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
 
-		if(*R_DMA_CH0_FIRST == 0 && tx_skb) {
+		if (*R_DMA_CH0_FIRST == 0 && tx_skb) {
 			np->stats.tx_bytes += tx_skb->len;
 			np->stats.tx_packets++;
 			/* dma is ready with the transmission of the data in tx_skb, so now
@@ -784,19 +972,19 @@
 	unsigned long irqbits = *R_IRQ_MASK0_RD;
 
 	/* check for underrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet receiver underrun!\n"));
 	}
 
 	/* check for overrun irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, overrun, active)) { 
 		update_rx_stats(&np->stats); /* this will ack the irq */
 		D(printk("ethernet receiver overrun!\n"));
 	}
 	/* check for excessive collision irq */
-	if(irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
 		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
 		np->stats.tx_errors++;
 		D(printk("ethernet excessive collisions!\n"));
@@ -809,11 +997,13 @@
 e100_rx(struct net_device *dev)
 {
 	struct sk_buff *skb;
-	int length=0;
-	int i;
+	int length = 0;
 	struct net_local *np = (struct net_local *)dev->priv;
 	struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
 	unsigned char *skb_data_ptr;
+#ifdef ETHDEBUG
+	int i;
+#endif
 
 	if (!led_active && jiffies > led_next_time) {
 		/* light the network leds depending on the current speed. */
@@ -822,6 +1012,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* If the packet is broken down in many small packages then merge
@@ -842,7 +1033,7 @@
 	printk("Got a packet of length %d:\n", length);
 	/* dump the first bytes in the packet */
 	skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
-	for(i = 0; i < 8; i++) {
+	for (i = 0; i < 8; i++) {
 		printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8,
 		       skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3],
 		       skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]);
@@ -869,7 +1060,7 @@
 
 	/* this loop can be made using max two memcpy's if optimized */
 
-	while(mySaveRxDesc != myNextRxDesc) {
+	while (mySaveRxDesc != myNextRxDesc) {
 		memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
 		       mySaveRxDesc->sw_len);
 		skb_data_ptr += mySaveRxDesc->sw_len;
@@ -946,6 +1137,37 @@
 	return 0;
 }
 
+static int
+e100_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	/* Maybe default should return -EINVAL instead? */
+	switch (cmd) {
+		case SET_ETH_SPEED_10:                  /* 10 Mbps */
+			e100_set_speed(10);
+			break;
+		case SET_ETH_SPEED_100:                /* 100 Mbps */
+			e100_set_speed(100);
+			break;
+		case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
+			e100_set_speed(0);
+			break;
+		case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+			e100_set_duplex(half);
+			break;
+		case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
+			e100_set_duplex(full);
+			break;
+		case SET_ETH_DUPLEX_AUTO:             /* Autonegotiate duplex*/
+			e100_set_duplex(autoneg);
+			break;
+		default: /* Auto neg */
+			e100_set_speed(0);
+			e100_set_duplex(autoneg);
+			break;
+	}
+	return 0;
+}
+
 static void
 update_rx_stats(struct net_device_stats *es)
 {
@@ -996,26 +1218,31 @@
 	int num_addr = dev->mc_count;
 	unsigned long int lo_bits;
 	unsigned long int hi_bits;
-	if (num_addr == -1)
+	if (dev->flags & IFF_PROMISC)
 	{
 		/* promiscuous mode */
 		lo_bits = 0xfffffffful;
 		hi_bits = 0xfffffffful;
 
-                /* Enable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, individual, receive);
+		/* Enable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, receive);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
+	} else if (dev->flags & IFF_ALLMULTI) {
+		/* enable all multicasts */
+		lo_bits = 0xfffffffful;
+		hi_bits = 0xfffffffful;
+
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else if (num_addr == 0) {
 		/* Normal, clear the mc list */
 		lo_bits = 0x00000000ul;
 		hi_bits = 0x00000000ul;
 
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG =  network_rec_config_shadow;
 	} else {
 		/* MC mode, receive normal and MC packets */
 		char hash_ix;
@@ -1057,10 +1284,9 @@
 			}
 			dmi = dmi->next;
 		}
-                /* Disable individual receive */
-		*R_NETWORK_REC_CONFIG =
-		  IO_STATE(R_NETWORK_REC_CONFIG, broadcast,  receive) |
-		  IO_STATE(R_NETWORK_REC_CONFIG, ma0,        enable);
+		/* Disable individual receive */
+		SETS(network_rec_config_shadow, R_NETWORK_REC_CONFIG, individual, discard);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 	}
 	*R_NETWORK_GA_0 = lo_bits;
 	*R_NETWORK_GA_1 = hi_bits;
@@ -1078,6 +1304,7 @@
 		/* Set the earliest time we may clear the LED */
 		led_next_time = jiffies + NET_FLASH_TIME;
 		led_active = 1;
+		mod_timer(&clear_led_timer, jiffies + HZ/10);
 	}
 
 	/* configure the tx dma descriptor */
@@ -1095,16 +1322,13 @@
 static void
 e100_clear_network_leds(unsigned long dummy)
 {
-        if (led_active && jiffies > led_next_time) {
+	if (led_active && jiffies > led_next_time) {
 		e100_set_network_leds(NO_NETWORK_ACTIVITY);
 
 		/* Set the earliest time we may set the LED */
 		led_next_time = jiffies + NET_FLASH_PAUSE;
 		led_active = 0;
 	}
-
-        clear_led_timer.expires = jiffies + HZ/10;
-	add_timer(&clear_led_timer);
 }
 
 static void
@@ -1143,7 +1367,7 @@
 
 	d->init = etrax_ethernet_init;
 
-	if(register_netdev(d) == 0)
+	if (register_netdev(d) == 0)
 		return 0;
 	else
 		return -ENODEV;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/gpio.c linux/arch/cris/drivers/gpio.c
--- linux.orig/arch/cris/drivers/gpio.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/gpio.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.11 2001/10/30 14:39:12 johana Exp $
+/* $Id: gpio.c,v 1.12 2001/11/12 19:42:15 pkj Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,10 @@
  *             Johan Adolfsson  (read/set directions, write)
  *
  * $Log: gpio.c,v $
+ * Revision 1.12  2001/11/12 19:42:15  pkj
+ * * Corrected return values from gpio_leds_ioctl().
+ * * Fixed compiler warnings.
+ *
  * Revision 1.11  2001/10/30 14:39:12  johana
  * Added D() around gpio_write printk.
  *
@@ -74,7 +78,9 @@
 
 static char gpio_name[] = "etrax gpio";
 
+#if 0
 static wait_queue_head_t *gpio_wq;
+#endif
 
 static int gpio_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg);
@@ -143,7 +149,7 @@
 {
 	/* TODO poll on alarms! */
 #if 0
-        if(!ANYTHING_WANTED) {
+        if (!ANYTHING_WANTED) {
 		D(printk("gpio_select sleeping task\n"));
 	        select_wait(&gpio_wq, table);
 	        return 0;
@@ -160,16 +166,14 @@
 	unsigned char data, clk_mask, data_mask, write_msb;
 	unsigned long flags;
 	ssize_t retval = count;
-	if (verify_area(VERIFY_READ, buf, count))
-	{
+	if (verify_area(VERIFY_READ, buf, count)) {
 		return -EFAULT;
 	}
 	clk_mask = priv->clk_mask;
 	data_mask = priv->data_mask;
 	/* It must have been configured using the IO_CFG_WRITE_MODE */
 	/* Perhaps a better error code? */
-	if (clk_mask == 0 || data_mask == 0) 
-	{
+	if (clk_mask == 0 || data_mask == 0) {
 		return -EPERM;
 	}
 	write_msb = priv->write_msb;
@@ -178,7 +182,7 @@
 		int i;
 		data = *buf++;
 		if (priv->write_msb) {
-			for (i = 7; i>=0;i--) {
+			for (i = 7; i >= 0;i--) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -190,7 +194,7 @@
 				restore_flags(flags);
 			}
 		} else {
-			for (i = 0; i<=7;i++) {
+			for (i = 0; i <= 7;i++) {
 				save_flags(flags); cli();
 				*priv->port = *priv->shadow &= ~clk_mask;
 				if (data & 1<<i)
@@ -212,13 +216,13 @@
 	struct gpio_private *priv;
 	int p = MINOR(inode->i_rdev);
 
-	if(p >= NUM_PORTS && p != LEDS)
+	if (p >= NUM_PORTS && p != LEDS)
 		return -EINVAL;
 
 	priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), 
 					      GFP_KERNEL);
 
-	if(!priv)
+	if (!priv)
 		return -ENOMEM;
 
 	priv->minor = p;
@@ -254,10 +258,10 @@
 
 	/* unlink from alarmlist and free the private structure */
 
-	if(p == todel) {
+	if (p == todel) {
 		alarmlist = todel->next;
 	} else {
-		while(p->next != todel)
+		while (p->next != todel)
 			p = p->next;
 		p->next = todel->next;
 	}
@@ -280,7 +284,7 @@
 {
 	unsigned long flags;
 	struct gpio_private *priv = (struct gpio_private *)file->private_data;
-	if(_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
+	if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
 		return -EINVAL;
 	}
 
@@ -353,7 +357,7 @@
 			if (!((priv->clk_mask & priv->changeable_bits) &&
 			      (priv->data_mask & priv->changeable_bits) &&
 			      (priv->clk_mask & *priv->dir_shadow) &&
-			      (priv->data_mask & *priv->dir_shadow)) )
+			      (priv->data_mask & *priv->dir_shadow)))
 			{
 				priv->clk_mask = 0;
 				priv->data_mask = 0;
@@ -361,7 +365,7 @@
 			}
 			break;
 		default:
-			if(priv->minor == LEDS)
+			if (priv->minor == LEDS)
 				return gpio_leds_ioctl(cmd, arg);
                         else
 				return -EINVAL;
@@ -375,6 +379,7 @@
 {
 	unsigned char green;
 	unsigned char red;
+
 	switch (_IOC_NR(cmd)) {
 		case IO_LEDACTIVE_SET:
 			green = ((unsigned char) arg) & 1;
@@ -382,14 +387,20 @@
 			LED_ACTIVE_SET_G(green);
 			LED_ACTIVE_SET_R(red);
 			break;
-                case IO_LED_SETBIT:                 
-                        LED_BIT_SET(arg);
-                       break;
-                case IO_LED_CLRBIT:
-                        LED_BIT_CLR(arg);
+
+		case IO_LED_SETBIT:                 
+			LED_BIT_SET(arg);
+			break;
+
+		case IO_LED_CLRBIT:
+			LED_BIT_CLR(arg);
+			break;
+
 		default:
 			return -EINVAL;
 	}
+
+	return 0;
 }
 
 struct file_operations gpio_fops = {
@@ -406,30 +417,32 @@
 static __init int
 gpio_init(void)
 {
-	int res,i;
+	extern void init_ioremap(void);
+	int res;
+#if defined (CONFIG_ETRAX_CSP0_LEDS)
+	int i;
+#endif
 
 	/* do the formalities */
 
 	res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
-	if(res < 0) {
+	if (res < 0) {
 		printk(KERN_ERR "gpio: couldn't get a major number.\n");
 		return res;
 	}
 
         /* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS)         || defined (CONFIG_ETRAX_PB_LEDS) 
-
-        init_ioremap();
-        LED_NETWORK_SET(0);
-        LED_ACTIVE_SET(0);
-        LED_DISK_READ(0);
-        LED_DISK_WRITE(0);        
+#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) 
+	init_ioremap();
+	LED_NETWORK_SET(0);
+	LED_ACTIVE_SET(0);
+	LED_DISK_READ(0);
+	LED_DISK_WRITE(0);        
 
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
-        for( i = 0; i < 32; i ++)
-        {
-            LED_BIT_SET(i);
-        }
+	for (i = 0; i < 32; i++) {
+		LED_BIT_SET(i);
+	}
 #endif
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/serial.c linux/arch/cris/drivers/serial.c
--- linux.orig/arch/cris/drivers/serial.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/serial.c	Tue Feb  5 17:03:00 2002
@@ -1,4 +1,4 @@
-/* $Id: serial.c,v 1.23 2001/10/30 17:53:26 pkj Exp $
+/* $Id: serial.c,v 1.29 2002/01/14 16:10:01 pkj Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
@@ -7,6 +7,42 @@
  *      Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.29  2002/01/14 16:10:01  pkj
+ * Allocate the receive buffers dynamically. The static 4kB buffer was
+ * too small for the peaks. This means that we can get rid of the extra
+ * buffer and the copying to it. It also means we require less memory
+ * under normal operations, but can use more when needed (there is a
+ * cap at 64kB for safety reasons). If there is no memory available
+ * we panic(), and die a horrible death...
+ *
+ * Revision 1.28  2001/12/18 15:04:53  johana
+ * Cleaned up write_rs485() - now it works correctly without padding extra
+ * char.
+ * Added sane default initialisation of rs485.
+ * Added #ifdef around dummy variables.
+ *
+ * Revision 1.27  2001/11/29 17:00:41  pkj
+ * 2kB seems to be too small a buffer when using 921600 bps,
+ * so increase it to 4kB (this was already done for the elinux
+ * version of the serial driver).
+ *
+ * Revision 1.26  2001/11/19 14:20:41  pkj
+ * Minor changes to comments and unused code.
+ *
+ * Revision 1.25  2001/11/12 20:03:43  pkj
+ * Fixed compiler warnings.
+ *
+ * Revision 1.24  2001/11/12 15:10:05  pkj
+ * Total redesign of the receiving part of the serial driver.
+ * Uses eight chained descriptors to write to a 4kB buffer.
+ * This data is then serialised into a 2kB buffer. From there it
+ * is copied into the TTY's flip buffers when they become available.
+ * A lot of copying, and the sizes of the buffers might need to be
+ * tweaked, but all in all it should work better than the previous
+ * version, without the need to modify the TTY code in any way.
+ * Also note that erroneous bytes are now correctly marked in the
+ * flag buffers (instead of always marking the first byte).
+ *
  * Revision 1.23  2001/10/30 17:53:26  pkj
  * * Set info->uses_dma to 0 when a port is closed.
  * * Mark the timer1 interrupt as a fast one (SA_INTERRUPT).
@@ -123,12 +159,12 @@
  * Changed %ul to %lu in printf's
  *
  * Revision 1.47  2000/10/18 15:06:53  pkj
- * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and
- * CONFIG_SERIAL_PROC_ENTRY together.
+ * Compile correctly with CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST and
+ * CONFIG_ETRAX_SERIAL_PROC_ENTRY together.
  * Some clean-up of the /proc/serial file.
  *
  * Revision 1.46  2000/10/16 12:59:40  johana
- * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info.
+ * Added CONFIG_ETRAX_SERIAL_PROC_ENTRY for statistics and debug info.
  *
  * Revision 1.45  2000/10/13 17:10:59  pkj
  * Do not flush DMAs while flipping TTY buffers.
@@ -171,7 +207,7 @@
  * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS.
  *
  * Revision 1.36  2000/09/20 13:12:52  johana
- * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS:
+ * Support for CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS:
  *   Number of timer ticks between flush of receive fifo (1 tick = 10ms).
  *   Try 0-3 for low latency applications. Approx 5 for high load
  *   applications (e.g. PPP). Maybe this should be more adaptive some day...
@@ -255,7 +291,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.23 $";
+static char *serial_version = "$Revision: 1.29 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -272,6 +308,7 @@
 #include <linux/string.h>
 #include <linux/fcntl.h>
 #include <linux/mm.h>
+#include <linux/slab.h>
 #if (LINUX_VERSION_CODE >= 131343)
 #include <linux/init.h>
 #endif
@@ -302,6 +339,8 @@
 #include "serial_compat.h"
 #endif
 
+#define _INLINE_ inline
+
 static DECLARE_TASK_QUEUE(tq_serial);
 
 struct tty_driver serial_driver, callout_driver;
@@ -313,11 +352,6 @@
 #define SERIAL_TYPE_CALLOUT	2
 #endif
 
-#define DEBUG_LOG(line, string, value)
-
-/* Add an x here to log a lot of timer stuff */
-#define TIMERD(x)
-
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
@@ -337,6 +371,13 @@
 
 #define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
 
+#define SERIAL_DESCR_BUF_SIZE 256
+
+/* Add an x here to log a lot of timer stuff */
+#define TIMERD(x)
+
+#define DEBUG_LOG(line, string, value)
+
 #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
 /* Default number of timer ticks before flushing rx fifo 
  * When using "little data, low latency applications: use 0
@@ -345,8 +386,6 @@
 #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 
 #endif
 
-#define _INLINE_ inline
-
 static void change_speed(struct e100_serial *info);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int rs_write(struct tty_struct * tty, int from_user,
@@ -403,9 +442,9 @@
 static struct e100_serial rs_table[] = {
 	{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
 	  R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
-	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW,
+	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
 	  R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
-	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW,
+	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
           1
@@ -416,9 +455,9 @@
 #ifndef CONFIG_SVINTO_SIM
 	{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
 	  R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
-	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW,
+	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
 	  R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
-	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW,
+	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 3 ,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
           1
@@ -429,9 +468,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4,  /* uses DMA 2 and 3 */
 	  R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
-	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW,
+	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
 	  R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
-	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW,
+	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
           1
@@ -442,9 +481,9 @@
 
 	{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8,  /* uses DMA 4 and 5 */
 	  R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
-	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW,
+	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
 	  R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
-	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW,
+	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
 	  STD_FLAGS, DEF_RX, DEF_TX, 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
           1
@@ -462,9 +501,9 @@
 static struct termios *serial_termios[NR_PORTS];
 static struct termios *serial_termios_locked[NR_PORTS];
 
-#ifdef CONFIG_SERIAL_PROC_ENTRY
+#ifdef CONFIG_ETRAX_SERIAL_PROC_ENTRY
 #define PROCSTAT(x) x
-struct ser_statistics_type{
+struct ser_statistics_type {
 	int overrun_cnt;
 	int early_errors_cnt;
 	int ser_ints_ok_cnt;
@@ -484,7 +523,7 @@
 
 #define PROCSTAT(x)
 
-#endif /* CONFIG_SERIAL_PROC_ENTRY */
+#endif /* CONFIG_ETRAX_SERIAL_PROC_ENTRY */
 
 /* RS-485 */
 #if defined(CONFIG_ETRAX_RS485)
@@ -497,14 +536,20 @@
 /* For now we assume that all bits are on the same port for each serial port */
 
 /* Dummy shadow variables */
+#if !defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
 static unsigned char dummy_ser0 = 0x00;
-static unsigned char dummy_ser1 = 0x00;
-static unsigned char dummy_ser2 = 0x00;
-static unsigned char dummy_ser3 = 0x00;
-
 static unsigned char dummy_dir_ser0 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
+static unsigned char dummy_ser1 = 0x00;
 static unsigned char dummy_dir_ser1 = 0x00;
+#endif
+#if !defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
+static unsigned char dummy_ser2 = 0x00;
 static unsigned char dummy_dir_ser2 = 0x00;
+#endif
+
+static unsigned char dummy_ser3 = 0x00;
 static unsigned char dummy_dir_ser3 = 0x00;
 
 /* Info needed for each ports extra control/status signals.
@@ -523,52 +568,56 @@
 
 static const struct control_pins e100_modem_pins[NR_PORTS] = 
 {
-/* Ser 0 */
-  {
+	/* Ser 0 */
+	{
 #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER0_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER0_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER0_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER0_CD_ON_PB_BIT
 #else
-    &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 1 */
-  {
+		&dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 1 */
+	{
 #if defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB)
-    R_PORT_PB_DATA,  &port_pb_data_shadow,  &port_pb_dir_shadow,
-    CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
-    CONFIG_ETRAX_SER1_RI_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_DSR_ON_PB_BIT, 
-    CONFIG_ETRAX_SER1_CD_ON_PB_BIT
+		R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow,
+		CONFIG_ETRAX_SER1_DTR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_RI_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_DSR_ON_PB_BIT,
+		CONFIG_ETRAX_SER1_CD_ON_PB_BIT
 #else
-    &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
-#endif   
-  },  
-/* Ser 2 */
-  {
+		&dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 2 */
+	{
 #if defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA)
-    R_PORT_PA_DATA,  &port_pa_data_shadow,  &port_pa_dir_shadow,
-    CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
-    CONFIG_ETRAX_SER2_RI_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_DSR_ON_PA_BIT, 
-    CONFIG_ETRAX_SER2_CD_ON_PA_BIT
+		R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow,
+		CONFIG_ETRAX_SER2_DTR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_RI_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_DSR_ON_PA_BIT,
+		CONFIG_ETRAX_SER2_CD_ON_PA_BIT
 #else
-    &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
-#endif   
-  },
-/* Ser 3 */
-  {
-    &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
-  }
+		&dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3
+#endif
+	},
+
+	/* Ser 3 */
+	{
+		&dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3
+	}
 };
 
 #if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA)
 unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT;
 #endif
 
+
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
@@ -840,8 +889,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* disable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl &=
-		~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl &= ~IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -850,8 +899,8 @@
 {
 #ifndef CONFIG_SVINTO_SIM
 	/* enable the receiver */
-	info->port[REG_REC_CTRL] = info->rx_ctrl |=
-		IO_MASK(R_SERIAL0_REC_CTRL, rec_enable);
+	info->port[REG_REC_CTRL] =
+		(info->rx_ctrl |= IO_MASK(R_SERIAL0_REC_CTRL, rec_enable));
 #endif
 }
 
@@ -943,13 +992,8 @@
 static int
 e100_write_rs485(struct tty_struct *tty,struct rs485_write *r)
 {
-	int stop_delay;
-	int total, i;
-	int max_j, delay_ms, bits;
-	tcflag_t cflags;
-	int size = (*r).outc_size;
+	int total;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
-	struct wait_queue wait = { current, NULL };
 
 	/* If we are in RS-485 mode, we need to toggle RTS and disable
 	 * the receiver before initiating a DMA transfer
@@ -975,44 +1019,20 @@
 	 * enable the receiver
 	 */	
 
-	/* wait on transmit shift register */
-	/* All is sent, check if we should wait more before toggling rts */
-	
-	/* calc. number of bits / data byte */
-	cflags = info->tty->termios->c_cflag;
-
-	/* databits + startbit and 1 stopbit */
-	if ((cflags & CSIZE) == CS7)
-		bits = 9;
-	else
-		bits = 10;  
-
-	if (cflags & CSTOPB)     /* 2 stopbits ? */
-		bits++;
-
-	if (cflags & PARENB)     /* parity bit ? */
-		bits++;
-	
-	/* calc timeout */
-	delay_ms = ((bits * size * 1000) / info->baud) + 1;
-	max_j = jiffies + (delay_ms * HZ)/1000 + 10;
-
-	while (jiffies < max_j) {
-		if (info->port[REG_STATUS] &
-		    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-			for (i = 0; i < 100; i++)
-				;
-			if (info->port[REG_STATUS] &
-			    IO_STATE(R_SERIAL0_STATUS, tr_ready, ready)) {
-				/* ~25 for loops per usec */
-				stop_delay = 1000000 / info->baud;
-				if (cflags & CSTOPB) 
-					stop_delay *= 2;
-				udelay(stop_delay);
-				break;
-			}
-		}
+	/* Sleep until all sent */
+	tty_wait_until_sent(tty, 0);
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+	/* Now sleep a little more so that shift register is empty */
+	schedule_usleep(info->char_time_usec * 2);
+#else
+	{
+		unsigned int val;
+		/* wait on transmit shift register */
+		do{
+			get_lsr_info(info, &val);
+		}while (!(val & TIOCSER_TEMT));
 	}
+#endif
 
 	e100_rts(info, info->rs485.rts_after_sent);
 	
@@ -1118,7 +1138,7 @@
 	}
 	return;
 #endif
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
+	/* acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
@@ -1168,14 +1188,14 @@
 
 #if defined(CONFIG_ETRAX_RS485)
 		/* Check if we should toggle RTS now */
-		if (info->rs485.enabled)
-		{
+		if (info->rs485.enabled) {
 			/* Make sure fifo is empty */
-			int in_fifo = 0 ;
-			do{
+			int in_fifo = 0;
+
+			do {
 				in_fifo = IO_EXTRACT(R_DMA_CH6_STATUS, avail,
-						    *info->ostatusadr);
-			}  while (in_fifo > 0) ;
+						     *info->ostatusadr);
+			}  while (in_fifo > 0);
 			/* Any way to really check transmitter empty? (TEMT) */
 			/* Control RTS to set to RX mode */
 			e100_rts(info, info->rs485.rts_after_sent); 
@@ -1201,7 +1221,6 @@
 	*info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
 	
 	/* DMA is now running (hopefully) */
-
 }
 
 static void 
@@ -1220,13 +1239,162 @@
 	transmit_chars(info);
 }
 
+#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
+static int serial_fast_timer_started = 0;
+static int serial_fast_timer_expired = 0;
+static void flush_timeout_function(unsigned long data);
+#define START_FLUSH_FAST_TIMER(info, string) {\
+  unsigned long timer_flags; \
+  save_flags(timer_flags); \
+  cli(); \
+  if (fast_timers[info->line].function == NULL) { \
+    serial_fast_timer_started++; \
+    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
+    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
+    start_one_shot_timer(&fast_timers[info->line], \
+                         flush_timeout_function, \
+                         (unsigned long)info, \
+                         info->char_time_usec*4, \
+                         string); \
+  } \
+  else { \
+    TIMERD(DEBUG_LOG(info->line, "timer %i already running\n", info->line)); \
+  } \
+  restore_flags(timer_flags); \
+}
+
+#else
+#define START_FLUSH_FAST_TIMER(info, string)
+#endif
+
+static struct etrax_recv_buffer *
+alloc_recv_buffer(unsigned int size)
+{
+	struct etrax_recv_buffer *buffer;
+
+	if (!(buffer = kmalloc(sizeof *buffer + size, GFP_ATOMIC)))
+		return NULL;
+
+	buffer->next = NULL;
+	buffer->length = 0;
+	buffer->error = TTY_NORMAL;
+
+	return buffer;
+}
+
+static void
+append_recv_buffer(struct e100_serial *info, struct etrax_recv_buffer *buffer)
+{
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	if (!info->first_recv_buffer)
+		info->first_recv_buffer = buffer;
+	else
+		info->last_recv_buffer->next = buffer;
+
+	info->last_recv_buffer = buffer;
+
+	info->recv_cnt += buffer->length;
+	if (info->recv_cnt > info->max_recv_cnt)
+		info->max_recv_cnt = info->recv_cnt;
+
+	restore_flags(flags);
+}
+
+static int
+add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
+{
+	struct etrax_recv_buffer *buffer;
+
+	if (!(buffer = alloc_recv_buffer(4)))
+		return 0;
+
+	buffer->length = 1;
+	buffer->error = flag;
+	buffer->buffer[0] = data;
+	
+	append_recv_buffer(info, buffer);
+
+	info->icount.rx++;
+
+	return 1;
+}
+
+static _INLINE_ unsigned int
+handle_descr_data(struct e100_serial *info, struct etrax_dma_descr *descr, unsigned int recvl)
+{
+	struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
+
+	if (info->recv_cnt + recvl > 65536) {
+		printk(__FUNCTION__ ": Too much pending incoming serial data! Dropping %u bytes.\n", recvl);
+		return 0;
+	}
+
+	buffer->length = recvl;
+
+	if (info->errorcode == ERRCODE_SET_BREAK)
+		buffer->error = TTY_BREAK;
+	info->errorcode = 0;
+
+	append_recv_buffer(info, buffer);
+
+	if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+		panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+
+	descr->buf = virt_to_phys(buffer->buffer);
+	
+	return recvl;
+}
+
+static _INLINE_ unsigned int
+handle_all_descr_data(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr;
+	unsigned int recvl;
+	unsigned int ret = 0;
+
+	while (1)
+	{
+		descr = &info->rec_descr[info->cur_rec_descr];
+
+		if (descr == phys_to_virt(*info->idescradr))
+			break;
+
+		if (++info->cur_rec_descr == SERIAL_RECV_DESCRIPTORS)
+			info->cur_rec_descr = 0;
+	
+		/* find out how many bytes were read */
+
+		/* if the eop bit was not set, all data has been received */
+		if (!(descr->status & d_eop)) {
+			recvl = descr->sw_len;
+		} else {
+			/* otherwise we find the amount of data received here */
+			recvl = descr->hw_len;
+		}
+
+		/* Reset the status information */
+		descr->status = 0;
+
+		DEBUG_LOG(info->line, "recvl %lu\n", recvl);
+
+		/* update stats */
+		info->icount.rx += recvl;
+
+		ret += handle_descr_data(info, descr, recvl);
+	}
+
+	return ret;
+}
+
 static _INLINE_ void 
 receive_chars(struct e100_serial *info)
 {
 	struct tty_struct *tty;
 	unsigned char rstat;
-	unsigned int recvl;
-	struct etrax_dma_descr *descr;
 
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
@@ -1235,168 +1403,88 @@
 	return;
 #endif
 
-	tty = info->tty;
-
-	/* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */
-
-	// ?
+	/* Acknowledge both dma_descr and dma_eop irq in R_DMA_CHx_CLR_INTR */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 
-	if (!tty) /* something wrong... */
+	tty = info->tty;
+	if (!tty) /* Something wrong... */
 		return;
 
-	descr = &info->rec_descr;
-
-	/* find out how many bytes were read */
+#ifdef SERIAL_HANDLE_EARLY_ERRORS
+	e100_enable_serial_data_irq(info);
+#endif	
 
-	/* if the eop bit was not set, all data has been received */
-	if (!(descr->status & d_eop)) {
-		recvl = descr->sw_len;
-	} else {
-		/* otherwise we find the amount of data received here */
-		recvl = descr->hw_len;
-	}
+	if (info->errorcode == ERRCODE_INSERT_BREAK)
+		add_char_and_flag(info, '\0', TTY_BREAK);
 
-	/* read the status register so we can detect errors,
-	 * but we can't really do anything about those errors
-	 * anyway, since we have the DMA in "force eop at error" mode
-	 * the fault characters are not in the buffer anyway.
-	 */
+	handle_all_descr_data(info);
 
+	/* Read the status register to detect errors */
 	rstat = info->port[REG_STATUS];
 
-	if ((rstat & SER_ERROR_MASK) != 0) {
-		unsigned char data;
-		/* if we got an error, we must reset it by reading the
+	if (rstat & SER_ERROR_MASK) {
+		/* If we got an error, we must reset it by reading the
 		 * data_in field
 		 */
-		data = info->port[REG_DATA];
+		unsigned char data = info->port[REG_DATA];
+
 		PROCSTAT(ser_stat[info->line].errors_cnt++);
-		DEBUG_LOG(info->line, " #dERR: s d 0x%04X\n",
+		DEBUG_LOG(info->line, "#dERR: s d 0x%04X\n",
 			  ((rstat & SER_ERROR_MASK) << 8) | data);
-		/* Only handle the saved error code, that indicates that we got
-		 * the last character of a break that looks like it's ok, but
-		 * is not
-		 */
 
-		if (info->errorcode == 0) {
-			*tty->flip.flag_buf_ptr = TTY_NORMAL;
-		} else {
-			unsigned char data;
-			data = info->port[REG_DATA];
-			if (info->errorcode & ERRCODE_INSERT) {
-				unsigned char *currbuf;
-				/* Get the current buffer */
-				if (tty->flip.buf_num) {
-					currbuf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-				} else {
-					currbuf = tty->flip.char_buf;
-				}
-				/* We should insert a character in the buffer! */
-				if (recvl == 0) {
-					recvl = 1;
-					DEBUG_LOG(info->line, "insert to %lu\n", recvl);
-				} else {
-					/* Move stuff around.. */
-					DEBUG_LOG(info->line, "#insert to %lu!\n", recvl);
-					if (recvl < TTY_FLIPBUF_SIZE) {
-						int i;
-						/* Move the data 1 step right */
-						i = recvl;
-						while (i) {
-							currbuf[i] = currbuf[i-1];
-							i--;
-						}
-						recvl++;
-					} else {
-						/* We can't move it all! Skip break! */
-						/* TODO: Handle full buffer? */
-						DEBUG_LOG(info->line, "#BRK skipped! %lu!\n", recvl);
-						info->errorcode = 0;
-					}
-				}
-			}
-	    
-			PROCSTAT(ser_stat[info->line].errors_cnt++);
-			DEBUG_LOG(info->line, " #bERR: s d 0x%04X\n",
-				  ((rstat & SER_ERROR_MASK) << 8) | data);
-			*tty->flip.flag_buf_ptr = (info->errorcode & 0xFF);
-			info->errorcode = 0;
-#if 0
-			printk("SERERR: 0x%02X data: 0x%02X\n", rstat & SER_ERROR_MASK, data);
-#endif
-			/* we only ever write errors into the first byte in
-			 * the flip flag buffer, so we dont have to clear it
-			 * all every time
-			 */
-		}
+		if (rstat & SER_PAR_ERR_MASK)
+			add_char_and_flag(info, data, TTY_PARITY);
+		else if (rstat & SER_OVERRUN_MASK)
+			add_char_and_flag(info, data, TTY_OVERRUN);
+		else if (rstat & SER_FRAMING_ERR_MASK)
+			add_char_and_flag(info, data, TTY_FRAME);
 	}
 
-	DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
-	if (recvl) {
-		unsigned char *buf;
-		struct async_icount *icount = &info->icount;
-
-		/* update stats */
-		icount->rx += recvl;
-
-		/* use the flip buffer next in turn to restart DMA into */
-
-		if (tty->flip.buf_num) {
-			buf = tty->flip.char_buf;
-		} else {
-			buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
-		}
-
-		if (buf == phys_to_virt(descr->buf)) {
-			printk("ttyS%d flip-buffer overrun!\n", info->line);
-			icount->overrun++;
-			*tty->flip.flag_buf_ptr = TTY_OVERRUN;
-			/* restart old buffer */
-		} else {
-			descr->buf = virt_to_phys(buf);
-
-			/* schedule or push a flip of the buffer */
+	START_FLUSH_FAST_TIMER(info, "receive_chars");
 
-			info->tty->flip.count = recvl;
+	/* Restart the receiving DMA */
+	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
+}
 
-#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */
-			/* this includes a check for low-latency */
-			tty_flip_buffer_push(tty);
-#else
-			queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#endif	
-		}
+static _INLINE_ int
+start_recv_dma(struct e100_serial *info)
+{
+	struct etrax_dma_descr *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+        int i;
+
+	/* Set up the receiving descriptors */
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
+		if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
+			panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+
+		descr[i].ctrl = d_int;
+		descr[i].buf = virt_to_phys(buffer->buffer);
+		descr[i].sw_len = SERIAL_DESCR_BUF_SIZE;
+		descr[i].hw_len = 0;
+		descr[i].status = 0;
+		descr[i].next = virt_to_phys(&descr[i+1]);
 	}
 
-	/* restart the receiving dma */
+	/* Link the last descriptor to the first */
+	descr[i-1].next = virt_to_phys(&descr[0]);
 
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->hw_len = 0;
-	descr->status = 0;
+	/* Start with the first descriptor in the list */
+	info->cur_rec_descr = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
+	/* Start the DMA */
+	*info->ifirstadr = virt_to_phys(&descr[info->cur_rec_descr]);
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
 
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-	e100_enable_serial_data_irq(info);
-#endif	
-	/* input dma should be running now */
-
-	/* unthrottle if we have throttled */
-	if (E100_RTS_GET(info))
-		tty->driver.unthrottle(info->tty);
+	/* Input DMA should be running now */
+	return 1;
 }
 
 static void 
 start_receive(struct e100_serial *info)
 {
-	struct etrax_dma_descr *descr;
-	
 #ifdef CONFIG_SVINTO_SIM
 	/* No receive in the simulator.  Will probably be when the rest of
 	 * the serial interface works, and this piece will just be removed.
@@ -1410,21 +1498,10 @@
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
-	descr = &info->rec_descr;
-	
-	/* start the receiving dma into the flip buffer */
-	
-	descr->ctrl = d_int | d_eol | d_eop;
-	descr->sw_len = TTY_FLIPBUF_SIZE;
-	descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr);
-	descr->hw_len = 0;
-	descr->status = 0;
-	
 	info->tty->flip.count = 0;
 
-	*info->ifirstadr = virt_to_phys(descr);
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);
-
+	start_recv_dma(info);
+	
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
 	start_flush_timer();
 #endif
@@ -1474,9 +1551,16 @@
 		info = rs_table + i;
 		if (!info->uses_dma) 
 			continue; 
-		/* check for dma_descr (dont need to check for dma_eop in output dma for serial */
+		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
 		if (ireg & info->irq) {  
 			/* we can send a new dma bunch. make it so. */
+			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			/* Read jiffies_usec first, 
+			 * we want this time to be as late as possible
+			 */
+ 			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
+			info->last_tx_active_usec = GET_JIFFIES_USEC();
+			info->last_tx_active = jiffies;
 			transmit_chars(info);
 		}
 		
@@ -1524,77 +1608,26 @@
 	}
 }
 
-#ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
-static int serial_fast_timer_started = 0;
-static int serial_fast_timer_expired = 0;
-static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
-  unsigned long timer_flags; \
-  save_flags(timer_flags); \
-  cli(); \
-  TIMERD(DEBUG_LOG(info->line, "start_timer? %i ", info->line)); \
-  if (fast_timers[info->line].function == NULL) { \
-    serial_fast_timer_started++; \
-    TIMERD(DEBUG_LOG(info->line, "start_timer %i ", info->line)); \
-    TIMERD(DEBUG_LOG(info->line, "num started: %i\n", serial_fast_timer_started)); \
-    start_one_shot_timer(&fast_timers[info->line], \
-                         flush_timeout_function, \
-                         (unsigned long)info, \
-                         info->char_time_usec*4, \
-                         string); \
-  } \
-  else { \
-    /* DEBUG_LOG(info->line, " ## timer %i running ##\n", info->line); */ \
-  } \
-  restore_flags(timer_flags); \
-}
-
-#else
-#define START_FLUSH_FAST_TIMER(info, string)
-#endif
-
-void _INLINE_ check_flush_timeout(struct e100_serial *info)
+static _INLINE_ int
+force_eop_if_needed(struct e100_serial *info)
 {
-	unsigned char rstat;
-	unsigned int magic;
-
-	if (0 /*info->tty->processing_flip*/) {
-		if (!E100_RTS_GET(info)) {
-			int left = (*info->ihwswadr >> 16) - (*info->istatusadr & 0x3F);
-
-			if (left < TTY_THROTTLE_LIMIT)
-				info->tty->driver.throttle(info->tty);
-		}
-
-		PROCSTAT(ser_stat[info->line].processing_flip++);
-		START_FLUSH_FAST_TIMER(info, "flip");
-		return;
-	}
-
 	/* We check data_avail bit to determine if data has 
 	 * arrived since last time
 	 */ 
-	magic = info->fifo_magic;
-#ifdef SERIAL_DEBUG_DATA
-	if (info->fifo_magic || info->fifo_didmagic) {
-		DEBUG_LOG(info->line, "timeout_int: did fifo_magic %03X\n",
-		    (info->fifo_didmagic << 8) | info->fifo_magic);
-	}
-#endif
-	rstat = info->port[REG_STATUS];
+	unsigned char rstat = info->port[REG_STATUS];
+
 	/* error or datavail? */
 	if (rstat & SER_ERROR_MASK) { 
-		/* Some error has occured */
-		/* If there has been valid data, 
-		 * an EOP interrupt will be made automatically.
-		 * If no data, the normal ser_interrupt should be enabled 
-		 * and handle it.
+		/* Some error has occurred. If there has been valid data, an
+		 * EOP interrupt will be made automatically. If no data, the
+		 * normal ser_interrupt should be enabled and handle it.
 		 * So do nothing!
 		 */
 		DEBUG_LOG(info->line, "timeout err: rstat 0x%03X\n",
 		          rstat | (info->line << 8));
-		return;
+		return 0;
 	}
+
 	if (rstat & SER_DATA_AVAIL_MASK) { 
 		/* Ok data, no error, count it */
 		TIMERD(DEBUG_LOG(info->line, "timeout: rstat 0x%03X\n",
@@ -1602,32 +1635,94 @@
 		/* Read data to clear status flags */
 		(void)info->port[REG_DATA];
 
-		magic++;
+		info->forced_eop = 0;
+		START_FLUSH_FAST_TIMER(info, "magic");
+		return 0;
 	}
 
-	if (magic != info->fifo_magic) {
-		info->fifo_magic = magic;
-		info->fifo_didmagic = 0;
-		START_FLUSH_FAST_TIMER(info, "magic");
-	} else {
-		/* hit the timeout, force an EOP for the input
-		 * dma channel if we haven't already
-		 */
-		if (!info->fifo_didmagic && magic) {
-			info->fifo_didmagic = 1;
-			info->fifo_magic = 0;
-			PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
-			DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
-			TIMERD(DEBUG_LOG(info->line, "timeout magic %i\n", magic));
-			FORCE_EOP(info);
+	/* hit the timeout, force an EOP for the input
+	 * dma channel if we haven't already
+	 */
+	if (!info->forced_eop) {
+		info->forced_eop = 1;
+		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
+		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		FORCE_EOP(info);
+	}
+
+	return 1;
+}
+
+static _INLINE_ void
+flush_to_flip_buffer(struct e100_serial *info)
+{
+	struct tty_struct *tty = info->tty;
+	struct etrax_recv_buffer *buffer;
+	unsigned int length;
+	unsigned long flags;
+
+	if (!info->first_recv_buffer)
+		return;
+
+	save_flags(flags);
+	cli();
+
+	length = tty->flip.count;
+	
+	while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) {
+		unsigned int count = buffer->length;
+
+		if (length + count > TTY_FLIPBUF_SIZE)
+			count = TTY_FLIPBUF_SIZE - length;
+
+		memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
+		memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
+		tty->flip.flag_buf_ptr[length] = buffer->error;
+
+		length += count;
+		info->recv_cnt -= count;
+
+		if (count == buffer->length) {
+			info->first_recv_buffer = buffer->next;
+			kfree(buffer);
+		} else {
+			buffer->length -= count;
+			memmove(buffer->buffer, buffer->buffer + count, buffer->length);
+			buffer->error = TTY_NORMAL;
 		}
 	}
-} /* check_flush_timeout */
+
+	if (!info->first_recv_buffer)
+		info->last_recv_buffer = NULL;
+
+	tty->flip.count = length;
+
+	restore_flags(flags);
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+	/* this includes a check for low-latency */
+	tty_flip_buffer_push(tty);
+#else
+	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+#endif
+}
+
+static _INLINE_ void
+check_flush_timeout(struct e100_serial *info)
+{
+	force_eop_if_needed(info);
+
+	flush_to_flip_buffer(info);
+
+	if (info->first_recv_buffer)
+		START_FLUSH_FAST_TIMER(info, "flip");
+}
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 static void flush_timeout_function(unsigned long data)
 {
 	struct e100_serial *info = (struct e100_serial *)data;
+
 	fast_timers[info->line].function = NULL;
 	serial_fast_timer_expired++;
 	TIMERD(DEBUG_LOG(info->line, "flush_timout %i ", info->line));
@@ -1681,7 +1776,6 @@
 {
 	struct e100_serial *info;
 	int i;
-	unsigned int magic;
 
 #ifdef CONFIG_SVINTO_SIM
 	return;
@@ -1689,35 +1783,8 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !(info->flags & ASYNC_INITIALIZED))
-			continue;
-
-		/* istatusadr (bit 6-0) hold number of bytes in fifo 
-		 * ihwswadr (bit 31-16) holds number of bytes in dma buffer
-		 * ihwswadr (bit 15-0) specifies size of dma buffer
-		 */
-
-		magic = (*info->istatusadr & 0x3f);
-		magic += ((*info->ihwswadr & 0xffff) - (*info->ihwswadr >> 16));
-
-		/* if magic is equal to fifo_magic (magic in previous
-		 * timeout_interrupt) then no new data has arrived since last
-		 * interrupt and we'll force eop to flush fifo+dma buffers
-		 */
-
-		if (magic != info->fifo_magic) {
-			info->fifo_magic = magic;
-			info->fifo_didmagic = 0;
-		} else {
-			/* hit the timeout, force an EOP for the input
-			 * dma channel if we haven't already
-			 */
-			if (!info->fifo_didmagic && magic) {
-				info->fifo_didmagic = 1;
-				info->fifo_magic = 0;
-				FORCE_EOP(info);
-			}
-		}
+		if (info->uses_dma) 
+			check_flush_timeout(info);
 	}
 
 	/* restart flush timer */
@@ -1770,7 +1837,7 @@
 
 Multiple frame errors with data == 0x00 (B),
 but the part of the break trigs is interpreted as a start bit (and possibly
-som 0 bits followed by a number of 1 bits and a stop bit).
+some 0 bits followed by a number of 1 bits and a stop bit).
 Depending on parity settings etc. this last character can be either
 a fake "valid" char (F) or have a parity error (E).
 
@@ -1805,19 +1872,20 @@
 /*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
 	if (rstat & SER_ERROR_MASK) {
 		unsigned char data;
+
 		info->last_rx_active_usec = GET_JIFFIES_USEC();
 		info->last_rx_active = jiffies;
-		/* if we got an error, we must reset it by
-		 * reading the data_in field
+		/* If we got an error, we must reset it by reading the
+		 * data_in field
 		 */
 		data = info->port[REG_DATA];
 
-		if ((data == 0x00) && (rstat & SER_FRAMING_ERR_MASK)) {
-			/* Most likely a break, but we get 
-			 * interrupts over and over again.
+		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
 			 */
 
-			if (info->break_detected_cnt == 0) {
+			if (!info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "#BRK start\n", 0);
 			}
 			if (rstat & SER_RXD_MASK) {
@@ -1833,46 +1901,46 @@
 			}
 			info->break_detected_cnt++;
 		} else {
-			/* Error doesn't look like a break,
-			 * but could be end of a break 
+			/* The error does not look like a break, but could be
+			 * the end of one
 			 */
 			if (info->break_detected_cnt) {
 				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
 				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK)
+					add_char_and_flag(info, '\0', TTY_BREAK);
+
+				if (rstat & SER_PAR_ERR_MASK)
+					add_char_and_flag(info, data, TTY_PARITY);
+				else if (rstat & SER_OVERRUN_MASK)
+					add_char_and_flag(info, data, TTY_OVERRUN);
+				else if (rstat & SER_FRAMING_ERR_MASK)
+					add_char_and_flag(info, data, TTY_FRAME);
+				info->errorcode = 0;
 			}
 			info->break_detected_cnt = 0;
 			DEBUG_LOG(info->line, "#iERR s d %04X\n",
 				  ((rstat & SER_ERROR_MASK) << 8) | data);
 		}
 		PROCSTAT(ser_stat[info->line].early_errors_cnt++);
-
-#if 0
-		/* Reset DMA before starting */
-		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-#endif
-	} else { /* it was a valid byte, now let the dma do the rest */
-		unsigned char data;
+	} else { /* It was a valid byte, now let the DMA do the rest */
 		unsigned long curr_time_u = GET_JIFFIES_USEC();
 		unsigned long curr_time = jiffies;
 		
 		if (info->break_detected_cnt) {
-			/* Detect if this character is a new
-			 * valid char or the last char in a
-			 * break sequence:
-			 * If LSBits are 0 and MSBits are high
-			 * AND the time is close to the
-			 * previous interrupt we should discard
-			 * it.
+			/* Detect if this character is a new valid char or the
+			 * last char in a break sequence: If LSBits are 0 and
+			 * MSBits are high AND the time is close to the
+			 * previous interrupt we should discard it.
 			 */
 			long elapsed_usec = 
-			  (curr_time - info->last_rx_active) * (1000000/HZ) + 
-			  curr_time_u - info->last_rx_active_usec;
-			if (elapsed_usec<2*info->char_time_usec) {
+				(curr_time - info->last_rx_active) * (1000000/HZ) + 
+				curr_time_u - info->last_rx_active_usec;
+			if (elapsed_usec < 2*info->char_time_usec) {
 				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
-				/* Report as BREAK (error) and 
-				 * let receive_chars handle it
+				/* Report as BREAK (error) and let
+				 * receive_chars() handle it
 				 */
 				info->errorcode = ERRCODE_SET_BREAK;
 			} else {
@@ -1880,22 +1948,19 @@
 			}
 			DEBUG_LOG(info->line, "num brk %i\n", info->break_detected_cnt);
 		}
-		/* Reset data_avail by
-		 * reading the data_in field
-		 */
-		data = info->port[REG_DATA];
-		info->break_detected_cnt = 0;
-		info->fifo_magic++; /* Count received chars */
+
 #ifdef SERIAL_DEBUG_INTR
 		printk("** OK, disabling ser_interupts\n");
 #endif
-		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
-		DEBUG_LOG(info->line, " ser_int OK %03X\n",
-		          (info->line << 8) | data);
 		e100_disable_serial_data_irq(info);
+
+		info->break_detected_cnt = 0;
+
+		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
+		DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
 	}
 
-	/* restart the DMA never hurts */
+	/* Restarting the DMA never hurts */
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
 	START_FLUSH_FAST_TIMER(info, "ser_int");
 } /* handle_ser_interrupt */
@@ -1973,7 +2038,7 @@
 {
 	struct e100_serial	*info = (struct e100_serial *) private_;
 	struct tty_struct	*tty;
-	
+
 	tty = info->tty;
 	if (!tty)
 		return;
@@ -1985,51 +2050,46 @@
 startup(struct e100_serial * info)
 {
 	unsigned long flags;
-	unsigned long page;
+	unsigned long xmit_page;
+	int i;
 
-	page = get_zeroed_page(GFP_KERNEL);
-	if (!page)
+	xmit_page = get_zeroed_page(GFP_KERNEL);
+	if (!xmit_page)
 		return -ENOMEM;
 
 	save_flags(flags); cli();
 
 	/* if it was already initialized, skip this */
-	
+
 	if (info->flags & ASYNC_INITIALIZED) {
-		free_page(page);
 		restore_flags(flags);
+		free_page(xmit_page);
 		return 0;
 	}
-	
+
 	if (info->xmit.buf)
-		free_page(page);
+		free_page(xmit_page);
 	else
-		info->xmit.buf = (unsigned char *) page;
-		
+		info->xmit.buf = (unsigned char *) xmit_page;
+
 #ifdef SERIAL_DEBUG_OPEN
-	printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit.buf);
+	printk("starting up ttyS%d (xmit_buf 0x%p, recv_buf 0x%p)...\n", info->line, info->xmit.buf, info->recv.buf);
 #endif
 
-	if (info->tty) {
-
-		/* clear the tty flip flag buffer since we will not
-		 * be using it (we only use the first byte..)
-		 */
-
-		memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2);
-	}
-
-	save_flags(flags);
-	cli();
-	
 #ifdef CONFIG_SVINTO_SIM
 	/* Bits and pieces collected from below.  Better to have them
 	   in one ifdef:ed clause than to mix in a lot of ifdefs,
 	   right? */
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+
 	info->xmit.head = info->xmit.tail = 0;
-	
+	info->first_recv_buffer = info->last_recv_buffer = NULL;
+	info->recv_cnt = info->max_recv_cnt = 0;
+
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		info->rec_descr[i].buf = NULL;
+
 	/* No real action in the simulator, but may set info important
 	   to ioctl. */
 	change_speed(info);
@@ -2039,34 +2099,40 @@
 	 * Clear the FIFO buffers and disable them
 	 * (they will be reenabled in change_speed())
 	 */
-	
+
 	/*
 	 * Reset the DMA channels and make sure their interrupts are cleared
 	 */
-	
+
 	info->uses_dma = 1;
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
-	/* wait until reset cycle is complete */
+	/* Wait until reset cycle is complete */
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
 	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
 	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-	
+
+	/* Make sure the irqs are cleared */
 	*info->iclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
 	*info->oclrintradr =
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
 		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-	
+
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
-        info->xmit.head = info->xmit.tail = 0;
+	info->xmit.head = info->xmit.tail = 0;
+	info->first_recv_buffer = info->last_recv_buffer = NULL;
+	info->recv_cnt = info->max_recv_cnt = 0;
 	
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		info->rec_descr[i].buf = 0;
+
 	/*
 	 * and set the speed and other flags of the serial port
 	 * this will start the rx/tx as well
@@ -2085,7 +2151,7 @@
 	e100_enable_txdma_irq(info);
 	e100_enable_rxdma_irq(info);
 
-	info->tr_running = 0;  /* to be sure we dont lock up the transmitter */
+	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
 
 	/* setup the dma input descriptor and start dma */
 	
@@ -2118,9 +2184,12 @@
 shutdown(struct e100_serial * info)
 {
 	unsigned long flags;
+	struct etrax_dma_descr *descr = info->rec_descr;
+	struct etrax_recv_buffer *buffer;
+	int i;
 
 #ifndef CONFIG_SVINTO_SIM	
-	/* shut down the transmitter and receiver  */
+	/* shut down the transmitter and receiver */
 
 	e100_disable_rx(info);
 	info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
@@ -2150,11 +2219,17 @@
 	cli(); /* Disable interrupts */
 	
 	if (info->xmit.buf) {
-		unsigned long pg = (unsigned long) info->xmit.buf;
-		info->xmit.buf = 0;
-		free_page(pg);
+		free_page((unsigned long)info->xmit.buf);
+		info->xmit.buf = NULL;
 	}
 
+	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++)
+		if (descr[i].buf) {
+			buffer = phys_to_virt(descr[i].buf) - sizeof *buffer;
+			kfree(buffer);
+			descr[i].buf = 0;
+		}
+
 	if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
 		/* hang up DTR and RTS if HUPCL is enabled */
 		e100_dtr(info, 0);
@@ -2258,11 +2333,11 @@
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
 	unsigned long flags;
 
-	if (info->tr_running
-	    || info->xmit.head == info->xmit.tail
-	    || tty->stopped
-	    || tty->hw_stopped
-	    || !info->xmit.buf)
+	if (info->tr_running ||
+	    info->xmit.head == info->xmit.tail ||
+	    tty->stopped ||
+	    tty->hw_stopped ||
+	    !info->xmit.buf)
 		return;
 
 #ifdef SERIAL_DEBUG_FLOW
@@ -2299,7 +2374,7 @@
 #ifdef CONFIG_SVINTO_SIM
 	/* Really simple.  The output is here and now. */
 	SIMCOUT(buf, count);
-	return;
+	return count;
 #endif
 	save_flags(flags);
 	
@@ -2370,8 +2445,8 @@
 	 * the IRQ's are not running anyway for this port.
 	 */
 	
-	if (info->xmit.head != info->xmit.tail
-	    && !tty->stopped &&
+	if (info->xmit.head != info->xmit.tail &&
+	    !tty->stopped &&
 	    !tty->hw_stopped &&
 	    !info->tr_running) {
 		start_transmit(info);
@@ -2528,20 +2603,20 @@
 	tmp.flags = info->flags;
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
-	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
 }
 
 static int
-set_serial_info(struct e100_serial * info,
-		struct serial_struct * new_info)
+set_serial_info(struct e100_serial *info,
+		struct serial_struct *new_info)
 {
 	struct serial_struct new_serial;
 	struct e100_serial old_info;
-	int 			retval = 0;
+	int retval = 0;
 
-	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
+	if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
 		return -EFAULT;
 
 	old_info = *info;
@@ -2636,6 +2711,7 @@
 char *get_control_state_str(int MLines, char *s)
 {
 	int i = 0;
+
 	s[0]='\0';
 	while (control_state_str[i].str != NULL) {
 		if (MLines & control_state_str[i].state) {
@@ -2805,9 +2881,13 @@
 rs_ioctl(struct tty_struct *tty, struct file * file,
 	 unsigned int cmd, unsigned long arg)
 {
-	int error;
 	struct e100_serial * info = (struct e100_serial *)tty->driver_data;
+#if defined(CONFIG_ETRAX_RS485) || (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
+	int error;
+#endif
+#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */
 	int retval;
+#endif
 	
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD)  &&
@@ -3108,7 +3188,7 @@
 		struct e100_serial *info)
 {
 	DECLARE_WAITQUEUE(wait, current);
-	unsigned long   flags;
+	unsigned long	flags;
 	int		retval;
 	int		do_clocal = 0, extra_count = 0;
 	
@@ -3292,7 +3372,7 @@
 	}
 
 	/*
-	 * If the port is the middle of closing, bail out now
+	 * If the port is in the middle of closing, bail out now
 	 */
 	if (tty_hung_up_p(filp) ||
 	    (info->flags & ASYNC_CLOSING)) {
@@ -3346,12 +3426,11 @@
 
 static inline int line_info(char *buf, struct e100_serial *info)
 {
-	char	stat_buf[30], control, status;
+	char	stat_buf[30];
 	int	ret;
-	unsigned long flags;
 
 	ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
-		      info->line, info->port, info->irq);
+		      info->line, (unsigned long)info->port, info->irq);
 
 	if (!info->port || (info->type == PORT_UNKNOWN)) {
 		ret += sprintf(buf+ret, "\n");
@@ -3376,19 +3455,28 @@
 	ret += sprintf(buf+ret, " baud:%d", info->baud);
 
 	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
-		       info->icount.tx, info->icount.rx);
+		       (unsigned long)info->icount.tx,
+		       (unsigned long)info->icount.rx);
+
+	ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
+		       (unsigned long)info->recv_cnt,
+		       (unsigned long)info->max_recv_cnt);
 
 	if (info->icount.frame)
-		ret += sprintf(buf+ret, " fe:%lu", info->icount.frame);
+		ret += sprintf(buf+ret, " fe:%lu",
+			       (unsigned long)info->icount.frame);
 	
 	if (info->icount.parity)
-		ret += sprintf(buf+ret, " pe:%lu", info->icount.parity);
+		ret += sprintf(buf+ret, " pe:%lu",
+			       (unsigned long)info->icount.parity);
 	
 	if (info->icount.brk)
-		ret += sprintf(buf+ret, " brk:%lu", info->icount.brk);	
+		ret += sprintf(buf+ret, " brk:%lu",
+			       (unsigned long)info->icount.brk);	
 
 	if (info->icount.overrun)
-		ret += sprintf(buf+ret, " oe:%lu", info->icount.overrun);
+		ret += sprintf(buf+ret, " oe:%lu",
+			       (unsigned long)info->icount.overrun);
 
 	/*
 	 * Last thing is the RS-232 status lines
@@ -3526,8 +3614,7 @@
 		info->tty = 0;
 		info->type = PORT_ETRAX;
 		info->tr_running = 0;
-		info->fifo_magic = 0;
-		info->fifo_didmagic = 0;
+		info->forced_eop = 0;
 		info->flags = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
@@ -3541,8 +3628,20 @@
 		info->normal_termios = serial_driver.init_termios;
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
-		info->xmit.buf = 0;
+		info->xmit.buf = NULL;
 		info->xmit.tail = info->xmit.head = 0;
+		info->first_recv_buffer = info->last_recv_buffer = NULL;
+		info->recv_cnt = info->max_recv_cnt = 0;
+		info->last_tx_active_usec = 0;
+		info->last_tx_active = 0;
+
+#if defined(CONFIG_ETRAX_RS485)
+		/* Set sane defaults */
+		info->rs485.rts_on_send = 0;
+		info->rs485.rts_after_sent = 1;
+		info->rs485.delay_rts_before_send = 0;
+		info->rs485.enabled = 0;
+#endif
 
 		if (info->enabled) {
 			printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n",
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/serial.h linux/arch/cris/drivers/serial.h
--- linux.orig/arch/cris/drivers/serial.h	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/serial.h	Tue Feb  5 17:03:01 2002
@@ -23,81 +23,101 @@
  * For definitions of the flags field, see tty.h
  */
 
+#define SERIAL_RECV_DESCRIPTORS 8
+
+struct etrax_recv_buffer {
+	struct etrax_recv_buffer *next;
+	unsigned short length;
+	unsigned char error;
+	unsigned char pad;
+
+	unsigned char buffer[0];
+};
+
 struct e100_serial {
-	int                   baud;
-	volatile u8 *port;             /* R_SERIALx_CTRL */
-	u32 irq;                       /* bitnr in R_IRQ_MASK2 for dmaX_descr */
-
-	volatile u8 *oclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, output */
-	volatile u32 *ofirstadr;       /* adr to R_DMA_CHx_FIRST, output */
-	volatile u8 *ocmdadr;          /* adr to R_DMA_CHx_CMD, output */
-	const volatile u8 *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */
-	volatile u32 *ohwswadr;        /* adr to R_DMA_CHx_HWSW, output */
-
-	volatile u8 *iclrintradr;      /* adr to R_DMA_CHx_CLR_INTR, input */
-	volatile u32 *ifirstadr;       /* adr to R_DMA_CHx_FIRST, input */
-	volatile u8 *icmdadr;          /* adr to R_DMA_CHx_CMD, input */
-	const volatile u8 *istatusadr; /* adr to R_DMA_CHx_STATUS, input */
-	volatile u32 *ihwswadr;        /* adr to R_DMA_CHx_HWSW, input */
-
-	int			flags; 		/* defined in tty.h */
-
-	u8           rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
-	u8           tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
-	u8           iseteop; /* bit number for R_SET_EOP for the input dma */
-	int          enabled;    /* Set to 1 if the port is enabled in HW config */
-  
+	int			baud;
+	volatile u8		*port; /* R_SERIALx_CTRL */
+	u32			irq;  /* bitnr in R_IRQ_MASK2 for dmaX_descr */
+
+	/* Output registers */
+	volatile u8		*oclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ofirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*ocmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*ostatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ohwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*odescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	/* Input registers */
+	volatile u8		*iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
+	volatile u32		*ifirstadr;   /* adr to R_DMA_CHx_FIRST */
+	volatile u8		*icmdadr;     /* adr to R_DMA_CHx_CMD */
+	const volatile u8	*istatusadr;  /* adr to R_DMA_CHx_STATUS */
+	volatile u32		*ihwswadr;    /* adr to R_DMA_CHx_HWSW */
+	volatile u32		*idescradr;   /* adr to R_DMA_CHx_DESCR */
+
+	int			flags;	/* defined in tty.h */
+
+	u8			rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+	u8			tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+	u8			iseteop; /* bit number for R_SET_EOP for the input dma */
+
+	int			enabled; /* Set to 1 if the port is enabled in HW config */
   
-/* end of fields defined in rs_table[] in .c-file */
-	int          uses_dma; /* Set to 1 if DMA should be used */
-	unsigned char           fifo_didmagic; /* a fifo eop has been forced */
+	/* end of fields defined in rs_table[] in .c-file */
 
-	struct etrax_dma_descr tr_descr, rec_descr;
+	int			uses_dma; /* Set to 1 if DMA should be used */
+	unsigned char           forced_eop; /* a fifo eop has been forced */
 
-	int                     fifo_magic; /* fifo amount - bytes left in dma buffer */
+	struct etrax_dma_descr	tr_descr;
+	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
+	int			cur_rec_descr;
 
-	volatile int            tr_running; /* 1 if output is running */
+	volatile int		tr_running; /* 1 if output is running */
 
-	struct tty_struct 	*tty;
+	struct tty_struct	*tty;
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			x_char;	/* xon/xoff character */
 	int			close_delay;
-	unsigned short	        closing_wait;
-	unsigned short	        closing_wait2;
+	unsigned short		closing_wait;
+	unsigned short		closing_wait2;
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
-	int                     type;  /* PORT_ETRAX */
+	int			type;  /* PORT_ETRAX */
 	int			count;	    /* # of fd on device */
 	int			blocked_open; /* # of blocked opens */
 	long			session; /* Session of opening process */
 	long			pgrp; /* pgrp of opening process */
-	struct circ_buf         xmit;
+	struct circ_buf		xmit;
+	struct etrax_recv_buffer *first_recv_buffer;
+	struct etrax_recv_buffer *last_recv_buffer;
+	unsigned int		recv_cnt;
+	unsigned int		max_recv_cnt;
 
 	struct tq_struct	tqueue;
-	struct async_icount     icount;   /* error-statistics etc.*/
-	struct termios	        normal_termios;
-	struct termios	        callout_termios;
+	struct async_icount	icount;   /* error-statistics etc.*/
+	struct termios		normal_termios;
+	struct termios		callout_termios;
 #ifdef DECLARE_WAITQUEUE
-	wait_queue_head_t       open_wait;
-        wait_queue_head_t       close_wait;
-#else   
-        struct wait_queue       *open_wait;
-        struct wait_queue       *close_wait;
+	wait_queue_head_t	open_wait;
+	wait_queue_head_t	close_wait;
+#else
+	struct wait_queue	*open_wait;
+	struct wait_queue	*close_wait;
 #endif  
 
-	unsigned long char_time_usec;       /* The time for 1 char, in usecs */
-	unsigned long last_tx_active_usec;  /* Last tx usec in the jiffies */
-	unsigned long last_tx_active;       /* Last tx time in jiffies */
-	unsigned long last_rx_active_usec;  /* Last rx usec in the jiffies */
-	unsigned long last_rx_active;       /* Last rx time in jiffies */
+	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
+	unsigned long		last_tx_active;       /* Last tx time in jiffies */
+	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
+	unsigned long		last_rx_active;       /* Last rx time in jiffies */
 
-	int break_detected_cnt;
-	int errorcode;
+	int			break_detected_cnt;
+	int			errorcode;
 
 #ifdef CONFIG_RS485
-	struct rs485_control    rs485;  /* RS-485 support */
+	struct rs485_control	rs485;  /* RS-485 support */
 #endif
 };
 
@@ -116,4 +136,4 @@
 
 #endif /* __KERNEL__ */
 
-#endif /* !(_ETRAX_SERIAL_H) */
+#endif /* !_ETRAX_SERIAL_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/sync_serial.c linux/arch/cris/drivers/sync_serial.c
--- linux.orig/arch/cris/drivers/sync_serial.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/sync_serial.c	Tue Jan  8 16:00:08 2002
@@ -711,7 +711,7 @@
 {
 	port->out_descr.hw_len = 0;
 	port->out_descr.next = 0;
-	port->out_descr.ctrl = d_int | d_eol | d_eop;
+	port->out_descr.ctrl = d_int | d_eol | d_eop | d_wait;
 	port->out_descr.sw_len = count;
 	port->out_descr.buf = virt_to_phys(port->out_buffer);
 	port->out_descr.status = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/drivers/usb-host.c linux/arch/cris/drivers/usb-host.c
--- linux.orig/arch/cris/drivers/usb-host.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/drivers/usb-host.c	Tue Jan  8 16:00:08 2002
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2001 Axis Communications AB.
  *
- * $Id: usb-host.c,v 1.11 2001/09/26 11:52:16 bjornw Exp $
+ * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj Exp $
  *
  */
 
@@ -34,7 +34,7 @@
 #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
 #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
 
-static const char *usb_hcd_version = "$Revision: 1.11 $";
+static const char *usb_hcd_version = "$Revision: 1.13 $";
 
 #undef KERN_DEBUG
 #define KERN_DEBUG ""
@@ -78,11 +78,11 @@
 #endif
 
 #ifdef USB_DEBUG_TRACE
-#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering : " __FUNCTION__ "\n"))
-#define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting  : " __FUNCTION__ "\n"))
+#define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n"))
+#define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting:  " __FUNCTION__ "\n"))
 #else
-#define DBFENTER (NULL)
-#define DBFEXIT  (NULL)
+#define DBFENTER do {} while (0)
+#define DBFEXIT  do {} while (0)
 #endif
 
 /*-------------------------------------------------------------------
@@ -163,7 +163,7 @@
 #define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
 {panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
 
-static submit_urb_count = 0;
+static unsigned long submit_urb_count = 0;
 
 //#define ETRAX_USB_INTR_IRQ
 //#define ETRAX_USB_INTR_ERROR_FATAL
@@ -178,6 +178,7 @@
 
 static __u32 ep_usage_bitmask;
 static __u32 ep_really_active;
+static __u32 ep_out_traffic;
 
 static unsigned char RxBuf[RX_BUF_SIZE];
 static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
@@ -196,22 +197,25 @@
 static kmem_cache_t *usb_desc_cache;
 static struct usb_bus *etrax_usb_bus;
 
+#ifdef USB_DEBUG_DESC
 static void dump_urb (purb_t purb);
+#endif
 static void init_rx_buffers(void);
 static int etrax_rh_unlink_urb (urb_t *urb);
 static void etrax_rh_send_irq(urb_t *urb);
 static void etrax_rh_init_int_timer(urb_t *urb);
 static void etrax_rh_int_timer_do(unsigned long ptr);
 
-static void etrax_usb_setup_epid(char epid, char devnum, char endpoint,
-				 char packsize, char slow);
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp);
+static void etrax_usb_setup_epid(int epid, char devnum, char endpoint,
+				 char packsize, char slow, char out_traffic);
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint,
+				 char slow, int maxp, char out_traffic);
 static int etrax_usb_allocate_epid(void);
-static void etrax_usb_free_epid(char epid);
+static void etrax_usb_free_epid(int epid);
 static void cleanup_sb(USB_SB_Desc_t *sb);
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen);
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen);
+static void etrax_usb_do_ctrl_hw_add(urb_t *urb, int epid, char maxlen);
+static void etrax_usb_do_bulk_hw_add(urb_t *urb, int epid, char maxlen);
 
 static int etrax_usb_submit_ctrl_urb(urb_t *urb);
 
@@ -292,10 +296,10 @@
 
 
 #else
-#define dump_urb(...)     (NULL)
-#define dump_ep_desc(...) (NULL)
-#define dump_sb_desc(...) (NULL)
-#define dump_in_desc(...) (NULL)
+#define dump_urb(...)     do {} while (0)
+#define dump_in_desc(...) do {} while (0)
+#define dump_sb_desc(...) do {} while (0)
+#define dump_ep_desc(...) do {} while (0)
 #endif
 
 static void init_rx_buffers(void)
@@ -423,25 +427,19 @@
 
 static int etrax_usb_unlink_intr_urb(urb_t *urb)
 {
-	struct usb_device *usb_dev = urb->dev;
-	etrax_hc_t *hc = usb_dev->bus->hcpriv;
-
 	USB_EP_Desc_t *tmp_ep;
 	USB_EP_Desc_t *first_ep;
 	
 	USB_EP_Desc_t *ep_desc;
-	USB_SB_Desc_t *sb_desc;
 	
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char slow;
 	int maxlen;
+	char out_traffic;
 	int i;
 	
-	etrax_urb_priv_t *urb_priv;
-	unsigned long flags;
-	
 	DBFENTER;
 
 	devnum = usb_pipedevice(urb->pipe);
@@ -449,8 +447,9 @@
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		err("Trying to unlink urb that is not in traffic queue!!");
 		return -1;  /* fix this */
@@ -517,18 +516,16 @@
 	USB_EP_Desc_t *tmp_ep;
 	USB_EP_Desc_t *first_ep;
 	
-	USB_SB_Desc_t *sb_desc;
-	
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 	int interval;
 	int i;
 	
 	etrax_urb_priv_t *urb_priv;
-	unsigned long flags;
 	
 	DBFENTER;
 
@@ -536,6 +533,7 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
 	slow = usb_pipeslow(urb->pipe);
 	interval = urb->interval;
@@ -543,7 +541,7 @@
 	dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d",
 		 devnum, endpoint, maxlen, slow);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -552,7 +550,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -641,7 +639,7 @@
 }
 
 
-static int handle_intr_transfer_attn(char epid, int status)
+static void handle_intr_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 
@@ -782,7 +780,7 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_usb_setup_epid(char epid, char devnum, char endpoint, char packsize, char slow)
+static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic)
 {
 	unsigned long flags;
 	
@@ -800,8 +798,6 @@
 	}
 	
 	set_bit(epid, (void *)&ep_usage_bitmask);
-	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d",
-	       epid, devnum, endpoint, packsize);
 	
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
@@ -811,12 +807,20 @@
 		IO_FIELD(R_USB_EPT_DATA, max_len, packsize) |
 		IO_FIELD(R_USB_EPT_DATA, low_speed, slow);
 
+	if (out_traffic)
+		set_bit(epid, (void *)&ep_out_traffic);
+	else
+		clear_bit(epid, (void *)&ep_out_traffic);
+
 	restore_flags(flags);
 
+	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)",
+	       epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN");
+
 	DBFEXIT;
 }
 
-static void etrax_usb_free_epid(char epid)
+static void etrax_usb_free_epid(int epid)
 {
 	unsigned long flags;
 	
@@ -846,7 +850,7 @@
 }
 
 
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp)
+static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp, char out_traffic)
 {
 	int i;
 	unsigned long flags;
@@ -855,10 +859,12 @@
 	DBFENTER;
 
 	save_flags(flags);
-	
+	cli();
+
 	/* Skip first ep_id since it is reserved when intr. or iso traffic is used */
 	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (test_bit(i, (void *)&ep_usage_bitmask)) {
+		if (test_bit(i, (void *)&ep_usage_bitmask) &&
+		    test_bit(i, (void *)&ep_out_traffic) == out_traffic) {
 			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
 			nop();
 			data = *R_USB_EPT_DATA;
@@ -869,8 +875,8 @@
 			    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) {
 				restore_flags(flags);
 	
-				dbg_ep("Found ep_id %d for devnum %d, endpoint %d",
-				       i, devnum, endpoint);
+				dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)",
+				       i, devnum, endpoint, out_traffic ? "OUT" : "IN");
 				DBFEXIT;
 				return i;
 			}
@@ -879,8 +885,8 @@
 
 	restore_flags(flags);
 	
-	dbg_ep("Found no ep_id for devnum %d, endpoint %d",
-	       devnum, endpoint);
+	dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)",
+	       devnum, endpoint, out_traffic ? "OUT" : "IN");
 	DBFEXIT;
 	return -1;
 }
@@ -906,15 +912,15 @@
 
 static int etrax_usb_submit_bulk_urb(urb_t *urb)
 {
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 
 	urb_t *tmp_urb;
 	
-	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
 	
 	DBFENTER;
@@ -923,9 +929,10 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -934,7 +941,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -962,14 +969,13 @@
 	return 0;
 }
 
-static int etrax_usb_do_bulk_hw_add(urb_t *urb, char epid, char maxlen)
+static void etrax_usb_do_bulk_hw_add(urb_t *urb, int epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-	__u32 r_usb_ept_data;
 
 	DBFENTER;
 
@@ -1078,7 +1084,7 @@
 	DBFEXIT;
 }
 
-static int handle_bulk_transfer_attn(char epid, int status)
+static void handle_bulk_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 	etrax_urb_priv_t *hc_priv;
@@ -1129,8 +1135,9 @@
 		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
 			      usb_pipeout(old_urb->pipe), toggle);
 	}
+
 	restore_flags(flags);
-	
+
 	/* If there are any more URB's in the list we'd better start sending */
 	if (URB_List[epid]) {
 		etrax_usb_do_bulk_hw_add(URB_List[epid], epid,
@@ -1161,15 +1168,15 @@
 
 static int etrax_usb_submit_ctrl_urb(urb_t *urb)
 {
-	char epid;
+	int epid;
 	char devnum;
 	char endpoint;
 	char maxlen;
+	char out_traffic;
 	char slow;
 
 	urb_t *tmp_urb;
 	
-	etrax_urb_priv_t *urb_priv;
 	unsigned long flags;
 	
 	DBFENTER;
@@ -1178,9 +1185,10 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 	if (epid == -1) {
 		epid = etrax_usb_allocate_epid();
 		if (epid == -1) {
@@ -1189,7 +1197,7 @@
 			return -ENOMEM;
 		}
 		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow);
+		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
@@ -1217,7 +1225,7 @@
 	return 0;
 }
 
-static int etrax_usb_do_ctrl_hw_add(urb_t *urb, char epid, char maxlen)
+static void etrax_usb_do_ctrl_hw_add(urb_t *urb, int epid, char maxlen)
 {
 	USB_SB_Desc_t *sb_desc_1;
 	USB_SB_Desc_t *sb_desc_2;
@@ -1226,8 +1234,6 @@
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-	__u32 r_usb_ept_data;
-	
 
 	DBFENTER;
 
@@ -1365,6 +1371,8 @@
 	
 	DBFENTER;
 
+	urb->next = NULL;
+
 	dump_urb(urb);
 	submit_urb_count++;
 	
@@ -1407,8 +1415,7 @@
 {
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
 	int epid;
-	int pos;
-	int devnum, endpoint, slow, maxlen;
+	int devnum, endpoint, slow, maxlen, out_traffic;
 	etrax_urb_priv_t *hc_priv;
 	unsigned long flags;
 	
@@ -1419,13 +1426,13 @@
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe,
 			       usb_pipeout(urb->pipe));
+	out_traffic = usb_pipeout(urb->pipe);
 
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen);
+	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
 
 	if (epid == -1)
 		return 0;
 	
-	
 	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
 		int ret;
 		ret =  etrax_rh_unlink_urb(urb);
@@ -1449,35 +1456,37 @@
 	
 	for (epid = 0; epid < 32; epid++) {
 		urb_t *u = URB_List[epid];
-		pos = 0;
+		urb_t *prev = NULL;
+		int pos = 0;
 
 		for (; u; u = u->next) {
 			pos++;
 			if (u == urb) {
-				info("Found urb at epid %d, pos %d", epid, pos);
+				if (!prev) {
+					URB_List[epid] = u->next;
+				} else {
+					prev->next = u->next;
+				}
 
-				if (pos == 1) {
+				restore_flags(flags);
+
+				if (!prev) {
 					if (usb_pipetype(u->pipe) == PIPE_CONTROL) {
 						if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
 							/* The EP was enabled, disable it and wait */
 							TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 							while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
 						}
-						
 					} else if (usb_pipetype(u->pipe) == PIPE_BULK) {
 						if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
 							TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 							while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
 						}
 					}
-
-					URB_List[epid] = u->next;
-				
-				} else {
-					urb_t *up;
-					for (up = URB_List[epid]; up->next != u; up = up->next);
-					up->next = u->next;
 				}
+
+				info("Found urb at epid %d, pos %d", epid, pos);
+
 				u->status = -ENOENT;
 				if (u->complete) {
 					u->complete(u);
@@ -1486,7 +1495,11 @@
 				hc_priv = (etrax_urb_priv_t *)u->hcpriv;
 				cleanup_sb(hc_priv->first_sb);
 				kfree(hc_priv);
+
+				DBFEXIT;
+				return 0;
 			}
+			prev = u;
 		}
 	}
 
@@ -1519,13 +1532,6 @@
 
 static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
 {
-	etrax_hc_t *hc = (etrax_hc_t *)vhc;
-	int epid;
-	char eol;
-	urb_t *urb;
-	USB_EP_Desc_t *tmp_ep;
-	USB_SB_Desc_t *tmp_sb;
-	
 	DBFENTER;
 
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
@@ -1589,8 +1595,8 @@
 			} else {
 				if ((urb_priv->rx_offset + myNextRxDesc->hw_len) >
 				    urb->transfer_buffer_length) {
-					err("Packet (epid: %d) in RX buffer was bigger "
-					    "than the URB has room for !!!", epid);
+					err("Packet (epid: %d) in RX buffer (%d) was bigger "
+					    "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length);
 					goto skip_out;
 				}
 				
@@ -1606,7 +1612,7 @@
 			
 		} else {
 			err("This is almost fatal, inpacket for epid %d which does not exist "
-			    " or is out !!!\nURB was at 0x%08X", epid, urb);
+			    " or is out!!!\nURB was at 0x%08lX", epid, (unsigned long)urb);
 			
 			goto skip_out;
 		}
@@ -1647,7 +1653,7 @@
 
 }
 
-static int handle_control_transfer_attn(char epid, int status)
+static void handle_control_transfer_attn(int epid, int status)
 {
 	urb_t *old_urb;
 	etrax_urb_priv_t *hc_priv;	
@@ -1710,7 +1716,6 @@
 static void etrax_usb_hc_intr_bottom_half(void *data)
 {
 	struct usb_reg_context *reg = (struct usb_reg_context *)data;
-	urb_t *old_urb;
 	
 	int error_code;
 	int epid;
@@ -1804,7 +1809,7 @@
 
 			if (URB_List[epid] == NULL) {
 				err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data);
-				err("submit urb has been called %d times..", submit_urb_count);
+				err("submit urb has been called %lu times..", submit_urb_count);
 				err("EPID_ATTN for epid %d, with NULL entry in list", epid);
 				return;
 			}
@@ -1822,7 +1827,7 @@
 				if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 ||
 				    IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) {
 				/* Actually there were transmission errors */
-					warn("Undefined error for endpoint %d", epid);
+					warn("Undefined error for epid %d", epid);
 					if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 						handle_control_transfer_attn(epid, -EPROTO);
 					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
@@ -1861,7 +1866,7 @@
 				}
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-				warn("Stall for endpoint %d", epid);
+				warn("Stall for epid %d", epid);
 				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 					handle_control_transfer_attn(epid, -EPIPE);
 				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
@@ -1872,10 +1877,10 @@
 				
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-				panic("USB bus error for endpoint %d\n", epid);
+				panic("USB bus error for epid %d\n", epid);
 				
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-				warn("Buffer error for endpoint %d", epid);
+				warn("Buffer error for epid %d", epid);
 
 				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
 					handle_control_transfer_attn(epid, -EPROTO);
@@ -1985,11 +1990,7 @@
 	void *data = urb->transfer_buffer;
 	int leni = urb->transfer_buffer_length;
 	int len = 0;
-	int status = 0;
 	int stat = 0;
-	int i;
-
-	__u16 cstatus;
 
 	__u16 bmRType_bReq;
 	__u16 wValue;
@@ -2281,7 +2282,7 @@
 						   0xff, "ETRAX 100LX",
 						   data, wLength);
 			if (len > 0) {
-				OK(min_t(int, leni, len));
+				OK(min(leni, len));
 			} else 
 				stat = -EPIPE;
 		}
@@ -2306,7 +2307,7 @@
 
 	urb->actual_length = len;
 	urb->status = stat;
-	urb->dev=NULL;
+	urb->dev = NULL;
 	if (urb->complete) {
 		urb->complete (urb);
 	}
@@ -2356,6 +2357,7 @@
 	ep_usage_bitmask = 0;
 	set_bit(0, (void *)&ep_usage_bitmask);
 	ep_really_active = 0;
+	ep_out_traffic = 0;
 
 	memset(URB_List, 0, sizeof(URB_List));
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/entry.S linux/arch/cris/kernel/entry.S
--- linux.orig/arch/cris/kernel/entry.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/entry.S	Tue Feb  5 17:03:01 2002
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.35 2001/10/30 17:10:15 bjornw Exp $
+/* $Id: entry.S,v 1.38 2002/01/16 15:15:30 bjornw Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,18 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.38  2002/01/16 15:15:30  bjornw
+ *  Use a C-code compatible watchdog reset when NICE_DOGGY is enabled
+ *
+ *  Revision 1.37  2001/12/07 17:03:55  bjornw
+ *  Call a c-hook called watchdog_bite_hook instead of show_registers directly
+ *
+ *  Revision 1.36  2001/11/22 13:36:36  bjornw
+ *  * In ret_from_intr, check regs->dccr for usermode reentrance instead of
+ *    DCCR explicitely (because the latter might not reflect current reality)
+ *  * In mmu_bus_fault, set $r9 _after_ calling the C-code instead of before
+ *    since $r9 is call-clobbered and is potentially needed afterwards
+ *
  *  Revision 1.35  2001/10/30 17:10:15  bjornw
  *  Add some syscalls
  *
@@ -217,8 +229,11 @@
 	
 ret_from_intr:
 	;; check for resched only if we're going back to user-mode
-
-	move	$ccr, $r0
+	;; this test matches the user_regs(regs) macro
+	;; we cannot simply test $dccr, because that does not necessarily
+	;; reflect what mode we'll return into.
+	
+	move.d	[$sp + LDCCR], $r0; regs->dccr
 	btstq	8, $r0		; U-flag
 	bpl	_Rexit		; go back directly
 	nop
@@ -468,8 +483,6 @@
 	moveq	1, $r10
 	push	$r10		; frametype == 1, BUSFAULT frame type
 
-	moveq	0, $r9		; busfault is equivalent to an irq
-		
 	move.d	$sp, $r10	; pt_regs argument to handle_mmu_bus_fault
 		
 	jsr	handle_mmu_bus_fault  ; in arch/cris/mm/fault.c
@@ -479,6 +492,8 @@
 	;; process due to a SEGV, scheduled due to a page blocking or
 	;; whatever.
 
+	moveq	0, $r9		; busfault is equivalent to an irq
+		
 	ba	ret_from_intr
 	nop
 		
@@ -559,6 +574,16 @@
 ;; We'll see this in ksymoops dumps.
 Watchdog_bite:
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	;; We just restart the watchdog here to be sure we dont get
+	;; hit while printing the watchdogmsg below
+	;; This restart is compatible with the rest of the C-code, so
+	;; the C-code can keep restarting the watchdog after this point.
+	;; The non-NICE_DOGGY code below though, disables the possibility
+	;; to restart since it changes the watchdog key, to avoid any
+	;; buggy loops etc. keeping the watchdog alive after this.
+	jsr	reset_watchdog
+#else
 ;; We need to extend the 3.3ms after the NMI at watchdog bite, so we have
 ;; time for an oops-dump over a 115k2 serial wire.  Another 100ms should do.
 
@@ -575,7 +600,8 @@
 			    ^ WD_INIT)					\
 		| IO_STATE (R_WATCHDOG, enable, start), $r10
 	move.d	$r10, [$r11]
-
+#endif
+	
 ;; Note that we don't do "setf m" here (or after two necessary NOPs),
 ;; since *not* doing that saves us from re-entrancy checks.  We don't want
 ;; to get here again due to possible subsequent NMIs; we want the watchdog
@@ -585,7 +611,7 @@
 	jsr	printk
 
 	move.d	$sp, $r10
-	jsr	show_registers
+	jsr	watchdog_bite_hook
 
 ;; This nop is here so we see the "Watchdog_bite" label in ksymoops dumps
 ;; rather than "spurious_interrupt".
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/head.S linux/arch/cris/kernel/head.S
--- linux.orig/arch/cris/kernel/head.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/head.S	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.41 2001/10/29 14:55:58 pkj Exp $
+/* $Id: head.S,v 1.43 2001/11/08 15:09:43 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,12 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.43  2001/11/08 15:09:43  starvik
+ * Only start MII clock if Ethernet is configured
+ *
+ * Revision 1.42  2001/11/08 14:37:34  starvik
+ * Start MII clock early to make sure that it is running at tranceiver reset
+ *
  * Revision 1.41  2001/10/29 14:55:58  pkj
  * Corrected pa$r0 to par0.
  *
@@ -156,7 +162,10 @@
 
 #define CRAMFS_MAGIC 0x28cd3d45
 #define RAM_INIT_MAGIC 0x56902387
-		
+
+#define START_ETHERNET_CLOCK IO_STATE(R_NETWORK_GEN_CONFIG, enable, on) |\
+                             IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk)
+				
 	;; exported symbols
 		
 	.globl	etrax_irv
@@ -301,6 +310,12 @@
 	;; after init.
 	.section ".text.init"
 _inflash:
+#ifdef CONFIG_ETRAX_ETHERNET	
+	;; Start MII clock to make sure it is running when tranceiver is reset
+	move.d START_ETHERNET_CLOCK, $r0
+	move.d $r0, [R_NETWORK_GEN_CONFIG]
+#endif
+		
 	;; We need to initialze DRAM registers before we start using the DRAM
 
 	cmp.d	RAM_INIT_MAGIC, $r8	; Already initialized?
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/irq.c linux/arch/cris/kernel/irq.c
--- linux.orig/arch/cris/kernel/irq.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/irq.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: irq.c,v 1.17 2001/07/25 16:08:01 bjornw Exp $
+/* $Id: irq.c,v 1.18 2001/11/21 13:40:18 bjornw Exp $
  *
  *	linux/arch/cris/kernel/irq.c
  *
@@ -137,7 +137,7 @@
 /* IRQ0 and 1 are special traps */
 void hwbreakpoint(void);
 void IRQ1_interrupt(void);
-BUILD_IRQ(2, 0x04)             /* the timer interrupt */
+BUILD_TIMER_IRQ(2, 0x04)       /* the timer interrupt is somewhat special */
 BUILD_IRQ(3, 0x08)
 BUILD_IRQ(4, 0x10)
 BUILD_IRQ(5, 0x20)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/ksyms.c linux/arch/cris/kernel/ksyms.c
--- linux.orig/arch/cris/kernel/ksyms.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/ksyms.c	Wed Feb  6 21:06:42 2002
@@ -23,34 +23,44 @@
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern unsigned long get_cmos_time(void);
+extern void __Udiv(void);
 extern void __ashrdi3(void);
 extern void iounmap(void *addr);
 
-/* platform dependent support */
-
+/* Platform dependent support */
 EXPORT_SYMBOL(dump_thread);
 EXPORT_SYMBOL(enable_irq);
 EXPORT_SYMBOL(disable_irq);
 EXPORT_SYMBOL(kernel_thread);
 EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
 
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
-EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(strstr);
-
+EXPORT_SYMBOL(strcpy);
 EXPORT_SYMBOL(strchr);
 EXPORT_SYMBOL(strcmp);
 EXPORT_SYMBOL(strlen);
 EXPORT_SYMBOL(strncat);
 EXPORT_SYMBOL(strncmp);
+
+/* Math functions */
+EXPORT_SYMBOL(__Udiv);
 EXPORT_SYMBOL(__ashrdi3);
 
+/* Memory functions */
 EXPORT_SYMBOL(__ioremap);
 EXPORT_SYMBOL(iounmap);
 
-/* export shadow registers for the CPU I/O pins */
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
 
+/* Export shadow registers for the CPU I/O pins */
 EXPORT_SYMBOL(genconfig_shadow);
 EXPORT_SYMBOL(port_pa_data_shadow);
 EXPORT_SYMBOL(port_pa_dir_shadow);
@@ -59,8 +69,7 @@
 EXPORT_SYMBOL(port_pb_config_shadow);
 EXPORT_SYMBOL(port_g_data_shadow);
 
-/* other stuff */
-
+/* Userspace access functions */
 EXPORT_SYMBOL(strncpy_from_user);
 EXPORT_SYMBOL(__strncpy_from_user);
 EXPORT_SYMBOL(__generic_copy_from_user);
@@ -71,8 +80,8 @@
 
 #undef memcpy
 #undef memset
-extern void * memset(void *,int,__kernel_size_t);
-extern void * memcpy(void *,const void *,__kernel_size_t);
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memset);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/process.c linux/arch/cris/kernel/process.c
--- linux.orig/arch/cris/kernel/process.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/process.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.20 2001/10/03 08:21:39 jonashg Exp $
+/* $Id: process.c,v 1.22 2001/11/13 09:40:43 orjanf Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -8,6 +8,12 @@
  *  Authors:   Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: process.c,v $
+ *  Revision 1.22  2001/11/13 09:40:43  orjanf
+ *  Added dump_fpu (needed for core dumps).
+ *
+ *  Revision 1.21  2001/11/12 18:26:21  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.20  2001/10/03 08:21:39  jonashg
  *  cause_of_death does not exist if CONFIG_SVINTO_SIM is defined.
  *
@@ -57,6 +63,7 @@
 #include <linux/slab.h>
 #include <linux/user.h>
 #include <linux/a.out.h>
+#include <linux/elfcore.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 
@@ -77,7 +84,6 @@
  * setup.
  */
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
@@ -136,14 +142,15 @@
 	 * code to know about it than the watchdog handler in entry.S and
 	 * this code, implementing hard reset through the watchdog.
 	 */
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	extern int cause_of_death;
+#endif
 
 	printk("*** HARD RESET ***\n");
 	cli();
 
 #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 	cause_of_death = 0xbedead;
-
 #else
 	/* Since we dont plan to keep on reseting the watchdog,
 	   the key can be arbitrary hence three */
@@ -244,9 +251,10 @@
  */
 void dump_thread(struct pt_regs * regs, struct user * dump)
 {
-	int i;
 #if 0
-/* changed the size calculations - should hopefully work better. lbt */
+	int i;
+
+	/* changed the size calculations - should hopefully work better. lbt */
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->esp & ~(PAGE_SIZE - 1);
@@ -264,6 +272,12 @@
 
 	dump->u_fpvalid = dump_fpu (regs, &dump->i387);
 #endif 
+}
+
+/* Fill in the fpu structure for a core dump. */
+int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpu)
+{
+        return 0;
 }
 
 /* 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/ptrace.c linux/arch/cris/kernel/ptrace.c
--- linux.orig/arch/cris/kernel/ptrace.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/ptrace.c	Tue Jan  8 16:00:08 2002
@@ -8,6 +8,9 @@
  * Authors:   Bjorn Wesen
  *
  * $Log: ptrace.c,v $
+ * Revision 1.8  2001/11/12 18:26:21  pkj
+ * Fixed compiler warnings.
+ *
  * Revision 1.7  2001/09/26 11:53:49  bjornw
  * PTRACE_DETACH works more simple in 2.4.10
  *
@@ -74,8 +77,6 @@
 static inline int put_reg(struct task_struct *task, unsigned int regno,
 			  unsigned long data)
 {
-	unsigned long *addr;
-
 	if (regno == PT_USP)
 		task->thread.usp = data;
 	else if (regno < PT_MAX)
@@ -207,9 +208,7 @@
 			break;
 
 		case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
-		case PTRACE_CONT: { /* restart after signal. */
-			long tmp;
-
+		case PTRACE_CONT: /* restart after signal. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -222,16 +221,13 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 /*
  * make the child exit.  Best I can do is send it a sigkill. 
  * perhaps it should be put in the status that it wants to 
  * exit.
  */
-		case PTRACE_KILL: {
-			long tmp;
-
+		case PTRACE_KILL:
 			ret = 0;
 			if (child->state == TASK_ZOMBIE) /* already dead */
 				break;
@@ -239,11 +235,8 @@
 			/* TODO: make sure any pending breakpoint is killed */
 			wake_up_process(child);
 			break;
-		}
-
-		case PTRACE_SINGLESTEP: {  /* set the trap flag. */
-			long tmp;
 
+		case PTRACE_SINGLESTEP: /* set the trap flag. */
 			ret = -EIO;
 			if ((unsigned long) data > _NSIG)
 				break;
@@ -256,7 +249,6 @@
 			wake_up_process(child);
 			ret = 0;
 			break;
-		}
 
 		case PTRACE_DETACH:
 			ret = ptrace_detach(child, data);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/setup.c linux/arch/cris/kernel/setup.c
--- linux.orig/arch/cris/kernel/setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/setup.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: setup.c,v 1.22 2001/10/23 17:42:58 pkj Exp $
+/* $Id: setup.c,v 1.24 2001/12/07 17:03:19 bjornw Exp $
  *
  *  linux/arch/cris/kernel/setup.c
  *
@@ -26,10 +26,12 @@
 #include <linux/config.h>
 #include <linux/init.h>
 #include <linux/bootmem.h>
+#include <linux/seq_file.h>
 
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <asm/smp.h>
+#include <asm/pgtable.h>
 #include <asm/types.h>
 #include <asm/svinto.h>
 
@@ -72,10 +74,10 @@
 void __init 
 setup_arch(char **cmdline_p)
 {
-        unsigned long bootmap_size;
+	extern void init_etrax_debug(void);
+	unsigned long bootmap_size;
 	unsigned long start_pfn, max_pfn;
 	unsigned long memory_start;
-	extern void console_print_etrax(const char *b);
 
  	/* register an initial console printing routine for printk's */
 
@@ -87,12 +89,12 @@
 
 	if(romfs_in_flash || !romfs_length) {
 		/* if we have the romfs in flash, or if there is no rom filesystem,
-		 * our free area starts directly after the BSS 
+		 * our free area starts directly after the BSS
 		 */
 		memory_start = (unsigned long) &_end;
 	} else {
 		/* otherwise the free area starts after the ROM filesystem */
-		printk("ROM fs in RAM, size %d bytes\n", romfs_length);
+		printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
 		memory_start = romfs_start + romfs_length;
 	}
 
@@ -193,7 +195,7 @@
 #define HAS_ATA		0x0020
 #define HAS_USB		0x0040
 #define HAS_IRQ_BUG	0x0080
-#define HAS_MMU_BUG     0x0100
+#define HAS_MMU_BUG	0x0100
 
 static struct cpu_info {
 	char *model;
@@ -213,50 +215,27 @@
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG },
 	{ "ETRAX 100",       8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA },
 	{ "ETRAX 100LX",     8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU | HAS_MMU_BUG },
-	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU },
+	{ "ETRAX 100LX v2",  8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU  },
 	{ "Unknown",         0, 0 }  /* This entry MUST be the last */
 };
 
-/*
- * get_cpuinfo - Get information on one CPU for use by the procfs.
- *
- *	Prints info on the next CPU into buffer.  Beware, doesn't check for
- *	buffer overflow.  Current implementation of procfs assumes that the
- *	resulting data is <= 1K.
- *
- *	BUFFER is PAGE_SIZE - 1K bytes long.
- *
- * Args:
- *	buffer	-- you guessed it, the data buffer
- *	cpu_np	-- Input: next cpu to get (start at 0).  Output: Updated.
- *
- *	Returns number of bytes written to buffer.
- */
-int get_cpuinfo(char *buffer, unsigned *cpu_np)
+static int show_cpuinfo(struct seq_file *m, void *v)
 {
-	int revision;
- 	struct cpu_info *info;
-	unsigned n;
+	unsigned long revision;
+	struct cpu_info *info;
 
 	/* read the version register in the CPU and print some stuff */
 
 	revision = rdvr();
 
-	if (revision < 0 || revision >= sizeof cpu_info/sizeof *cpu_info) {
+	if (revision >= sizeof cpu_info/sizeof *cpu_info)
 		info = &cpu_info[sizeof cpu_info/sizeof *cpu_info - 1];
-	} else
+	else
 		info = &cpu_info[revision];
 
-  	/* No SMP at the moment, so just toggle 0/1 */
-	n = *cpu_np;
-	*cpu_np = 1;
-	if (n != 0) {
-		return (0);
-	}
-
-	return sprintf(buffer,
+	return seq_printf(m,
 		       "cpu\t\t: CRIS\n"
-		       "cpu revision\t: %d\n"
+		       "cpu revision\t: %lu\n"
 		       "cpu model\t: %s\n"
 		       "cache size\t: %d kB\n"
 		       "fpu\t\t: %s\n"
@@ -283,4 +262,28 @@
 		       (loops_per_jiffy * HZ + 500) / 500000,
 		       ((loops_per_jiffy * HZ + 500) / 5000) % 100);
 }
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	/* We only got one CPU... */
+	return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+struct seq_operations cpuinfo_op = {
+	start:  c_start,
+	next:   c_next,
+	stop:   c_stop,
+	show:   show_cpuinfo,
+};
+
 #endif /* CONFIG_PROC_FS */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/time.c linux/arch/cris/kernel/time.c
--- linux.orig/arch/cris/kernel/time.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/time.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.9 2001/10/25 10:26:37 johana Exp $
+/* $Id: time.c,v 1.11 2001/11/12 18:26:22 pkj Exp $
  *
  *  linux/arch/cris/kernel/time.c
  *
@@ -18,6 +18,7 @@
  * Linux/CRIS specific code:
  *
  * Authors:    Bjorn Wesen
+ *             Johan Adolfsson  
  *
  */
 
@@ -61,6 +62,7 @@
 static unsigned long do_slow_gettimeoffset(void)
 {
 	unsigned long count;
+	unsigned long usec_count = 0;
 
 	static unsigned long count_p = LATCH;    /* for the first call after boot */
 	static unsigned long jiffies_p = 0;
@@ -93,16 +95,20 @@
  */
 	if( jiffies_t == jiffies_p ) {
 		if( count > count_p ) {
+			/* Timer wrapped */
+			count = count_p;
+			usec_count = 1000000/CLOCK_TICK_RATE/2;
 		}
 	} else
 		jiffies_p = jiffies_t;
-
         count_p = count;
-
+	/* Convert timer value to usec using table lookup */
+	usec_count += cris_timer0_value_us[count];
+#if 0
 	count = ((LATCH-1) - count) * TICK_SIZE;
 	count = (count + LATCH/2) / LATCH;
-
-	return count;
+#endif
+	return usec_count;
 }
 
 static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset;
@@ -160,9 +166,8 @@
 {
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
-	unsigned char save_control, save_freq_select;
 
-	printk("set_rtc_mmss(%d)\n", nowtime);
+	printk("set_rtc_mmss(%lu)\n", nowtime);
 
 	if(!have_rtc)
 		return 0;
@@ -225,7 +230,9 @@
 /* right now, starting the watchdog is the same as resetting it */
 #define start_watchdog reset_watchdog
 
+#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
 static int watchdog_key = 0;  /* arbitrary number */
+#endif
 
 /* number of pages to consider "out of memory". it is normal that the memory
  * is used though, so put this really low.
@@ -306,12 +313,12 @@
 	if ((time_status & STA_UNSYNC) == 0 &&
 	    xtime.tv_sec > last_rtc_update + 660 &&
 	    xtime.tv_usec > 500000 - (tick >> 1) &&
-	    xtime.tv_usec < 500000 + (tick >> 1))
+	    xtime.tv_usec < 500000 + (tick >> 1)) {
 		if (set_rtc_mmss(xtime.tv_sec) == 0)
 			last_rtc_update = xtime.tv_sec;
 		else
 			last_rtc_update = xtime.tv_sec - 600;
-	
+	}
 }
 
 #if 0
@@ -322,6 +329,7 @@
 {
 	unsigned long flags;
 	unsigned int newjiff;
+
 	save_flags(flags);
 	cli();
 	newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); 
@@ -337,7 +345,6 @@
 get_cmos_time(void)
 {
 	unsigned int year, mon, day, hour, min, sec;
-	int i;
 
 	sec = CMOS_READ(RTC_SECONDS);
 	min = CMOS_READ(RTC_MINUTES);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/kernel/traps.c linux/arch/cris/kernel/traps.c
--- linux.orig/arch/cris/kernel/traps.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/kernel/traps.c	Tue Jan  8 16:00:08 2002
@@ -1,4 +1,4 @@
-/* $Id: traps.c,v 1.15 2001/07/18 14:02:37 bjornw Exp $
+/* $Id: traps.c,v 1.17 2001/12/07 17:02:34 bjornw Exp $
  *
  *  linux/arch/cris/traps.c
  *
@@ -30,6 +30,55 @@
 
 int kstack_depth_to_print = 24;
 
+void show_trace(unsigned long * stack)
+{
+	unsigned long addr, module_start, module_end;
+	extern char _stext, _etext;
+	int i;
+
+        printk("\nCall Trace: ");
+
+        i = 1;
+        module_start = VMALLOC_START;
+        module_end = VMALLOC_END;
+
+        while (((long) stack & (THREAD_SIZE-1)) != 0) {
+		if (__get_user (addr, stack)) {
+			/* This message matches "failing address" marked
+			   s390 in ksymoops, so lines containing it will
+			   not be filtered out by ksymoops.  */
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
+			break;
+		}
+		stack++;
+
+                /*
+                 * If the address is either in the text segment of the
+                 * kernel, or in the region which contains vmalloc'ed
+                 * memory, it *may* be the address of a calling
+                 * routine; if so, print it so that someone tracing
+                 * down the cause of the crash will be able to figure
+                 * out the call path that was taken.
+                 */
+                if (((addr >= (unsigned long) &_stext) &&
+                     (addr <= (unsigned long) &_etext)) ||
+                    ((addr >= module_start) && (addr <= module_end))) {
+                        if (i && ((i % 8) == 0))
+                                printk("\n       ");
+                        printk("[<%08lx>] ", addr);
+                        i++;
+                }
+        }
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/* TODO, this is not really useful since its called from
+	 * SysRq-T and we don't have a keyboard.. :) 
+	 */
+}
+
+
 /*
  * These constants are for searching for possible module text
  * segments. MODULE_RANGE is a guess of how much space is likely
@@ -48,9 +97,8 @@
 void 
 show_stack(unsigned long *sp)
 {
-        unsigned long *stack, addr, module_start, module_end;
+        unsigned long *stack, addr;
         int i;
-	extern char _stext, _etext;
 
 	/*
 	 * debugging aid: "show_stack(NULL);" prints a
@@ -62,7 +110,7 @@
 
         stack = sp;
 
-	printk("\nStack from %08lx:\n       ", stack);
+	printk("\nStack from %08lx:\n       ", (unsigned long)stack);
         for(i = 0; i < kstack_depth_to_print; i++) {
                 if (((long) stack & (THREAD_SIZE-1)) == 0)
                         break;
@@ -72,45 +120,13 @@
 			/* This message matches "failing address" marked
 			   s390 in ksymoops, so lines containing it will
 			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
+			printk ("Failing address 0x%lx\n", (unsigned long)stack);
 			break;
 		}
 		stack++;
 		printk("%08lx ", addr);
         }
-
-        printk("\nCall Trace: ");
-        stack = sp;
-        i = 1;
-        module_start = VMALLOC_START;
-        module_end = VMALLOC_END;
-        while (((long) stack & (THREAD_SIZE-1)) != 0) {
-		if (__get_user (addr, stack)) {
-			/* This message matches "failing address" marked
-			   s390 in ksymoops, so lines containing it will
-			   not be filtered out by ksymoops.  */
-			printk ("Failing address 0x%lx\n", stack);
-			break;
-		}
-		stack++;
-
-                /*
-                 * If the address is either in the text segment of the
-                 * kernel, or in the region which contains vmalloc'ed
-                 * memory, it *may* be the address of a calling
-                 * routine; if so, print it so that someone tracing
-                 * down the cause of the crash will be able to figure
-                 * out the call path that was taken.
-                 */
-                if (((addr >= (unsigned long) &_stext) &&
-                     (addr <= (unsigned long) &_etext)) ||
-                    ((addr >= module_start) && (addr <= module_end))) {
-                        if (i && ((i % 8) == 0))
-                                printk("\n       ");
-                        printk("[<%08lx>] ", addr);
-                        i++;
-                }
-        }
+	show_trace(sp);
 }
 
 #if 0
@@ -148,7 +164,7 @@
 	       regs->r8, regs->r9, regs->r10, regs->r11);
 	printk("r12: %08lx r13: %08lx oR10: %08lx\n",
 	       regs->r12, regs->r13, regs->orig_r10);
-	printk("R_MMU_CAUSE: %08lx\n", *R_MMU_CAUSE);
+	printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
 	printk("Process %s (pid: %d, stackpage=%08lx)\n",
 	       current->comm, current->pid, (unsigned long)current);
 
@@ -195,25 +211,56 @@
         }
 }
 
+/* Called from entry.S when the watchdog has bitten
+ * We print out something resembling an oops dump, and if
+ * we have the nice doggy development flag set, we halt here
+ * instead of rebooting.
+ */
+
+void
+watchdog_bite_hook(struct pt_regs *regs)
+{
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	cli();
+	stop_watchdog();
+	show_registers(regs);
+	while(1) /* nothing */;
+#else
+	show_registers(regs);
+#endif	
+}
+
+/* This is normally the 'Oops' routine */
+
 void 
 die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
+	extern void reset_watchdog(void);
+	extern void stop_watchdog(void);
+
 	if(user_mode(regs))
 		return;
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	/* This printout might take too long and trigger the 
+	 * watchdog normally. If we're in the nice doggy
+	 * development mode, stop the watchdog during printout.
+	 */
 	stop_watchdog();
+#endif
 
 	printk("%s: %04lx\n", str, err & 0xffff);
 
 	show_registers(regs);
 
+#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
 	reset_watchdog();
-
+#endif
 	do_exit(SIGSEGV);
 }
 
 void __init 
 trap_init(void)
 {
-  /* Nothing needs to be done */
+	/* Nothing needs to be done */
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/mm/fault.c linux/arch/cris/mm/fault.c
--- linux.orig/arch/cris/mm/fault.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/mm/fault.c	Tue Jan  8 16:00:08 2002
@@ -6,6 +6,17 @@
  *  Authors:  Bjorn Wesen 
  * 
  *  $Log: fault.c,v $
+ *  Revision 1.20  2001/11/22 13:34:06  bjornw
+ *  * Bug workaround (LX TR89): force a rerun of the whole of an interrupted
+ *    unaligned write, because the second half of the write will be corrupted
+ *    otherwise. Affected unaligned writes spanning not-yet mapped pages.
+ *  * Optimization: use the wr_rd bit in R_MMU_CAUSE to know whether a miss
+ *    was due to a read or a write (before we didn't know this until the next
+ *    restart of the interrupted instruction, thus wasting one fault-irq)
+ *
+ *  Revision 1.19  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.18  2001/07/18 22:14:32  bjornw
  *  Enable interrupts in the bulk of do_page_fault
  *
@@ -78,7 +89,14 @@
 			      int error_code);
 
 /* debug of low-level TLB reload */
+#undef DEBUG
+
+#ifdef DEBUG
+#define D(x) x
+#else
 #define D(x)
+#endif
+
 /* debug of higher-level faults */
 #define DPG(x)
 
@@ -94,9 +112,12 @@
 handle_mmu_bus_fault(struct pt_regs *regs)
 {
 	int cause, select;
+#ifdef DEBUG
 	int index;
 	int page_id;
-	int miss, we, acc, inv;  
+	int acc, inv;
+#endif
+	int miss, we, writeac;
 	pmd_t *pmd;
 	pte_t pte;
 	int errcode;
@@ -106,75 +127,83 @@
 	select = *R_TLB_SELECT;
 
 	address = cause & PAGE_MASK; /* get faulting address */
-	
-	D(page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause));
-	D(acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause));
-	D(inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause));  
-	D(index  =  IO_EXTRACT(R_TLB_SELECT, index,     select));
+
+#ifdef DEBUG
+	page_id = IO_EXTRACT(R_MMU_CAUSE,  page_id,   cause);
+	acc     = IO_EXTRACT(R_MMU_CAUSE,  acc_excp,  cause);
+	inv     = IO_EXTRACT(R_MMU_CAUSE,  inv_excp,  cause);  
+	index   = IO_EXTRACT(R_TLB_SELECT, index,     select);
+#endif
 	miss    = IO_EXTRACT(R_MMU_CAUSE,  miss_excp, cause);
 	we      = IO_EXTRACT(R_MMU_CAUSE,  we_excp,   cause);
+	writeac = IO_EXTRACT(R_MMU_CAUSE,  wr_rd,     cause);
+
+	/* ETRAX 100LX TR89 bugfix: if the second half of an unaligned
+	 * write causes a MMU-fault, it will not be restarted correctly.
+	 * This could happen if a write crosses a page-boundary and the
+	 * second page is not yet COW'ed or even loaded. The workaround
+	 * is to clear the unaligned bit in the CPU status record, so 
+	 * that the CPU will rerun both the first and second halves of
+	 * the instruction. This will not have any sideeffects unless
+	 * the first half goes to any device or memory that can't be
+	 * written twice, and which is mapped through the MMU.
+	 *
+	 * We only need to do this for writes.
+	 */
+
+	if(writeac)
+		regs->csrinstr &= ~(1 << 5);
 	
-	/* Note: the reason we don't set errcode's r/w flag here
-	 * using the 'we' flag, is because the latter is only given
-	 * if there is a write-protection exception, not given as a
-	 * general r/w access mode flag. It is currently not possible
-	 * to get this from the MMU (TODO: check if this is the case
-	 * for LXv2).
-	 * 
-	 * The page-fault code won't care, but there will be two page-
-	 * faults instead of one for the case of a write to a non-tabled
-	 * page (miss, then write-protection).
+	/* Set errcode's R/W flag according to the mode which caused the
+	 * fault
 	 */
 
-	errcode = 0;
+	errcode = writeac << 1;
 
-	D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, "
-		 "idx %d pid %d\n",
+	D(printk("bus_fault from IRP 0x%lx: addr 0x%lx, miss %d, inv %d, we %d, acc %d, dx %d pid %d\n",
 		 regs->irp, address, miss, inv, we, acc, index, page_id));
 
 	/* for a miss, we need to reload the TLB entry */
 
-	if(miss) {
-
+	if (miss) {
 		/* see if the pte exists at all
 		 * refer through current_pgd, dont use mm->pgd
 		 */
-		
+
 		pmd = (pmd_t *)(current_pgd + pgd_index(address));
-		if(pmd_none(*pmd))
+		if (pmd_none(*pmd))
 			goto dofault;
-		if(pmd_bad(*pmd)) {
-			printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd);
+		if (pmd_bad(*pmd)) {
+			printk("bad pgdir entry 0x%lx at 0x%p\n", *(unsigned long*)pmd, pmd);
 			pmd_clear(pmd);
 			return;
 		}
 		pte = *pte_offset(pmd, address);
-		if(!pte_present(pte))
+		if (!pte_present(pte))
 			goto dofault;
-		
-		D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte)));
-		D(
-		  {
-			  if(pte_val(pte) & _PAGE_SILENT_WRITE)
-				  printk("Silent-W ");
-			  if(pte_val(pte) & _PAGE_KERNEL)
-				  printk("Kernel ");
-			  if(pte_val(pte) & _PAGE_SILENT_READ)
-				  printk("Silent-R ");
-			  if(pte_val(pte) & _PAGE_GLOBAL)
-				  printk("Global ");
-			  if(pte_val(pte) & _PAGE_PRESENT)
-				  printk("Present ");
-			  if(pte_val(pte) & _PAGE_ACCESSED)
-				  printk("Accessed ");
-			  if(pte_val(pte) & _PAGE_MODIFIED)
-				  printk("Modified ");
-			  if(pte_val(pte) & _PAGE_READ)
-				  printk("Readable ");
-			  if(pte_val(pte) & _PAGE_WRITE)
-				  printk("Writeable ");
-			  printk("\n");
-		  });
+
+#ifdef DEBUG
+		printk(" found pte %lx pg %p ", pte_val(pte), pte_page(pte));
+		if (pte_val(pte) & _PAGE_SILENT_WRITE)
+			printk("Silent-W ");
+		if (pte_val(pte) & _PAGE_KERNEL)
+			printk("Kernel ");
+		if (pte_val(pte) & _PAGE_SILENT_READ)
+			printk("Silent-R ");
+		if (pte_val(pte) & _PAGE_GLOBAL)
+			printk("Global ");
+		if (pte_val(pte) & _PAGE_PRESENT)
+			printk("Present ");
+		if (pte_val(pte) & _PAGE_ACCESSED)
+			printk("Accessed ");
+		if (pte_val(pte) & _PAGE_MODIFIED)
+			printk("Modified ");
+		if (pte_val(pte) & _PAGE_READ)
+			printk("Readable ");
+		if (pte_val(pte) & _PAGE_WRITE)
+			printk("Writeable ");
+		printk("\n");
+#endif
 
 		/* load up the chosen TLB entry
 		 * this assumes the pte format is the same as the TLB_LO layout.
@@ -189,10 +218,10 @@
 	} 
 
 	errcode = 1 | (we << 1);
-	
+
  dofault:
 	/* leave it to the MM system fault handler below */
-	D(printk("do_page_fault %p errcode %d\n", address, errcode));
+	D(printk("do_page_fault %lx errcode %d\n", address, errcode));
 	do_page_fault(address, regs, errcode);
 }
 
@@ -221,20 +250,19 @@
 	struct mm_struct *mm;
 	struct vm_area_struct * vma;
 	int writeaccess;
-	int fault;
 	unsigned long fixup;
 	siginfo_t info;
 
 	tsk = current;
 
-        /*
-         * We fault-in kernel-space virtual memory on-demand. The
-         * 'reference' page table is init_mm.pgd.
-         *
-         * NOTE! We MUST NOT take any locks for this case. We may
-         * be in an interrupt or a critical region, and should
-         * only copy the information from the master page table,
-         * nothing more.
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
 	 *
 	 * NOTE2: This is done so that, when updating the vmalloc
 	 * mappings we don't have to walk all processes pgdirs and
@@ -243,13 +271,13 @@
 	 * bit set so sometimes the TLB can use a lingering entry.
 	 *
 	 * This verifies that the fault happens in kernel space
-         * and that the fault was not a protection error (error_code & 1).
-         */
+	 * and that the fault was not a protection error (error_code & 1).
+	 */
 
-        if (address >= VMALLOC_START &&
+	if (address >= VMALLOC_START &&
 	    !(error_code & 1) &&
 	    !user_mode(regs))
-                goto vmalloc_fault;
+		goto vmalloc_fault;
 
 	/* we can and should enable interrupts at this point */
 	sti();
@@ -312,28 +340,27 @@
 	 */
 
 	switch (handle_mm_fault(mm, vma, address, writeaccess)) {
-        case 1:
-                tsk->min_flt++;
-                break;
-        case 2:
-                tsk->maj_flt++;
-                break;
-        case 0:
-                goto do_sigbus;
-        default:
-                goto out_of_memory;
+	case 1:
+		tsk->min_flt++;
+		break;
+	case 2:
+		tsk->maj_flt++;
+		break;
+	case 0:
+		goto do_sigbus;
+	default:
+		goto out_of_memory;
 	}
 
 	up_read(&mm->mmap_sem);
 	return;
-	
+
 	/*
 	 * Something tried to access memory that isn't in our memory map..
 	 * Fix it, but check if it's kernel or user first..
 	 */
 
  bad_area:
-
 	up_read(&mm->mmap_sem);
 
  bad_area_nosemaphore:
@@ -361,10 +388,10 @@
 	 *  code)
 	 */
 
-        if ((fixup = search_exception_table(regs->irp)) != 0) {
+	if ((fixup = search_exception_table(regs->irp)) != 0) {
 		/* Adjust the instruction pointer in the stackframe */
 
-                regs->irp = fixup;
+		regs->irp = fixup;
 
 		/* We do not want to return by restoring the CPU-state
 		 * anymore, so switch frame-types (see ptrace.h)
@@ -372,9 +399,9 @@
 
 		regs->frametype = CRIS_FRAME_NORMAL;
 
-		D(printk("doing fixup to 0x%x\n", fixup));
-                return;
-        }
+		D(printk("doing fixup to 0x%lx\n", fixup));
+		return;
+	}
 
 	/*
 	 * Oops. The kernel tried to access some bad page. We'll have to
@@ -397,9 +424,9 @@
 	 */
 
  out_of_memory:
-        up_read(&mm->mmap_sem);
+	up_read(&mm->mmap_sem);
 	printk("VM: killing process %s\n", tsk->comm);
-	if(user_mode(regs))
+	if (user_mode(regs))
 		do_exit(SIGKILL);
 	goto no_context;
 
@@ -407,40 +434,40 @@
 	up_read(&mm->mmap_sem);
 
 	/*
-         * Send a sigbus, regardless of whether we were in kernel
-         * or user mode.
-         */
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
 	info.si_code = SIGBUS;
 	info.si_errno = 0;
 	info.si_code = BUS_ADRERR;
 	info.si_addr = (void *)address;
 	force_sig_info(SIGBUS, &info, tsk);
-	
-        /* Kernel mode? Handle exceptions or die */
-        if (!user_mode(regs))
-                goto no_context;
-        return;
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(regs))
+		goto no_context;
+	return;
 
 vmalloc_fault:
-        {
-                /*
-                 * Synchronize this task's top level page-table
-                 * with the 'reference' page table.
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
 		 *
 		 * Use current_pgd instead of tsk->active_mm->pgd
 		 * since the latter might be unavailable if this
 		 * code is executed in a misfortunately run irq
 		 * (like inside schedule() between switch_mm and
 		 *  switch_to...).
-                 */
+		 */
 
-                int offset = pgd_index(address);
-                pgd_t *pgd, *pgd_k;
-                pmd_t *pmd, *pmd_k;
+		int offset = pgd_index(address);
+		pgd_t *pgd, *pgd_k;
+		pmd_t *pmd, *pmd_k;
 		pte_t *pte_k;
 
-                pgd = current_pgd + offset;
-                pgd_k = init_mm.pgd + offset;
+		pgd = (pgd_t *)current_pgd + offset;
+		pgd_k = init_mm.pgd + offset;
 
 		/* Since we're two-level, we don't need to do both
 		 * set_pgd and set_pmd (they do the same thing). If
@@ -454,13 +481,13 @@
 		 * it exists.
 		 */
 
-                pmd = pmd_offset(pgd, address);
-                pmd_k = pmd_offset(pgd_k, address);
+		pmd = pmd_offset(pgd, address);
+		pmd_k = pmd_offset(pgd_k, address);
 
-                if (!pmd_present(*pmd_k))
-                        goto bad_area_nosemaphore;
+		if (!pmd_present(*pmd_k))
+			goto bad_area_nosemaphore;
 
-                set_pmd(pmd, *pmd_k);
+		set_pmd(pmd, *pmd_k);
 
 		/* Make sure the actual PTE exists as well to
 		 * catch kernel vmalloc-area accesses to non-mapped
@@ -468,10 +495,10 @@
 		 * silently loop forever.
 		 */
 
-                pte_k = pte_offset(pmd_k, address);
-                if (!pte_present(*pte_k))
-                        goto no_context;
+		pte_k = pte_offset(pmd_k, address);
+		if (!pte_present(*pte_k))
+			goto no_context;
 
-                return;
-        }
+		return;
+	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/cris/mm/init.c linux/arch/cris/mm/init.c
--- linux.orig/arch/cris/mm/init.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/cris/mm/init.c	Tue Jan  8 16:00:08 2002
@@ -7,6 +7,12 @@
  *  Authors:  Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: init.c,v $
+ *  Revision 1.31  2001/11/13 16:22:00  bjornw
+ *  Skip calculating totalram and sharedram in si_meminfo
+ *
+ *  Revision 1.30  2001/11/12 19:02:10  pkj
+ *  Fixed compiler warnings.
+ *
  *  Revision 1.29  2001/07/25 16:09:50  bjornw
  *  val->sharedram will stay 0
  *
@@ -459,29 +465,18 @@
                 free_page(addr);
                 totalram_pages++;
         }
-        printk ("Freeing unused kernel memory: %dk freed\n", 
+        printk ("Freeing unused kernel memory: %luk freed\n", 
 		(&__init_end - &__init_begin) >> 10);
 }
 
 void 
 si_meminfo(struct sysinfo *val)
 {
-	int i;
-
-	i = max_mapnr;
-	val->totalram = 0;
-	val->sharedram = 0;
-	val->freeram = nr_free_pages();
-	val->bufferram = atomic_read(&buffermem_pages);
-	while (i-- > 0)  {
-		if (PageReserved(mem_map+i))
-			continue;
-		val->totalram++;
-		if (!atomic_read(&mem_map[i].count))
-			continue;
-		val->sharedram += atomic_read(&mem_map[i].count) - 1;
-	}
-	val->mem_unit = PAGE_SIZE;
-	val->totalhigh = 0;
-	val->freehigh = 0;
+        val->totalram = totalram_pages;
+        val->sharedram = 0;
+        val->freeram = nr_free_pages();
+        val->bufferram = atomic_read(&buffermem_pages);
+        val->totalhigh = 0;
+        val->freehigh = 0;
+        val->mem_unit = PAGE_SIZE;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/boot/compressed/Makefile linux/arch/i386/boot/compressed/Makefile
--- linux.orig/arch/i386/boot/compressed/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/boot/compressed/Makefile	Thu Jan 10 20:08:20 2002
@@ -32,8 +32,10 @@
 head.o: head.S
 	$(CC) $(AFLAGS) -traditional -c head.S
 
+comma	:= ,
+
 misc.o: misc.c
-	$(CC) $(CFLAGS) -c misc.c
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -c misc.c
 
 piggy.o:	$(SYSTEM)
 	tmppiggy=_tmp_$$$$piggy; \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/boot/setup.S linux/arch/i386/boot/setup.S
--- linux.orig/arch/i386/boot/setup.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/boot/setup.S	Wed Jan 23 19:52:23 2002
@@ -42,6 +42,9 @@
  * if CX/DX have been changed in the e801 call and if so use AX/BX .
  * Michael Miller, April 2001 <michaelm@mjmm.org>
  *
+ * New A20 code ported from SYSLINUX by H. Peter Anvin. AMD Elan bugfixes
+ * by Robert Schwebel, December 2001 <robert@schwebel.de>
+ *
  */
 
 #include <linux/config.h>
@@ -50,7 +53,8 @@
 #include <linux/compile.h>
 #include <asm/boot.h>
 #include <asm/e820.h>
-
+#include <asm/page.h>
+	
 /* Signature words to ensure LILO loaded us right */
 #define SIG1	0xAA55
 #define SIG2	0x5A5A
@@ -79,7 +83,7 @@
 # This is the setup header, and it must start at %cs:2 (old 0x9020:2)
 
 		.ascii	"HdrS"		# header signature
-		.word	0x0202		# header version number (>= 0x0105)
+		.word	0x0203		# header version number (>= 0x0105)
 					# or else old loadlin-1.5 will fail)
 realmode_swtch:	.word	0, 0		# default_switch, SETUPSEG
 start_sys_seg:	.word	SYSSEG
@@ -153,6 +157,10 @@
 					# can be located anywhere in
 					# low memory 0x10000 or higher.
 
+ramdisk_max:	.long __MAXMEM-1	# (Header version 0x0203 or later)
+					# The highest safe address for
+					# the contents of an initrd
+
 trampoline:	call	start_of_setup
 		.space	1024
 # End of setup header #####################################################
@@ -646,7 +654,18 @@
 #
 # Enable A20.  This is at the very best an annoying procedure.
 # A20 code ported from SYSLINUX 1.52-1.63 by H. Peter Anvin.
+# AMD Elan bug fix by Robert Schwebel.
 #
+
+#if defined(CONFIG_MELAN)
+	movb $0x02, %al			# alternate A20 gate
+	outb %al, $0x92			# this works on SC410/SC520
+a20_elan_wait:
+        call a20_test
+        jz a20_elan_wait
+	jmp a20_done
+#endif
+
 
 A20_TEST_LOOPS		=  32		# Iterations per wait
 A20_ENABLE_LOOPS	= 255		# Total loops to try		
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/config.in linux/arch/i386/config.in
--- linux.orig/arch/i386/config.in	Fri Dec 21 17:41:53 2001
+++ linux/arch/i386/config.in	Mon Feb  4 19:11:15 2002
@@ -37,6 +37,7 @@
 	 Pentium-4				CONFIG_MPENTIUM4 \
 	 K6/K6-II/K6-III			CONFIG_MK6 \
 	 Athlon/Duron/K7			CONFIG_MK7 \
+	 Elan					CONFIG_MELAN \
 	 Crusoe					CONFIG_MCRUSOE \
 	 Winchip-C6				CONFIG_MWINCHIPC6 \
 	 Winchip-2				CONFIG_MWINCHIP2 \
@@ -126,6 +127,11 @@
    define_bool CONFIG_X86_PGE y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
 fi
+if [ "$CONFIG_MELAN" = "y" ]; then
+   define_int  CONFIG_X86_L1_CACHE_SHIFT 4
+   define_bool CONFIG_X86_USE_STRING_486 y
+   define_bool CONFIG_X86_ALIGNMENT_16 y
+fi
 if [ "$CONFIG_MCYRIXIII" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_TSC y
@@ -141,18 +147,21 @@
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+   define_bool CONFIG_X86_OOSTORE y
 fi
 if [ "$CONFIG_MWINCHIP2" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+   define_bool CONFIG_X86_OOSTORE y
 fi
 if [ "$CONFIG_MWINCHIP3D" = "y" ]; then
    define_int  CONFIG_X86_L1_CACHE_SHIFT 5
    define_bool CONFIG_X86_ALIGNMENT_16 y
    define_bool CONFIG_X86_TSC y
    define_bool CONFIG_X86_USE_PPRO_CHECKSUM y
+   define_bool CONFIG_X86_OOSTORE y
 fi
 tristate 'Toshiba Laptop support' CONFIG_TOSHIBA
 tristate 'Dell laptop support' CONFIG_I8K
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/defconfig linux/arch/i386/defconfig
--- linux.orig/arch/i386/defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/defconfig	Thu Jan 10 18:12:40 2002
@@ -496,6 +496,9 @@
 # IrDA (infrared) support
 #
 # CONFIG_IRDA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
 
 #
 # ISDN subsystem
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/apic.c linux/arch/i386/kernel/apic.c
--- linux.orig/arch/i386/kernel/apic.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/apic.c	Mon Feb  4 17:05:47 2002
@@ -56,6 +56,14 @@
 	maxlvt = get_maxlvt();
 
 	/*
+	 * Masking an LVT entry on a P6 can trigger a local APIC error
+	 * if the vector is zero. Mask LVTERR first to prevent this.
+	 */
+	if (maxlvt >= 3) {
+		v = ERROR_APIC_VECTOR; /* any non-zero vector will do */
+		apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
+	}
+	/*
 	 * Careful: we have to set masks only first to deassert
 	 * any level-triggered sources.
 	 */
@@ -65,10 +73,6 @@
 	apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
 	v = apic_read(APIC_LVT1);
 	apic_write_around(APIC_LVT1, v | APIC_LVT_MASKED);
-	if (maxlvt >= 3) {
-		v = apic_read(APIC_LVTERR);
-		apic_write_around(APIC_LVTERR, v | APIC_LVT_MASKED);
-	}
 	if (maxlvt >= 4) {
 		v = apic_read(APIC_LVTPC);
 		apic_write_around(APIC_LVTPC, v | APIC_LVT_MASKED);
@@ -84,6 +88,12 @@
 		apic_write_around(APIC_LVTERR, APIC_LVT_MASKED);
 	if (maxlvt >= 4)
 		apic_write_around(APIC_LVTPC, APIC_LVT_MASKED);
+	v = GET_APIC_VERSION(apic_read(APIC_LVR));
+	if (APIC_INTEGRATED(v)) {	/* !82489DX */
+		if (maxlvt > 3)
+			apic_write(APIC_ESR, 0);
+		apic_read(APIC_ESR);
+	}
 }
 
 void __init connect_bsp_APIC(void)
@@ -480,6 +490,7 @@
 	l &= ~MSR_IA32_APICBASE_BASE;
 	l |= MSR_IA32_APICBASE_ENABLE | APIC_DEFAULT_PHYS_BASE;
 	wrmsr(MSR_IA32_APICBASE, l, h);
+	apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED);
 	apic_write(APIC_ID, apic_pm_state.apic_id);
 	apic_write(APIC_DFR, apic_pm_state.apic_dfr);
 	apic_write(APIC_LDR, apic_pm_state.apic_ldr);
@@ -487,15 +498,15 @@
 	apic_write(APIC_SPIV, apic_pm_state.apic_spiv);
 	apic_write(APIC_LVT0, apic_pm_state.apic_lvt0);
 	apic_write(APIC_LVT1, apic_pm_state.apic_lvt1);
+	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
+	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
+	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);
+	apic_write(APIC_TMICT, apic_pm_state.apic_tmict);
 	apic_write(APIC_ESR, 0);
 	apic_read(APIC_ESR);
 	apic_write(APIC_LVTERR, apic_pm_state.apic_lvterr);
 	apic_write(APIC_ESR, 0);
 	apic_read(APIC_ESR);
-	apic_write(APIC_LVTPC, apic_pm_state.apic_lvtpc);
-	apic_write(APIC_LVTT, apic_pm_state.apic_lvtt);
-	apic_write(APIC_TDCR, apic_pm_state.apic_tdcr);
-	apic_write(APIC_TMICT, apic_pm_state.apic_tmict);
 	__restore_flags(flags);
 	if (apic_pm_state.perfctr_pmdev)
 		pm_send(apic_pm_state.perfctr_pmdev, PM_RESUME, data);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/apm.c linux/arch/i386/kernel/apm.c
--- linux.orig/arch/i386/kernel/apm.c	Fri Nov  9 21:58:02 2001
+++ linux/arch/i386/kernel/apm.c	Wed Jan 23 21:13:14 2002
@@ -39,6 +39,7 @@
  * Feb 2000, Version 1.13
  * Nov 2000, Version 1.14
  * Oct 2001, Version 1.15
+ * Jan 2002, Version 1.16
  *
  * History:
  *    0.6b: first version in official kernel, Linux 1.3.46
@@ -85,7 +86,7 @@
  *         change APM_NOINTS to CONFIG_APM_ALLOW_INTS
  *         remove dependency on CONFIG_PROC_FS
  *         Stephen Rothwell
- *    1.9: Fix small typo.  <laslo@ilo.opole.pl>
+ *    1.9: Fix small typo.  <laslo@wodip.opole.pl>
  *         Try to cope with BIOS's that need to have all display
  *         devices blanked and not just the first one.
  *         Ross Paterson <ross@soi.city.ac.uk>
@@ -164,8 +165,18 @@
  *         If an APM idle fails log it and idle sensibly
  *   1.15: Don't queue events to clients who open the device O_WRONLY.
  *         Don't expect replies from clients who open the device O_RDONLY.
- *         (Idea from Thomas Hood <jdthood at yahoo.co.uk>)
- *         Minor waitqueue cleanups.(John Fremlin <chief@bandits.org>)
+ *         (Idea from Thomas Hood <jdthood@mail.com>)
+ *         Minor waitqueue cleanups. (John Fremlin <chief@bandits.org>)
+ *   1.16: Fix idle calling. (Andreas Steinmetz <ast@domdv.de> et al.)
+ *         Notify listeners of standby or suspend events before notifying
+ *         drivers. Return EBUSY to ioctl() if suspend is rejected.
+ *         (Russell King <rmk@arm.linux.org.uk> and Thomas Hood)
+ *         Ignore first resume after we generate our own resume event
+ *         after a suspend (Thomas Hood <jdthood@mail.com>)
+ *         Daemonize now gets rid of our controlling terminal (sfr).
+ *         CONFIG_APM_CPU_IDLE now just affects the default value of
+ *         idle_threshold (sfr).
+ *         Change name of kernel apm daemon (as it no longer idles) (sfr).
  *
  * APM 1.1 Reference:
  *
@@ -238,6 +249,12 @@
  *	    [no-]power[-_]off		power off on shutdown
  *	    bounce[-_]interval=<n>	number of ticks to ignore suspend
  *	    				bounces
+ *          idle[-_]threshold=<n>       System idle percentage above which to
+ *                                      make APM BIOS idle calls. Set it to
+ *                                      100 to disable.
+ *          idle[-_]period=<n>          Period (in 1/100s of a second) over
+ *                                      which the idle percentage is
+ *                                      calculated.
  */
 
 /* KNOWN PROBLEM MACHINES:
@@ -341,15 +358,26 @@
 #define APM_BIOS_MAGIC		0x4101
 
 /*
+ * idle percentage above which bios idle calls are done
+ */
+#ifdef CONFIG_APM_CPU_IDLE
+#define DEFAULT_IDLE_THRESHOLD	95
+#else
+#define DEFAULT_IDLE_THRESHOLD	100
+#endif
+#define DEFAULT_IDLE_PERIOD	(100 / 3)
+
+/*
  * Local variables
  */
 static struct {
 	unsigned long	offset;
 	unsigned short	segment;
 }				apm_bios_entry;
-#ifdef CONFIG_APM_CPU_IDLE
 static int			clock_slowed;
-#endif
+static int			idle_threshold = DEFAULT_IDLE_THRESHOLD;
+static int			idle_period = DEFAULT_IDLE_PERIOD;
+static int			set_pm_idle;
 static int			suspends_pending;
 static int			standbys_pending;
 static int			waiting_for_resume;
@@ -388,7 +416,7 @@
 static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
 static struct apm_user *	user_list;
 
-static char			driver_version[] = "1.15";	/* no spaces */
+static char			driver_version[] = "1.16";	/* no spaces */
 
 /*
  *	APM event names taken from the APM 1.2 specification. These are
@@ -684,8 +712,6 @@
 	return set_power_state(APM_DEVICE_ALL, state);
 }
 
-#ifdef CONFIG_APM_CPU_IDLE
-
 /**
  *	apm_do_idle	-	perform power saving
  *
@@ -735,63 +761,89 @@
 	}
 }
 
-#if 0
-extern int hlt_counter;
-
 /*
- * If no process has been interested in this
- * CPU for some time, we want to wake up the
- * power management thread - we probably want
+ * If no process has really been interested in
+ * the CPU for some time, we want to call BIOS
+ * power management - we probably want
  * to conserve power.
  */
-#define HARD_IDLE_TIMEOUT (HZ/3)
+#define IDLE_CALC_LIMIT   (HZ * 100)
+#define IDLE_LEAKY_MAX    16
 
-/* This should wake up kapmd and ask it to slow the CPU */
-#define powermanagement_idle()  do { } while (0)
+static void (*sys_idle)(void);
+
+extern void default_idle(void);
 
 /**
  * apm_cpu_idle		-	cpu idling for APM capable Linux
  *
  * This is the idling function the kernel executes when APM is available. It 
- * tries to save processor time directly by using hlt instructions. A
- * separate apm thread tries to do the BIOS power management.
- *
- * N.B. This is curently not used for kernels 2.4.x.
+ * tries to do BIOS powermanagement based on the average system idle time.
+ * Furthermore it calls the system default idle routine.
  */
 
 static void apm_cpu_idle(void)
 {
-	unsigned int start_idle;
+	static int use_apm_idle = 0;
+	static unsigned int last_jiffies = 0;
+	static unsigned int last_stime = 0;
+
+	int apm_is_idle = 0;
+	unsigned int jiffies_since_last_check = jiffies - last_jiffies;
+	unsigned int t1;
+
+
+recalc:
+	if (jiffies_since_last_check > IDLE_CALC_LIMIT) {
+		use_apm_idle = 0;
+		last_jiffies = jiffies;
+		last_stime = current->times.tms_stime;
+	} else if (jiffies_since_last_check > idle_period) {
+		unsigned int idle_percentage;
+
+		idle_percentage = current->times.tms_stime - last_stime;
+		idle_percentage *= 100;
+		idle_percentage /= jiffies_since_last_check;
+		use_apm_idle = (idle_percentage > idle_threshold);
+		last_jiffies = jiffies;
+		last_stime = current->times.tms_stime;
+	}
+
+	t1 = IDLE_LEAKY_MAX;
+
+	while (!current->need_resched) {
+		if (use_apm_idle) {
+			unsigned int t;
 
-	start_idle = jiffies;
-	while (1) {
-		if (!current->need_resched) {
-			if (jiffies - start_idle < HARD_IDLE_TIMEOUT) {
-				if (!current_cpu_data.hlt_works_ok)
-					continue;
-				if (hlt_counter)
+			t = jiffies;
+			switch (apm_do_idle()) {
+			case 0: apm_is_idle = 1;
+				if (t != jiffies) {
+					if (t1) {
+						t1 = IDLE_LEAKY_MAX;
+						continue;
+					}
+				} else if (t1) {
+					t1--;
 					continue;
-				__cli();
-				if (!current->need_resched)
-					safe_halt();
-				else
-					__sti();
-				continue;
+				}
+				break;
+			case 1: apm_is_idle = 1;
+				break;
 			}
-
-			/*
-			 * Ok, do some power management - we've been idle for too long
-			 */
-			powermanagement_idle();
 		}
-
-		schedule();
-		check_pgt_cache();
-		start_idle = jiffies;
+		if (sys_idle)
+			sys_idle();
+		else
+			default_idle();
+		jiffies_since_last_check = jiffies - last_jiffies;
+		if (jiffies_since_last_check > idle_period)
+			goto recalc;
 	}
+
+	if (apm_is_idle)
+		apm_do_busy();
 }
-#endif
-#endif
 
 #ifdef CONFIG_SMP
 static int apm_magic(void * unused)
@@ -1133,57 +1185,42 @@
 #endif
 }
 
-static int send_event(apm_event_t event)
+static int suspend(int vetoable)
 {
-	switch (event) {
-	case APM_SYS_SUSPEND:
-	case APM_CRITICAL_SUSPEND:
-	case APM_USER_SUSPEND:
-		/* map all suspends to ACPI D3 */
-		if (pm_send_all(PM_SUSPEND, (void *)3)) {
-			if (event == APM_CRITICAL_SUSPEND) {
-				printk(KERN_CRIT
-					"apm: Critical suspend was vetoed, "
-					"expect armageddon\n" );
-				return 0;
-			}
+	int		err;
+	struct apm_user	*as;
+
+	if (pm_send_all(PM_SUSPEND, (void *)3)) {
+		/* Vetoed */
+		if (vetoable) {
 			if (apm_info.connection_version > 0x100)
 				apm_set_power_state(APM_STATE_REJECT);
-			return 0;
+			err = -EBUSY;
+			waiting_for_resume = 0;
+			printk(KERN_WARNING "apm: suspend was vetoed.\n");
+			goto out;
 		}
-		break;
-	case APM_NORMAL_RESUME:
-	case APM_CRITICAL_RESUME:
-		/* map all resumes to ACPI D0 */
-		(void) pm_send_all(PM_RESUME, (void *)0);
-		break;
+		printk(KERN_CRIT "apm: suspend was vetoed, but suspending anyway.\n");
 	}
-
-	return 1;
-}
-
-static int suspend(void)
-{
-	int		err;
-	struct apm_user	*as;
-
 	get_time_diff();
 	cli();
 	err = apm_set_power_state(APM_STATE_SUSPEND);
 	reinit_timer();
 	set_time();
+	sti();
 	if (err == APM_NO_ERROR)
 		err = APM_SUCCESS;
 	if (err != APM_SUCCESS)
 		apm_error("suspend", err);
-	send_event(APM_NORMAL_RESUME);
-	sti();
+	err = (err == APM_SUCCESS) ? 0 : -EIO;
+	pm_send_all(PM_RESUME, (void *)0);
 	queue_event(APM_NORMAL_RESUME, NULL);
+	ignore_normal_resume = 1;
+ out:
 	for (as = user_list; as != NULL; as = as->next) {
 		as->suspend_wait = 0;
-		as->suspend_result = ((err == APM_SUCCESS) ? 0 : -EIO);
+		as->suspend_result = err;
 	}
-	ignore_normal_resume = 1;
 	wake_up_interruptible(&apm_suspend_waitqueue);
 	return err;
 }
@@ -1192,6 +1229,7 @@
 {
 	int	err;
 
+	/* If needed, notify drivers here */
 	get_time_diff();
 	err = apm_set_power_state(APM_STATE_STANDBY);
 	if ((err != APM_SUCCESS) && (err != APM_NO_ERROR))
@@ -1235,17 +1273,13 @@
 		if (ignore_bounce
 		    && ((jiffies - last_resume) > bounce_interval))
 			ignore_bounce = 0;
-		if (ignore_normal_resume && (event != APM_NORMAL_RESUME))
-			ignore_normal_resume = 0;
 
 		switch (event) {
 		case APM_SYS_STANDBY:
 		case APM_USER_STANDBY:
-			if (send_event(event)) {
-				queue_event(event, NULL);
-				if (standbys_pending <= 0)
-					standby();
-			}
+			queue_event(event, NULL);
+			if (standbys_pending <= 0)
+				standby();
 			break;
 
 		case APM_USER_SUSPEND:
@@ -1270,12 +1304,10 @@
 			 */
 			if (waiting_for_resume)
 				return;
-			if (send_event(event)) {
-				queue_event(event, NULL);
-				waiting_for_resume = 1;
-				if (suspends_pending <= 0)
-					(void) suspend();
-			}
+			waiting_for_resume = 1;
+			queue_event(event, NULL);
+			if (suspends_pending <= 0)
+				(void) suspend(1);
 			break;
 
 		case APM_NORMAL_RESUME:
@@ -1287,16 +1319,17 @@
 			if ((event != APM_NORMAL_RESUME)
 			    || (ignore_normal_resume == 0)) {
 				set_time();
-				send_event(event);
+				pm_send_all(PM_RESUME, (void *)0);
 				queue_event(event, NULL);
 			}
+			ignore_normal_resume = 0;
 			break;
 
 		case APM_CAPABILITY_CHANGE:
 		case APM_LOW_BATTERY:
 		case APM_POWER_STATUS_CHANGE:
-			send_event(event);
 			queue_event(event, NULL);
+			/* If needed, notify drivers here */
 			break;
 
 		case APM_UPDATE_TIME:
@@ -1304,12 +1337,10 @@
 			break;
 
 		case APM_CRITICAL_SUSPEND:
-			send_event(event);
 			/*
-			 * We can only hope it worked - we are not allowed
-			 * to reject a critical suspend.
+			 * We are not allowed to reject a critical suspend.
 			 */
-			(void) suspend();
+			(void) suspend(0);
 			break;
 		}
 	}
@@ -1337,63 +1368,24 @@
 
 /*
  * This is the APM thread main loop.
- *
- * Check whether we're the only running process to
- * decide if we should just power down.
- *
  */
-#define system_idle() (nr_running == 1)
 
 static void apm_mainloop(void)
 {
-	int timeout = HZ;
 	DECLARE_WAITQUEUE(wait, current);
 
 	add_wait_queue(&apm_waitqueue, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	for (;;) {
-		/* Nothing to do, just sleep for the timeout */
-		timeout = 2 * timeout;
-		if (timeout > APM_CHECK_TIMEOUT)
-			timeout = APM_CHECK_TIMEOUT;
-		schedule_timeout(timeout);
+		schedule_timeout(APM_CHECK_TIMEOUT);
 		if (exit_kapmd)
 			break;
-
 		/*
 		 * Ok, check all events, check for idle (and mark us sleeping
 		 * so as not to count towards the load average)..
 		 */
 		set_current_state(TASK_INTERRUPTIBLE);
 		apm_event_handler();
-#ifdef CONFIG_APM_CPU_IDLE
-		if (!system_idle())
-			continue;
-		
-		/*
-		 *	If we can idle...
-		 */
-		if (apm_do_idle() != -1) {
-			unsigned long start = jiffies;
-			while ((!exit_kapmd) && system_idle()) {
-				if (apm_do_idle()) {
-					set_current_state(TASK_INTERRUPTIBLE);
-					/* APM needs us to snooze .. either
-					   the BIOS call failed (-1) or it
-					   slowed the clock (1). We sleep
-					   until it talks to us again */
-					schedule_timeout(1);
-				}
-				if ((jiffies - start) > APM_CHECK_TIMEOUT) {
-					apm_event_handler();
-					start = jiffies;
-				}
-			}
-			apm_do_busy();
-			apm_event_handler();
-			timeout = 1;
-		}
-#endif
 	}
 	remove_wait_queue(&apm_waitqueue, &wait);
 }
@@ -1479,9 +1471,7 @@
 			as->standbys_read--;
 			as->standbys_pending--;
 			standbys_pending--;
-		} else if (!send_event(APM_USER_STANDBY))
-			return -EAGAIN;
-		else
+		} else
 			queue_event(APM_USER_STANDBY, as);
 		if (standbys_pending <= 0)
 			standby();
@@ -1491,13 +1481,10 @@
 			as->suspends_read--;
 			as->suspends_pending--;
 			suspends_pending--;
-		} else if (!send_event(APM_USER_SUSPEND))
-			return -EAGAIN;
-		else
+		} else
 			queue_event(APM_USER_SUSPEND, as);
 		if (suspends_pending <= 0) {
-			if (suspend() != APM_SUCCESS)
-				return -EIO;
+			return suspend(1);
 		} else {
 			as->suspend_wait = 1;
 			wait_event_interruptible(apm_suspend_waitqueue,
@@ -1528,7 +1515,7 @@
 	if (as->suspends_pending > 0) {
 		suspends_pending -= as->suspends_pending;
 		if (suspends_pending <= 0)
-			(void) suspend();
+			(void) suspend(1);
 	}
 	if (user_list == as)
 		user_list = as->next;
@@ -1676,9 +1663,8 @@
 
 	daemonize();
 
-	strcpy(current->comm, "kapm-idled");
+	strcpy(current->comm, "kapmd");
 	sigfillset(&current->blocked);
-	current->tty = NULL;	/* get rid of controlling tty */
 
 	if (apm_info.connection_version == 0) {
 		apm_info.connection_version = apm_info.bios.version;
@@ -1797,7 +1783,14 @@
 		if ((strncmp(str, "bounce-interval=", 16) == 0) ||
 		    (strncmp(str, "bounce_interval=", 16) == 0))
 			bounce_interval = simple_strtol(str + 16, NULL, 0);
-		invert = (strncmp(str, "no-", 3) == 0);
+		if ((strncmp(str, "idle-threshold=", 15) == 0) ||
+		    (strncmp(str, "idle_threshold=", 15) == 0))
+			idle_threshold = simple_strtol(str + 15, NULL, 0);
+		if ((strncmp(str, "idle-period=", 12) == 0) ||
+		    (strncmp(str, "idle_period=", 12) == 0))
+			idle_threshold = simple_strtol(str + 15, NULL, 0);
+		invert = (strncmp(str, "no-", 3) == 0) ||
+			(strncmp(str, "no_", 3) == 0);
 		if (invert)
 			str += 3;
 		if (strncmp(str, "debug", 5) == 0)
@@ -1968,6 +1961,14 @@
 
 	misc_register(&apm_device);
 
+	if (HZ != 100)
+		idle_period = (idle_period * HZ) / 100;
+	if (idle_threshold < 100) {
+		sys_idle = pm_idle;
+		pm_idle  = apm_cpu_idle;
+		set_pm_idle = 1;
+	}
+
 	return 0;
 }
 
@@ -1975,6 +1976,8 @@
 {
 	int	error;
 
+	if (set_pm_idle)
+		pm_idle = sys_idle;
 	if (((apm_info.bios.flags & APM_BIOS_DISENGAGED) == 0)
 	    && (apm_info.connection_version > 0x0100)) {
 		error = apm_engage_power_management(APM_DEVICE_ALL, 0);
@@ -2012,5 +2015,11 @@
 MODULE_PARM(realmode_power_off, "i");
 MODULE_PARM_DESC(realmode_power_off,
 		"Switch to real mode before powering off");
+MODULE_PARM(idle_threshold, "i");
+MODULE_PARM_DESC(idle_threshold,
+	"System idle percentage above which to make APM BIOS idle calls");
+MODULE_PARM(idle_period, "i");
+MODULE_PARM_DESC(idle_period,
+	"Period (in sec/100) over which to caculate the idle percentage");
 
 EXPORT_NO_SYMBOLS;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/dmi_scan.c linux/arch/i386/kernel/dmi_scan.c
--- linux.orig/arch/i386/kernel/dmi_scan.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/dmi_scan.c	Tue Jan  8 16:18:17 2002
@@ -467,6 +467,11 @@
 			MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
 			NO_MATCH, NO_MATCH
 			} },
+	{ set_bios_reboot, "Dell PowerEdge 2400", {  /* Handle problems with rebooting on Dell 300/800's */
+			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
+			MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
+			NO_MATCH, NO_MATCH
+			} },
 	{ set_apm_ints, "Dell Inspiron", {	/* Allow interrupts during suspend on Dell Inspiron laptops*/
 			MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 			MATCH(DMI_PRODUCT_NAME, "Inspiron 4000"),
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/entry.S linux/arch/i386/kernel/entry.S
--- linux.orig/arch/i386/kernel/entry.S	Sat Nov  3 01:18:49 2001
+++ linux/arch/i386/kernel/entry.S	Mon Feb  4 17:24:05 2002
@@ -622,6 +622,18 @@
 	.long SYMBOL_NAME(sys_ni_syscall)	/* Reserved for Security */
 	.long SYMBOL_NAME(sys_gettid)
 	.long SYMBOL_NAME(sys_readahead)	/* 225 */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for setxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for lsetxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for fsetxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for getxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* 230 reserved for lgetxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for fgetxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for listxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for llistxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for flistxattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* 235 reserved for removexattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for lremovexattr */
+	.long SYMBOL_NAME(sys_ni_syscall)	/* reserved for fremovexattr */
 
 	.rept NR_syscalls-(.-sys_call_table)/4
 		.long SYMBOL_NAME(sys_ni_syscall)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S
--- linux.orig/arch/i386/kernel/head.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/head.S	Thu Jan 10 20:08:20 2002
@@ -446,12 +446,3 @@
 	.quad 0x00009a0000000000	/* 0x50 APM CS 16 code (16 bit) */
 	.quad 0x0040920000000000	/* 0x58 APM DS    data */
 	.fill NR_CPUS*4,8,0		/* space for TSS's and LDT's */
-		
-/*
- * This is to aid debugging, the various locking macros will be putting
- * code fragments here.  When an oops occurs we'd rather know that it's
- * inside the .text.lock section rather than as some offset from whatever
- * function happens to be last in the .text segment.
- */
-.section .text.lock
-ENTRY(stext_lock)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/i386_ksyms.c linux/arch/i386/kernel/i386_ksyms.c
--- linux.orig/arch/i386/kernel/i386_ksyms.c	Tue Nov 13 17:13:20 2001
+++ linux/arch/i386/kernel/i386_ksyms.c	Wed Feb  6 21:06:42 2002
@@ -35,6 +35,8 @@
 #if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE)
 extern void machine_real_restart(unsigned char *, int);
 EXPORT_SYMBOL(machine_real_restart);
+extern void default_idle(void);
+EXPORT_SYMBOL(default_idle);
 #endif
 
 #ifdef CONFIG_SMP
@@ -93,7 +95,6 @@
 
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
-EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(strstr);
 
 EXPORT_SYMBOL(strncpy_from_user);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/pci-irq.c linux/arch/i386/kernel/pci-irq.c
--- linux.orig/arch/i386/kernel/pci-irq.c	Sun Nov  4 17:31:58 2001
+++ linux/arch/i386/kernel/pci-irq.c	Wed Feb  6 20:47:54 2002
@@ -225,12 +225,12 @@
  */
 static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
 {
-	return read_config_nybble(router, 0x5C, pirq-1);
+	return read_config_nybble(router, 0x5C, (pirq-1)^1);
 }
 
 static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
 {
-	write_config_nybble(router, 0x5C, pirq-1, irq);
+	write_config_nybble(router, 0x5C, (pirq-1)^1, irq);
 	return 1;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/pci-pc.c linux/arch/i386/kernel/pci-pc.c
--- linux.orig/arch/i386/kernel/pci-pc.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/pci-pc.c	Tue Feb  5 18:24:33 2002
@@ -1109,22 +1109,29 @@
 }
 
 /*
- * Nobody seems to know what this does. Damn.
+ * Addresses issues with problems in the memory write queue timer in
+ * certain VIA Northbridges.  This bugfix is per VIA's specifications.
  *
- * But it does seem to fix some unspecified problem
- * with 'movntq' copies on Athlons.
- *
- * VIA 8363 chipset:
- *  - bit 7 at offset 0x55: Debug (RW)
+ * VIA 8363,8622,8361 Northbridges:
+ *  - bits  5, 6, 7 at offset 0x55 need to be turned off
+ * VIA 8367 (KT266x) Northbridges:
+ *  - bits  5, 6, 7 at offset 0x95 need to be turned off
  */
-static void __init pci_fixup_via_athlon_bug(struct pci_dev *d)
+static void __init pci_fixup_via_northbridge_bug(struct pci_dev *d)
 {
 	u8 v;
-	pci_read_config_byte(d, 0x55, &v);
-	if (v & 0x80) {
-		printk("Trying to stomp on Athlon bug...\n");
-		v &= 0x7f; /* clear bit 55.7 */
-		pci_write_config_byte(d, 0x55, v);
+	int where = 0x55;
+
+	if (d->device == PCI_DEVICE_ID_VIA_8367_0) {
+		where = 0x95; /* the memory write queue timer register is 
+				 different for the kt266x's: 0x95 not 0x55 */
+	}
+
+	pci_read_config_byte(d, where, &v);
+	if (v & 0xe0) {
+		printk("Disabling VIA memory write queue: [%02x] %02x->%02x\n", where, v, v & 0x1f);
+		v &= 0x1f; /* clear bits 5, 6, 7 */
+		pci_write_config_byte(d, where, v);
 	}
 }
 
@@ -1137,7 +1144,10 @@
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5597,		pci_fixup_latency },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_SI,	PCI_DEVICE_ID_SI_5598,		pci_fixup_latency },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371AB_3,	pci_fixup_piix4_acpi },
-	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	pci_fixup_via_athlon_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8363_0,	pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8622,	        pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8361,	        pci_fixup_via_northbridge_bug },
+	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_VIA,	PCI_DEVICE_ID_VIA_8367_0,	pci_fixup_via_northbridge_bug },
 	{ 0 }
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/process.c linux/arch/i386/kernel/process.c
--- linux.orig/arch/i386/kernel/process.c	Fri Oct  5 01:42:54 2001
+++ linux/arch/i386/kernel/process.c	Wed Feb 13 17:43:18 2002
@@ -41,6 +41,7 @@
 #include <asm/ldt.h>
 #include <asm/processor.h>
 #include <asm/i387.h>
+#include <asm/irq.h>
 #include <asm/desc.h>
 #include <asm/mmu_context.h>
 #ifdef CONFIG_MATH_EMULATION
@@ -77,7 +78,7 @@
  * We use this if we don't have any better
  * idle routine..
  */
-static void default_idle(void)
+void default_idle(void)
 {
 	if (current_cpu_data.hlt_works_ok && !hlt_counter) {
 		__cli();
@@ -542,6 +543,8 @@
 			BUG();
 		}
 	}
+
+	release_x86_irqs(dead_task);
 }
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/setup.c linux/arch/i386/kernel/setup.c
--- linux.orig/arch/i386/kernel/setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/setup.c	Wed Feb  6 21:02:21 2002
@@ -827,10 +827,8 @@
 #define PFN_PHYS(x)	((x) << PAGE_SHIFT)
 
 /*
- * 128MB for vmalloc and initrd
+ * Reserved space for vmalloc and iomap - defined in asm/page.h
  */
-#define VMALLOC_RESERVE	(unsigned long)(128 << 20)
-#define MAXMEM		(unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE)
 #define MAXMEM_PFN	PFN_DOWN(MAXMEM)
 #define MAX_NONPAE_PFN	(1 << 20)
 
@@ -2234,7 +2232,7 @@
 			 */
 #define NR_SIBLINGS	2
 			if (smp_num_siblings != NR_SIBLINGS) {
-				printk(KERN_WARNING "CPU: Unsuppored number of the siblings %d", smp_num_siblings);
+				printk(KERN_WARNING "CPU: Unsupported number of the siblings %d", smp_num_siblings);
 				smp_num_siblings = 1;
 				goto too_many_siblings;
 			}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- linux.orig/arch/i386/kernel/signal.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/signal.c	Fri Dec 21 19:35:58 2001
@@ -28,7 +28,7 @@
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
+int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
 
 int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c
--- linux.orig/arch/i386/kernel/time.c	Sun Nov 11 18:20:21 2001
+++ linux/arch/i386/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -681,7 +681,6 @@
 #ifndef do_gettimeoffset
 			do_gettimeoffset = do_fast_gettimeoffset;
 #endif
-			do_get_fast_time = do_gettimeofday;
 
 			/* report CPU clock rate in Hz.
 			 * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/kernel/vm86.c linux/arch/i386/kernel/vm86.c
--- linux.orig/arch/i386/kernel/vm86.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/kernel/vm86.c	Wed Feb 13 17:43:18 2002
@@ -16,6 +16,7 @@
 #include <asm/uaccess.h>
 #include <asm/pgalloc.h>
 #include <asm/io.h>
+#include <asm/irq.h>
 
 /*
  * Known problems:
@@ -62,7 +63,7 @@
         ( (unsigned)( & (((struct kernel_vm86_regs *)0)->VM86_REGS_PART2) ) )
 #define VM86_REGS_SIZE2 (sizeof(struct kernel_vm86_regs) - VM86_REGS_SIZE1)
 
-asmlinkage struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs));
+struct pt_regs * FASTCALL(save_v86_state(struct kernel_vm86_regs * regs));
 struct pt_regs * save_v86_state(struct kernel_vm86_regs * regs)
 {
 	struct tss_struct *tss;
@@ -610,6 +611,14 @@
 	}
 	read_unlock(&tasklist_lock);
 	return ret;
+}
+
+void release_x86_irqs(struct task_struct *task)
+{
+	int i;
+	for (i=3; i<16; i++)
+	    if (vm86_irqs[i].tsk == task)
+		free_vm86_irq(i);
 }
 
 static inline void handle_irq_zombies(void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/mm/fault.c linux/arch/i386/mm/fault.c
--- linux.orig/arch/i386/mm/fault.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/mm/fault.c	Wed Dec 26 15:25:28 2001
@@ -27,8 +27,6 @@
 
 extern void die(const char *,struct pt_regs *,long);
 
-extern int console_loglevel;
-
 /*
  * Ugly, ugly, but the goto's result in better assembly..
  */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/i386/vmlinux.lds linux/arch/i386/vmlinux.lds
--- linux.orig/arch/i386/vmlinux.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/i386/vmlinux.lds	Thu Jan 10 20:08:20 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x9090
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
 
   _etext = .;			/* End of text section */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ia64/kernel/time.c linux/arch/ia64/kernel/time.c
--- linux.orig/arch/ia64/kernel/time.c	Fri Nov  9 22:26:17 2001
+++ linux/arch/ia64/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -145,9 +145,6 @@
 	tv->tv_usec = usec;
 }
 
-/* XXX there should be a cleaner way for declaring an alias... */
-asm (".global get_fast_time; get_fast_time = do_gettimeofday");
-
 static void
 timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ia64/sn/fprom/Makefile linux/arch/ia64/sn/fprom/Makefile
--- linux.orig/arch/ia64/sn/fprom/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ia64/sn/fprom/Makefile	Thu Jan 10 20:08:20 2002
@@ -18,10 +18,12 @@
 fprom: $(OBJ)
 	$(LD) -static -Tfprom.lds -o fprom $(OBJ) $(LIB)
 
+comma	:= ,
+
 .S.o:
 	$(CC)  -D__ASSEMBLY__ $(AFLAGS) $(AFLAGS_KERNEL) -c -o $*.o $<
 .c.o:
-	$(CC)  $(CFLAGS) $(CFLAGS_KERNEL) -c -o $*.o $<
+	$(CC)  $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) $(CFLAGS_KERNEL) -c -o $*.o $<
 
 clean:
 	rm -f *.o fprom
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ia64/tools/Makefile linux/arch/ia64/tools/Makefile
--- linux.orig/arch/ia64/tools/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ia64/tools/Makefile	Thu Jan 10 20:08:20 2002
@@ -31,8 +31,10 @@
 offsets.h: print_offsets
 	./print_offsets > offsets.h
 
+comma	:= ,
+
 print_offsets: print_offsets.c FORCE_RECOMPILE
-	$(CC) $(CFLAGS) print_offsets.c -o $@
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) print_offsets.c -o $@
 
 FORCE_RECOMPILE:
 
@@ -42,7 +44,7 @@
 	$(AWK) -f print_offsets.awk $^ > $@
 
 print_offsets.s: print_offsets.c
-	$(CC) $(CFLAGS) -S print_offsets.c -o $@
+	$(CC) $(CFLAGS) -DKBUILD_BASENAME=$(subst $(comma),_,$(subst -,_,$(*F))) -S print_offsets.c -o $@
 
 endif
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/m68k/vmlinux-sun3.lds linux/arch/m68k/vmlinux-sun3.lds
--- linux.orig/arch/m68k/vmlinux-sun3.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/m68k/vmlinux-sun3.lds	Thu Jan 10 20:08:20 2002
@@ -10,7 +10,6 @@
 	*(.head)
 	*(.text)
 	*(.fixup)
-	*(.text.lock)		/* out-of-line lock text */
 	*(.gnu.warning)
 	} = 0x4e75
   .kstrtab : { *(.kstrtab) }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/m68k/vmlinux.lds linux/arch/m68k/vmlinux.lds
--- linux.orig/arch/m68k/vmlinux.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/m68k/vmlinux.lds	Thu Jan 10 20:08:20 2002
@@ -9,7 +9,6 @@
   .text : {
 	*(.text)
 	*(.fixup)
-	*(.text.lock)		/* out-of-line lock text */
 	*(.gnu.warning)
 	} = 0x4e75
   .rodata : { *(.rodata) *(.rodata.*) }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/mips/kernel/mips_ksyms.c linux/arch/mips/kernel/mips_ksyms.c
--- linux.orig/arch/mips/kernel/mips_ksyms.c	Sun Sep  9 17:43:01 2001
+++ linux/arch/mips/kernel/mips_ksyms.c	Wed Feb  6 21:06:42 2002
@@ -51,7 +51,6 @@
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memmove);
-EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL_NOVERS(strcat);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strlen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/mips64/kernel/mips64_ksyms.c linux/arch/mips64/kernel/mips64_ksyms.c
--- linux.orig/arch/mips64/kernel/mips64_ksyms.c	Sun Sep  9 17:43:01 2001
+++ linux/arch/mips64/kernel/mips64_ksyms.c	Wed Feb  6 21:06:42 2002
@@ -48,7 +48,6 @@
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memcpy);
 EXPORT_SYMBOL_NOVERS(memmove);
-EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL_NOVERS(strcat);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strlen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/parisc/kernel/traps.c linux/arch/parisc/kernel/traps.c
--- linux.orig/arch/parisc/kernel/traps.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/parisc/kernel/traps.c	Wed Dec 26 15:25:28 2001
@@ -43,7 +43,6 @@
 
 static inline void console_verbose(void)
 {
-	extern int console_loglevel;
 	console_loglevel = 15;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/8260_io/uart.c linux/arch/ppc/8260_io/uart.c
--- linux.orig/arch/ppc/8260_io/uart.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/8260_io/uart.c	Tue Jan  8 16:58:05 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.6 05/17/01 18:14:20 cort
+ * BK Id: SCCS/s.uart.c 1.13 12/29/01 14:50:03 trini
  */
 /*
  *  UART driver for MPC8260 CPM SCC or SMC
@@ -1736,7 +1736,7 @@
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
-		if (timeout && ((orig_jiffies + timeout) < jiffies))
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 		bdp = info->tx_cur;
 	} while (bdp->cbd_sc & BD_SC_READY);
@@ -2325,7 +2325,11 @@
 	__clear_user(&serial_driver,sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
+	serial_driver.name = "tts/%d";
+#else
 	serial_driver.name = "ttyS";
+#endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NR_PORTS;
@@ -2363,7 +2367,11 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
+#ifdef CONFIG_DEVFS_FS
+	callout_driver.name = "cua/%d";
+#else
 	callout_driver.name = "cua";
+#endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/8xx_io/uart.c linux/arch/ppc/8xx_io/uart.c
--- linux.orig/arch/ppc/8xx_io/uart.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/8xx_io/uart.c	Tue Jan  8 16:58:05 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.uart.c 1.19 10/26/01 09:59:32 trini
+ * BK Id: SCCS/s.uart.c 1.23 12/29/01 14:50:03 trini
  */
 /*
  *  UART driver for MPC860 CPM SCC or SMC
@@ -1802,7 +1802,7 @@
 		schedule_timeout(char_time);
 		if (signal_pending(current))
 			break;
-		if (timeout && ((orig_jiffies + timeout) < jiffies))
+		if (timeout && time_after(jiffies, orig_jiffies + timeout))
 			break;
 
 		/* The 'tx_cur' is really the next buffer to send.  We
@@ -2529,7 +2529,11 @@
 	__clear_user(&serial_driver,sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 	serial_driver.driver_name = "serial";
+#ifdef CONFIG_DEVFS_FS
+	serial_driver.name = "tts/%d";
+#else
 	serial_driver.name = "ttyS";
+#endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
 	serial_driver.num = NR_PORTS;
@@ -2567,7 +2571,11 @@
 	 * major number and the subtype code.
 	 */
 	callout_driver = serial_driver;
+#ifdef CONFIG_DEVFS_FS
+	callout_driver.name = "cua/%d";
+#else
 	callout_driver.name = "cua";
+#endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 	callout_driver.read_proc = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/common/misc-simple.c linux/arch/ppc/boot/common/misc-simple.c
--- linux.orig/arch/ppc/boot/common/misc-simple.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/common/misc-simple.c	Wed Dec 26 16:28:34 2001
@@ -45,6 +45,15 @@
 char *cmd_line = cmd_buf;
 
 unsigned long initrd_start = 0, initrd_end = 0;
+
+/* These values must be variables.  If not, the compiler optimizer
+ * will remove some code, causing the size of the code to vary
+ * when these values are zero.  This is bad because we first
+ * compile with these zero to determine the size and offsets
+ * in an image, than compile again with these set to the proper
+ * discovered value.
+ */
+unsigned int initrd_offset, initrd_size;
 char *zimage_start;
 int zimage_size;
 
@@ -69,7 +78,8 @@
 	 * were relocated to.
 	 */
 	puts("loaded at:     "); puthex(load_addr);
-	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
+	puts(" "); puthex((unsigned long)(load_addr + (4*num_words)));
+	puts("\n");
 	if ( (unsigned long)load_addr != (unsigned long)&start )
 	{
 		puts("relocated to:  "); puthex((unsigned long)&start);
@@ -82,45 +92,38 @@
 	   the size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
 	zimage_size = ZIMAGE_SIZE;
+	initrd_offset = INITRD_OFFSET;
+	initrd_size = INITRD_SIZE;
 
-	if ( INITRD_OFFSET )
-		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
+	if ( initrd_offset )
+		initrd_start = load_addr - 0x10000 + initrd_offset;
 	else
 		initrd_start = 0;
-	initrd_end = INITRD_SIZE + initrd_start;
+	initrd_end = initrd_size + initrd_start;
 
-	/*
-	 * Find a place to stick the zimage and initrd and 
-	 * relocate them if we have to. -- Cort
-	 */
+	/* Relocate the zImage */
 	avail_ram = (char *)PAGE_ALIGN((unsigned long)_end);
 	puts("zimage at:     "); puthex((unsigned long)zimage_start);
-	puts(" "); puthex((unsigned long)(zimage_size+zimage_start)); puts("\n");
-	if ( (unsigned long)zimage_start <= 0x00800000 )
-	{
-		memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
-		zimage_start = (char *)avail_ram;
-		puts("relocated to:  "); puthex((unsigned long)zimage_start);
-		puts(" ");
-		puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
-		puts("\n");
+	puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
+	puts("\n");
+	memcpy( (void *)avail_ram, (void *)zimage_start, zimage_size );
+	zimage_start = (char *)avail_ram;
+	puts("relocated to:  "); puthex((unsigned long)zimage_start);
+	puts(" ");
+	puthex((unsigned long)zimage_size+(unsigned long)zimage_start);
+	puts("\n");
 
-		/* relocate initrd */
-		if ( initrd_start )
-		{
-			puts("initrd at:     "); puthex(initrd_start);
-			puts(" "); puthex(initrd_end); puts("\n");
-			avail_ram = (char *)PAGE_ALIGN(
-				(unsigned long)zimage_size+(unsigned long)zimage_start);
-			memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
-			initrd_start = (unsigned long)avail_ram;
-			initrd_end = initrd_start + INITRD_SIZE;
-			puts("relocated to:  "); puthex(initrd_start);
-			puts(" "); puthex(initrd_end); puts("\n");
-		}
-	} else if ( initrd_start ) {
+	if ( initrd_start ) {
 		puts("initrd at:     "); puthex(initrd_start);
 		puts(" "); puthex(initrd_end); puts("\n");
+		/* relocate initrd */
+		avail_ram = (char *)PAGE_ALIGN((unsigned long)zimage_size + 
+				(unsigned long)zimage_start);
+		memcpy( (void *)avail_ram, (void *)initrd_start, initrd_size );
+		initrd_start = (unsigned long)avail_ram;
+		initrd_end = initrd_start + initrd_size;
+		puts("relocated to:  "); puthex(initrd_start);
+		puts(" "); puthex(initrd_end); puts("\n");
 	}
 
 	avail_ram = (char *)0x00400000;
@@ -161,11 +164,9 @@
 	puts("\n");
 
 	/* mappings on early boot can only handle 16M */
-	if ( (int)(cmd_line[0]) > (16<<20))
+	if ( (u32)(cmd_line) > (16<<20))
 		puts("cmd_line located > 16M\n");
-	if ( initrd_start > (16<<20))
-		puts("initrd_start located > 16M\n");
-       
+
 	puts("Uncompressing Linux...");
 
 	gunzip(0, 0x400000, zimage_start, &zimage_size);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/common/ns16550.c linux/arch/ppc/boot/common/ns16550.c
--- linux.orig/arch/ppc/boot/common/ns16550.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/common/ns16550.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ns16550.c 1.9 07/30/01 17:19:40 trini
+ * BK Id: SCCS/s.ns16550.c 1.12 10/08/01 17:16:50 paulus
  */
 /*
  * COM1 NS16550 support
@@ -10,6 +10,9 @@
 #include <linux/serial_reg.h>
 #include <asm/serial.h>
 
+/* Default serial baud rate */
+#define SERIAL_BAUD	9600
+
 extern void outb(int port, unsigned char val);
 extern unsigned char inb(int port);
 extern unsigned long ISA_io;
@@ -46,13 +49,20 @@
 	outb(com_port + (UART_IER << shift), 0x00);
 	/* Access baud rate */
 	outb(com_port + (UART_LCR << shift), 0x80);
-#ifdef CONFIG_SERIAL_CONSOLE_NONSTD
-	/* Input clock. */
-	outb(com_port + (UART_DLL << shift), 
-			(BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD));
-	outb(com_port + (UART_DLM << shift), 
-		(BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8);
-#endif
+	/*
+	 * Test if serial port is unconfigured.
+	 * We assume that no-one uses less than 110 baud or
+	 * less than 7 bits per character these days.
+	 *  -- paulus.
+	 */
+	if (inb(com_port + (UART_DLM << shift)) > 4
+	    || (inb(com_port + (UART_LCR << shift)) & 2) == 0) {
+		/* Input clock. */
+		outb(com_port + (UART_DLL << shift),
+		     (BASE_BAUD / SERIAL_BAUD));
+		outb(com_port + (UART_DLM << shift),
+		     (BASE_BAUD / SERIAL_BAUD) >> 8);
+	}
 	 /* 8 data, 1 stop, no parity */
 	outb(com_port + (UART_LCR << shift), 0x03);
 	/* RTS/DTR */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/images/Makefile linux/arch/ppc/boot/images/Makefile
--- linux.orig/arch/ppc/boot/images/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/images/Makefile	Wed Dec 26 16:28:34 2001
@@ -9,4 +9,4 @@
 	gzip -vf9 vmlinux
 
 clean:
-	rm -f sImage vmapus vmlinux.* miboot.image* zImage* zvmlinux.*
+	rm -f sImage vmapus vmlinux* miboot* zImage* zvmlinux*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/mbx/Makefile linux/arch/ppc/boot/mbx/Makefile
--- linux.orig/arch/ppc/boot/mbx/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/mbx/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.7 06/05/01 20:20:05 paulus
+# BK Id: SCCS/s.Makefile 1.9 10/15/01 10:53:29 trini
 #
 #
 # arch/ppc/mbxboot/Makefile
@@ -73,6 +73,12 @@
 		-DZIMAGE_SIZE=0 -c -o $@ $*.c
 
 zvmlinux.initrd: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.o again with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0x0008c8e3 -DINITRD_SIZE=0x0000111a \
+		-DZIMAGE_OFFSET=0x00018000 -DZIMAGE_SIZE=0x000748e2 \
+		-c -o misc.o misc.c
 	$(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=initrd=../images/ramdisk.image.gz \
@@ -88,6 +94,8 @@
 		--add-section=initrd=../images/ramdisk.image.gz \
 		--add-section=image=../images/vmlinux.gz \
 		$@.tmp ../images/$@.embedded
+# Remove zvmlinux and zvmlinux.temp, we have ../images/zvmlinux.embedded
+	rm -f $@.tmp $@
 
 zImage: zvmlinux
 ifeq ($(CONFIG_RPXCLASSIC),y)
@@ -104,6 +112,12 @@
 endif
 
 zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.o again with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=0x00018000 -DZIMAGE_SIZE=0x000748e2 \
+		-c -o misc.o misc.c
 #
 # build the boot loader image and then compute the offset into it
 # for the kernel image
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/mbx/misc.c linux/arch/ppc/boot/mbx/misc.c
--- linux.orig/arch/ppc/boot/mbx/misc.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/mbx/misc.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.c 1.13 07/27/01 11:44:37 trini
+ * BK Id: SCCS/s.misc.c 1.15 10/15/01 10:53:29 trini
  */
 /*
  * Adapted for PowerPC by Gary Thomas
@@ -44,10 +44,6 @@
 char *avail_ram;
 char *end_avail;
 
-/* See comment below.....
-*/
-unsigned int initrd_offset, initrd_size;
-
 /* Because of the limited amount of memory on embedded, it presents
  * loading problems.  The biggest is that we load this boot program
  * into a relatively low memory address, and the Linux kernel Bss often
@@ -75,14 +71,13 @@
 bd_t *hold_residual = &hold_resid_buf;
 unsigned long initrd_start = 0, initrd_end = 0;
 char *zimage_start;
-int zimage_size;
 
 extern void gunzip(void *, int, unsigned char *, int *);
 
 unsigned long
 decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum, bd_t *bp)
 {
-	int timer;
+	int timer, zimage_size = ZIMAGE_SIZE;
 	extern unsigned long start;
 	char *cp, ch;
 
@@ -93,17 +88,6 @@
 	serial_init(bp);
 #endif
 
-	/* These values must be variables.  If not, the compiler optimizer
-	 * will remove some code, causing the size of the code to vary
-	 * when these values are zero.  This is bad because we first
-	 * compile with these zero to determine the size and offsets
-	 * in an image, than compile again with these set to the proper
-	 * discovered value.....Ya know, we used to read these from the
-	 * header a long time ago.....
-	 */
-	initrd_offset = INITRD_OFFSET;
-	initrd_size = INITRD_SIZE;
-
 	/* Grab some space for the command line and board info.  Since
 	 * we no longer use the ELF header, but it was loaded, grab
 	 * that space.
@@ -154,13 +138,12 @@
 	/* we have to subtract 0x10000 here to correct for objdump including the
 	   size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
-	zimage_size = ZIMAGE_SIZE;
 
-	if ( initrd_offset )
-		initrd_start = load_addr - 0x10000 + initrd_offset;
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
 	else
 		initrd_start = 0;
-	initrd_end = initrd_size + initrd_start;
+	initrd_end = INITRD_SIZE + initrd_start;
 
 	/*
 	 * setup avail_ram - this is the first part of ram usable
@@ -201,9 +184,9 @@
 		if ((unsigned long)initrd_start > 0x01000000) {
 			memcpy ((void *)PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE),
 				(void *)initrd_start,
-				initrd_size );
+				INITRD_SIZE );
 			initrd_start = PAGE_ALIGN(-PAGE_SIZE+(unsigned long)end_avail-INITRD_SIZE);
-			initrd_end = initrd_start + initrd_size;
+			initrd_end = initrd_start + INITRD_SIZE;
 			end_avail = (char *)initrd_start;
 			puts("relocated to:  "); puthex(initrd_start);
 			puts(" "); puthex(initrd_end); puts("\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/pmac/Makefile linux/arch/ppc/boot/pmac/Makefile
--- linux.orig/arch/ppc/boot/pmac/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/pmac/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.14 07/27/01 20:24:17 trini
+# BK Id: SCCS/s.Makefile 1.16 09/28/01 07:39:37 trini
 #
 # Makefile for making XCOFF bootable images for booting on PowerMacs
 # using Open Firmware.
@@ -48,9 +48,9 @@
 	cp ../images/vmlinux.coff $(TFTPIMAGE)
 	cp ../images/vmlinux.elf-pmac $(TFTPIMAGE).elf
 
-znetboot.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac
-	cp ../images/vmlinux.coff.initrd $(TFTPIMAGE)
-	cp ../images/vmlinux.elf-pmac.initrd $(TFTPIMAGE).elf
+znetboot.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac
+	cp ../images/vmlinux.initrd.coff $(TFTPIMAGE)
+	cp ../images/vmlinux.initrd.elf-pmac $(TFTPIMAGE).elf
 
 #floppy: zImage
 #	mount -t hfs /dev/fd0 /mnt
@@ -61,7 +61,7 @@
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=image=../images/vmlinux.gz \
 		dummy.o ../images/$@
 
-miboot.image.initrd: miboot.image ../images/ramdisk.image.gz
+miboot.initrd.image: miboot.image ../images/ramdisk.image.gz
 	$(OBJCOPY) $(OBJCOPY_ARGS) --add-section=initrd=../images/ramdisk.image.gz \
 		../images/miboot.image ../images/$@
 
@@ -83,11 +83,11 @@
 	rm -f coffboot
 	ln -sf vmlinux.coff ../images/zImage.pmac
 
-vmlinux.coff.initrd: coffboot.initrd $(HACKCOFF)
+vmlinux.initrd.coff: coffboot.initrd $(HACKCOFF)
 	$(OBJCOPY) $(OBJCOPY_ARGS) coffboot.initrd ../images/$@
 	$(HACKCOFF) ../images/$@
 	rm -f coffboot.initrd
-	ln -sf vmlinux.coff.initrd ../images/zImage.initrd.pmac
+	ln -sf vmlinux.initrd.coff ../images/zImage.initrd.pmac
 
 vmlinux.elf-pmac: $(CHRPOBJS) $(LIBS) ../common/no_initrd.o $(MKNOTE) ../images/vmlinux.gz
 	$(LD) $(CHRP_LD_ARGS) -o ../images/$@ $(CHRPOBJS) ../common/no_initrd.o $(LIBS)
@@ -110,6 +110,6 @@
 
 zImage: vmlinux.coff vmlinux.elf-pmac miboot.image
 
-zImage.initrd: vmlinux.coff.initrd vmlinux.initrd.elf-pmac miboot.image.initrd
+zImage.initrd: vmlinux.initrd.coff vmlinux.initrd.elf-pmac miboot.initrd.image
 
 include $(TOPDIR)/Rules.make
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/prep/Makefile linux/arch/ppc/boot/prep/Makefile
--- linux.orig/arch/ppc/boot/prep/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/prep/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.26 09/25/01 07:54:40 trini
+# BK Id: SCCS/s.Makefile 1.28 10/21/01 20:47:58 trini
 #
 # arch/ppc/boot/Makefile
 #
@@ -45,6 +45,12 @@
 		-DZIMAGE_SIZE=0 -c -o $@ $*.c
 
 zvmlinux.initrd: $(obj-y) $(LIBS) ../images/vmlinux.gz
+#
+# Recompile misc.oagain with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0x00138466 -DINITRD_SIZE=0x0000111a \
+		-DZIMAGE_OFFSET=0x0001b000 -DZIMAGE_SIZE=0x0011d460 \
+		-c -o misc.o misc.c
 	$(LD) $(ZLINKFLAGS) -o $@.tmp $(obj-y) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=initrd=../images/ramdisk.image.gz \
@@ -60,7 +66,7 @@
 		--add-section=initrd=../images/ramdisk.image.gz \
 		--add-section=image=../images/vmlinux.gz \
 		$@.tmp $@
-	rm -f $@.tmp zvmlinux
+	rm -f $@.tmp
 
 zImage: zvmlinux $(MKPREP)
 	$(MKPREP) -pbp zvmlinux ../images/$@.prep
@@ -72,6 +78,12 @@
 
 zvmlinux: $(obj-y) $(LIBS) ../images/vmlinux.gz
 #
+# Recompile misc.oagain with more 'correct' bogus offsets
+#
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=0x0001b000 -DZIMAGE_SIZE=0x0011d460 \
+		-c -o misc.o misc.c
+#
 # build the boot loader image and then compute the offset into it
 # for the kernel image
 #
@@ -88,7 +100,7 @@
 	$(LD) $(ZLINKFLAGS) -o zvmlinux.tmp $(obj-y) $(LIBS)
 	$(OBJCOPY) $(OBJCOPY_ARGS) -R .comment \
 		--add-section=image=../images/vmlinux.gz $@.tmp $@
-	rm $@.tmp
+	rm -f $@.tmp
 
 floppy: zImage
 	dd if=../images/zImage.prep of=/dev/fd0H1440 bs=64b
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/prep/misc.c linux/arch/ppc/boot/prep/misc.c
--- linux.orig/arch/ppc/boot/prep/misc.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/prep/misc.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.c 1.20 09/24/01 18:42:54 trini
+ * BK Id: SCCS/s.misc.c 1.22 10/15/01 17:46:21 trini
  *
  * arch/ppc/boot/prep/misc.c
  *
@@ -48,14 +48,6 @@
 RESIDUAL *hold_residual = &hold_resid_buf;
 unsigned long initrd_start = 0, initrd_end = 0;
 
-/* These values must be variables.  If not, the compiler optimizer
- * will remove some code, causing the size of the code to vary
- * when these values are zero.  This is bad because we first
- * compile with these zero to determine the size and offsets
- * in an image, than compile again with these set to the proper
- * discovered value.
- */
-unsigned int initrd_offset, initrd_size;
 char *zimage_start;
 int zimage_size;
 
@@ -311,14 +303,12 @@
 	   size of the elf header which we strip -- Cort */
 	zimage_start = (char *)(load_addr - 0x10000 + ZIMAGE_OFFSET);
 	zimage_size = ZIMAGE_SIZE;
-	initrd_offset = INITRD_OFFSET;
-	initrd_size = INITRD_SIZE;
 
-	if ( initrd_offset )
-		initrd_start = load_addr - 0x10000 + initrd_offset;
+	if ( INITRD_OFFSET )
+		initrd_start = load_addr - 0x10000 + INITRD_OFFSET;
 	else
 		initrd_start = 0;
-	initrd_end = initrd_size + initrd_start;
+	initrd_end = INITRD_SIZE + initrd_start;
 
 	/*
 	 * Find a place to stick the zimage and initrd and 
@@ -343,9 +333,9 @@
 			puts(" "); puthex(initrd_end); puts("\n");
 			avail_ram = (char *)PAGE_ALIGN(
 				(unsigned long)zimage_size+(unsigned long)zimage_start);
-			memcpy ((void *)avail_ram, (void *)initrd_start, initrd_size );
+			memcpy ((void *)avail_ram, (void *)initrd_start, INITRD_SIZE );
 			initrd_start = (unsigned long)avail_ram;
-			initrd_end = initrd_start + initrd_size;
+			initrd_end = initrd_start + INITRD_SIZE;
 			puts("relocated to:  "); puthex(initrd_start);
 			puts(" "); puthex(initrd_end); puts("\n");
 		}
@@ -395,7 +385,7 @@
 	puts("\n");
 
 	/* mappings on early boot can only handle 16M */
-	if ( (int)(cmd_line[0]) > (16<<20))
+	if ( (int)(cmd_line) > (16<<20))
 		puts("cmd_line located > 16M\n");
 	if ( (int)hold_residual > (16<<20))
 		puts("hold_residual located > 16M\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/mksimage.c linux/arch/ppc/boot/utils/mksimage.c
--- linux.orig/arch/ppc/boot/utils/mksimage.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/utils/mksimage.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.mksimage.c 1.6 05/18/01 15:16:42 cort
+ * BK Id: SCCS/s.mksimage.c 1.7 10/11/01 11:59:05 trini
  */
 /*
  *
@@ -96,7 +96,7 @@
 		die("can't open loader: %s", strerror(errno));
 
 	copy_blocks(fd, ofd, &ld_off, &ld_size);
-	len = sprintf(buffer, "bootloader: %x %x\n", ld_off, ld_size);
+	len = sprintf(buffer, "bootloader: %lx %lx\n", ld_off, ld_size);
 	close(fd);
 
 	fd = open(kernel, O_RDONLY);
@@ -104,7 +104,7 @@
 		die("can't open kernel: %s", strerror(errno));
 
 	copy_blocks(fd, ofd, &kern_off, &kern_size);
-	len += sprintf(buffer+len, "zimage: %x %x\n", kern_off, kern_size);
+	len += sprintf(buffer+len, "zimage: %lx %lx\n", kern_off, kern_size);
 	close(fd);
 	
 	if (rdimage) {
@@ -116,7 +116,7 @@
 		close(fd);
 	}
 
-	len += sprintf(buffer+len, "initrd: %x %x", rd_off, rd_size);
+	len += sprintf(buffer+len, "initrd: %lx %lx", rd_off, rd_size);
 
 	close(ofd);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/offset linux/arch/ppc/boot/utils/offset
--- linux.orig/arch/ppc/boot/utils/offset	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/utils/offset	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
-OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux| awk '{print $6}'`
+OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $6}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/sioffset linux/arch/ppc/boot/utils/sioffset
--- linux.orig/arch/ppc/boot/utils/sioffset	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/utils/sioffset	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 OFFSET=`grep $1 sImage.map | awk '{print $2}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/sisize linux/arch/ppc/boot/utils/sisize
--- linux.orig/arch/ppc/boot/utils/sisize	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/utils/sisize	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
 OFFSET=`grep $1 sImage.map | awk '{print $3}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/boot/utils/size linux/arch/ppc/boot/utils/size
--- linux.orig/arch/ppc/boot/utils/size	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/boot/utils/size	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
 
-OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux | awk '{print $3}'`
+OFFSET=`$1 -h $2 | grep $3 | grep -v zvmlinux | awk '{print $3}'`
 echo "0x"$OFFSET
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/config.in linux/arch/ppc/config.in
--- linux.orig/arch/ppc/config.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/config.in	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.config.in 1.45 11/08/01 07:57:40 paulus
+# BK Id: SCCS/s.config.in 1.47 12/01/01 20:09:06 benh
 #
 # For a description of the syntax of this configuration file,
 # see Documentation/kbuild/config-language.txt.
@@ -328,6 +328,10 @@
   bool 'Support for PMU  based PowerMacs' CONFIG_ADB_PMU
   if [ "$CONFIG_ADB_PMU" = "y" ]; then
     bool '   Power management support for PowerBooks' CONFIG_PMAC_PBOOK
+    if [ "$CONFIG_PMAC_PBOOK" = "y" ]; then
+      define_bool CONFIG_PM y
+      tristate '     APM emulation' CONFIG_PMAC_APM_EMU
+    fi
     # made a separate option since backlight may end up beeing used
     # on non-powerbook machines (but only on PMU based ones AFAIK)
     bool '   Backlight control for LCD screens' CONFIG_PMAC_BACKLIGHT
@@ -352,6 +356,9 @@
   # layer is used.
   if [ "$CONFIG_INPUT" != "n" ]; then
     define_bool CONFIG_MAC_HID y
+  fi
+  if [ "$CONFIG_ADB_CUDA" != "n" ]; then
+    bool 'Support for ANS LCD display' CONFIG_ANSLCD
   fi
 fi
 endmenu
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/common_defconfig linux/arch/ppc/configs/common_defconfig
--- linux.orig/arch/ppc/configs/common_defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/configs/common_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -119,8 +120,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -133,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -343,15 +343,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -388,7 +384,9 @@
 #
 # Appletalk devices
 #
-# CONFIG_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -443,6 +441,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -544,6 +543,7 @@
 CONFIG_FB_MATROX_MILLENIUM=y
 CONFIG_FB_MATROX_MYSTIQUE=y
 # CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
 # CONFIG_FB_MATROX_G450 is not set
 # CONFIG_FB_MATROX_MULTIHEAD is not set
 CONFIG_FB_ATY=y
@@ -597,6 +597,7 @@
 CONFIG_MAC_ADBKEYCODES=y
 CONFIG_MAC_EMUMOUSEBTN=y
 CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
 
 #
 # Character devices
@@ -612,7 +613,12 @@
 #
 # I2C support
 #
-# CONFIG_I2C is not set
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
 
 #
 # Mice
@@ -693,11 +699,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=m
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -710,6 +720,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -734,6 +745,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -751,6 +763,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -818,8 +832,10 @@
 # Sound
 #
 CONFIG_SOUND=m
-CONFIG_DMASOUND_AWACS=m
+CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
@@ -854,7 +870,6 @@
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_LONG_TIMEOUT is not set
-# CONFIG_USB_LARGE_CONFIG is not set
 
 #
 # USB Controllers
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/gemini_defconfig linux/arch/ppc/configs/gemini_defconfig
--- linux.orig/arch/ppc/configs/gemini_defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/configs/gemini_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -109,8 +110,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -122,6 +121,7 @@
 # CONFIG_IP_PNP is not set
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 # CONFIG_SYN_COOKIES is not set
 
@@ -221,15 +221,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -432,11 +428,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 # CONFIG_FAT_FS is not set
 # CONFIG_MSDOS_FS is not set
 # CONFIG_UMSDOS_FS is not set
@@ -449,6 +449,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -473,6 +474,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -490,6 +492,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/ibmchrp_defconfig linux/arch/ppc/configs/ibmchrp_defconfig
--- linux.orig/arch/ppc/configs/ibmchrp_defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/configs/ibmchrp_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -112,8 +113,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -126,6 +125,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -252,15 +252,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -343,6 +339,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -546,7 +543,7 @@
 # CONFIG_WATCHDOG is not set
 # CONFIG_INTEL_RNG is not set
 CONFIG_NVRAM=y
-CONFIG_RTC=y
+# CONFIG_RTC is not set
 # CONFIG_DTLK is not set
 # CONFIG_R3964 is not set
 # CONFIG_APPLICOM is not set
@@ -571,11 +568,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -588,6 +589,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -612,6 +614,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 # CONFIG_NFS_FS is not set
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -629,6 +632,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -677,7 +682,7 @@
 # CONFIG_NLS_CODEPAGE_874 is not set
 # CONFIG_NLS_ISO8859_8 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
-# CONFIG_NLS_ISO8859_1 is not set
+CONFIG_NLS_ISO8859_1=m
 # CONFIG_NLS_ISO8859_2 is not set
 # CONFIG_NLS_ISO8859_3 is not set
 # CONFIG_NLS_ISO8859_4 is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/pmac_defconfig linux/arch/ppc/configs/pmac_defconfig
--- linux.orig/arch/ppc/configs/pmac_defconfig	Thu Jan  1 00:00:00 1970
+++ linux/arch/ppc/configs/pmac_defconfig	Wed Dec 26 16:28:34 2001
@@ -0,0 +1,1076 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODVERSIONS=y
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_6xx=y
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8xx is not set
+# CONFIG_8260 is not set
+CONFIG_PPC_STD_MMU=y
+CONFIG_ALL_PPC=y
+# CONFIG_APUS is not set
+# CONFIG_GEMINI is not set
+# CONFIG_SMP is not set
+CONFIG_ALTIVEC=y
+CONFIG_TAU=y
+# CONFIG_TAU_INT is not set
+# CONFIG_TAU_AVERAGE is not set
+
+#
+# General setup
+#
+# CONFIG_HIGHMEM is not set
+# CONFIG_ISA is not set
+# CONFIG_EISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_MCA is not set
+CONFIG_PCI=y
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+CONFIG_BINFMT_MISC=m
+CONFIG_PCI_NAMES=y
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+CONFIG_CARDBUS=y
+CONFIG_I82092=y
+CONFIG_I82365=y
+CONFIG_TCIC=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+CONFIG_PPC_RTC=y
+CONFIG_PPC601_SYNC_FIX=y
+CONFIG_PROC_DEVICETREE=y
+CONFIG_PPC_RTAS=y
+CONFIG_BOOTX_TEXT=y
+# CONFIG_PREP_RESIDUAL is not set
+# CONFIG_CMDLINE_BOOL is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_INITRD=y
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_LVM is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+CONFIG_SYN_COOKIES=y
+
+#
+#   IP: Netfilter Configuration
+#
+CONFIG_IP_NF_CONNTRACK=m
+CONFIG_IP_NF_FTP=m
+CONFIG_IP_NF_IRC=m
+# CONFIG_IP_NF_QUEUE is not set
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_LIMIT=m
+CONFIG_IP_NF_MATCH_MAC=m
+CONFIG_IP_NF_MATCH_MARK=m
+CONFIG_IP_NF_MATCH_MULTIPORT=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_LENGTH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_TCPMSS=m
+CONFIG_IP_NF_MATCH_STATE=m
+CONFIG_IP_NF_MATCH_UNCLEAN=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_MIRROR=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_NAT_SNMP_BASIC=m
+CONFIG_IP_NF_NAT_IRC=m
+CONFIG_IP_NF_NAT_FTP=m
+# CONFIG_IP_NF_MANGLE is not set
+# CONFIG_IP_NF_TARGET_LOG is not set
+CONFIG_IP_NF_TARGET_TCPMSS=m
+CONFIG_IP_NF_COMPAT_IPCHAINS=m
+CONFIG_IP_NF_NAT_NEEDED=y
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+CONFIG_ATALK=m
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE 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
+
+#
+# IDE chipset support/bugfixes
+#
+# 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_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_ADMA=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_IDEDMA_PCI_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
+# 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_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_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_SL82C105=y
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC=y
+CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO=y
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI 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
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_SR_EXTRA_DEVS=2
+CONFIG_CHR_DEV_SG=y
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_DEBUG_QUEUES is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+CONFIG_SCSI_AIC7XXX=m
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
+CONFIG_AIC7XXX_RESET_DELAY_MS=15000
+# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
+CONFIG_SCSI_AIC7XXX_OLD=m
+# CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT is not set
+CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8
+CONFIG_AIC7XXX_OLD_PROC_STATS=y
+# CONFIG_SCSI_DPT_I2O is not set
+CONFIG_SCSI_ADVANSYS=m
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_DEBUG is not set
+CONFIG_SCSI_MESH=y
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MAC53C94=y
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+CONFIG_IEEE1394=m
+
+#
+# Device Drivers
+#
+CONFIG_IEEE1394_PCILYNX=m
+# CONFIG_IEEE1394_PCILYNX_LOCALRAM is not set
+# CONFIG_IEEE1394_PCILYNX_PORTS is not set
+CONFIG_IEEE1394_OHCI1394=m
+
+#
+# Protocol Drivers
+#
+CONFIG_IEEE1394_VIDEO1394=m
+CONFIG_IEEE1394_SBP2=m
+CONFIG_IEEE1394_RAWIO=m
+# CONFIG_IEEE1394_VERBOSEDEBUG is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+
+#
+# Appletalk devices
+#
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+CONFIG_MACE=y
+# CONFIG_MACE_AAUI_PORT is not set
+CONFIG_BMAC=y
+# CONFIG_GMAC is not set
+# CONFIG_SUNLANCE is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNBMAC is not set
+# CONFIG_SUNQE is not set
+# CONFIG_SUNLANCE is not set
+CONFIG_SUNGEM=y
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=y
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+CONFIG_TULIP=y
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+CONFIG_DE4X5=m
+# CONFIG_DGRS is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_NE3210 is not set
+# CONFIG_ES3210 is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_TLAN is not set
+# CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
+# CONFIG_WINBOND_840 is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=y
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPPOE is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+CONFIG_NET_RADIO=y
+# CONFIG_STRIP is not set
+# CONFIG_WAVELAN is not set
+# CONFIG_ARLAN is not set
+# CONFIG_AIRONET4500 is not set
+# CONFIG_AIRONET4500_NONCS is not set
+# CONFIG_AIRONET4500_PROC is not set
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+CONFIG_APPLE_AIRPORT=m
+# CONFIG_PLX_HERMES is not set
+
+#
+# Wireless Pcmcia cards support
+#
+CONFIG_PCMCIA_HERMES=m
+# CONFIG_AIRO_CS is not set
+CONFIG_NET_WIRELESS=y
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_ARCNET_COM20020_CS is not set
+# CONFIG_PCMCIA_IBMTR is not set
+# CONFIG_PCMCIA_XIRCOM is not set
+# CONFIG_PCMCIA_XIRTULIP is not set
+CONFIG_NET_PCMCIA_RADIO=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_PCMCIA_NETWAVE is not set
+# CONFIG_PCMCIA_WAVELAN is not set
+# CONFIG_AIRONET4500_CS is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+# CONFIG_IRDA_OPTIONS is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=m
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+# CONFIG_VLSI_FIR is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_OF=y
+CONFIG_FB_CONTROL=y
+CONFIG_FB_PLATINUM=y
+CONFIG_FB_VALKYRIE=y
+CONFIG_FB_CT65550=y
+CONFIG_FB_IMSTT=y
+# CONFIG_FB_S3TRIO is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_MATROX=y
+CONFIG_FB_MATROX_MILLENIUM=y
+CONFIG_FB_MATROX_MYSTIQUE=y
+# CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
+# CONFIG_FB_MATROX_G450 is not set
+# CONFIG_FB_MATROX_MULTIHEAD is not set
+CONFIG_FB_ATY=y
+CONFIG_FB_ATY_GX=y
+CONFIG_FB_ATY_CT=y
+CONFIG_FB_RADEON=y
+CONFIG_FB_ATY128=y
+# CONFIG_FB_SIS is not set
+CONFIG_FB_3DFX=y
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FBCON_ADVANCED is not set
+CONFIG_FBCON_CFB8=y
+CONFIG_FBCON_CFB16=y
+CONFIG_FBCON_CFB24=y
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+CONFIG_FONT_SUN8x16=y
+CONFIG_FONT_SUN12x22=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FB_COMPAT_XPMAC=y
+
+#
+# Input core support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_KEYBDEV=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+
+#
+# Macintosh device drivers
+#
+CONFIG_ADB_CUDA=y
+CONFIG_ADB_PMU=y
+CONFIG_PMAC_PBOOK=y
+CONFIG_PM=y
+CONFIG_PMAC_APM_EMU=y
+CONFIG_PMAC_BACKLIGHT=y
+CONFIG_MAC_FLOPPY=y
+CONFIG_MAC_SERIAL=y
+# CONFIG_SERIAL_CONSOLE is not set
+CONFIG_ADB=y
+CONFIG_ADB_MACIO=y
+CONFIG_INPUT_ADBHID=y
+CONFIG_MAC_ADBKEYCODES=y
+CONFIG_MAC_EMUMOUSEBTN=y
+CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=m
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
+
+#
+# Mice
+#
+CONFIG_BUSMOUSE=y
+# CONFIG_ATIXL_BUSMOUSE is not set
+# CONFIG_LOGIBUSMOUSE is not set
+# CONFIG_MS_BUSMOUSE is not set
+CONFIG_MOUSE=y
+CONFIG_PSMOUSE=y
+# CONFIG_82C710_MOUSE is not set
+# CONFIG_PC110_PAD is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_NS558 is not set
+# CONFIG_INPUT_LIGHTNING is not set
+# CONFIG_INPUT_PCIGAME is not set
+# CONFIG_INPUT_CS461X is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_INPUT_SERIO is not set
+# CONFIG_INPUT_SERPORT is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_ANALOG is not set
+# CONFIG_INPUT_A3D is not set
+# CONFIG_INPUT_ADI is not set
+# CONFIG_INPUT_COBRA is not set
+# CONFIG_INPUT_GF2K is not set
+# CONFIG_INPUT_GRIP is not set
+# CONFIG_INPUT_INTERACT is not set
+# CONFIG_INPUT_TMDC is not set
+# CONFIG_INPUT_SIDEWINDER is not set
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_IFORCE_232 is not set
+# CONFIG_INPUT_WARRIOR is not set
+# CONFIG_INPUT_MAGELLAN is not set
+# CONFIG_INPUT_SPACEORB is not set
+# CONFIG_INPUT_SPACEBALL is not set
+# CONFIG_INPUT_STINGER is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+CONFIG_NVRAM=y
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_PCMCIA_SERIAL_CS is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+CONFIG_HFS_FS=m
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=m
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+# CONFIG_RAMFS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+CONFIG_DEVFS_FS=y
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_ROOT_NFS is not set
+CONFIG_NFSD=y
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+CONFIG_SMB_NLS=y
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ISO8859_1=m
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+CONFIG_DMASOUND_PMAC=m
+CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_MIDI_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_MIDI_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+# CONFIG_USB_LONG_TIMEOUT is not set
+
+#
+# USB Controllers
+#
+# CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
+CONFIG_USB_OHCI=y
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_AUDIO is not set
+# CONFIG_USB_BLUETOOTH is not set
+# CONFIG_USB_STORAGE is not set
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_HP8200e is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDDEV is not set
+# CONFIG_USB_WACOM is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
+CONFIG_USB_SCANNER=m
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_HPUSBSCSI is not set
+
+#
+# USB Multimedia devices
+#
+
+#
+#   Video4Linux support is needed for USB Multimedia device support
+#
+
+#
+# USB Network adaptors
+#
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_USBNET is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+# CONFIG_USB_SERIAL_GENERIC is not set
+# CONFIG_USB_SERIAL_BELKIN is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set
+# CONFIG_USB_SERIAL_EMPEG is not set
+# CONFIG_USB_SERIAL_FTDI_SIO is not set
+CONFIG_USB_SERIAL_VISOR=m
+# CONFIG_USB_SERIAL_IR is not set
+# CONFIG_USB_SERIAL_EDGEPORT is not set
+# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set
+# CONFIG_USB_SERIAL_KEYSPAN is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_MCT_U232 is not set
+# CONFIG_USB_SERIAL_PL2303 is not set
+# CONFIG_USB_SERIAL_CYBERJACK is not set
+# CONFIG_USB_SERIAL_XIRCOM is not set
+# CONFIG_USB_SERIAL_OMNINET is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_RIO500 is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# Kernel hacking
+#
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_KGDB is not set
+CONFIG_XMON=y
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/configs/power3_defconfig linux/arch/ppc/configs/power3_defconfig
--- linux.orig/arch/ppc/configs/power3_defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/configs/power3_defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -66,6 +67,7 @@
 # CONFIG_PARPORT_AMIGA is not set
 # CONFIG_PARPORT_MFC3 is not set
 # CONFIG_PARPORT_ATARI is not set
+# CONFIG_PARPORT_GSC is not set
 # CONFIG_PARPORT_SUNBPP is not set
 # CONFIG_PARPORT_OTHER is not set
 # CONFIG_PARPORT_1284 is not set
@@ -119,8 +121,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 # CONFIG_NETFILTER is not set
 # CONFIG_FILTER is not set
@@ -132,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 # CONFIG_IPV6 is not set
@@ -224,15 +225,11 @@
 # CONFIG_SCSI_IMM is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-# CONFIG_SCSI_NCR53C8XX is not set
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -315,6 +312,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -474,6 +472,7 @@
 # CONFIG_I2C_VELLEMAN is not set
 CONFIG_I2C_ALGOPCF=y
 # CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_KEYWEST is not set
 CONFIG_I2C_CHARDEV=y
 # CONFIG_I2C_PROC is not set
 
@@ -553,11 +552,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=y
 CONFIG_MSDOS_FS=y
 # CONFIG_UMSDOS_FS is not set
@@ -570,6 +573,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -594,6 +598,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -611,6 +616,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -665,7 +672,7 @@
 # Sound
 #
 CONFIG_SOUND=y
-# CONFIG_DMASOUND_AWACS is not set
+# CONFIG_DMASOUND_PMAC is not set
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/defconfig linux/arch/ppc/defconfig
--- linux.orig/arch/ppc/defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/defconfig	Wed Dec 26 16:28:34 2001
@@ -4,6 +4,7 @@
 # CONFIG_UID16 is not set
 # CONFIG_RWSEM_GENERIC_SPINLOCK is not set
 CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_HAVE_DEC_LOCK=y
 
 #
 # Code maturity level options
@@ -119,8 +120,6 @@
 #
 CONFIG_PACKET=y
 # CONFIG_PACKET_MMAP is not set
-CONFIG_NETLINK=y
-# CONFIG_RTNETLINK is not set
 # CONFIG_NETLINK_DEV is not set
 CONFIG_NETFILTER=y
 # CONFIG_NETFILTER_DEBUG is not set
@@ -133,6 +132,7 @@
 # CONFIG_NET_IPIP is not set
 # CONFIG_NET_IPGRE is not set
 # CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 CONFIG_SYN_COOKIES=y
 
@@ -343,15 +343,11 @@
 # CONFIG_SCSI_INIA100 is not set
 # CONFIG_SCSI_NCR53C406A is not set
 # CONFIG_SCSI_NCR53C7xx is not set
-CONFIG_SCSI_NCR53C8XX=y
-CONFIG_SCSI_SYM53C8XX=y
-CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=8
-CONFIG_SCSI_NCR53C8XX_MAX_TAGS=32
-CONFIG_SCSI_NCR53C8XX_SYNC=20
-# CONFIG_SCSI_NCR53C8XX_PROFILE is not set
-# CONFIG_SCSI_NCR53C8XX_IOMAPPED is not set
-# CONFIG_SCSI_NCR53C8XX_PQS_PDS is not set
-# CONFIG_SCSI_NCR53C8XX_SYMBIOS_COMPAT is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=0
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+# CONFIG_SCSI_SYM53C8XX_IOMAPPED is not set
 # CONFIG_SCSI_PAS16 is not set
 # CONFIG_SCSI_PCI2000 is not set
 # CONFIG_SCSI_PCI2220I is not set
@@ -388,7 +384,9 @@
 #
 # Appletalk devices
 #
-# CONFIG_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPDDP is not set
 # CONFIG_DUMMY is not set
 # CONFIG_BONDING is not set
 # CONFIG_EQUALIZER is not set
@@ -443,6 +441,7 @@
 # CONFIG_SUNDANCE is not set
 # CONFIG_TLAN is not set
 # CONFIG_VIA_RHINE is not set
+# CONFIG_VIA_RHINE_MMIO is not set
 # CONFIG_WINBOND_840 is not set
 # CONFIG_NET_POCKET is not set
 
@@ -544,6 +543,7 @@
 CONFIG_FB_MATROX_MILLENIUM=y
 CONFIG_FB_MATROX_MYSTIQUE=y
 # CONFIG_FB_MATROX_G100 is not set
+# CONFIG_FB_MATROX_I2C is not set
 # CONFIG_FB_MATROX_G450 is not set
 # CONFIG_FB_MATROX_MULTIHEAD is not set
 CONFIG_FB_ATY=y
@@ -597,6 +597,7 @@
 CONFIG_MAC_ADBKEYCODES=y
 CONFIG_MAC_EMUMOUSEBTN=y
 CONFIG_MAC_HID=y
+# CONFIG_ANSLCD is not set
 
 #
 # Character devices
@@ -612,7 +613,12 @@
 #
 # I2C support
 #
-# CONFIG_I2C is not set
+CONFIG_I2C=m
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_KEYWEST=m
+CONFIG_I2C_CHARDEV=m
+CONFIG_I2C_PROC=m
 
 #
 # Mice
@@ -693,11 +699,15 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 CONFIG_HFS_FS=m
 # CONFIG_BFS_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_JBD is not set
+# CONFIG_JBD_DEBUG is not set
 CONFIG_FAT_FS=m
 CONFIG_MSDOS_FS=m
 # CONFIG_UMSDOS_FS is not set
@@ -710,6 +720,7 @@
 # CONFIG_RAMFS is not set
 CONFIG_ISO9660_FS=y
 # CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
@@ -734,6 +745,7 @@
 # Network File Systems
 #
 # CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
@@ -751,6 +763,8 @@
 # CONFIG_NCPFS_SMALLDOS is not set
 # CONFIG_NCPFS_NLS is not set
 # CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+# CONFIG_ZLIB_FS_INFLATE is not set
 
 #
 # Partition Types
@@ -818,8 +832,10 @@
 # Sound
 #
 CONFIG_SOUND=m
-CONFIG_DMASOUND_AWACS=m
+CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
 # CONFIG_SOUND_EMU10K1 is not set
@@ -854,7 +870,6 @@
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
 # CONFIG_USB_LONG_TIMEOUT is not set
-# CONFIG_USB_LARGE_CONFIG is not set
 
 #
 # USB Controllers
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- linux.orig/arch/ppc/kernel/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/Makefile	Wed Dec 26 16:28:34 2001
@@ -1,4 +1,4 @@
-# BK Id: SCCS/s.Makefile 1.34 10/16/01 15:58:42 trini
+# BK Id: SCCS/s.Makefile 1.36 12/01/01 20:09:06 benh
 #
 #
 # Makefile for the linux kernel.
@@ -56,7 +56,7 @@
 obj-$(CONFIG_PCI)		+= apus_pci.o
 endif
 obj-$(CONFIG_ALL_PPC)		+= pmac_pic.o pmac_setup.o pmac_time.o prom.o \
-					feature.o pmac_pci.o chrp_setup.o \
+					pmac_feature.o pmac_pci.o chrp_setup.o \
 					chrp_time.o chrp_pci.o 	open_pic.o \
 					indirect_pci.o i8259.o prep_pci.o \
 					prep_time.o prep_nvram.o prep_setup.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/btext.c linux/arch/ppc/kernel/btext.c
--- linux.orig/arch/ppc/kernel/btext.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/btext.c	Wed Dec 26 16:28:34 2001
@@ -133,14 +133,19 @@
 {
 	unsigned long offset = reloc_offset();
 	boot_infos_t* bi = PTRRELOC(RELOC(disp_bi));
-	unsigned long addr = (unsigned long)bi->dispDeviceBase;
 	unsigned long vaddr = KERNELBASE + 0x10000000;
+	unsigned long addr;
 	unsigned long lowbits;
 
 	if (!RELOC(disp_bi)) {
 		RELOC(boot_text_mapped) = 0;
 		return;
 	}
+	addr = (unsigned long)bi->dispDeviceBase;
+	if (!addr) {
+		RELOC(boot_text_mapped) = 0;
+		return;
+	}
 	if (PVR_VER(mfspr(PVR)) != 1) {
 		/* 603, 604, G3, G4, ... */
 		lowbits = addr & ~0xFF000000UL;
@@ -231,10 +236,10 @@
 {
 	if (disp_bi == 0)
 		return;
-	/* check it's the same frame buffer (within 64MB) */
-	if ((phys ^ (unsigned long)disp_bi->dispDeviceBase) & 0xfc000000) {
+
+	/* check it's the same frame buffer (within 256MB) */
+	if ((phys ^ (unsigned long)disp_bi->dispDeviceBase) & 0xf0000000)
 		return;
-	}
 
 	disp_bi->dispDeviceBase = (__u8 *) phys;
 	disp_bi->dispDeviceRect[0] = 0;
@@ -423,9 +428,11 @@
 	int rb			= bi->dispDeviceRowBytes;
 	
 	switch(bi->dispDeviceDepth) {
+	case 24:
 	case 32:
 		draw_byte_32(font, (unsigned long *)base, rb);
 		break;
+	case 15:
 	case 16:
 		draw_byte_16(font, (unsigned long *)base, rb);
 		break;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- linux.orig/arch/ppc/kernel/chrp_setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/chrp_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.chrp_setup.c 1.38 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.chrp_setup.c 1.40 12/19/01 09:45:54 trini
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -374,7 +374,7 @@
 	openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq);
 	for ( i = 0 ; i < NUM_8259_INTERRUPTS  ; i++ )
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init();
+	i8259_init(NULL);
 #if defined(CONFIG_VT) && defined(CONFIG_ADB_KEYBOARD) && defined(XMON)
 	/* see if there is a keyboard in the device tree
 	   with a parent of type "adb" */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/chrp_smp.c linux/arch/ppc/kernel/chrp_smp.c
--- linux.orig/arch/ppc/kernel/chrp_smp.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/chrp_smp.c	Wed Dec 26 16:28:34 2001
@@ -36,7 +36,6 @@
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/residual.h>
-#include <asm/feature.h>
 #include <asm/time.h>
 
 #include "open_pic.h"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/cputable.c linux/arch/ppc/kernel/cputable.c
--- linux.orig/arch/ppc/kernel/cputable.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/cputable.c	Wed Dec 26 16:28:34 2001
@@ -26,6 +26,7 @@
 extern void __setup_cpu_604(int cpu_nr);
 extern void __setup_cpu_750(int cpu_nr);
 extern void __setup_cpu_7400(int cpu_nr);
+extern void __setup_cpu_7410(int cpu_nr);
 extern void __setup_cpu_7450(int cpu_nr);
 extern void __setup_cpu_power3(int cpu_nr);
 extern void __setup_cpu_power4(int cpu_nr);
@@ -113,14 +114,22 @@
 	32, 32,
 	__setup_cpu_604
     },
-    {	/* 750 (0x4202, don't support TAU ?) */
-    	0xffffffff, 0x00084202, "750",
+    {	/* 740/750 (0x4202, don't support TAU ?) */
+    	0xffffffff, 0x00084202, "740/750",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
 	CPU_FTR_L2CR | CPU_FTR_HPTE_TABLE,
 	COMMON_PPC,
 	32, 32,
 	__setup_cpu_750
     },
+    {	/* 745/755 */
+    	0xfffff000, 0x00083000, "745/755",
+    	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
+	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_HPTE_TABLE,
+	COMMON_PPC,
+	32, 32,
+	__setup_cpu_750
+    },
     {	/* 750CX */
     	0xffffff00, 0x00082200, "750CX",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
@@ -159,9 +168,18 @@
 	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP | CPU_FTR_HPTE_TABLE,
 	COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC,
 	32, 32,
-	__setup_cpu_7400
+	__setup_cpu_7410
+    },
+    {	/* 7450 2.0 - no doze/nap */
+    	0xffffffff, 0x80000200, "7450",
+    	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB |
+	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP |
+	CPU_FTR_HPTE_TABLE | CPU_FTR_SPEC7450,
+	COMMON_PPC | PPC_FEATURE_HAS_ALTIVEC,
+	32, 32,
+	__setup_cpu_7450
     },
-    {	/* 7450 */
+    {	/* 7450 others */
     	0xffff0000, 0x80000000, "7450",
     	CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB |
 	CPU_FTR_L2CR | CPU_FTR_TAU | CPU_FTR_ALTIVEC_COMP |
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/entry.S linux/arch/ppc/kernel/entry.S
--- linux.orig/arch/ppc/kernel/entry.S	Mon Nov 26 13:29:17 2001
+++ linux/arch/ppc/kernel/entry.S	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.entry.S 1.24 11/23/01 16:38:29 paulus
+ * BK Id: SCCS/s.entry.S 1.26 01/25/02 15:15:24 benh
  */
 /*
  *  PowerPC version 
@@ -338,13 +338,24 @@
 	REST_4GPRS(3, r1)
 	REST_2GPRS(7, r1)
 
+#ifndef CONFIG_SMP
 	/* We have to "dummy" load from the context save area in case
 	 * these instructions cause an MMU fault.  If this happens
 	 * after we load SRR0/SRR1, our return context is hosed.  -- Dan
+	 * 
+	 * This workaround is not enough, we must also make sure the
+	 * actual code for this routine is in the TLB or BAT mapped.
+	 * For 6xx/Power3, we know the code is in a BAT, so this should
+	 * be enough in UP. In SMP, I limit lowmem to the amount of
+	 * RAM that can be BAT mapped. Other CPUs may need additional
+	 * tweaks, especially if used SMP or if the code for this routine
+	 * crosses page boundaries. The TLB pin down for 4xx should help
+	 * for example. --BenH.
 	 */
 	lwz	r0,GPR0(r1)
 	lwz	r0,GPR2(r1)
 	lwz	r0,GPR1(r1)
+#endif /* ndef CONFIG_SMP */
 
 	/* We re-use r3,r4 here (the load above was to cause the MMU
 	 * fault if necessary).  Using r3,r4 removes the need to "dummy"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/feature.c linux/arch/ppc/kernel/feature.c
--- linux.orig/arch/ppc/kernel/feature.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/feature.c	Thu Jan  1 00:00:00 1970
@@ -1,1300 +0,0 @@
-/*
- * BK Id: SCCS/s.feature.c 1.21 09/08/01 15:47:42 paulus
- */
-/*
- *  arch/ppc/kernel/feature.c
- *
- *  Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
- *                     Ben. Herrenschmidt (benh@kernel.crashing.org)
- *
- *  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/types.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/spinlock.h>
-#include <asm/sections.h>
-#include <asm/errno.h>
-#include <asm/ohare.h>
-#include <asm/heathrow.h>
-#include <asm/keylargo.h>
-#include <asm/uninorth.h>
-#include <asm/io.h>
-#include <asm/prom.h>
-#include <asm/feature.h>
-#include <asm/dbdma.h>
-#include <asm/machdep.h>
-
-#undef DEBUG_FEATURE
-
-#define MAX_FEATURE_CONTROLLERS		2
-#define FREG(c,r)			(&(((c)->reg)[(r)>>2]))
-
-/* Keylargo reg. access. */
-#define KL_FCR(r)		(keylargo_base + ((r) >> 2))
-#define KL_IN(r)		(in_le32(KL_FCR(r)))
-#define KL_OUT(r,v)		(out_le32(KL_FCR(r), (v)))
-#define KL_BIS(r,v)		(KL_OUT((r), KL_IN(r) | (v)))
-#define KL_BIC(r,v)		(KL_OUT((r), KL_IN(r) & ~(v)))
-#define KL_GPIO_IN(r)		(in_8(((volatile u8 *)keylargo_base)+(r)))
-#define KL_GPIO_OUT(r,v)	(out_8(((volatile u8 *)keylargo_base)+(r), (v)))
-#define KL_LOCK()		spin_lock_irqsave(&keylargo->lock, flags)
-#define KL_UNLOCK()		spin_unlock_irqrestore(&keylargo->lock, flags)
-
-/* Uni-N reg. access. Note that Uni-N regs are big endian */
-#define UN_REG(r)	(uninorth_base + ((r) >> 2))
-#define UN_IN(r)	(in_be32(UN_REG(r)))
-#define UN_OUT(r,v)	(out_be32(UN_REG(r), (v)))
-#define UN_BIS(r,v)	(UN_OUT((r), UN_IN(r) | (v)))
-#define UN_BIC(r,v)	(UN_OUT((r), UN_IN(r) & ~(v)))
-
-typedef struct feature_bit {
-	int		reg;		/* reg. offset from mac-io base */
-	unsigned int	polarity;	/* 0 = normal, 1 = inverse */
-	unsigned int	mask;		/* bit mask */
-} fbit;
-
-/* Those features concern for OHare-based PowerBooks (2400, 3400, 3500)
- */
-static fbit feature_bits_ohare_pbook[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,OH_SCC_RESET},		/* FEATURE_Serial_reset */
-	{0x38,0,OH_SCC_ENABLE},		/* FEATURE_Serial_enable */
-	{0x38,0,OH_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,OH_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,OH_FLOPPY_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,OH_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,OH_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,OH_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,OH_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,OH_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,OH_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,OH_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,OH_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,OH_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,OH_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,0},			/* FEATURE_BMac_reset */
-	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
-	{0x38,0,0},			/* FEATURE_Modem_power */
-	{0x38,0,0},			/* FEATURE_Slow_SCC_PCLK */
-	{0x38,0,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits concern heathrow-based desktop machines (Beige G3s). We have removed
- * the SCC related bits and init them once. They have proven to occasionally cause
- * problems with the desktop units.
- */
-static fbit feature_bits_heathrow[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,0},			/* FEATURE_Serial_reset */
-	{0x38,0,0},			/* FEATURE_Serial_enable */
-	{0x38,0,0},			/* FEATURE_Serial_IO_A */
-	{0x38,0,0},			/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,0},			/* FEATURE_Mediabay_reset */
-	{0x38,1,0},			/* FEATURE_Mediabay_power */
-	{0x38,0,0},			/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,0},			/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits concern heathrow-based PowerBooks (wallstreet/mainstreet).
- * Heathrow-based desktop macs (Beige G3s) are _not_ handled here
- */
-static fbit feature_bits_wallstreet[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,HRW_RESET_SCC},		/* FEATURE_Serial_reset */
-	{0x38,0,HRW_SCC_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,HRW_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,HRW_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,HRW_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
-	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/*
- * Those bits are from a 1999 G3 PowerBook, with a paddington chip.
- * Mostly the same as the heathrow. They are used on both PowerBooks
- * and desktop machines using the paddington chip
- */
-static fbit feature_bits_paddington[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,PADD_RESET_SCC},	/* FEATURE_Serial_reset */
-	{0x38,0,HRW_SCC_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,HRW_SCCA_IO},		/* FEATURE_Serial_IO_A */
-	{0x38,0,HRW_SCCB_IO},		/* FEATURE_Serial_IO_B */
-	{0x38,0,HRW_SWIM_ENABLE},	/* FEATURE_SWIM3_enable */
-	{0x38,0,HRW_MESH_ENABLE},	/* FEATURE_MESH_enable */
-	{0x38,0,HRW_IDE0_ENABLE},	/* FEATURE_IDE0_enable */
-	{0x38,1,HRW_IDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,HRW_IOBUS_ENABLE},	/* FEATURE_IOBUS_enable */
-	{0x38,1,HRW_BAY_RESET_N},	/* FEATURE_Mediabay_reset */
-	{0x38,1,HRW_BAY_POWER_N},	/* FEATURE_Mediabay_power */
-	{0x38,0,HRW_BAY_PCI_ENABLE},	/* FEATURE_Mediabay_PCI_enable */
-	{0x38,0,HRW_BAY_IDE_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x38,1,HRW_IDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,HRW_BAY_FLOPPY_ENABLE},	/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,HRW_BMAC_RESET},	/* FEATURE_BMac_reset */
-	{0x38,0,HRW_BMAC_IO_ENABLE},	/* FEATURE_BMac_IO_enable */
-	{0x38,1,PADD_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,HRW_SLOW_SCC_PCLK},	/* FEATURE_Slow_SCC_PCLK */
-	{0x38,1,HRW_SOUND_POWER_N},	/* FEATURE_Sound_Power */
-	{0x38,0,HRW_SOUND_CLK_ENABLE},	/* FEATURE_Sound_CLK_Enable */
-	{0x38,0,0},			/* FEATURE_IDE2_enable */
-	{0x38,0,0},			/* FEATURE_IDE2_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_IDE_switch */
-	{0x38,0,0},			/* FEATURE_Mediabay_content */
-	{0x38,0,0},			/* FEATURE_Airport_reset */
-};
-
-/* Those bits are for Core99 machines (iBook,G4,iMacSL/DV,Pismo,...).
- * Note: Different sets may be needed for iBook, especially for sound
- */
-static fbit feature_bits_keylargo[] __pmacdata = {
-	{0x38,0,0},			/* FEATURE_null */
-	{0x38,0,KL0_SCC_RESET},		/* FEATURE_Serial_reset */
-	{0x38,0,KL0_SERIAL_ENABLE},	/* FEATURE_Serial_enable */
-	{0x38,0,KL0_SCC_A_INTF_ENABLE},	/* FEATURE_Serial_IO_A */
-	{0x38,0,KL0_SCC_B_INTF_ENABLE},	/* FEATURE_Serial_IO_B */
-	{0x38,0,0},			/* FEATURE_SWIM3_enable */
-	{0x38,0,0},			/* FEATURE_MESH_enable */
-	{0x3c,0,KL1_EIDE0_ENABLE},	/* FEATURE_IDE0_enable */
- 	{0x3c,1,KL1_EIDE0_RESET_N},	/* FEATURE_IDE0_reset */
-	{0x38,0,0},			/* FEATURE_IOBUS_enable */
-	{0x34,1,KL_MBCR_MB0_DEV_RESET},	/* FEATURE_Mediabay_reset */
-	{0x34,1,KL_MBCR_MB0_DEV_POWER},	/* FEATURE_Mediabay_power */
-	{0x38,0,0},			/* FEATURE_Mediabay_PCI_enable */
-	{0x3c,0,KL1_EIDE1_ENABLE},	/* FEATURE_IDE1_enable */
-	{0x3c,1,KL1_EIDE1_RESET_N},	/* FEATURE_IDE1_reset */
-	{0x38,0,0},			/* FEATURE_Mediabay_floppy_enable */
-	{0x38,0,0},			/* FEATURE_BMac_reset */
-	{0x38,0,0},			/* FEATURE_BMac_IO_enable */
-	{0x40,1,KL2_MODEM_POWER_N},	/* FEATURE_Modem_power */
-	{0x38,0,0},			/* FEATURE_Slow_SCC_PCLK */
-	{0x38,0,0},			/* FEATURE_Sound_Power */
-	{0x38,0,0},			/* FEATURE_Sound_CLK_Enable */
-	{0x3c,0,KL1_UIDE_ENABLE},	/* FEATURE_IDE2_enable */
-	{0x3c,1,KL1_UIDE_RESET_N},	/* FEATURE_IDE2_reset */
-	{0x34,0,KL_MBCR_MB0_DEV_ENABLE},/* FEATURE_Mediabay_IDE_switch */
-	{0x34,0,KL_MBCR_MB0_ENABLE},	/* FEATURE_Mediabay_content */
-	{0x40,1,KL2_AIRPORT_RESET_N},	/* FEATURE_Airport_reset */
-};
-
-/* definition of a feature controller object */
-struct feature_controller {
-	fbit*			bits;
-	volatile u32*		reg;
-	struct device_node*	device;
-	spinlock_t		lock;
-};
-
-/* static functions */
-static struct feature_controller*
-feature_add_controller(struct device_node *controller_device, fbit* bits);
-
-static struct feature_controller*
-feature_lookup_controller(struct device_node *device);
-
-static void uninorth_init(void);
-static void keylargo_init(void);
-#ifdef CONFIG_PMAC_PBOOK
-static void heathrow_prepare_for_sleep(struct feature_controller* ctrler);
-static void heathrow_wakeup(struct feature_controller* ctrler);
-static void core99_prepare_for_sleep(struct feature_controller* ctrler);
-static void core99_wake_up(struct feature_controller* ctrler);
-#endif /* CONFIG_PMAC_PBOOK */
-
-/* static variables */
-static struct feature_controller	controllers[MAX_FEATURE_CONTROLLERS];
-static int				controller_count = 0;
-
-/* Core99 stuffs */
-/*static*/ volatile u32*		uninorth_base;
-static volatile u32*			keylargo_base;
-static struct feature_controller*	keylargo;
-static int				uninorth_rev;
-static int				keylargo_rev;
-static u32				board_features;
-static int				airport_pwr_state;
-static struct device_node*		airport_dev;
-static struct device_node*		uninorth_fw;
-
-/* Feature bits for Apple motherboards */
-#define FTR_NEED_OPENPIC_TWEAK		0x00000001
-#define FTR_CAN_NAP			0x00000002
-#define FTR_HAS_FW_POWER		0x00000004
-#define FTR_CAN_SLEEP			0x00000008
-
-/* This table currently lacks most oldworld machines, but
- * they currently don't need it so...
- * 
- * Todo: The whole feature_xxx mecanism need to be redone
- * some way to be able to handle the new kind of features
- * exposed by core99. At this point, the main "tables" will
- * probably be in this table, which will have to be filled with
- * all known machines
- */
-/* Warning: Don't change ordering of entries as some machines
- * adverstise beeing compatible with several models. In those
- * case, the "highest" has to be first
- */
-static struct board_features_t {
-	char*	compatible;
-	u32	features;
-} board_features_datas[] __initdata = 
-{
-  {	"AAPL,PowerMac G3",	0				}, /* Beige G3 */
-  {	"iMac,1",		0				}, /* First iMac (gossamer) */
-  {	"PowerMac1,1",		0				}, /* B&W G3 / Yikes */
-  {	"PowerMac1,2",		0				}, /* B&W G3 / Yikes */
-  {	"PowerMac2,1",		FTR_CAN_SLEEP			}, /* r128 based iMac */
-  {	"PowerMac2,2",		FTR_HAS_FW_POWER|FTR_CAN_SLEEP	}, /* Summer 2000 iMac */
-  {	"PowerMac4,1",		FTR_CAN_SLEEP			}, /* iMac "Flower Power" */
-  {	"PowerMac3,1",		FTR_NEED_OPENPIC_TWEAK		}, /* Sawtooth (G4) */
-  {	"PowerMac3,2",		0				}, /* G4/Dual G4 */
-  {	"PowerMac3,3",		FTR_NEED_OPENPIC_TWEAK		}, /* G4/Dual G4 */
-  {	"PowerMac3,4",		0				}, /* QuickSilver G4 */
-  {	"PowerMac3,5",		0				}, /* QuickSilver G4 */
-  {	"PowerMac5,1",		FTR_CAN_NAP			}, /* Cube */
-  {	"AAPL,3400/2400",	FTR_CAN_SLEEP			}, /* 2400/3400 PowerBook */
-  {	"AAPL,3500",		FTR_CAN_SLEEP			}, /* 3500 PowerBook (G3) */
-  {	"AAPL,PowerBook1998",	FTR_CAN_SLEEP			}, /* Wallstreet PowerBook */
-  {	"PowerBook1,1",		FTR_CAN_SLEEP			}, /* 101 (Lombard) PowerBook */
-  {	"PowerBook4,1",		FTR_CAN_NAP|FTR_CAN_SLEEP|	   /* New polycarbonate iBook */
-  				0/*FTR_HAS_FW_POWER*/		}, 
-  {	"PowerBook2,1",		FTR_CAN_SLEEP 			}, /* iBook */
-  {	"PowerBook2,2",		FTR_CAN_SLEEP /*| FTR_CAN_NAP*/	}, /* iBook FireWire */
-  {	"PowerBook3,1",		FTR_CAN_SLEEP|FTR_CAN_NAP|	   /* PowerBook 2000 (Pismo) */
-  				FTR_HAS_FW_POWER		}, 
-  {	"PowerBook3,2",		FTR_CAN_NAP|FTR_CAN_SLEEP|	   /* PowerBook Titanium */
-  				0/*FTR_HAS_FW_POWER*/		},
-  {	NULL, 0 }
-};
-
-extern unsigned long powersave_nap;
-
-void __init
-feature_init(void)
-{
-	struct device_node *np;
-	u32 *rev;
-	int i;
-	char* model;
-	
-	if (_machine != _MACH_Pmac)
-		return;
-
-	np = find_path_device("/");
-	if (!np) {
-		printk(KERN_ERR "feature.c: Can't find device-tree root !\n");
-		return;
-	}
-	model = (char*)get_property(np, "model", NULL);
-	if (model) {
-		printk("PowerMac model: %s\n", model);
-		/* Figure out motherboard type & options */
-		for(i=0;board_features_datas[i].compatible;i++) {
-			if (!strcmp(board_features_datas[i].compatible, model)) {
-				board_features = board_features_datas[i].features;
-				break;
-			}
-		}
-	}
-
-	/* Set default value of powersave_nap on machines that support it */
-	if (board_features & FTR_CAN_NAP)
-		powersave_nap = 1;
-		
-	/* Track those poor mac-io's */
-	np = find_devices("mac-io");
-	while (np != NULL) {
-		/* KeyLargo contains several (5 ?) FCR registers in mac-io,
-		 * plus some gpio's which could eventually be handled here.
-		 */
-		if (device_is_compatible(np, "Keylargo")) {
-			struct feature_controller* ctrler =
-				feature_add_controller(np, feature_bits_keylargo);
-			if (ctrler) {
-				keylargo = ctrler;
-				keylargo_base = ctrler->reg;
-				rev = (u32 *)get_property(ctrler->device, "revision-id", NULL);
-				if (rev)
-					keylargo_rev = *rev;
-				
-				rev = (u32 *)get_property(ctrler->device, "device-id", NULL);
-				if (rev && (*rev) == 0x0025)
-					keylargo_rev |= KL_PANGEA_REV;
-			}
-		} else if (device_is_compatible(np, "paddington")) {
-			feature_add_controller(np, feature_bits_paddington);
-			/* We disable the modem power bit on Yikes as it can
-			 * do bad things (it's the nvram power)
-			 */
-			if (machine_is_compatible("PowerMac1,1") ||
-				machine_is_compatible("PowerMac1,2")) {
-				feature_bits_paddington[FEATURE_Modem_power].mask = 0;
-			}
-		} else if (machine_is_compatible("AAPL,PowerBook1998")) {
-			feature_add_controller(np, feature_bits_wallstreet);
-		} else {
-			struct feature_controller* ctrler =
-				feature_add_controller(np, feature_bits_heathrow);
-			if (ctrler)
-				out_le32(FREG(ctrler,HEATHROW_FEATURE_REG),
-					in_le32(FREG(ctrler,HEATHROW_FEATURE_REG)) | HRW_DEFAULTS);
-			
-		}
-		np = np->next;
-	}
-	if (controller_count == 0)
-	{
-		np = find_devices("ohare");
-		if (np) {
-			if (find_devices("via-pmu") != NULL)
-				feature_add_controller(np, feature_bits_ohare_pbook);
-			else
-				/* else not sure; maybe this is a Starmax? */
-				feature_add_controller(np, NULL);
-		}
-	}
-
-	/* Locate core99 Uni-N */
-	np = find_devices("uni-n");
-	if (np && np->n_addrs > 0) {
-		uninorth_base = ioremap(np->addrs[0].address, 0x1000);
-		uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
-		printk("Uninorth at 0x%08x\n", np->addrs[0].address);
-	}
-	if (uninorth_base && keylargo_base)
-		printk("Uni-N revision: %d, KeyLargo revision: %d %s\n",
-			uninorth_rev, keylargo_rev,
-			(keylargo_rev & KL_PANGEA_REV) ? "(Pangea chipset)" : "");
-
-	if (uninorth_base)
-		uninorth_init();
-	if (keylargo_base)
-		keylargo_init();
-
-	if (controller_count)
-		printk(KERN_INFO "Registered %d feature controller(s)\n", controller_count);
-
-#if defined(CONFIG_PMAC_PBOOK) && !defined(CONFIG_DMASOUND_AWACS)
-	/* On PowerBooks, we disable the sound chip when dmasound is a module
-	 * or not used at all
-	 */
-	if (controller_count && find_devices("via-pmu") != NULL) {
-		feature_clear(controllers[0].device, FEATURE_Sound_power);
-		feature_clear(controllers[0].device, FEATURE_Sound_CLK_enable);
-	}
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
-	/* On PowerBooks, we disable the serial ports by default, they
-	 * will be re-enabled by the driver
-	 */
-#ifndef CONFIG_XMON
-	if (controller_count && find_devices("via-pmu") != NULL) {
-		feature_set_modem_power(NULL, 0);
-		feature_clear(controllers[0].device, FEATURE_Serial_IO_A);
-		feature_clear(controllers[0].device, FEATURE_Serial_IO_B);
-		feature_clear(controllers[0].device, FEATURE_Serial_enable);
-		if (controller_count > 1) {
-			feature_clear(controllers[1].device, FEATURE_Serial_IO_A);
-			feature_clear(controllers[1].device, FEATURE_Serial_IO_B);
-			feature_clear(controllers[1].device, FEATURE_Serial_enable);
-		}
-	}
-#endif
-#endif
-}
-
-static struct feature_controller __init *
-feature_add_controller(struct device_node *controller_device, fbit* bits)
-{
-	struct feature_controller*	controller;
-	
-	if (controller_count >= MAX_FEATURE_CONTROLLERS) {
-		printk(KERN_INFO "Feature controller %s skipped(MAX:%d)\n",
-			controller_device->full_name, MAX_FEATURE_CONTROLLERS);
-		return NULL;
-	}
-	controller = &controllers[controller_count];
-
-	controller->bits	= bits;
-	controller->device	= controller_device;
-	if (controller_device->n_addrs == 0) {
-		printk(KERN_ERR "No addresses for %s\n",
-			controller_device->full_name);
-		return NULL;
-	}
-
-	/* We remap the entire mac-io here. Normally, this will just
-	 * give us back our already existing BAT mapping
-	 */
-	controller->reg		= (volatile u32 *)ioremap(
-		controller_device->addrs[0].address,
-		controller_device->addrs[0].size);
-
-	if (bits == NULL) {
-		printk(KERN_INFO "Twiddling the magic ohare bits\n");
-		out_le32(FREG(controller,OHARE_FEATURE_REG), STARMAX_FEATURES);
-		return NULL;
-	}
-
-	spin_lock_init(&controller->lock);
-
-	controller_count++;
-
-	return controller;
-}
-
-static struct feature_controller __pmac *
-feature_lookup_controller(struct device_node *device)
-{
-	int	i;
-	
-	if (device == NULL)
-		return NULL;
-		
-	while(device)
-	{
-		for (i=0; i<controller_count; i++)
-			if (device == controllers[i].device)
-				return &controllers[i];
-		device = device->parent;
-	}
-
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> not found on any controller\n",
-		device->name);
-#endif
-	
-	return NULL;
-}
-
-int __pmac
-feature_set(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			flags;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> setting feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-
-	spin_lock_irqsave(&controller->lock, flags);
-	value = in_le32(FREG(controller, bit->reg));
-	value = bit->polarity ? (value & ~bit->mask) : (value | bit->mask);
-	out_le32(FREG(controller, bit->reg), value);
-	(void)in_le32(FREG(controller, bit->reg));
-	spin_unlock_irqrestore(&controller->lock, flags);
-	
-	return 0;
-}
-
-int __pmac
-feature_clear(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			flags;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-
-	spin_lock_irqsave(&controller->lock, flags);
-	value = in_le32(FREG(controller, bit->reg));
-	value = bit->polarity ? (value | bit->mask) : (value & ~bit->mask);
-	out_le32(FREG(controller, bit->reg), value);
-	(void)in_le32(FREG(controller, bit->reg));
-	spin_unlock_irqrestore(&controller->lock, flags);
-	
-	return 0;
-}
-
-int __pmac
-feature_test(struct device_node* device, enum system_feature f)
-{
-	struct feature_controller*	controller;
-	unsigned long			value;
-	fbit*				bit;
-
-	if (f >= FEATURE_last)
-		return -EINVAL;	
-
-	controller = feature_lookup_controller(device);
-	if (!controller)
-		return -ENODEV;
-	bit = &controller->bits[f];
-	if (!bit->mask)
-		return -EINVAL;
-	
-#ifdef DEBUG_FEATURE
-	printk("feature: <%s> clearing feature %d in controller @0x%x\n",
-		device->name, (int)f, (unsigned int)controller->reg);
-#endif
-	/* If one feature contains several bits, all of them must be set
-	 * for value to be true, or all of them must be 0 if polarity is
-	 * inverse
-	 */
-	value = (in_le32(FREG(controller, bit->reg)) & bit->mask);
-	return bit->polarity ? (value == 0) : (value == bit->mask);
-}
-
-int __pmac
-feature_can_sleep(void)
-{
-	return ((board_features & FTR_CAN_SLEEP) != 0);	
-}
-
-/*
- * Core99 functions
- * 
- * Note: We currently assume there is _one_ UniN chip and _one_ KeyLargo
- *       chip, which is the case on all Core99 machines so far
- */
-
-/* Only one GMAC is assumed */
-void __pmac
-feature_set_gmac_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	
-	if (!uninorth_base || !keylargo)
-		return;
-		
-	/* TODO: Handle save/restore of PCI config space here
-	 */
-
-	/* XXX We use the keylargo spinlock, but we never
-	 * have uninorth without keylargo, so...
-	 */
-	KL_LOCK();
-	if (power)
-		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
-	else
-		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
-	(void)UN_IN(UNI_N_CLOCK_CNTL);
-	KL_UNLOCK();
-	udelay(20);
-}
-
-void __pmac
-feature_gmac_phy_reset(struct device_node* device)
-{
-	unsigned long flags;
-	
-	if (!keylargo_base || !keylargo)
-		return;
-		
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
-	(void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET);
-	KL_UNLOCK();
-	mdelay(10);
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_ETH_PHY_RESET,
-		KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-	(void)KL_GPIO_IN(KL_GPIO_ETH_PHY_RESET);
-	KL_UNLOCK();
-	mdelay(10);
-}
-
-/* Pass the node of the correct controller, please */
-void __pmac
-feature_set_usb_power(struct device_node* device, int power)
-{
-	char* prop;
-	int number;
-	u32 reg;
-	
-	unsigned long flags;
-	
-	if (!keylargo_base || !keylargo)
-		return;
-		
-	prop = (char *)get_property(device, "AAPL,clock-id", NULL);
-	if (!prop)
-		return;
-	if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0)
-		number = 0;
-	else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0)
-		number = 2;
-	else
-		return;
-
-	KL_LOCK();
-	if (power) {
-		/* Turn ON */
-			
-		if (number == 0) {
-			KL_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-			KL_UNLOCK();
-			mdelay(1);
-			KL_LOCK();
-			KL_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
-		} else {
-			KL_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
-			KL_UNLOCK();
-			(void)KL_IN(KEYLARGO_FCR0);
-			mdelay(1);
-			KL_LOCK();
-			KL_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
-		}
-		reg = KL_IN(KEYLARGO_FCR4);
-		reg &=	~(KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
-			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number));
-		reg &=	~(KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
-			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1));
-		KL_OUT(KEYLARGO_FCR4, reg);
-		(void)KL_IN(KEYLARGO_FCR4);
-		udelay(10);
-	} else {
-		/* Turn OFF */
-		
-		reg = KL_IN(KEYLARGO_FCR4);
-		reg |=	KL4_SET_PORT_ENABLE(number) | KL4_SET_PORT_RESUME(number) |
-			KL4_SET_PORT_CONNECT(number) | KL4_SET_PORT_DISCONNECT(number);
-		reg |=	KL4_SET_PORT_ENABLE(number+1) | KL4_SET_PORT_RESUME(number+1) |
-			KL4_SET_PORT_CONNECT(number+1) | KL4_SET_PORT_DISCONNECT(number+1);
-		KL_OUT(KEYLARGO_FCR4, reg);
-		(void)KL_IN(KEYLARGO_FCR4);
-		udelay(1);
-		if (number == 0) {
-			KL_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
-			(void)KL_IN(KEYLARGO_FCR0);
-			udelay(1);
-			KL_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-		} else {
-			KL_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
-			(void)KL_IN(KEYLARGO_FCR0);
-			udelay(1);
-			KL_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
-			(void)KL_IN(KEYLARGO_FCR0);
-		}
-		udelay(1);
-	}
-	KL_UNLOCK();
-}
-
-void  __pmac
-feature_set_firewire_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-
-	/* TODO: should probably handle save/restore of PCI config space here
-	 */
-		
-	if (!uninorth_fw || (device && uninorth_fw != device))
-		return;
-	if (!uninorth_base)
-		return;
-
-	/* XXX We use the keylargo spinlock, but we never
-	 * have uninorth without keylargo, so...
-	 */
-	KL_LOCK();
-	if (power)
-		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
-	else
-		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
-	(void)UN_IN(UNI_N_CLOCK_CNTL);
-	KL_UNLOCK();
-	udelay(20);
-}
-
-/* Warning: will kill the PHY.. */
-void  __pmac
-feature_set_firewire_cable_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	u8 gpioValue = power ? 0 : 4;
-
-	if (!uninorth_fw || (device && uninorth_fw != device))
-		return;
-	if (!keylargo_base || !(board_features & FTR_HAS_FW_POWER))
-		return;
-	KL_LOCK();
-	KL_GPIO_OUT(KL_GPIO_FW_CABLE_POWER, gpioValue);
-	(void)KL_GPIO_IN(KL_GPIO_FW_CABLE_POWER);
-	KL_UNLOCK();		
-}
-
-void
-feature_set_modem_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-
-	if (!device) {
-		device = find_devices("ch-a");
-		while(device && !device_is_compatible(device, "cobalt"))
-			device = device->next;
-		if (!device)
-			return;
-	}
-	if (keylargo && (keylargo_rev & KL_PANGEA_REV)) {
-		KL_LOCK();
-		if (power) {
-			/* Assert modem reset */
-			KL_GPIO_OUT(KL_GPIO_MODEM_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_RESET);
-			udelay(10);
-			/* Power up modem */
-			KL_GPIO_OUT(KL_GPIO_MODEM_POWER, KEYLARGO_GPIO_OUTPUT_ENABLE);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_POWER);
-			udelay(10);
-			/* Release modem reset */
-			KL_GPIO_OUT(KL_GPIO_MODEM_RESET,
-				KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_RESET);
-		} else {
-			/* Power down modem */
-			KL_GPIO_OUT(KL_GPIO_MODEM_POWER,
-				KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-			(void)KL_GPIO_IN(KL_GPIO_MODEM_POWER);
-		}
-		KL_UNLOCK();
-	} else {
-		if (power) {
-			mdelay(300);
-			feature_set(device, FEATURE_Modem_power);
-	   		mdelay(5);
-			feature_clear(device, FEATURE_Modem_power);
-	   		mdelay(10);
-			feature_set(device, FEATURE_Modem_power);
-		} else {
-			feature_clear(device, FEATURE_Modem_power);
-			mdelay(10);
-		}
-	}
-}
-
-#ifdef CONFIG_SMP
-void __pmac
-feature_core99_kick_cpu(int cpu_nr)
-{
-	const int reset_lines[] = {	KL_GPIO_RESET_CPU0,
-					KL_GPIO_RESET_CPU1,
-					KL_GPIO_RESET_CPU2,
-					KL_GPIO_RESET_CPU3 };
-	int reset_io;
-	unsigned long flags;
-	
-	if (!keylargo_base || cpu_nr > 3)
-		return;
-	reset_io = reset_lines[cpu_nr];
-	
-	KL_LOCK();
-	KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE);
-	(void)KL_GPIO_IN(reset_io);
-	udelay(1);
-	KL_GPIO_OUT(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
-	(void)KL_GPIO_IN(reset_io);
-	KL_UNLOCK();
-}
-#endif /* CONFIG_SMP */
-
-void __pmac
-feature_set_airport_power(struct device_node* device, int power)
-{
-	unsigned long flags;
-	
-	if (!keylargo_base || !airport_dev || airport_dev != device)
-		return;
-	if (airport_pwr_state == power)
-		return;	
-	if (power) {
-		/* This code is a reproduction of OF enable-cardslot
-		 * and init-wireless methods, slightly hacked until
-		 * I got it working.
-		 */
-		KL_LOCK();
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 5);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf);
-		KL_UNLOCK();
-		mdelay(10);
-		KL_LOCK();
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xf, 4);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xf);
-		KL_UNLOCK();
-
-		mdelay(10);
-
-		KL_LOCK();
-		KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xb, 0);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xb);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xa);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_EXTINT_0+0xd);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xd, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xd);
-		udelay(10);
-		KL_GPIO_OUT(KEYLARGO_GPIO_0+0xe, 0x28);
-		(void)KL_GPIO_IN(KEYLARGO_GPIO_0+0xe);
-		KL_UNLOCK();
-		udelay(10);
-		KL_OUT(0x1c000, 0);
-		
-		mdelay(1);
-		out_8((volatile u8*)KL_FCR(0x1a3e0), 0x41);
-		(void)in_8((volatile u8*)KL_FCR(0x1a3e0));
-		udelay(10);
-		KL_LOCK();
-		KL_BIS(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		KL_UNLOCK();
-		mdelay(100);
-	} else {
-		KL_LOCK();
-		KL_BIC(KEYLARGO_FCR2, KL2_AIRPORT_RESET_N);
-		(void)KL_IN(KEYLARGO_FCR2);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_0, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_1, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_2, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_3, 0);
-		KL_GPIO_OUT(KL_GPIO_AIRPORT_4, 0);
-		(void)KL_GPIO_IN(KL_GPIO_AIRPORT_4);
-		KL_UNLOCK();
-	}
-	airport_pwr_state = power;	
-}
-
-/* Initialize the Core99 UniNorth host bridge and memory controller
- */
-static void __init
-uninorth_init(void)
-{
-	struct device_node* gmac, *fw;
-	unsigned long actrl;
-	
-	/* Set the arbitrer QAck delay according to what Apple does
-	 */
-	if (uninorth_rev < 0x10) {
-		actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
-		actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
-			UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
-		UN_OUT(UNI_N_ARB_CTRL, actrl);
-	}
-	
-	/* Enable GMAC for now for PCI probing. It will be disabled
-	 * later on after PCI probe
-	 */
-	gmac = find_devices("ethernet");
-	while(gmac) {
-		if (device_is_compatible(gmac, "gmac"))
-			break;
-		gmac = gmac->next;
-	}
-	if (gmac)
-		feature_set_gmac_power(gmac, 1);
-
-	/* Enable FW before PCI probe. Will be disabled later on
-	 */
-	fw = find_devices("firewire");
-	if (fw && (device_is_compatible(fw, "pci106b,18") || 
-			device_is_compatible(fw, "pci106b,30"))) {
-		uninorth_fw = fw;
-		feature_set_firewire_power(fw, 1);
-	}
-}
-
-/* Initialize the Core99 KeyLargo ASIC. 
- */
-static void __init
-keylargo_init(void)
-{
-	struct device_node* np;
-	
-	KL_BIS(KEYLARGO_FCR2, KL2_MPIC_ENABLE);
-
-	/* Lookup for an airport card, and disable it if found
-	 * to save power (will be re-enabled by driver if used)
-	 */
-	np = find_devices("radio");
-	if (np && np->parent == keylargo->device)
-		airport_dev = np;
-
-	if (airport_dev) {
-		airport_pwr_state = 1;
-		feature_set_airport_power(airport_dev, 0);
-	}
-	
-}
-
-#ifdef CONFIG_PMAC_PBOOK
-void __pmac
-feature_prepare_for_sleep(void)
-{
-	/* We assume gatwick is second */
-	struct feature_controller* ctrler = &controllers[0];
-
-	if (controller_count > 1 &&
-		device_is_compatible(ctrler->device, "gatwick"))
-		ctrler = &controllers[1];
-
-	if (ctrler->bits == feature_bits_wallstreet ||
-		ctrler->bits == feature_bits_paddington) {
-		heathrow_prepare_for_sleep(ctrler);
-		return;
-	}
-	if (ctrler->bits == feature_bits_keylargo) {
-		core99_prepare_for_sleep(ctrler);
-		return;
-	}
-}
-
-void __pmac
-feature_wake_up(void)
-{
-	struct feature_controller* ctrler = &controllers[0];
-
-	if (controller_count > 1 &&
-		device_is_compatible(ctrler->device, "gatwick"))
-		ctrler = &controllers[1];
-	
-	if (ctrler->bits == feature_bits_wallstreet ||
-		ctrler->bits == feature_bits_paddington) {
-		heathrow_wakeup(ctrler);
-		return;
-	}
-	if (ctrler->bits == feature_bits_keylargo) {
-		core99_wake_up(ctrler);
-		return;
-	}
-}
-
-static u32 save_fcr[5];
-static u32 save_mbcr;
-static u32 save_gpio_levels[2];
-static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT];
-static u8 save_gpio_normal[KEYLARGO_GPIO_CNT];
-static u32 save_unin_clock_ctl;
-static struct dbdma_regs save_dbdma[13];
-
-static void __pmac
-heathrow_prepare_for_sleep(struct feature_controller* ctrler)
-{
-	save_mbcr = in_le32(FREG(ctrler, 0x34));
-	save_fcr[0] = in_le32(FREG(ctrler, 0x38));
-	save_fcr[1] = in_le32(FREG(ctrler, 0x3c));
-
-	out_le32(FREG(ctrler, 0x38), save_fcr[0] & ~HRW_IOBUS_ENABLE);
-}
-
-static void __pmac
-heathrow_wakeup(struct feature_controller* ctrler)
-{
-	out_le32(FREG(ctrler, 0x38), save_fcr[0]);
-	out_le32(FREG(ctrler, 0x3c), save_fcr[1]);
-	out_le32(FREG(ctrler, 0x34), save_mbcr);
-	mdelay(1);
-	out_le32(FREG(ctrler, 0x38), save_fcr[0] | HRW_IOBUS_ENABLE);
-	mdelay(1);
-}
-
-static void __pmac
-turn_off_keylargo(void)
-{
-	u32 temp;
-
-	mdelay(1);
-	KL_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
-	(void)KL_IN(KEYLARGO_FCR0);
-	mdelay(100);
-
-	KL_BIC(KEYLARGO_FCR0,	KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
-				KL0_SCC_CELL_ENABLE |
-		      		KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
-		      		KL0_IRDA_CLK19_ENABLE);
-
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-
-	KL_BIC(KEYLARGO_FCR1,
-		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
-		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
-		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
-		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
-		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
-		KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
-		KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
-		KL1_UIDE_ENABLE);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-
-	KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
- 	udelay(10);
- 	KL_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
- 	udelay(10);
-	temp = KL_IN(KEYLARGO_FCR3);
-	if (keylargo_rev >= 2)
-		temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL);
-		
-	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
-		KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12;
-	temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
-		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
-		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
-	KL_OUT(KEYLARGO_FCR3, temp);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-}
-
-static void __pmac
-turn_off_pangea(void)
-{
-	u32 temp;
-
-	KL_BIC(KEYLARGO_FCR0,	KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
-				KL0_SCC_CELL_ENABLE |
-				KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
-
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_BIS(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_ENABLE);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-
-	KL_BIC(KEYLARGO_FCR1,
-		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
-		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
-		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
-		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
-		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
-		KL1_UIDE_ENABLE);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-
-	KL_BIS(KEYLARGO_FCR2, KL2_MODEM_POWER_N);
- 	udelay(10);
-	temp = KL_IN(KEYLARGO_FCR3);
-	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
-		KL3_SHUTDOWN_PLLKW35;
-	temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
-		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
-		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
-	KL_OUT(KEYLARGO_FCR3, temp);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-}
-
-static void __pmac
-core99_prepare_for_sleep(struct feature_controller* ctrler)
-{
-	int i;
-	volatile u8* base8;
-	
-	/*
-	 * Save various bits of KeyLargo
-	 */
-
-	/* We power off the wireless slot in case it was not done
-	 * by the driver. We don't power it on automatically however
-	 */
-	feature_set_airport_power(airport_dev, 0);
-
-	/* We power off the FW cable. Should be done by the driver... */
-	feature_set_firewire_power(NULL, 0);
-	feature_set_firewire_cable_power(NULL, 0);
-
-	/* We make sure int. modem is off (in case driver lost it) */
-	feature_set_modem_power(NULL, 0);
-	 
-	/* Save the state of the various GPIOs */
-	save_gpio_levels[0] = KL_IN(KEYLARGO_GPIO_LEVELS0);
-	save_gpio_levels[1] = KL_IN(KEYLARGO_GPIO_LEVELS1);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0;
-	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
-		save_gpio_extint[i] = in_8(base8+i);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_0;
-	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
-		save_gpio_normal[i] = in_8(base8+i);
-
-	/* Save the FCRs */
-	save_mbcr = KL_IN(KEYLARGO_MBCR);
-	save_fcr[0] = KL_IN(KEYLARGO_FCR0);
-	save_fcr[1] = KL_IN(KEYLARGO_FCR1);
-	save_fcr[2] = KL_IN(KEYLARGO_FCR2);
-	save_fcr[3] = KL_IN(KEYLARGO_FCR3);
-	save_fcr[4] = KL_IN(KEYLARGO_FCR4);
-
-	/* Save state & config of DBDMA channels */
-	for (i=0; i<13; i++) {
-		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
-			(keylargo_base + ((0x8000+i*0x100)>>2));
-		save_dbdma[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
-		save_dbdma[i].cmdptr = in_le32(&chan->cmdptr);
-		save_dbdma[i].intr_sel = in_le32(&chan->intr_sel);
-		save_dbdma[i].br_sel = in_le32(&chan->br_sel);
-		save_dbdma[i].wait_sel = in_le32(&chan->wait_sel);
-	}
-
-	/*
-	 * Turn off as much as we can
-	 */
-	if (keylargo_rev & KL_PANGEA_REV)
-		turn_off_pangea();
-	else
-		turn_off_keylargo();
-	
-	/* 
-	 * Put the host bridge to sleep
-	 */
-
-	save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
-	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
-		~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
-	udelay(100);
-	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
-	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
-
-	/*
-	 * FIXME: A bit of black magic with OpenPIC (don't ask me why)
-	 */
-	if (board_features & FTR_NEED_OPENPIC_TWEAK) {
-		KL_BIS(0x506e0, 0x00400000);
-		KL_BIS(0x506e0, 0x80000000);
-	}
-}
-
-static void __pmac
-core99_wake_up(struct feature_controller* ctrler)
-{
-	int i;
-	volatile u8* base8;
-
-	/*
-	 * Wakeup the host bridge
-	 */
-	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
-	udelay(10);
-	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
-	udelay(10);
-	
-	/*
-	 * Restore KeyLargo
-	 */
-	 
-	KL_OUT(KEYLARGO_MBCR, save_mbcr);
-	(void)KL_IN(KEYLARGO_MBCR); udelay(10);
-	KL_OUT(KEYLARGO_FCR0, save_fcr[0]);
-	(void)KL_IN(KEYLARGO_FCR0); udelay(10);
-	KL_OUT(KEYLARGO_FCR1, save_fcr[1]);
-	(void)KL_IN(KEYLARGO_FCR1); udelay(10);
-	KL_OUT(KEYLARGO_FCR2, save_fcr[2]);
-	(void)KL_IN(KEYLARGO_FCR2); udelay(10);
-	KL_OUT(KEYLARGO_FCR3, save_fcr[3]);
-	(void)KL_IN(KEYLARGO_FCR3); udelay(10);
-	KL_OUT(KEYLARGO_FCR4, save_fcr[4]);
-	(void)KL_IN(KEYLARGO_FCR4); udelay(10);
-
-	for (i=0; i<13; i++) {
-		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
-			(keylargo_base + ((0x8000+i*0x100)>>2));
-		out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
-		while (in_le32(&chan->status) & ACTIVE)
-			mb();
-		out_le32(&chan->cmdptr_hi, save_dbdma[i].cmdptr_hi);
-		out_le32(&chan->cmdptr, save_dbdma[i].cmdptr);
-		out_le32(&chan->intr_sel, save_dbdma[i].intr_sel);
-		out_le32(&chan->br_sel, save_dbdma[i].br_sel);
-		out_le32(&chan->wait_sel, save_dbdma[i].wait_sel);
-	}
-
-	KL_OUT(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
-	KL_OUT(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_EXTINT_0;
-	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
-		out_8(base8+i, save_gpio_extint[i]);
-	base8 = ((volatile u8 *)keylargo_base) + KEYLARGO_GPIO_0;
-	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
-		out_8(base8+i, save_gpio_normal[i]);
-
-	/* FIXME more black magic with OpenPIC ... */
-	if (board_features & FTR_NEED_OPENPIC_TWEAK) {
-		KL_BIC(0x506e0, 0x00400000);
-		KL_BIC(0x506e0, 0x80000000);
-	}
-
-	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
-	udelay(100);
-}
-#endif /* CONFIG_PMAC_PBOOK */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- linux.orig/arch/ppc/kernel/head.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/head.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.head.S 1.31 10/18/01 15:02:09 trini
+ * BK Id: SCCS/s.head.S 1.34 12/02/01 11:35:27 benh
  */
 /*
  *  PowerPC version 
@@ -1347,7 +1347,19 @@
 	bl	setup_750_7400_hid0
 	mtlr	r4
 	blr
+_GLOBAL(__setup_cpu_7410)
+	mflr	r4
+	bl	setup_common_caches
+	bl	setup_750_7400_hid0
+	li	r3,0
+	mtspr	SPRN_L2CR2,r3
+	mtlr	r4
+	blr
 _GLOBAL(__setup_cpu_7450)
+	mflr	r4
+	bl	setup_common_caches
+	bl	setup_7450_hid0
+	mtlr	r4
 	blr
 _GLOBAL(__setup_cpu_power3)
 	blr
@@ -1400,6 +1412,47 @@
 	li	r3,HID0_SPD
 	andc	r11,r11,r3		/* clear SPD: enable speculative */
  	li	r3,0
+ 	mtspr	ICTC,r3			/* Instruction Cache Throttling off */
+	isync
+	mtspr	HID0,r11
+	sync
+	isync
+	blr
+
+/* 7450
+ * Enable Store Gathering (SGE), Branch Folding (FOLD)
+ * Branch History Table (BHTE), Branch Target ICache (BTIC)
+ * Dynamic Power Management (DPM), Speculative (SPD)
+ * Ensure our data cache instructions really operate.
+ * Timebase has to be running or we wouldn't have made it here,
+ * just ensure we don't disable it.
+ * Clear Instruction cache throttling (ICTC)
+ */
+setup_7450_hid0:
+	/* We check for the presence of an L3 cache setup by
+	 * the firmware. If any, we disable DOZE capability
+	 */
+	mfspr	r11,SPRN_L3CR
+	andis.	r11,r11,L3CR_L3E@h
+	beq	1f
+	li	r7,CPU_FTR_CAN_DOZE
+	lwz	r6,CPU_SPEC_FEATURES(r5)
+	andc	r6,r6,r7
+	stw	r6,CPU_SPEC_FEATURES(r5)
+1:	
+	mfspr	r11,HID0
+
+	/* All of the bits we have to set.....
+	*/
+	ori	r11,r11,HID0_SGE | HID0_FOLD | HID0_BHTE | HID0_BTIC
+	oris	r11,r11,HID0_DPM@h	/* enable dynamic power mgmt */
+
+	/* All of the bits we have to clear....
+	*/
+	li	r3,HID0_SPD | HID0_NOPDST | HID0_NOPTI
+	andc	r11,r11,r3		/* clear SPD: enable speculative */
+ 	li	r3,0
+
  	mtspr	ICTC,r3			/* Instruction Cache Throttling off */
 	isync
 	mtspr	HID0,r11
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/i8259.c linux/arch/ppc/kernel/i8259.c
--- linux.orig/arch/ppc/kernel/i8259.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/i8259.c	Wed Dec 26 16:28:34 2001
@@ -1,14 +1,18 @@
 /*
- * BK Id: SCCS/s.i8259.c 1.7 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.i8259.c 1.11 12/19/01 09:45:54 trini
  */
 
 #include <linux/stddef.h>
 #include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <asm/io.h>
 #include "i8259.h"
 
+static volatile char *pci_intack; /* RO, gives us the irq vector */
+
 unsigned char cached_8259[2] = { 0xff, 0xff };
 #define cached_A1 (cached_8259[0])
 #define cached_21 (cached_8259[1])
@@ -17,32 +21,56 @@
 
 int i8259_pic_irq_offset;
 
-int i8259_irq(int cpu)
+/* Acknowledge the irq using the PCI host bridge's interrupt acknowledge
+ * feature. (Polling is somehow broken on some IBM and Motorola PReP boxes.)
+ */
+int i8259_irq(void)
+{
+	int irq;
+
+	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
+
+	irq = *pci_intack & 0xff;
+	if (irq==7) {
+		/*
+		 * This may be a spurious interrupt.
+		 *
+		 * Read the interrupt status register (ISR). If the most
+		 * significant bit is not set then there is no valid
+		 * interrupt.
+		 */
+		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(void)
 {
 	int irq;
-	
+
 	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
-        /*
-         * Perform an interrupt acknowledge cycle on controller 1
-         */                                                             
-        outb(0x0C, 0x20);
-        irq = inb(0x20) & 7;                                   
-        if (irq == 2)                                                     
-        {                                                                   
-                /*                                     
-                 * Interrupt is cascaded so perform interrupt
-                 * acknowledge on controller 2
-                 */
-                outb(0x0C, 0xA0);                      
-                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
+	/*
+	 * 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);
@@ -58,44 +86,44 @@
 static void i8259_mask_and_ack_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
 
-        if (irq_nr > 7) {                                                   
-                cached_A1 |= 1 << (irq_nr-8);                                   
-                inb(0xA1);      /* DUMMY */                                     
-                outb(cached_A1,0xA1);                                           
-                outb(0x20,0xA0);        /* Non-specific EOI */             
-                outb(0x20,0x20);        /* Non-specific EOI to cascade */
-        } else {                                                            
-                cached_21 |= 1 << irq_nr;                                   
-                inb(0x21);      /* DUMMY */                                 
-                outb(cached_21,0x21);
-                outb(0x20,0x20);        /* Non-specific EOI */                 
-        }                                                                
+	if (irq_nr > 7) {
+		cached_A1 |= 1 << (irq_nr-8);
+		inb(0xA1); /* DUMMY */
+		outb(cached_A1,0xA1);
+		outb(0x20,0xA0); /* Non-specific EOI */
+		outb(0x20,0x20); /* Non-specific EOI to cascade */
+	} else {
+		cached_21 |= 1 << irq_nr;
+		inb(0x21); /* DUMMY */
+		outb(cached_21,0x21);
+		outb(0x20,0x20); /* Non-specific EOI */
+	}
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
 static void i8259_set_irq_mask(int irq_nr)
 {
-        outb(cached_A1,0xA1);
-        outb(cached_21,0x21);
+	outb(cached_A1,0xA1);
+	outb(cached_21,0x21);
 }
-
+ 
 static void i8259_mask_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
 
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
-        if ( irq_nr < 8 )
-                cached_21 |= 1 << irq_nr;
-        else
-                cached_A1 |= 1 << (irq_nr-8);
-        i8259_set_irq_mask(irq_nr);
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 |= 1 << irq_nr;
+	else
+		cached_A1 |= 1 << (irq_nr-8);
+	i8259_set_irq_mask(irq_nr);
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
@@ -104,13 +132,13 @@
 	unsigned long flags;
 
 	spin_lock_irqsave(&i8259_lock, flags);
-        if ( irq_nr >= i8259_pic_irq_offset )
-                irq_nr -= i8259_pic_irq_offset;
-        if ( irq_nr < 8 )
-                cached_21 &= ~(1 << irq_nr);
-        else
-                cached_A1 &= ~(1 << (irq_nr-8));
-        i8259_set_irq_mask(irq_nr);
+	if ( irq_nr >= i8259_pic_irq_offset )
+		irq_nr -= i8259_pic_irq_offset;
+	if ( irq_nr < 8 )
+		cached_21 &= ~(1 << irq_nr);
+	else
+		cached_A1 &= ~(1 << (irq_nr-8));
+	i8259_set_irq_mask(irq_nr);
 	spin_unlock_irqrestore(&i8259_lock, flags);
 }
 
@@ -121,36 +149,62 @@
 }
 
 struct hw_interrupt_type i8259_pic = {
-        " i8259    ",
-        NULL,
-        NULL,
-        i8259_unmask_irq,
-        i8259_mask_irq,
-        i8259_mask_and_ack_irq,
-        i8259_end_irq,
-        NULL
+	" i8259    ",
+	NULL,
+	NULL,
+	i8259_unmask_irq,
+	i8259_mask_irq,
+	i8259_mask_and_ack_irq,
+	i8259_end_irq,
+	NULL
+};
+
+static struct resource pic1_iores = {
+	"8259 (master)", 0x20, 0x21, IORESOURCE_BUSY
 };
 
-void __init i8259_init(void)
+static struct resource pic2_iores = {
+	"8259 (slave)", 0xa0, 0xa1, IORESOURCE_BUSY
+};
+
+static struct resource pic_edgectrl_iores = {
+	"8259 edge control", 0x4d0, 0x4d1, IORESOURCE_BUSY
+};
+
+void __init i8259_init(long intack_addr)
 {
 	unsigned long flags;
-	
+
 	spin_lock_irqsave(&i8259_lock, flags);
-        /* init master interrupt controller */
-        outb(0x11, 0x20); /* Start init sequence */
-        outb(0x00, 0x21); /* Vector base */
-        outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
-        outb(0x01, 0x21); /* Select 8086 mode */
-        outb(0xFF, 0x21); /* Mask all */
-        /* init slave interrupt controller */
-        outb(0x11, 0xA0); /* Start init sequence */
-        outb(0x08, 0xA1); /* Vector base */
-        outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
-        outb(0x01, 0xA1); /* Select 8086 mode */
-        outb(0xFF, 0xA1); /* Mask all */
-        outb(cached_A1, 0xA1);
-        outb(cached_21, 0x21);
+	/* init master interrupt controller */
+	outb(0x11, 0x20); /* Start init sequence */
+	outb(0x00, 0x21); /* Vector base */
+	outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
+	outb(0x01, 0x21); /* Select 8086 mode */
+
+	/* init slave interrupt controller */
+	outb(0x11, 0xA0); /* Start init sequence */
+	outb(0x08, 0xA1); /* Vector base */
+	outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
+	outb(0x01, 0xA1); /* Select 8086 mode */
+
+	/* always read ISR */
+	outb(0x0B, 0x20);
+	outb(0x0B, 0xA0);
+
+	/* Mask all interrupts */
+	outb(cached_A1, 0xA1);
+	outb(cached_21, 0x21);
+
 	spin_unlock_irqrestore(&i8259_lock, flags);
-        request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT,
-                     "82c59 secondary cascade", NULL );
+
+	/* reserve our resources */
+	request_irq( i8259_pic_irq_offset + 2, no_action, SA_INTERRUPT,
+				"82c59 secondary cascade", NULL );
+	request_resource(&ioport_resource, &pic1_iores);
+	request_resource(&ioport_resource, &pic2_iores);
+	request_resource(&ioport_resource, &pic_edgectrl_iores);
+
+	if (intack_addr)
+		pci_intack = ioremap(intack_addr, 1);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/i8259.h linux/arch/ppc/kernel/i8259.h
--- linux.orig/arch/ppc/kernel/i8259.h	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/i8259.h	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.i8259.h 1.5 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.i8259.h 1.8 12/19/01 09:45:54 trini
  */
 
 #ifndef _PPC_KERNEL_i8259_H
@@ -9,7 +9,8 @@
 
 extern struct hw_interrupt_type i8259_pic;
 
-void i8259_init(void);
-int i8259_irq(int);
+void i8259_init(long);
+int i8259_irq(void);
+int i8259_poll(void);
 
 #endif /* _PPC_KERNEL_i8259_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/idle.c linux/arch/ppc/kernel/idle.c
--- linux.orig/arch/ppc/kernel/idle.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/idle.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.idle.c 1.16 10/16/01 15:58:42 trini
+ * BK Id: SCCS/s.idle.c 1.18 12/01/01 20:09:06 benh
  */
 /*
  * Idle daemon for PowerPC.  Idle daemon will handle any action
@@ -58,7 +58,6 @@
 	init_idle();
 	for (;;) {
 #ifdef CONFIG_SMP
-
 		if (!do_power_save) {
 			/*
 			 * Deal with another CPU just having chosen a thread to
@@ -231,6 +230,13 @@
 void power_save(void)
 {
 	unsigned long hid0;
+	int nap = powersave_nap;
+	
+	/* 7450 has no DOZE mode mode, we return if powersave_nap
+	 * isn't enabled
+	 */
+	if (!nap &&  cur_cpu_spec[smp_processor_id()]->cpu_features & CPU_FTR_SPEC7450)
+		return;
 	/*
 	 * Disable interrupts to prevent a lost wakeup
 	 * when going to sleep.  This is necessary even with
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- linux.orig/arch/ppc/kernel/irq.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/irq.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.irq.c 1.32 08/24/01 20:07:37 paulus
+ * BK Id: SCCS/s.irq.c 1.34 12/01/01 20:09:06 benh
  */
 /*
  *  arch/ppc/kernel/irq.c
@@ -537,21 +537,29 @@
 int do_IRQ(struct pt_regs *regs)
 {
 	int cpu = smp_processor_id();
-	int irq;
-        hardirq_enter(cpu);
+	int irq, first = 1;
+        hardirq_enter( cpu );
 
-	/* every arch is required to have a get_irq -- Cort */
-	irq = ppc_md.get_irq(regs);
+	for (;;) {
+		/*
+		 * Every arch is required to implement ppc_md.get_irq.
+		 * This function will either return an irq number or -1 to
+		 * indicate there are no more pending.  But the first time
+		 * through the loop this means there wasn't and IRQ pending.
+		 * The value -2 is for buggy hardware and means that this IRQ
+		 * has already been handled. -- Tom
+		 */
+		irq = ppc_md.get_irq( regs );
 
-	if (irq >= 0) {
-		ppc_irq_dispatch_handler( regs, irq );
-	} else if (irq != -2) {
-		/* -2 means ignore, already handled */
-		if (ppc_spurious_interrupts < 10)
-			printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n",
-			       irq, regs->nip);
-		/* That's not SMP safe ... but who cares ? */
-		ppc_spurious_interrupts++;
+		if (irq >= 0)
+			ppc_irq_dispatch_handler( regs, irq );
+		else {
+			if (irq != -2 && first)
+				/* That's not SMP safe ... but who cares ? */
+				ppc_spurious_interrupts++;
+			break;
+		}
+		first = 0;
 	}
         hardirq_exit( cpu );
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/l2cr.S linux/arch/ppc/kernel/l2cr.S
--- linux.orig/arch/ppc/kernel/l2cr.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/l2cr.S	Wed Dec 26 16:28:34 2001
@@ -30,6 +30,12 @@
 	***********
 	Thu, July 13, 2000.
 	- Terry: Added isync to correct for an errata.
+
+	22 August 2001.
+	- DanM: Finally added the 7450 patch I've had for the past
+		several months.  The L2CR is similar, but I'm going
+		to assume the user of this functions knows what they
+		are doing.
 	
 	Author:	Terry Greeniaus (tgree@phys.ualberta.ca)
 	Please e-mail updates to this file to me, thanks!
@@ -71,6 +77,15 @@
 	features, such as L2DO which caches only data, or L2TS which
 	causes cache pushes from the L1 cache to go to the L2 cache
 	instead of to main memory.
+
+IMPORTANT:
+	Starting with the 7450, the bits in this register have moved
+	or behave differently.  The Enable, Parity Enable, Size,
+	and L2 Invalidate are the only bits that have not moved.
+	The size is read-only for these processors with internal L2
+	cache, and the invalidate is a control as well as status.
+		-- Dan
+
 */
 /*
  * Summary: this procedure ignores the L2I bit in the value passed in,
@@ -115,6 +130,8 @@
 	 /**** Might be a good idea to set L2DO here - to prevent instructions
 	       from getting into the cache.  But since we invalidate
 	       the next time we enable the cache it doesn't really matter.
+	       Don't do this unless you accomodate all processor variations.
+	       The bit moved on the 7450.....
 	  ****/
 
 	lis	r4,0x0002
@@ -159,12 +176,21 @@
 	sync
 	isync				/* For errata */
 
+BEGIN_FTR_SECTION
+	/* On the 7450, we wait for the L2I bit to clear......
+	*/
+10:	mfspr	r3,L2CR
+	andis.	r4,r3,0x0020
+	bne	10b
+	b	11f
+END_FTR_SECTION_IFSET(CPU_FTR_SPEC7450)
+
 	/* Wait for the invalidation to complete */
 3:	mfspr	r3,L2CR
 	rlwinm.	r4,r3,0,31,31
 	bne	3b
 	
-	rlwinm	r3,r3,0,11,9		/* Turn off the L2I bit */
+11:	rlwinm	r3,r3,0,11,9		/* Turn off the L2I bit */
 	sync
 	mtspr	L2CR,r3
 	sync
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/misc.S linux/arch/ppc/kernel/misc.S
--- linux.orig/arch/ppc/kernel/misc.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/misc.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.misc.S 1.34 11/23/01 16:38:29 paulus
+ * BK Id: SCCS/s.misc.S 1.36 12/01/01 20:09:06 benh
  */
 /*
  * This file contains miscellaneous low-level functions.
@@ -128,6 +128,9 @@
 /*
  * call_setup_cpu - call the setup_cpu function for this cpu
  * r3 = data offset, r24 = cpu number
+ *
+ * Don't change register layout, the setup function may rely
+ * on r5 containing a relocated pointer to the current cpu spec.
  */
 _GLOBAL(call_setup_cpu)
 	addis	r5,r3,cur_cpu_spec@ha
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/open_pic.c linux/arch/ppc/kernel/open_pic.c
--- linux.orig/arch/ppc/kernel/open_pic.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/open_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.open_pic.c 1.31 10/11/01 12:09:11 trini
+ * BK Id: SCCS/s.open_pic.c 1.33 12/19/01 09:45:54 trini
  */
 /*
  *  arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
@@ -28,6 +28,7 @@
 #include "local_irq.h"
 #include "open_pic.h"
 #include "open_pic_defs.h"
+#include "i8259.h"
 
 void* OpenPIC_Addr;
 static volatile struct OpenPIC *OpenPIC = NULL;
@@ -784,9 +785,6 @@
 /*
  * Clean up needed. -VAL
  */
-#ifndef CONFIG_GEMINI
-	extern int i8259_irq(int cpu);
-#endif
 	int irq = openpic_irq();
 
 	/* Management of the cascade should be moved out of here */
@@ -801,7 +799,7 @@
 			irq = *chrp_int_ack_special;
 #ifndef CONFIG_GEMINI
 		else
-			irq = i8259_irq( smp_processor_id() );
+			irq = i8259_poll();
 #endif
 		openpic_eoi();
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- linux.orig/arch/ppc/kernel/pci.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pci.c	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pci.c 1.35 11/13/01 08:19:57 trini
+ * BK Id: SCCS/s.pci.c 1.40 01/25/02 15:15:24 benh
  */
 /*
  * Common pmac/prep/chrp pci routines. -- Cort
@@ -19,6 +19,7 @@
 #include <asm/processor.h>
 #include <asm/io.h>
 #include <asm/prom.h>
+#include <asm/sections.h>
 #include <asm/pci-bridge.h>
 #include <asm/residual.h>
 #include <asm/byteorder.h>
@@ -66,7 +67,7 @@
 	{ PCI_FIXUP_HEADER,	PCI_ANY_ID,		PCI_ANY_ID,			pcibios_fixup_resources },
 #ifdef CONFIG_ALL_PPC
 	/* We should add per-machine fixup support in xxx_setup.c or xxx_pci.c */
-	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_TI,	PCI_DEVICE_ID_TI_1211,		pcibios_fixup_cardbus }, 
+	{ PCI_FIXUP_FINAL,	PCI_VENDOR_ID_TI,	PCI_ANY_ID,			pcibios_fixup_cardbus }, 
 #endif /* CONFIG_ALL_PPC */
  	{ 0 }
 };
@@ -175,18 +176,25 @@
 static void
 pcibios_fixup_cardbus(struct pci_dev* dev)
 {
+	if (_machine != _MACH_Pmac)
+		return;
 	/*
 	 * Fix the interrupt routing on the TI1211 chip on the 1999
 	 * G3 powerbook, which doesn't get initialized properly by OF.
+	 * Same problem with the 1410 of the new titanium pbook which
+	 * has the same register.
 	 */
 	if (dev->vendor == PCI_VENDOR_ID_TI
-	    && dev->device == PCI_DEVICE_ID_TI_1211) {
-		u32 val;
+	    && (dev->device == PCI_DEVICE_ID_TI_1211 ||
+	        dev->device == PCI_DEVICE_ID_TI_1410)) {
+		u8 val;
 		/* 0x8c == TI122X_IRQMUX, 2 says to route the INTA
 		   signal out the MFUNC0 pin */
-		if (pci_read_config_dword(dev, 0x8c, &val) == 0
-		    && val == 0)
-			pci_write_config_dword(dev, 0x8c, 2);
+		if (pci_read_config_byte(dev, 0x8c, &val) == 0)
+			pci_write_config_byte(dev, 0x8c, (val & ~0x0f) | 2);
+		/* Disable ISA interrupt mode */	
+		if (pci_read_config_byte(dev, 0x92, &val) == 0)
+			pci_write_config_byte(dev, 0x92, val & ~0x06);
 	}
 }
 #endif /* CONFIG_ALL_PPC */
@@ -276,9 +284,17 @@
 			if (bus->parent == NULL)
 				pr = (res->flags & IORESOURCE_IO)?
 					&ioport_resource: &iomem_resource;
-			else
+			else {
 				pr = pci_find_parent_resource(bus->self, res);
-
+				if (pr == res) {
+					/* this happens when the generic PCI
+					 * code (wrongly) decides that this
+					 * bridge is transparent  -- paulus
+					 */
+					continue;
+				}
+			}
+			
 			if (pr && request_resource(pr, res) == 0)
 				continue;
 			printk(KERN_ERR "PCI: Cannot allocate resource region "
@@ -438,7 +454,7 @@
 /*
  * Functions below are used on OpenFirmware machines.
  */
-static void
+static void __openfirmware
 make_one_node_map(struct device_node* node, u8 pci_bus)
 {
 	int *bus_range;
@@ -472,7 +488,7 @@
 	}
 }
 		
-void
+void __openfirmware
 pcibios_make_OF_bus_map(void)
 {
 	int i;
@@ -512,17 +528,17 @@
 #endif	
 }
 
-static struct device_node*
-scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
+typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data);
+
+static struct device_node* __openfirmware
+scan_OF_pci_childs(struct device_node* node, pci_OF_scan_iterator filter, void* data)
 {
 	struct device_node* sub_node;
 	
 	for (; node != 0;node = node->sibling) {
-		unsigned int *class_code, *reg;
+		unsigned int *class_code;
 		
-		reg = (unsigned int *) get_property(node, "reg", 0);
-		if (reg && ((reg[0] >> 8) & 0xff) == dev_fn
-			&& ((reg[0] >> 16) & 0xff) == bus)
+		if (filter(node, data))
 			return node;
 
 		/* For PCI<->PCI bridges or CardBus bridges, we go down
@@ -535,13 +551,34 @@
 			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
 			strcmp(node->name, "multifunc-device"))
 			continue;
-		sub_node = scan_OF_childs_for_device(node->child, bus, dev_fn);
+		sub_node = scan_OF_pci_childs(node->child, filter, data);
 		if (sub_node)
 			return sub_node;
 	}
 	return NULL;
 }
 
+static int
+scan_OF_pci_childs_iterator(struct device_node* node, void* data)
+{
+	unsigned int *reg;
+	u8* fdata = (u8*)data;
+		
+	reg = (unsigned int *) get_property(node, "reg", 0);
+	if (reg && ((reg[0] >> 8) & 0xff) == fdata[1]
+		&& ((reg[0] >> 16) & 0xff) == fdata[0])
+		return 1;
+	return 0;
+}
+
+static struct device_node* __openfirmware
+scan_OF_childs_for_device(struct device_node* node, u8 bus, u8 dev_fn)
+{
+	u8 filter_data[2] = {bus, dev_fn};
+
+	return scan_OF_pci_childs(node, scan_OF_pci_childs_iterator, filter_data);
+}
+
 /* 
  * Scans the OF tree for a device node matching a PCI device
  */
@@ -598,6 +635,12 @@
 	return NULL;
 }
 
+static int __openfirmware
+find_OF_pci_device_filter(struct device_node* node, void* data)
+{
+	return ((void *)node == data);
+}
+
 /* 
  * Returns the PCI device matching a given OF node
  */
@@ -605,21 +648,40 @@
 pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
 {
 	unsigned int *reg;
-	int i;
+	struct pci_controller* hose;
+	struct pci_dev* dev;
 		
 	if (!have_of)
 		return -ENODEV;
+	/* Make sure it's really a PCI device */
+	hose = pci_find_hose_for_OF_device(node);
+	if (!hose || !hose->arch_data)
+		return -ENODEV;
+	if (!scan_OF_pci_childs(((struct device_node*)hose->arch_data)->child,
+			find_OF_pci_device_filter, (void *)node))
+		return -ENODEV;
 	reg = (unsigned int *) get_property(node, "reg", 0);
 	if (!reg)
 		return -ENODEV;
 	*bus = (reg[0] >> 16) & 0xff;
-	for (i=0; pci_to_OF_bus_map && i<pci_bus_count; i++)
-		if (pci_to_OF_bus_map[i] == *bus) {
-			*bus = i;
-			break;
-		}
 	*devfn = ((reg[0] >> 8) & 0xff);
-	return 0;
+
+	/* Ok, here we need some tweak. If we have already renumbered
+	 * all busses, we can't rely on the OF bus number any more.
+	 * the pci_to_OF_bus_map is not enough as several PCI busses
+	 * may match the same OF bus number.
+	 */
+	if (!pci_to_OF_bus_map)
+		return 0;
+	pci_for_each_dev(dev) {
+		if (pci_to_OF_bus_map[dev->bus->number] != *bus)
+			continue;
+		if (dev->devfn != *devfn)
+			continue;
+		*bus = dev->bus->number;
+		return 0;
+	}
+	return -ENODEV;
 }
 
 void __init
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_backlight.c linux/arch/ppc/kernel/pmac_backlight.c
--- linux.orig/arch/ppc/kernel/pmac_backlight.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_backlight.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_backlight.c 1.8 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_backlight.c 1.10 12/01/01 20:09:06 benh
  */
 /*
  * Miscellaneous procedures for dealing with the PowerMac hardware.
@@ -78,7 +78,7 @@
 		pmu_request(&req, NULL, 2, 0xd9, 0);
 		while (!req.complete)
 			pmu_poll();
-		backlight_level = req.reply[1] >> 4;
+		backlight_level = req.reply[0] >> 4;
 	}
 #endif
 	if (!backlighter->set_enable(1, backlight_level, data))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_feature.c linux/arch/ppc/kernel/pmac_feature.c
--- linux.orig/arch/ppc/kernel/pmac_feature.c	Thu Jan  1 00:00:00 1970
+++ linux/arch/ppc/kernel/pmac_feature.c	Mon Feb  4 18:47:24 2002
@@ -0,0 +1,2122 @@
+/*
+ * BK Id: %F% %I% %G% %U% %#%
+ */
+/*
+ *  arch/ppc/kernel/pmac_feature.c
+ *
+ *  Copyright (C) 1996-2001 Paul Mackerras (paulus@cs.anu.edu.au)
+ *                          Ben. Herrenschmidt (benh@kernel.crashing.org)
+ *
+ *  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.
+ *  
+ *  TODO: 
+ *  
+ *   - Replace mdelay with some schedule loop if possible
+ *   - Shorten some obfuscated delays on some routines (like modem
+ *     power)
+ *
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <asm/sections.h>
+#include <asm/errno.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
+#include <asm/uninorth.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/dbdma.h>
+
+#undef DEBUG_FEATURE
+
+#ifdef DEBUG_FEATURE
+#define DBG(fmt,...) printk(KERN_DEBUG fmt)
+#else
+#define DBG(fmt,...)
+#endif
+
+/* Exported from arch/ppc/kernel/idle.c */
+extern unsigned long powersave_nap;
+
+/*
+ * We use a single global lock to protect accesses. Each driver has
+ * to take care of it's own locking
+ */
+static spinlock_t feature_lock  __pmacdata = SPIN_LOCK_UNLOCKED;
+
+#define LOCK(flags)	spin_lock_irqsave(&feature_lock, flags);
+#define UNLOCK(flags)	spin_unlock_irqrestore(&feature_lock, flags);
+
+/*
+ * Helper functions regarding the various flavors of mac-io
+ */
+ 
+#define MAX_MACIO_CHIPS		2
+
+enum {
+	macio_unknown = 0,
+	macio_grand_central,
+	macio_ohare,
+	macio_ohareII,
+	macio_heathrow,
+	macio_gatwick,
+	macio_paddington,
+	macio_keylargo,
+	macio_pangea
+};
+
+static const char* macio_names[] __pmacdata = 
+{
+	"Unknown",
+	"Grand Central",
+	"OHare",
+	"OHareII",
+	"Heathrow",
+	"Gatwick",
+	"Paddington",
+	"Keylargo",
+	"Pangea"
+};
+
+static struct macio_chip
+{
+	struct device_node*	of_node;
+	int			type;
+	int			rev;
+	volatile u32*		base;
+	unsigned long		flags;
+} macio_chips[MAX_MACIO_CHIPS]  __pmacdata;
+
+#define MACIO_FLAG_SCCA_ON	0x00000001
+#define MACIO_FLAG_SCCB_ON	0x00000002
+#define MACIO_FLAG_SCC_LOCKED	0x00000004
+#define MACIO_FLAG_AIRPORT_ON	0x00000010
+#define MACIO_FLAG_FW_SUPPORTED	0x00000020
+
+static struct macio_chip* __pmac
+macio_find(struct device_node* child, int type)
+{
+	while(child) {
+		int	i;
+	
+		for (i=0; i < MAX_MACIO_CHIPS && macio_chips[i].of_node; i++)
+			if (child == macio_chips[i].of_node &&
+			    (!type || macio_chips[i].type == type))
+				return &macio_chips[i];
+		child = child->parent;
+	}
+	return NULL;
+}
+
+#define MACIO_FCR32(macio, r)	((macio)->base + ((r) >> 2))
+#define MACIO_FCR8(macio, r)	(((volatile u8*)((macio)->base)) + (r))
+
+#define MACIO_IN32(r)		(in_le32(MACIO_FCR32(macio,r)))
+#define MACIO_OUT32(r,v)	(out_le32(MACIO_FCR32(macio,r), (v)))
+#define MACIO_BIS(r,v)		(MACIO_OUT32((r), MACIO_IN32(r) | (v)))
+#define MACIO_BIC(r,v)		(MACIO_OUT32((r), MACIO_IN32(r) & ~(v)))
+#define MACIO_IN8(r)		(in_8(MACIO_FCR8(macio,r)))
+#define MACIO_OUT8(r,v)		(out_8(MACIO_FCR8(macio,r), (v)))
+
+/*
+ * Uninorth reg. access. Note that Uni-N regs are big endian
+ */
+ 
+#define UN_REG(r)	(uninorth_base + ((r) >> 2))
+#define UN_IN(r)	(in_be32(UN_REG(r)))
+#define UN_OUT(r,v)	(out_be32(UN_REG(r), (v)))
+#define UN_BIS(r,v)	(UN_OUT((r), UN_IN(r) | (v)))
+#define UN_BIC(r,v)	(UN_OUT((r), UN_IN(r) & ~(v)))
+
+static struct device_node* uninorth_node __pmacdata;
+static u32* uninorth_base __pmacdata;
+static u32 uninorth_rev __pmacdata;
+
+
+/*
+ * For each motherboard family, we have a table of functions pointers
+ * that handle the various features.
+ */
+
+typedef int (*feature_call)(struct device_node* node, int param, int value);
+
+struct feature_table_entry {
+	unsigned int	selector;	
+	feature_call	function;
+};
+
+struct pmac_mb_def
+{
+	const char*			model_string;
+	const char*			model_name;
+	int				model_id;
+	struct feature_table_entry* 	features;
+	unsigned long			board_flags;
+};
+static struct pmac_mb_def pmac_mb __pmacdata;
+
+/*
+ * Here are the chip specific feature functions
+ */
+
+static inline int __pmac
+simple_feature_tweak(struct device_node* node, int type, int reg, u32 mask, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, type);
+	if (!macio)
+		return -ENODEV;
+	LOCK(flags);
+	if (value)
+		MACIO_BIS(reg, mask);
+	else
+		MACIO_BIC(reg, mask);
+	(void)MACIO_IN32(reg);
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+generic_scc_enable(struct device_node* node, u32 enable_mask, u32 reset_mask,
+	int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		chan_mask;
+	unsigned long		fcr;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (!strcmp(node->name, "ch-a"))
+		chan_mask = MACIO_FLAG_SCCA_ON;
+	else if (!strcmp(node->name, "ch-b"))
+		chan_mask = MACIO_FLAG_SCCB_ON;
+	else
+		return -ENODEV;
+
+	if (value) {
+		LOCK(flags);
+		fcr = MACIO_IN32(OHARE_FCR);
+		/* Check if scc cell need enabling */
+		if (!(fcr & OH_SCC_ENABLE)) {
+			fcr |= enable_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+			fcr |= reset_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+			UNLOCK(flags);
+			(void)MACIO_IN32(OHARE_FCR);
+			mdelay(15);
+			LOCK(flags);
+			fcr &= ~reset_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+		}
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr |= OH_SCCA_IO;
+		if (chan_mask & MACIO_FLAG_SCCB_ON)
+			fcr |= OH_SCCB_IO;
+		MACIO_OUT32(OHARE_FCR, fcr);
+		macio->flags |= chan_mask;
+		UNLOCK(flags);
+		if (param & PMAC_SCC_FLAG_XMON)
+			macio->flags |= MACIO_FLAG_SCC_LOCKED;
+	} else {
+		if (macio->flags & MACIO_FLAG_SCC_LOCKED)
+			return -EPERM;
+		LOCK(flags);
+		fcr = MACIO_IN32(OHARE_FCR);
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr &= ~OH_SCCA_IO;
+		if (chan_mask & MACIO_FLAG_SCCB_ON)
+			fcr &= ~OH_SCCB_IO;
+		MACIO_OUT32(OHARE_FCR, fcr);
+		if ((fcr & (OH_SCCA_IO | OH_SCCB_IO)) == 0) {
+			fcr &= ~enable_mask;
+			MACIO_OUT32(OHARE_FCR, fcr);
+		}
+		macio->flags &= ~(chan_mask);
+		UNLOCK(flags);
+		mdelay(10);
+	}
+	return 0;
+}
+
+static int __pmac
+ohare_scc_enable(struct device_node* node, int param, int value)
+{
+	int rc;
+
+#ifdef CONFIG_ADB_PMU
+	if (value && (param & 0xfff) == PMAC_SCC_IRDA)
+		pmu_enable_irled(1);
+#endif /* CONFIG_ADB_PMU */		
+	rc = generic_scc_enable(node, OH_SCC_ENABLE, OH_SCC_RESET, param, value);
+#ifdef CONFIG_ADB_PMU
+	if ((param & 0xfff) == PMAC_SCC_IRDA && (rc || !value))
+		pmu_enable_irled(0);
+#endif /* CONFIG_ADB_PMU */		
+	return rc;
+}
+
+static int __pmac
+ohare_floppy_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_ohare,
+		OHARE_FCR, OH_FLOPPY_ENABLE, value);
+}
+
+static int __pmac
+ohare_mesh_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_ohare,
+		OHARE_FCR, OH_MESH_ENABLE, value);
+}
+
+static int __pmac
+ohare_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+	    	/* For some reason, setting the bit in set_initial_features()
+	    	 * doesn't stick. I'm still investigating... --BenH.
+	    	 */
+	    	if (value)
+	    		simple_feature_tweak(node, macio_ohare,
+				OHARE_FCR, OH_IOBUS_ENABLE, 1);
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_BAY_IDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+ohare_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_ohare,
+			OHARE_FCR, OH_IDE1_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+ohare_sleep_state(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio = &macio_chips[0];
+
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value) {
+		MACIO_BIC(OHARE_FCR, OH_IOBUS_ENABLE);
+	} else {
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	}
+	
+	return 0;
+}
+
+static int __pmac
+heathrow_scc_enable(struct device_node* node, int param, int value)
+{
+	int rc;
+	
+#ifdef CONFIG_ADB_PMU
+	if (value && param == PMAC_SCC_IRDA)
+		pmu_enable_irled(1);
+#endif /* CONFIG_ADB_PMU */		
+	/* Fixme: It's possible that wallstreet (heathrow) is different
+	 * than other paddington machines. I still have to figure that
+	 * out exactly, for now, the paddington values are used
+	 */
+	rc = generic_scc_enable(node, HRW_SCC_ENABLE, PADD_RESET_SCC, param, value);
+#ifdef CONFIG_ADB_PMU
+	if (param == PMAC_SCC_IRDA && (rc || !value))
+		pmu_enable_irled(0);
+#endif /* CONFIG_ADB_PMU */		
+	return rc;
+}
+
+static int __pmac
+heathrow_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, macio_unknown);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1;
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+	if (pmac_mb.model_id != PMAC_TYPE_YOSEMITE &&
+	    pmac_mb.model_id != PMAC_TYPE_YIKES) {
+	    	LOCK(flags);
+	    	/* We use the paddington values as they seem to work properly
+	    	 * on the wallstreet (heathrow) as well. I can't tell why we
+	    	 * had to flip them on older feature.c, the fact is that new
+	    	 * code uses the paddington values which are also the ones used
+	    	 * in Darwin, and that works on wallstreet !
+	    	 */
+	    	if (value)
+	    		MACIO_BIC(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	    	else
+	    		MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	    	UNLOCK(flags);
+	    	(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(250);
+	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(HRW_GPIO_MODEM_RESET, gpio | 1);
+		(void)MACIO_IN8(HRW_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+heathrow_floppy_enable(struct device_node* node, int param, int value)
+{
+	return simple_feature_tweak(node, macio_unknown,
+		HEATHROW_FCR,
+		HRW_SWIM_ENABLE|HRW_BAY_FLOPPY_ENABLE,
+		value);
+}
+
+static int __pmac
+heathrow_mesh_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, macio_unknown);
+	if (!macio)
+		return -ENODEV;
+	LOCK(flags);
+	/* Set clear mesh cell enable */
+	if (value)
+		MACIO_BIS(HEATHROW_FCR, HRW_MESH_ENABLE);
+	else
+		MACIO_BIC(HEATHROW_FCR, HRW_MESH_ENABLE);
+	(void)MACIO_IN32(HEATHROW_FCR);
+	udelay(10);
+	/* Set/Clear termination power (todo: test ! the bit value
+	 * used by Darwin doesn't seem to match what we used so
+	 * far. If you experience problems, turn #if 1 into #if 0
+	 * and tell me about it --BenH.
+	 */
+#if 1
+	if (value)
+		MACIO_BIC(HEATHROW_MBCR, 0x00000004);
+	else
+		MACIO_BIS(HEATHROW_MBCR, 0x00000004);
+#else
+	if (value)
+		MACIO_BIC(HEATHROW_MBCR, 0x00040000);
+	else
+		MACIO_BIS(HEATHROW_MBCR, 0x00040000);
+#endif		
+	(void)MACIO_IN32(HEATHROW_MBCR);
+	udelay(10);
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+heathrow_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_BAY_IDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+heathrow_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			HEATHROW_FCR, HRW_IDE1_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+heathrow_bmac_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (value) {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
+		MACIO_BIS(HEATHROW_FCR, HRW_BMAC_RESET);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(10);
+		LOCK(flags);
+		MACIO_BIC(HEATHROW_FCR, HRW_BMAC_RESET);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+		mdelay(10);
+	} else {
+		LOCK(flags);
+		MACIO_BIC(HEATHROW_FCR, HRW_BMAC_IO_ENABLE);
+		UNLOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+heathrow_sound_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+
+	/* B&W G3 and Yikes don't support that properly (the
+	 * sound appear to never come back after beeing shut down).
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_YOSEMITE ||
+	    pmac_mb.model_id == PMAC_TYPE_YIKES)
+		return 0;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (value) {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		UNLOCK(flags);
+		(void)MACIO_IN32(HEATHROW_FCR);
+	} else {
+		LOCK(flags);
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		UNLOCK(flags);
+	}
+	return 0;
+}
+
+static u32 save_fcr[5] __pmacdata;
+static u32 save_mbcr __pmacdata;
+static u32 save_gpio_levels[2] __pmacdata;
+static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT] __pmacdata;
+static u8 save_gpio_normal[KEYLARGO_GPIO_CNT] __pmacdata;
+static u32 save_unin_clock_ctl __pmacdata;
+static struct dbdma_regs save_dbdma[13] __pmacdata;
+static struct dbdma_regs save_alt_dbdma[13] __pmacdata;
+
+static void __pmac
+dbdma_save(struct macio_chip* macio, struct dbdma_regs* save)
+{
+	int i;
+	
+	/* Save state & config of DBDMA channels */
+	for (i=0; i<13; i++) {
+		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+			(macio->base + ((0x8000+i*0x100)>>2));
+		save[i].cmdptr_hi = in_le32(&chan->cmdptr_hi);
+		save[i].cmdptr = in_le32(&chan->cmdptr);
+		save[i].intr_sel = in_le32(&chan->intr_sel);
+		save[i].br_sel = in_le32(&chan->br_sel);
+		save[i].wait_sel = in_le32(&chan->wait_sel);
+	}
+}
+
+static void __pmac
+dbdma_restore(struct macio_chip* macio, struct dbdma_regs* save)
+{
+	int i;
+	
+	/* Save state & config of DBDMA channels */
+	for (i=0; i<13; i++) {
+		volatile struct dbdma_regs* chan = (volatile struct dbdma_regs*)
+			(macio->base + ((0x8000+i*0x100)>>2));
+		out_le32(&chan->control, (ACTIVE|DEAD|WAKE|FLUSH|PAUSE|RUN)<<16);
+		while (in_le32(&chan->status) & ACTIVE)
+			mb();
+		out_le32(&chan->cmdptr_hi, save[i].cmdptr_hi);
+		out_le32(&chan->cmdptr, save[i].cmdptr);
+		out_le32(&chan->intr_sel, save[i].intr_sel);
+		out_le32(&chan->br_sel, save[i].br_sel);
+		out_le32(&chan->wait_sel, save[i].wait_sel);
+	}
+}
+
+static void __pmac
+heathrow_sleep(struct macio_chip* macio, int secondary)
+{
+	if (secondary) {
+		dbdma_save(macio, save_alt_dbdma);
+		save_fcr[2] = MACIO_IN32(0x38);
+		save_fcr[3] = MACIO_IN32(0x3c);
+	} else {
+		dbdma_save(macio, save_dbdma);
+		save_fcr[0] = MACIO_IN32(0x38);
+		save_fcr[1] = MACIO_IN32(0x3c);
+		save_mbcr = MACIO_IN32(0x34);
+		/* Make sure sound is shut down */
+		MACIO_BIS(HEATHROW_FCR, HRW_SOUND_POWER_N);
+		MACIO_BIC(HEATHROW_FCR, HRW_SOUND_CLK_ENABLE);
+		/* This seems to be necessary as well or the fan
+		 * keeps coming up and battery drains fast */
+		MACIO_BIC(HEATHROW_FCR, HRW_IOBUS_ENABLE);
+	}
+	/* Make sure modem is shut down */
+	MACIO_OUT8(HRW_GPIO_MODEM_RESET,
+		MACIO_IN8(HRW_GPIO_MODEM_RESET) & ~1);
+	MACIO_BIS(HEATHROW_FCR, PADD_MODEM_POWER_N);
+	MACIO_BIC(HEATHROW_FCR, OH_SCCA_IO|OH_SCCB_IO|HRW_SCC_ENABLE);
+
+	/* Let things settle */
+	(void)MACIO_IN32(HEATHROW_FCR);
+	mdelay(1);
+}
+
+static void __pmac
+heathrow_wakeup(struct macio_chip* macio, int secondary)
+{
+	if (secondary) {
+		MACIO_OUT32(0x38, save_fcr[2]);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x3c, save_fcr[3]);
+		(void)MACIO_IN32(0x38);
+		mdelay(10);
+		dbdma_restore(macio, save_alt_dbdma);
+	} else {
+		MACIO_OUT32(0x38, save_fcr[0] | HRW_IOBUS_ENABLE);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x3c, save_fcr[1]);
+		(void)MACIO_IN32(0x38);
+		mdelay(1);
+		MACIO_OUT32(0x34, save_mbcr);
+		(void)MACIO_IN32(0x38);
+		mdelay(10);
+		dbdma_restore(macio, save_dbdma);
+	}
+}
+
+static int __pmac
+heathrow_sleep_state(struct device_node* node, int param, int value)
+{
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value == 1) {
+		if (macio_chips[1].type == macio_gatwick)
+			heathrow_sleep(&macio_chips[0], 1);
+		heathrow_sleep(&macio_chips[0], 0);
+	} else if (value == 0) {
+		heathrow_wakeup(&macio_chips[0], 0);
+		if (macio_chips[1].type == macio_gatwick)
+			heathrow_wakeup(&macio_chips[0], 1);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_scc_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	unsigned long		chan_mask;
+	u32			fcr;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	if (!strcmp(node->name, "ch-a"))
+		chan_mask = MACIO_FLAG_SCCA_ON;
+	else if (!strcmp(node->name, "ch-b"))
+		chan_mask = MACIO_FLAG_SCCB_ON;
+	else
+		return -ENODEV;
+
+	if (value) {
+		int need_reset_scc = 0;
+		int need_reset_irda = 0;
+		
+		LOCK(flags);
+		fcr = MACIO_IN32(KEYLARGO_FCR0);
+		/* Check if scc cell need enabling */
+		if (!(fcr & KL0_SCC_CELL_ENABLE)) {
+			fcr |= KL0_SCC_CELL_ENABLE;
+			need_reset_scc = 1;
+		}
+		if (chan_mask & MACIO_FLAG_SCCA_ON) {
+			fcr |= KL0_SCCA_ENABLE;
+			/* Don't enable line drivers for I2S modem */
+			if ((param & 0xfff) == PMAC_SCC_I2S1)
+				fcr &= ~KL0_SCC_A_INTF_ENABLE;
+			else
+				fcr |= KL0_SCC_A_INTF_ENABLE;
+		}
+		if (chan_mask & MACIO_FLAG_SCCB_ON) {
+			fcr |= KL0_SCCB_ENABLE;
+			/* Perform irda specific inits */
+			if ((param & 0xfff) == PMAC_SCC_IRDA) {
+				fcr &= ~KL0_SCC_B_INTF_ENABLE;
+				fcr |= KL0_IRDA_ENABLE;
+				fcr |= KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE;
+				fcr |= KL0_IRDA_SOURCE1_SEL;
+				fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
+				fcr &= ~(KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
+				need_reset_irda = 1;
+			} else
+				fcr |= KL0_SCC_B_INTF_ENABLE;
+		}
+		MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		macio->flags |= chan_mask;
+		if (need_reset_scc)  {
+			MACIO_BIS(KEYLARGO_FCR0, KL0_SCC_RESET);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(15);
+			LOCK(flags);
+			MACIO_BIC(KEYLARGO_FCR0, KL0_SCC_RESET);
+		}
+		if (need_reset_irda)  {
+			MACIO_BIS(KEYLARGO_FCR0, KL0_IRDA_RESET);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(15);
+			LOCK(flags);
+			MACIO_BIC(KEYLARGO_FCR0, KL0_IRDA_RESET);
+		}
+		UNLOCK(flags);
+		if (param & PMAC_SCC_FLAG_XMON)
+			macio->flags |= MACIO_FLAG_SCC_LOCKED;
+	} else {
+		if (macio->flags & MACIO_FLAG_SCC_LOCKED)
+			return -EPERM;
+		LOCK(flags);
+		fcr = MACIO_IN32(KEYLARGO_FCR0);
+		if (chan_mask & MACIO_FLAG_SCCA_ON)
+			fcr &= ~KL0_SCCA_ENABLE;
+		if (chan_mask & MACIO_FLAG_SCCB_ON) {
+			fcr &= ~KL0_SCCB_ENABLE;
+			/* Perform irda specific clears */
+			if ((param & 0xfff) == PMAC_SCC_IRDA) {
+				fcr &= ~KL0_IRDA_ENABLE;
+				fcr &= ~(KL0_IRDA_CLK32_ENABLE | KL0_IRDA_CLK19_ENABLE);
+				fcr &= ~(KL0_IRDA_FAST_CONNECT|KL0_IRDA_DEFAULT1|KL0_IRDA_DEFAULT0);
+				fcr &= ~(KL0_IRDA_SOURCE1_SEL|KL0_IRDA_SOURCE2_SEL|KL0_IRDA_HIGH_BAND);
+			}
+		}
+		MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		if ((fcr & (KL0_SCCA_ENABLE | KL0_SCCB_ENABLE)) == 0) {
+			fcr &= ~KL0_SCC_CELL_ENABLE;
+			MACIO_OUT32(KEYLARGO_FCR0, fcr);
+		}
+		macio->flags &= ~(chan_mask);
+		UNLOCK(flags);
+		mdelay(10);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
+	gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
+	gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
+	
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+    	LOCK(flags);
+    	if (value) {
+    		MACIO_BIC(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
+	    	UNLOCK(flags);
+	    	(void)MACIO_IN32(KEYLARGO_FCR2);
+		mdelay(250);
+    	} else {
+    		MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
+	    	UNLOCK(flags);
+    	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_ide_enable(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE0_ENABLE, value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE1_ENABLE, value);
+	    case 2:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_UIDE_ENABLE, value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+core99_ide_reset(struct device_node* node, int param, int value)
+{
+	switch(param) {
+	    case 0:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE0_RESET_N, !value);
+	    case 1:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_EIDE1_RESET_N, !value);
+	    case 2:
+		return simple_feature_tweak(node, macio_unknown,
+			KEYLARGO_FCR1, KL1_UIDE_RESET_N, !value);
+	    default:
+	    	return -ENODEV;
+	}
+}
+
+static int __pmac
+core99_gmac_enable(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+
+	LOCK(flags);
+	if (value)
+		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
+	else
+		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_GMAC);
+	(void)UN_IN(UNI_N_CLOCK_CNTL);
+	UNLOCK(flags);
+	udelay(20);
+
+	return 0;
+}
+
+static int __pmac
+core99_gmac_phy_reset(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+
+	LOCK(flags);
+	MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE);
+	(void)MACIO_IN8(KL_GPIO_ETH_PHY_RESET);
+	UNLOCK(flags);
+	mdelay(10);
+	LOCK(flags);
+	MACIO_OUT8(KL_GPIO_ETH_PHY_RESET, KEYLARGO_GPIO_OUTPUT_ENABLE
+		| KEYLARGO_GPIO_OUTOUT_DATA);
+	UNLOCK(flags);
+	mdelay(10);
+
+	return 0;
+}
+
+static int __pmac
+core99_sound_chip_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+
+	/* Do a better probe code, screamer G4 desktops &
+	 * iMacs can do that too, add a recalibrate  in
+	 * the driver as well
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_PISMO ||
+	    pmac_mb.model_id == PMAC_TYPE_TITANIUM) {
+		LOCK(flags);
+		if (value)
+	    		MACIO_OUT8(KL_GPIO_SOUND_POWER,
+	    			KEYLARGO_GPIO_OUTPUT_ENABLE |
+	    			KEYLARGO_GPIO_OUTOUT_DATA);
+	    	else
+	    		MACIO_OUT8(KL_GPIO_SOUND_POWER,
+	    			KEYLARGO_GPIO_OUTPUT_ENABLE);
+	    	(void)MACIO_IN8(KL_GPIO_SOUND_POWER);
+	    	UNLOCK(flags);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_airport_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	unsigned long		flags;
+	int			state;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	
+	/* Hint: we allow passing of macio itself for the sake of the
+	 * sleep code
+	 */
+	if (node != macio->of_node &&
+	    (!node->parent || node->parent != macio->of_node))
+		return -ENODEV;
+	state = (macio->flags & MACIO_FLAG_AIRPORT_ON) != 0;
+	if (value == state)
+		return 0;
+	if (value) {
+		/* This code is a reproduction of OF enable-cardslot
+		 * and init-wireless methods, slightly hacked until
+		 * I got it working.
+		 */
+		LOCK(flags);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 5);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
+		UNLOCK(flags);
+		mdelay(10);
+		LOCK(flags);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xf, 4);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xf);
+		UNLOCK(flags);
+
+		mdelay(10);
+
+		LOCK(flags);
+		MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xb, 0);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xb);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xa, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xa);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+0xd, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+0xd);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xd, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xd);
+		udelay(10);
+		MACIO_OUT8(KEYLARGO_GPIO_0+0xe, 0x28);
+		(void)MACIO_IN8(KEYLARGO_GPIO_0+0xe);
+		UNLOCK(flags);
+		udelay(10);
+		MACIO_OUT32(0x1c000, 0);
+		mdelay(1);
+		MACIO_OUT8(0x1a3e0, 0x41);
+		(void)MACIO_IN8(0x1a3e0);
+		udelay(10);
+		LOCK(flags);
+		MACIO_BIS(KEYLARGO_FCR2, KL2_CARDSEL_16);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		UNLOCK(flags);
+		mdelay(100);
+
+		macio->flags |= MACIO_FLAG_AIRPORT_ON;
+	} else {
+		LOCK(flags);
+		MACIO_BIC(KEYLARGO_FCR2, KL2_CARDSEL_16);
+		(void)MACIO_IN32(KEYLARGO_FCR2);
+		MACIO_OUT8(KL_GPIO_AIRPORT_0, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_1, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_2, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_3, 0);
+		MACIO_OUT8(KL_GPIO_AIRPORT_4, 0);
+		(void)MACIO_IN8(KL_GPIO_AIRPORT_4);
+		UNLOCK(flags);
+
+		macio->flags &= ~MACIO_FLAG_AIRPORT_ON;
+	}
+	return 0;
+}
+
+#ifdef CONFIG_SMP
+static int __pmac
+core99_reset_cpu(struct device_node* node, int param, int value)
+{
+	const int reset_lines[] = {	KL_GPIO_RESET_CPU0,
+					KL_GPIO_RESET_CPU1,
+					KL_GPIO_RESET_CPU2,
+					KL_GPIO_RESET_CPU3 };
+	int reset_io;
+	unsigned long flags;
+	struct macio_chip* macio;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (param > 3 || param < 0)
+		return -ENODEV;
+
+	reset_io = reset_lines[param];
+	
+	LOCK(flags);
+	MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE);
+	(void)MACIO_IN8(reset_io);
+	udelay(1);
+	MACIO_OUT8(reset_io, KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+	(void)MACIO_IN8(reset_io);
+	UNLOCK(flags);
+
+	return 0;
+}
+#endif /* CONFIG_SMP */
+
+static int __pmac
+core99_usb_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio;
+	unsigned long flags;
+	char* prop;
+	int number;
+	u32 reg;
+	
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	
+	prop = (char *)get_property(node, "AAPL,clock-id", NULL);
+	if (!prop)
+		return -ENODEV;
+	if (strncmp(prop, "usb0u048", strlen("usb0u048")) == 0)
+		number = 0;
+	else if (strncmp(prop, "usb1u148", strlen("usb1u148")) == 0)
+		number = 2;
+	else
+		return -ENODEV;
+
+	/* Sorry for the brute-force locking, but this is only used during
+	 * sleep and the timing seem to be critical
+	 */
+	LOCK(flags);
+	if (value) {
+		/* Turn ON */
+		if (number == 0) {
+			MACIO_BIC(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			UNLOCK(flags);
+			mdelay(1);
+			LOCK(flags);
+			MACIO_BIS(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+		} else {
+			MACIO_BIC(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+			UNLOCK(flags);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			mdelay(1);
+			LOCK(flags);
+			MACIO_BIS(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+		}
+		reg = MACIO_IN32(KEYLARGO_FCR4);
+		reg &=	~(KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) |
+			KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number));
+		reg &=	~(KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) |
+			KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1));
+		MACIO_OUT32(KEYLARGO_FCR4, reg);
+		(void)MACIO_IN32(KEYLARGO_FCR4);
+		udelay(10);
+	} else {
+		/* Turn OFF */
+		reg = MACIO_IN32(KEYLARGO_FCR4);
+		reg |=	KL4_PORT_WAKEUP_ENABLE(number) | KL4_PORT_RESUME_WAKE_EN(number) |
+			KL4_PORT_CONNECT_WAKE_EN(number) | KL4_PORT_DISCONNECT_WAKE_EN(number);
+		reg |=	KL4_PORT_WAKEUP_ENABLE(number+1) | KL4_PORT_RESUME_WAKE_EN(number+1) |
+			KL4_PORT_CONNECT_WAKE_EN(number+1) | KL4_PORT_DISCONNECT_WAKE_EN(number+1);
+		MACIO_OUT32(KEYLARGO_FCR4, reg);
+		(void)MACIO_IN32(KEYLARGO_FCR4);
+		udelay(1);
+		if (number == 0) {
+			MACIO_BIC(KEYLARGO_FCR0, KL0_USB0_CELL_ENABLE);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			udelay(1);
+			MACIO_BIS(KEYLARGO_FCR0, (KL0_USB0_PAD_SUSPEND0 | KL0_USB0_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+		} else {
+			MACIO_BIC(KEYLARGO_FCR0, KL0_USB1_CELL_ENABLE);
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+			udelay(1);
+			MACIO_BIS(KEYLARGO_FCR0, (KL0_USB1_PAD_SUSPEND0 | KL0_USB1_PAD_SUSPEND1));
+			(void)MACIO_IN32(KEYLARGO_FCR0);
+		}
+		udelay(1);
+	}
+	UNLOCK(flags);
+
+	return 0;
+}
+
+static int __pmac
+core99_firewire_enable(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
+		return -ENODEV;
+	
+	LOCK(flags);
+	if (value) {
+		UN_BIS(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
+		(void)UN_IN(UNI_N_CLOCK_CNTL);
+	} else {
+		UN_BIC(UNI_N_CLOCK_CNTL, UNI_N_CLOCK_CNTL_FW);
+		(void)UN_IN(UNI_N_CLOCK_CNTL);
+	}
+	UNLOCK(flags);
+	mdelay(1);
+
+	return 0;
+}
+
+static int __pmac
+core99_firewire_cable_power(struct device_node* node, int param, int value)
+{
+	unsigned long flags;
+	struct macio_chip* macio;
+
+	/* Trick: we allow NULL node */
+	if ((pmac_mb.board_flags & PMAC_MB_HAS_FW_POWER) == 0)
+	    	return -ENODEV;
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	if (!(macio->flags & MACIO_FLAG_FW_SUPPORTED))
+		return -ENODEV;
+	
+	LOCK(flags);
+	if (value) {
+		MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 0);
+		MACIO_IN8(KL_GPIO_FW_CABLE_POWER);
+		udelay(10);
+	} else {
+		MACIO_OUT8(KL_GPIO_FW_CABLE_POWER , 4);
+		MACIO_IN8(KL_GPIO_FW_CABLE_POWER); udelay(10);
+	}
+	UNLOCK(flags);
+	mdelay(1);
+
+	return 0;
+}
+
+static int __pmac
+core99_read_gpio(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio = &macio_chips[0];
+	
+	return MACIO_IN8(param);
+}
+
+
+static int __pmac
+core99_write_gpio(struct device_node* node, int param, int value)
+{
+	struct macio_chip* macio = &macio_chips[0];
+
+	MACIO_OUT8(param, (u8)(value & 0xff));
+	return 0;
+}
+
+static void __pmac
+keylargo_shutdown(struct macio_chip* macio, int restart)
+{
+	u32 temp;
+
+	mdelay(1);
+	MACIO_BIS(KEYLARGO_FCR0, KL0_USB_REF_SUSPEND);
+	(void)MACIO_IN32(KEYLARGO_FCR0);
+	mdelay(100);
+
+	MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+				KL0_SCC_CELL_ENABLE |
+		      		KL0_IRDA_ENABLE | KL0_IRDA_CLK32_ENABLE |
+		      		KL0_IRDA_CLK19_ENABLE);
+
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+
+	MACIO_BIC(KEYLARGO_FCR1,
+		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+		KL1_EIDE0_ENABLE | KL1_EIDE0_RESET_N |
+		KL1_EIDE1_ENABLE | KL1_EIDE1_RESET_N |
+		KL1_UIDE_ENABLE);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+
+	MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
+ 	udelay(10);
+ 	MACIO_BIC(KEYLARGO_FCR2, KL2_IOBUS_ENABLE);
+ 	udelay(10);
+	temp = MACIO_IN32(KEYLARGO_FCR3);
+	if (macio->rev >= 2)
+		temp |= (KL3_SHUTDOWN_PLL2X | KL3_SHUTDOWN_PLL_TOTAL);
+		
+	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+		KL3_SHUTDOWN_PLLKW35 | KL3_SHUTDOWN_PLLKW12;
+	temp &= ~(KL3_CLK66_ENABLE | KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+	MACIO_OUT32(KEYLARGO_FCR3, temp);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+}
+
+static void __pmac
+pangea_shutdown(struct macio_chip* macio, int restart)
+{
+	u32 temp;
+
+	MACIO_BIC(KEYLARGO_FCR0,KL0_SCCA_ENABLE | KL0_SCCB_ENABLE |
+				KL0_SCC_CELL_ENABLE |
+				KL0_USB0_CELL_ENABLE | KL0_USB1_CELL_ENABLE);
+
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_BIC(KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+
+	MACIO_BIC(KEYLARGO_FCR1,
+		KL1_AUDIO_SEL_22MCLK | KL1_AUDIO_CLK_ENABLE_BIT |
+		KL1_AUDIO_CLK_OUT_ENABLE | KL1_AUDIO_CELL_ENABLE |
+		KL1_I2S0_CELL_ENABLE | KL1_I2S0_CLK_ENABLE_BIT |
+		KL1_I2S0_ENABLE | KL1_I2S1_CELL_ENABLE |
+		KL1_I2S1_CLK_ENABLE_BIT | KL1_I2S1_ENABLE |
+		KL1_UIDE_ENABLE);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+
+	MACIO_BIS(KEYLARGO_FCR2, KL2_ALT_DATA_OUT);
+ 	udelay(10);
+	temp = MACIO_IN32(KEYLARGO_FCR3);
+	temp |= KL3_SHUTDOWN_PLLKW6 | KL3_SHUTDOWN_PLLKW4 |
+		KL3_SHUTDOWN_PLLKW35;
+	temp &= ~(KL3_CLK49_ENABLE | KL3_CLK45_ENABLE
+		| KL3_CLK31_ENABLE | KL3_TIMER_CLK18_ENABLE | KL3_I2S1_CLK18_ENABLE
+		| KL3_I2S0_CLK18_ENABLE | KL3_VIA_CLK16_ENABLE);
+	MACIO_OUT32(KEYLARGO_FCR3, temp);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+}
+
+static int __pmac
+core99_sleep(void)
+{
+	struct macio_chip* macio;
+	int i;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+	
+	/* We power off the wireless slot in case it was not done
+	 * by the driver. We don't power it on automatically however
+	 */
+	if (macio->flags & MACIO_FLAG_AIRPORT_ON)
+		core99_airport_enable(macio->of_node, 0, 0);
+
+	/* We power off the FW cable. Should be done by the driver... */
+	if (macio->flags & MACIO_FLAG_FW_SUPPORTED) {
+		core99_firewire_enable(NULL, 0, 0);
+		core99_firewire_cable_power(NULL, 0, 0);
+	}
+
+	/* We make sure int. modem is off (in case driver lost it) */
+	core99_modem_enable(macio->of_node, 0, 0);
+	/* We make sure the sound is off as well */
+	core99_sound_chip_enable(macio->of_node, 0, 0);
+	 
+	/*
+	 * Save various bits of KeyLargo
+	 */
+
+	/* Save the state of the various GPIOs */
+	save_gpio_levels[0] = MACIO_IN32(KEYLARGO_GPIO_LEVELS0);
+	save_gpio_levels[1] = MACIO_IN32(KEYLARGO_GPIO_LEVELS1);
+	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+		save_gpio_extint[i] = MACIO_IN8(KEYLARGO_GPIO_EXTINT_0+i);
+	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+		save_gpio_normal[i] = MACIO_IN8(KEYLARGO_GPIO_0+i);
+
+	/* Save the FCRs */
+	save_mbcr = MACIO_IN32(KEYLARGO_MBCR);
+	save_fcr[0] = MACIO_IN32(KEYLARGO_FCR0);
+	save_fcr[1] = MACIO_IN32(KEYLARGO_FCR1);
+	save_fcr[2] = MACIO_IN32(KEYLARGO_FCR2);
+	save_fcr[3] = MACIO_IN32(KEYLARGO_FCR3);
+	save_fcr[4] = MACIO_IN32(KEYLARGO_FCR4);
+
+	/* Save state & config of DBDMA channels */
+	dbdma_save(macio, save_dbdma);
+	
+	/*
+	 * Turn off as much as we can
+	 */
+	if (macio->type == macio_pangea)
+		pangea_shutdown(macio, 0);
+	else if (macio->type == macio_keylargo)
+		keylargo_shutdown(macio, 0);
+	
+	/* 
+	 * Put the host bridge to sleep
+	 */
+
+	save_unin_clock_ctl = UN_IN(UNI_N_CLOCK_CNTL);
+	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl &
+		~(UNI_N_CLOCK_CNTL_GMAC|UNI_N_CLOCK_CNTL_FW/*|UNI_N_CLOCK_CNTL_PCI*/));
+	udelay(100);
+	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_SLEEPING);
+	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_SLEEP);
+
+	/*
+	 * FIXME: A bit of black magic with OpenPIC (don't ask me why)
+	 */
+	if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
+		MACIO_BIS(0x506e0, 0x00400000);
+		MACIO_BIS(0x506e0, 0x80000000);
+	}
+	return 0;
+}
+
+static int __pmac
+core99_wake_up(void)
+{
+	struct macio_chip* macio;
+	int i;
+
+	macio = &macio_chips[0];
+	if (macio->type != macio_keylargo && macio->type != macio_pangea)
+		return -ENODEV;
+
+	/*
+	 * Wakeup the host bridge
+	 */
+	UN_OUT(UNI_N_POWER_MGT, UNI_N_POWER_MGT_NORMAL);
+	udelay(10);
+	UN_OUT(UNI_N_HWINIT_STATE, UNI_N_HWINIT_STATE_RUNNING);
+	udelay(10);
+	
+	/*
+	 * Restore KeyLargo
+	 */
+	 
+	MACIO_OUT32(KEYLARGO_MBCR, save_mbcr);
+	(void)MACIO_IN32(KEYLARGO_MBCR); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR0, save_fcr[0]);
+	(void)MACIO_IN32(KEYLARGO_FCR0); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR1, save_fcr[1]);
+	(void)MACIO_IN32(KEYLARGO_FCR1); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR2, save_fcr[2]);
+	(void)MACIO_IN32(KEYLARGO_FCR2); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR3, save_fcr[3]);
+	(void)MACIO_IN32(KEYLARGO_FCR3); udelay(10);
+	MACIO_OUT32(KEYLARGO_FCR4, save_fcr[4]);
+	(void)MACIO_IN32(KEYLARGO_FCR4); udelay(10);
+
+	dbdma_restore(macio, save_dbdma);
+
+	MACIO_OUT32(KEYLARGO_GPIO_LEVELS0, save_gpio_levels[0]);
+	MACIO_OUT32(KEYLARGO_GPIO_LEVELS1, save_gpio_levels[1]);
+	for (i=0; i<KEYLARGO_GPIO_EXTINT_CNT; i++)
+		MACIO_OUT8(KEYLARGO_GPIO_EXTINT_0+i, save_gpio_extint[i]);
+	for (i=0; i<KEYLARGO_GPIO_CNT; i++)
+		MACIO_OUT8(KEYLARGO_GPIO_0+i, save_gpio_normal[i]);
+
+	/* FIXME more black magic with OpenPIC ... */
+	if (pmac_mb.model_id == PMAC_TYPE_SAWTOOTH) {
+		MACIO_BIC(0x506e0, 0x00400000);
+		MACIO_BIC(0x506e0, 0x80000000);
+	}
+
+	UN_OUT(UNI_N_CLOCK_CNTL, save_unin_clock_ctl);
+	udelay(100);
+
+	return 0;
+}
+
+static int __pmac
+core99_sleep_state(struct device_node* node, int param, int value)
+{
+	if ((pmac_mb.board_flags & PMAC_MB_CAN_SLEEP) == 0)
+		return -EPERM;
+	if (value == 1)
+		return core99_sleep();
+	else if (value == 0)
+		return core99_wake_up();
+	return 0;
+}
+
+static int __pmac
+pangea_modem_enable(struct device_node* node, int param, int value)
+{
+	struct macio_chip*	macio;
+	u8			gpio;
+	unsigned long		flags;
+	
+	macio = macio_find(node, 0);
+	if (!macio)
+		return -ENODEV;
+	gpio = MACIO_IN8(KL_GPIO_MODEM_RESET);
+	gpio |= KEYLARGO_GPIO_OUTPUT_ENABLE;
+	gpio &= ~KEYLARGO_GPIO_OUTOUT_DATA;
+	
+	if (!value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		UNLOCK(flags);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+		mdelay(250);
+	}
+    	LOCK(flags);
+	if (value) {
+		MACIO_OUT8(KL_GPIO_MODEM_POWER,
+			KEYLARGO_GPIO_OUTPUT_ENABLE);
+    		UNLOCK(flags);
+	    	(void)MACIO_IN32(KEYLARGO_FCR2);
+		mdelay(250);
+	} else {
+		MACIO_OUT8(KL_GPIO_MODEM_POWER,
+			KEYLARGO_GPIO_OUTPUT_ENABLE | KEYLARGO_GPIO_OUTOUT_DATA);
+    		UNLOCK(flags);
+	}
+	if (value) {
+		LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+		MACIO_OUT8(KL_GPIO_MODEM_RESET, gpio | KEYLARGO_GPIO_OUTOUT_DATA);
+		(void)MACIO_IN8(KL_GPIO_MODEM_RESET);
+	    	UNLOCK(flags); mdelay(250); LOCK(flags);
+	}
+	return 0;
+}
+
+
+static int __pmac
+generic_get_mb_info(struct device_node* node, int param, int value)
+{
+	switch(param) {
+		case PMAC_MB_INFO_MODEL:
+			return pmac_mb.model_id;
+		case PMAC_MB_INFO_FLAGS:
+			return pmac_mb.board_flags;
+		case PMAC_MB_INFO_NAME:	
+			/* hack hack hack... but should work */
+			return (int)pmac_mb.model_name;
+	}
+	return 0;
+}
+
+
+/* 
+ * Table definitions
+ */
+ 
+/* Used on any machine
+ */
+static struct feature_table_entry any_features[]  __pmacdata = {
+	{ PMAC_FTR_GET_MB_INFO,		generic_get_mb_info },
+	{ 0, NULL }
+};
+
+/* OHare based motherboards. Currently, we only use these on the
+ * 2400,3400 and 3500 series powerbooks. Some older desktops seem
+ * to have issues with turning on/off those asic cells
+ */
+static struct feature_table_entry ohare_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		ohare_scc_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	ohare_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		ohare_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		ohare_ide_enable},
+	{ PMAC_FTR_IDE_RESET,		ohare_ide_reset},
+	{ PMAC_FTR_SLEEP_STATE,		ohare_sleep_state },
+	{ 0, NULL }
+};
+
+/* Heathrow desktop machines (Beige G3).
+ * Separated as some features couldn't be properly tested
+ * and the serial port control bits appear to confuse it.
+ */
+static struct feature_table_entry heathrow_desktop_features[]  __pmacdata = {
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ 0, NULL }
+};
+
+/* Heathrow based laptop, that is the Wallstreet and mainstreet
+ * powerbooks.
+ */
+static struct feature_table_entry heathrow_laptop_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		heathrow_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	heathrow_modem_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	heathrow_sound_enable },
+	{ PMAC_FTR_SLEEP_STATE,		heathrow_sleep_state },
+	{ 0, NULL }
+};
+
+/* Paddington based machines
+ * The lombard (101) powerbook, first iMac models, B&W G3 and Yikes G4.
+ */
+static struct feature_table_entry paddington_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		heathrow_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	heathrow_modem_enable },
+	{ PMAC_FTR_SWIM3_ENABLE,	heathrow_floppy_enable },
+	{ PMAC_FTR_MESH_ENABLE,		heathrow_mesh_enable },
+	{ PMAC_FTR_IDE_ENABLE,		heathrow_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		heathrow_ide_reset },
+	{ PMAC_FTR_BMAC_ENABLE,		heathrow_bmac_enable },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	heathrow_sound_enable },
+	{ PMAC_FTR_SLEEP_STATE,		heathrow_sleep_state },
+	{ 0, NULL }
+};
+
+/* Core99 & MacRISC 2 machines (all machines released since the
+ * iBook (included), that is all AGP machines, except pangea
+ * chipset. The pangea chipset is the "combo" UniNorth/KeyLargo
+ * used on iBook2 & iMac "flow power".
+ */
+static struct feature_table_entry core99_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		core99_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	core99_modem_enable },
+	{ PMAC_FTR_IDE_ENABLE,		core99_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		core99_ide_reset },
+	{ PMAC_FTR_GMAC_ENABLE,		core99_gmac_enable },
+	{ PMAC_FTR_GMAC_PHY_RESET,	core99_gmac_phy_reset },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	core99_sound_chip_enable },
+	{ PMAC_FTR_AIRPORT_ENABLE,	core99_airport_enable },
+	{ PMAC_FTR_USB_ENABLE,		core99_usb_enable },
+	{ PMAC_FTR_1394_ENABLE,		core99_firewire_enable },
+	{ PMAC_FTR_1394_CABLE_POWER,	core99_firewire_cable_power },
+	{ PMAC_FTR_SLEEP_STATE,		core99_sleep_state },
+#ifdef CONFIG_SMP
+	{ PMAC_FTR_RESET_CPU,		core99_reset_cpu },
+#endif /* CONFIG_SMP */
+	{ PMAC_FTR_READ_GPIO,		core99_read_gpio },
+	{ PMAC_FTR_WRITE_GPIO,		core99_write_gpio },
+	{ 0, NULL }
+};
+
+/* Pangea features
+ */
+static struct feature_table_entry pangea_features[]  __pmacdata = {
+	{ PMAC_FTR_SCC_ENABLE,		core99_scc_enable },
+	{ PMAC_FTR_MODEM_ENABLE,	pangea_modem_enable },
+	{ PMAC_FTR_IDE_ENABLE,		core99_ide_enable },
+	{ PMAC_FTR_IDE_RESET,		core99_ide_reset },
+	{ PMAC_FTR_GMAC_ENABLE,		core99_gmac_enable },
+	{ PMAC_FTR_GMAC_PHY_RESET,	core99_gmac_phy_reset },
+	{ PMAC_FTR_SOUND_CHIP_ENABLE,	core99_sound_chip_enable },
+	{ PMAC_FTR_AIRPORT_ENABLE,	core99_airport_enable },
+	{ PMAC_FTR_USB_ENABLE,		core99_usb_enable },
+	{ PMAC_FTR_1394_ENABLE,		core99_firewire_enable },
+	{ PMAC_FTR_1394_CABLE_POWER,	core99_firewire_cable_power },
+	{ PMAC_FTR_SLEEP_STATE,		core99_sleep_state },
+	{ PMAC_FTR_READ_GPIO,		core99_read_gpio },
+	{ PMAC_FTR_WRITE_GPIO,		core99_write_gpio },
+	{ 0, NULL }
+};
+	
+static struct pmac_mb_def pmac_mb_defs[] __pmacdata = {
+	/* Warning: ordering is important as some models may claim
+	 * beeing compatible with several types
+	 */
+	{	"AAPL,8500",			"PowerMac 8500/8600",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,9500",			"PowerMac 9500/9600",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,7500",			"PowerMac 7500",
+		PMAC_TYPE_PSURGE,		NULL,
+		0
+	},
+	{	"AAPL,e407",			"Alchemy",
+		PMAC_TYPE_ALCHEMY,		NULL,
+		0
+	},
+	{	"AAPL,e411",			"Gazelle",
+		PMAC_TYPE_GAZELLE,		NULL,
+		0
+	},
+	{	"AAPL,3400/2400",		"PowerBook 3400",
+		PMAC_TYPE_HOOPER,		ohare_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,3500",			"PowerBook 3500",
+		PMAC_TYPE_KANGA,		ohare_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,Gossamer",		"PowerMac G3 (Gossamer)",
+		PMAC_TYPE_GOSSAMER,		heathrow_desktop_features,
+		0
+	},
+	{	"AAPL,PowerMac G3",		"PowerMac G3 (Silk)",
+		PMAC_TYPE_SILK,			heathrow_desktop_features,
+		0
+	},
+	{	"AAPL,PowerBook1998",		"PowerBook Wallstreet",
+		PMAC_TYPE_WALLSTREET,		heathrow_laptop_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"AAPL,PowerBook1,1",		"PowerBook 101 (Lombard)",
+		PMAC_TYPE_101_PBOOK,		paddington_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"iMac,1",			"iMac (first generation)",
+		PMAC_TYPE_ORIG_IMAC,		paddington_features,
+		0
+	},
+	{	"PowerMac4,1",			"iMac \"Flower Power\"",
+		PMAC_TYPE_PANGEA_IMAC,		pangea_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook4,1",			"iBook 2",
+		PMAC_TYPE_IBOOK2,		pangea_features,
+		PMAC_MB_CAN_SLEEP | PMAC_MB_HAS_FW_POWER
+	},
+	{	"PowerMac1,1",			"Blue&White G3",
+		PMAC_TYPE_YOSEMITE,		paddington_features,
+		0
+	},
+	{	"PowerMac1,2",			"PowerMac G4 PCI Graphics",
+		PMAC_TYPE_YIKES,		paddington_features,
+		0
+	},
+	{	"PowerBook2,1",			"iBook (first generation)",
+		PMAC_TYPE_ORIG_IBOOK,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac3,1",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac3,2",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac3,3",			"PowerMac G4 AGP Graphics",
+		PMAC_TYPE_SAWTOOTH,		core99_features,
+		0
+	},
+	{	"PowerMac2,1",			"iMac FireWire",
+		PMAC_TYPE_FW_IMAC,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerMac2,2",			"iMac FireWire",
+		PMAC_TYPE_FW_IMAC,		core99_features,
+		PMAC_MB_CAN_SLEEP
+	},
+	{	"PowerBook2,2",			"iBook FireWire",
+		PMAC_TYPE_FW_IBOOK,		core99_features,
+		PMAC_MB_CAN_SLEEP | PMAC_MB_HAS_FW_POWER
+	},
+	{	"PowerMac5,1",			"PowerMac G4 Cube",
+		PMAC_TYPE_CUBE,			core99_features,
+	},
+	{	"PowerMac3,4",			"PowerMac G4 Silver",
+		PMAC_TYPE_QUICKSILVER,		core99_features,
+		0
+	},
+	{	"PowerMac3,5",			"PowerMac G4 Silver",
+		PMAC_TYPE_QUICKSILVER,		core99_features,
+		0
+	},
+	{	"PowerBook3,1",			"PowerBook Pismo",
+		PMAC_TYPE_PISMO,		core99_features,
+		PMAC_MB_CAN_SLEEP | PMAC_MB_HAS_FW_POWER
+	},
+	{	"PowerBook3,2",			"PowerBook Titanium",
+		PMAC_TYPE_TITANIUM,		core99_features,
+		PMAC_MB_CAN_SLEEP | PMAC_MB_HAS_FW_POWER
+	},
+	{	"PowerBook3,3",			"PowerBook Titanium II",
+		PMAC_TYPE_TITANIUM2,		core99_features,
+		PMAC_MB_CAN_SLEEP | PMAC_MB_HAS_FW_POWER
+	},
+};
+
+/*
+ * The toplevel feature_call callback
+ */
+int __pmac
+pmac_do_feature_call(unsigned int selector, ...)
+{
+	struct device_node* node;
+	int param, value, i;
+	feature_call func = NULL;
+	va_list args;
+	
+	if (!pmac_mb.features)
+		return -ENODEV;
+	for (i=0; pmac_mb.features[i].function; i++)
+		if (pmac_mb.features[i].selector == selector) {
+			func = pmac_mb.features[i].function;
+			break;
+		}
+	if (!func)
+		for (i=0; any_features[i].function; i++)
+			if (any_features[i].selector == selector) {
+				func = any_features[i].function;
+				break;
+			}
+	if (!func)
+		return -ENODEV;
+
+	va_start(args, selector);
+	node = (struct device_node*)va_arg(args, void*);
+	param = va_arg(args, int);
+	value = va_arg(args, int);
+	va_end(args);
+
+	return func(node, param, value);
+}
+
+static int __init
+probe_motherboard(void)
+{
+	int i;
+	struct macio_chip* macio = &macio_chips[0];
+
+	/* Lookup known motherboard type in device-tree */
+	for(i=0; i<(sizeof(pmac_mb_defs)/sizeof(struct pmac_mb_def)); i++) {
+	    if (machine_is_compatible(pmac_mb_defs[i].model_string)) {
+		pmac_mb = pmac_mb_defs[i];
+		goto found;
+	    }
+	}
+
+	/* Fallback to selection depending on mac-io chip type */
+	switch(macio->type) {
+	    case macio_grand_central:
+		pmac_mb.model_id = PMAC_TYPE_PSURGE;
+		pmac_mb.model_name = "Unknown PowerSurge";
+		break;
+	    case macio_ohare:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_OHARE;
+		pmac_mb.model_name = "Unknown OHare-based";
+	    	break;
+	    case macio_heathrow:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_HEATHROW;
+		pmac_mb.model_name = "Unknown Heathrow-based";
+		pmac_mb.features = heathrow_desktop_features;
+		break;
+	    case macio_paddington:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PADDINGTON;
+		pmac_mb.model_name = "Unknown Paddington-based";
+	    	pmac_mb.features = paddington_features;
+		break;
+	    case macio_keylargo:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_CORE99;
+		pmac_mb.model_name = "Unknown Keylargo-based";
+	    	pmac_mb.features = core99_features;
+		break;
+	    case macio_pangea:
+		pmac_mb.model_id = PMAC_TYPE_UNKNOWN_PANGEA;
+		pmac_mb.model_name = "Unknown Pangea-based";
+	    	pmac_mb.features = pangea_features;
+		break;
+	    default:
+	    	return -ENODEV;
+	}
+found:
+	/* Fixup Hooper vs. Comet */
+	if (pmac_mb.model_id == PMAC_TYPE_HOOPER) {
+		u32* mach_id_ptr = (u32*)ioremap(0xf3000034, 4);
+		if (!mach_id_ptr)
+			return -ENODEV;
+		/* Here, I used to disable the media-bay on comet. It
+		 * appears this is wrong, the floppy connector is actually
+		 * a kind of media-bay and works with the current driver.
+		 */
+		if ((*mach_id_ptr) & 0x20000000UL)
+			pmac_mb.model_id = PMAC_TYPE_COMET;
+		iounmap(mach_id_ptr);
+	}
+
+	/* Set default value of powersave_nap on machines that support it.
+	 * It appears that uninorth rev 3 has a problem with it, we don't
+	 * enable it on those. In theory, the flush-on-lock property is
+	 * supposed to be set when not supported, but I'm not very confident
+	 * that all Apple OF revs did it properly, I do it the paranoid way.
+	 */
+	while (uninorth_base && uninorth_rev > 3) {
+		struct device_node* np = find_path_device("/cpus");
+		u32 pvr = mfspr(PVR);
+		if (!np || !np->child) {
+			printk(KERN_WARNING "Can't find CPU(s) in device tree !\n");
+			break;
+		}
+		np = np->child;
+		/* Nap mode not supported on SMP */
+		if (np->sibling)
+			break;
+		/* Nap mode not supported if flush-on-lock property is present */
+		if (get_property(np, "flush-on-lock", NULL))
+			break;
+		/* Some 7450 may have problem with NAP mode too ... */
+		if (((pvr >> 16) == 0x8000) && ((pvr & 0xffff) < 0x0201))
+			break;
+		powersave_nap = 1;
+		printk(KERN_INFO "Processor NAP mode on idle enabled.\n");
+		break;
+	}
+
+	printk(KERN_INFO "PowerMac motherboard: %s\n", pmac_mb.model_name);
+	return 0;
+}
+
+/* Initialize the Core99 UniNorth host bridge and memory controller
+ */
+static void __init
+probe_uninorth(void)
+{
+	unsigned long actrl;
+	
+	/* Locate core99 Uni-N */
+	uninorth_node = find_devices("uni-n");
+	if (uninorth_node && uninorth_node->n_addrs > 0) {
+		uninorth_base = ioremap(uninorth_node->addrs[0].address, 0x1000);
+		uninorth_rev = in_be32(UN_REG(UNI_N_VERSION));
+	} else
+		uninorth_node = NULL;
+		
+	if (!uninorth_node)
+		return;
+	
+	printk(KERN_INFO "Found Uninorth memory controller & host bridge, revision: %d\n",
+			uninorth_rev);
+
+	/* Set the arbitrer QAck delay according to what Apple does
+	 */
+	if (uninorth_rev < 0x10) {
+		actrl = UN_IN(UNI_N_ARB_CTRL) & ~UNI_N_ARB_CTRL_QACK_DELAY_MASK;
+		actrl |= ((uninorth_rev < 3) ? UNI_N_ARB_CTRL_QACK_DELAY105 :
+			UNI_N_ARB_CTRL_QACK_DELAY) << UNI_N_ARB_CTRL_QACK_DELAY_SHIFT;
+		UN_OUT(UNI_N_ARB_CTRL, actrl);
+	}
+}	
+
+static void __init
+probe_one_macio(const char* name, const char* compat, int type)
+{
+	struct device_node*	node;
+	int			i;
+	volatile u32*		base;
+	u32*			revp;
+	
+	node = find_devices(name);
+	if (!node || !node->n_addrs)
+		return;
+	if (compat)
+		do {
+			if (device_is_compatible(node, compat))
+				break;
+			node = node->next;
+		} while (node);
+	if (!node)
+		return;
+	for(i=0; i<MAX_MACIO_CHIPS; i++) {
+		if (!macio_chips[i].of_node)
+			break;
+		if (macio_chips[i].of_node == node)
+			return;
+	}
+	if (i >= MAX_MACIO_CHIPS) {
+		printk(KERN_ERR "pmac_feature: Please increase MAX_MACIO_CHIPS !\n");
+		printk(KERN_ERR "pmac_feature: %s skipped\n", node->full_name);
+		return;
+	}
+	base = (volatile u32*)ioremap(node->addrs[0].address, node->addrs[0].size);
+	if (!base) {
+		printk(KERN_ERR "pmac_feature: Can't map mac-io chip !\n");
+		return;
+	}
+	if (type == macio_keylargo) {
+		u32* did = (u32 *)get_property(node, "device-id", NULL);
+		if (*did == 0x00000025)
+			type = macio_pangea;
+	}
+	macio_chips[i].of_node	= node;
+	macio_chips[i].type	= type;
+	macio_chips[i].base	= base;
+	macio_chips[i].flags	= MACIO_FLAG_SCCB_ON | MACIO_FLAG_SCCB_ON;
+	revp = (u32 *)get_property(node, "revision-id", NULL);
+	if (revp)
+		macio_chips[i].rev = *revp;
+	printk(KERN_INFO "Found a %s mac-io controller, rev: %d, mapped at 0x%p\n",
+		macio_names[type], macio_chips[i].rev, macio_chips[i].base);
+}
+
+static int __init
+probe_macios(void)
+{
+	/* Warning, ordering is important */
+	probe_one_macio("gc", NULL, macio_grand_central);
+	probe_one_macio("ohare", NULL, macio_ohare);
+	probe_one_macio("pci106b,7", NULL, macio_ohareII);
+	probe_one_macio("mac-io", "keylargo", macio_keylargo);
+	probe_one_macio("mac-io", "paddington", macio_paddington);
+	probe_one_macio("mac-io", "gatwick", macio_gatwick);
+	probe_one_macio("mac-io", "heathrow", macio_heathrow);
+
+	/* Make sure the "main" macio chip appear first */
+	if (macio_chips[0].type == macio_gatwick
+	    && macio_chips[1].type == macio_heathrow) {
+		struct macio_chip temp = macio_chips[0];
+		macio_chips[0] = macio_chips[1];
+		macio_chips[1] = temp;
+	}
+	if (macio_chips[0].type == macio_ohareII
+	    && macio_chips[1].type == macio_ohare) {
+		struct macio_chip temp = macio_chips[0];
+		macio_chips[0] = macio_chips[1];
+		macio_chips[1] = temp;
+	}
+
+	return (macio_chips[0].of_node == NULL) ? -ENODEV : 0;
+}
+
+static void __init
+initial_serial_shutdown(struct device_node* np)
+{
+	int len;
+	struct slot_names_prop {
+		int	count;
+		char	name[1];
+	} *slots;
+	char *conn;
+	int port_type = PMAC_SCC_ASYNC;
+	int modem = 0;
+	
+	slots = (struct slot_names_prop *)get_property(np, "slot-names", &len);
+	conn = get_property(np, "AAPL,connector", &len);
+	if (conn && (strcmp(conn, "infrared") == 0))
+		port_type = PMAC_SCC_IRDA;
+	else if (device_is_compatible(np, "cobalt"))
+		modem = 1;
+	else if (slots && slots->count > 0) {
+		if (strcmp(slots->name, "IrDA") == 0)
+			port_type = PMAC_SCC_IRDA;
+		else if (strcmp(slots->name, "Modem") == 0)
+			modem = 1;
+	}
+	if (modem)
+		pmac_call_feature(PMAC_FTR_MODEM_ENABLE, np, 0, 0);
+	pmac_call_feature(PMAC_FTR_SCC_ENABLE, np, port_type, 0);
+}
+
+static void __init
+set_initial_features(void)
+{
+	struct device_node* np;
+	
+	/* That hack appears to be necessary for some StarMax motherboards
+	 * but I'm not too sure it was audited for side-effects on other
+	 * ohare based machines...
+	 * Since I still have difficulties figuring the right way to
+	 * differenciate them all and since that hack was there for a long
+	 * time, I'll keep it around
+	 */
+	if (macio_chips[0].type == macio_ohare && !find_devices("via-pmu")) {
+		struct macio_chip* macio = &macio_chips[0];
+		MACIO_OUT32(OHARE_FCR, STARMAX_FEATURES);
+	} else if (macio_chips[0].type == macio_ohare) {
+		struct macio_chip* macio = &macio_chips[0];
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	} else if (macio_chips[1].type == macio_ohare) {
+		struct macio_chip* macio = &macio_chips[1];
+		MACIO_BIS(OHARE_FCR, OH_IOBUS_ENABLE);
+	}
+
+	if (macio_chips[0].type == macio_keylargo ||
+	    macio_chips[0].type == macio_pangea) {
+		/* Enable GMAC for now for PCI probing. It will be disabled
+		 * later on after PCI probe
+		 */
+		np = find_devices("ethernet");
+		while(np) {
+			if (np->parent
+			    && device_is_compatible(np->parent, "uni-north")
+			    && device_is_compatible(np, "gmac"))
+				core99_gmac_enable(np, 0, 1);
+			np = np->next;
+		}
+
+		/* Enable FW before PCI probe. Will be disabled later on
+		 * Note: We should have a batter way to check that we are
+		 * dealing with uninorth internal cell and not a PCI cell
+		 * on the external PCI. The code below works though.
+		 */
+		np = find_devices("firewire");
+		while(np) {
+			if (np->parent
+			    && device_is_compatible(np->parent, "uni-north")
+			    && (device_is_compatible(np, "pci106b,18") || 
+	     		        device_is_compatible(np, "pci106b,30") ||
+	     		        device_is_compatible(np, "pci11c1,5811"))) {
+				macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED;
+				core99_firewire_enable(np, 0, 1);
+			}
+			np = np->next;
+		}
+		
+		/* Switch airport off */
+		np = find_devices("radio");
+		while(np) {
+			if (np && np->parent == macio_chips[0].of_node) {
+				macio_chips[0].flags |= MACIO_FLAG_AIRPORT_ON;
+				core99_airport_enable(np, 0, 0);
+			}
+			np = np->next;
+		}
+	}
+
+	/* On all machines, switch sound off */
+	if (macio_chips[0].of_node)
+		pmac_do_feature_call(PMAC_FTR_SOUND_CHIP_ENABLE,
+			macio_chips[0].of_node, 0, 0);
+
+	/* On all machines, switch modem & serial ports off */
+	np = find_devices("ch-a");
+	while(np) {
+		initial_serial_shutdown(np);
+		np = np->next;
+	}
+	np = find_devices("ch-b");
+	while(np) {
+		initial_serial_shutdown(np);
+		np = np->next;
+	}
+	
+	/* Let hardware settle down */
+	mdelay(10);
+}
+
+void __init
+pmac_feature_init(void)
+{
+	/* Detect the UniNorth memory controller */
+	probe_uninorth();
+
+	/* Probe mac-io controllers */
+	if (probe_macios()) {
+		printk(KERN_WARNING "No mac-io chip found\n");
+		return;
+	}
+
+	/* Probe machine type */
+	if (probe_motherboard())
+		printk(KERN_WARNING "Unknown PowerMac !\n");
+
+	/* Set some initial features (turn off some chips that will
+	 * be later turned on)
+	 */
+	set_initial_features();
+}
+
+void __init
+pmac_feature_late_init(void)
+{
+	struct device_node* np;
+	
+	/* Request some resources late */
+	if (uninorth_node)
+		request_OF_resource(uninorth_node, 0, NULL);
+	np = find_devices("hammerhead");
+	if (np)
+		request_OF_resource(np, 0, NULL);
+	np = find_devices("interrupt-controller");
+	if (np)
+		request_OF_resource(np, 0, NULL);
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_nvram.c linux/arch/ppc/kernel/pmac_nvram.c
--- linux.orig/arch/ppc/kernel/pmac_nvram.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_nvram.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_nvram.c 1.15 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_nvram.c 1.17 12/01/01 20:09:06 benh
  */
 /*
  * Miscellaneous procedures for dealing with the PowerMac hardware.
@@ -326,7 +326,7 @@
 			break;
 		while (!req.complete)
 			pmu_poll();
-		return req.reply[1];
+		return req.reply[0];
 	}
 #endif
 	case 1:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- linux.orig/arch/ppc/kernel/pmac_pci.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_pci.c	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_pci.c 1.27 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_pci.c 1.31 01/20/02 23:53:11 benh
  */
 /*
  * Support for PCI bridges found on Power Macintoshes.
@@ -27,7 +27,7 @@
 #include <asm/prom.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 
 #include "pci.h"
 
@@ -529,29 +529,37 @@
 {
 	struct device_node* node;
 	int updatecfg = 0;
-
+	int uninorth_child;
+	
 	node = pci_device_to_OF_node(dev);
-
+	
 	/* We don't want to enable USB controllers absent from the OF tree
 	 * (iBook second controller)
 	 */
 	if (dev->vendor == PCI_VENDOR_ID_APPLE
 	    && dev->device == PCI_DEVICE_ID_APPLE_KL_USB && !node)
 		return -EINVAL;
+
+	if (!node)
+		return 0;
+		
+	uninorth_child = node->parent &&
+		device_is_compatible(node->parent, "uni-north");
 		
 	/* Firewire & GMAC were disabled after PCI probe, the driver is
 	 * claiming them, we must re-enable them now.
 	 */
-	if (node && !strcmp(node->name, "firewire") && 
+	if (uninorth_child && !strcmp(node->name, "firewire") && 
 	    (device_is_compatible(node, "pci106b,18") || 
-	     device_is_compatible(node, "pci106b,30"))) {
-		feature_set_firewire_cable_power(node, 1);
-		feature_set_firewire_power(node, 1);
+	     device_is_compatible(node, "pci106b,30") ||
+	     device_is_compatible(node, "pci11c1,5811"))) {
+		pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, node, 0, 1);
+		pmac_call_feature(PMAC_FTR_1394_ENABLE, node, 0, 1);
 		updatecfg = 1;
 	}
-	if (node && !strcmp(node->name, "ethernet") && 
+	if (uninorth_child && !strcmp(node->name, "ethernet") && 
 	    device_is_compatible(node, "gmac")) {
-		feature_set_gmac_power(node, 1);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, node, 0, 1);
 		updatecfg = 1;
 	}
 
@@ -608,10 +616,11 @@
 	nd = find_devices("firewire");
 	while (nd) {
 		if (nd->parent && (device_is_compatible(nd, "pci106b,18") ||
-					device_is_compatible(nd, "pci106b,30"))
+				   device_is_compatible(nd, "pci106b,30") ||
+				   device_is_compatible(nd, "pci11c1,5811"))
 		    && device_is_compatible(nd->parent, "uni-north")) {
-			feature_set_firewire_power(nd, 0);
-			feature_set_firewire_cable_power(nd, 0);
+			pmac_call_feature(PMAC_FTR_1394_ENABLE, nd, 0, 0);
+			pmac_call_feature(PMAC_FTR_1394_CABLE_POWER, nd, 0, 0);
 		}
 		nd = nd->next;
 	}
@@ -619,7 +628,7 @@
 	while (nd) {
 		if (nd->parent && device_is_compatible(nd, "gmac")
 		    && device_is_compatible(nd->parent, "uni-north"))
-			feature_set_gmac_power(nd, 0);
+			pmac_call_feature(PMAC_FTR_GMAC_ENABLE, nd, 0, 0);
 		nd = nd->next;
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_pic.c linux/arch/ppc/kernel/pmac_pic.c
--- linux.orig/arch/ppc/kernel/pmac_pic.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_pic.c 1.20 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_pic.c 1.24 12/19/01 10:53:01 paulus
  */
 #include <linux/config.h>
 #include <linux/stddef.h>
@@ -18,44 +18,52 @@
 #include "pmac_pic.h"
 #include "open_pic.h"
 
-/* pmac */struct pmac_irq_hw {
-        unsigned int    flag;
+struct pmac_irq_hw {
+        unsigned int    event;
         unsigned int    enable;
         unsigned int    ack;
         unsigned int    level;
 };
 
-/* XXX these addresses should be obtained from the device tree */
-static volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
+/* Default addresses */
+static volatile struct pmac_irq_hw *pmac_irq_hw[4] __pmacdata = {
         (struct pmac_irq_hw *) 0xf3000020,
         (struct pmac_irq_hw *) 0xf3000010,
         (struct pmac_irq_hw *) 0xf4000020,
         (struct pmac_irq_hw *) 0xf4000010,
 };
 
-static int max_irqs;
-static int max_real_irqs;
+#define GC_LEVEL_MASK		0x3ff00000
+#define OHARE_LEVEL_MASK	0x1ff00000
+#define HEATHROW_LEVEL_MASK	0x1ff00000
+
+static int max_irqs __pmacdata;
+static int max_real_irqs __pmacdata;
+static u32 level_mask[4] __pmacdata;
 
-static spinlock_t pmac_pic_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t pmac_pic_lock __pmacdata = SPIN_LOCK_UNLOCKED;
 
 
 #define GATWICK_IRQ_POOL_SIZE        10
-static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
+static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE] __pmacdata;
 
 /*
  * Mark an irq as "lost".  This is only used on the pmac
  * since it can lose interrupts (see pmac_set_irq_mask).
  * -- Cort
  */
-void __pmac __set_lost(unsigned long irq_nr)
+void __pmac
+__set_lost(unsigned long irq_nr, int nokick)
 {
 	if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
 		atomic_inc(&ppc_n_lost_interrupts);
-		set_dec(1);
+		if (!nokick)
+			set_dec(1);
 	}
 }
 
-static void __pmac pmac_mask_and_ack_irq(unsigned int irq_nr)
+static void __pmac
+pmac_mask_and_ack_irq(unsigned int irq_nr)
 {
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
@@ -68,18 +76,18 @@
         if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
                 atomic_dec(&ppc_n_lost_interrupts);
 	spin_lock_irqsave(&pmac_pic_lock, flags);
-        out_le32(&pmac_irq_hw[i]->ack, bit);
         out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
         out_le32(&pmac_irq_hw[i]->ack, bit);
         do {
                 /* make sure ack gets to controller before we enable
                    interrupts */
                 mb();
-        } while(in_le32(&pmac_irq_hw[i]->flag) & bit);
+        } while((in_le32(&pmac_irq_hw[i]->enable) & bit)
+                != (ppc_cached_irq_mask[i] & bit));
 	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
-static void __pmac pmac_set_irq_mask(unsigned int irq_nr)
+static void __pmac pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
 {
         unsigned long bit = 1UL << (irq_nr & 0x1f);
         int i = irq_nr >> 5;
@@ -104,31 +112,29 @@
          * when the device interrupt is already on *doesn't* set
          * the bit in the flag register or request another interrupt.
          */
-        if ((bit & ppc_cached_irq_mask[i])
-            && (ld_le32(&pmac_irq_hw[i]->level) & bit)
-            && !(ld_le32(&pmac_irq_hw[i]->flag) & bit))
-		__set_lost((ulong)irq_nr);
+        if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
+		__set_lost((ulong)irq_nr, nokicklost);
 	spin_unlock_irqrestore(&pmac_pic_lock, flags);
 }
 
 static void __pmac pmac_mask_irq(unsigned int irq_nr)
 {
         clear_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr);
+        pmac_set_irq_mask(irq_nr, 0);
         mb();
 }
 
 static void __pmac pmac_unmask_irq(unsigned int irq_nr)
 {
         set_bit(irq_nr, ppc_cached_irq_mask);
-        pmac_set_irq_mask(irq_nr);
+        pmac_set_irq_mask(irq_nr, 0);
 }
 
 static void __pmac pmac_end_irq(unsigned int irq_nr)
 {
 	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
         	set_bit(irq_nr, ppc_cached_irq_mask);
-	        pmac_set_irq_mask(irq_nr);
+	        pmac_set_irq_mask(irq_nr, 1);
 	}
 }
 
@@ -161,8 +167,10 @@
 	
 	for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
 		int i = irq >> 5;
-		bits = ld_le32(&pmac_irq_hw[i]->flag)
-			| ppc_lost_interrupts[i];
+		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
+		/* We must read level interrupts from the level register */
+		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
+		bits &= ppc_cached_irq_mask[i];
 		if (bits == 0)
 			continue;
 		irq += __ilog2(bits);
@@ -195,8 +203,10 @@
 #endif /* CONFIG_SMP */
 	for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
 		int i = irq >> 5;
-		bits = ld_le32(&pmac_irq_hw[i]->flag)
-			| ppc_lost_interrupts[i];
+		bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
+		/* We must read level interrupts from the level register */
+		bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]);
+		bits &= ppc_cached_irq_mask[i];
 		if (bits == 0)
 			continue;
 		irq += __ilog2(bits);
@@ -374,6 +384,24 @@
 		irqctrler = NULL;
 	}
 
+	/* Get the level/edge settings, assume if it's not
+	 * a Grand Central nor an OHare, then it's an Heathrow
+	 * (or Paddington).
+	 */
+	if (find_devices("gc"))
+		level_mask[0] = GC_LEVEL_MASK;
+	else if (find_devices("ohare")) {
+		level_mask[0] = OHARE_LEVEL_MASK;
+		/* We might have a second cascaded ohare */
+		level_mask[1] = OHARE_LEVEL_MASK;
+	} else {
+		level_mask[0] = HEATHROW_LEVEL_MASK;
+		level_mask[1] = 0;
+		/* We might have a second cascaded heathrow */
+		level_mask[2] = HEATHROW_LEVEL_MASK;
+		level_mask[3] = 0;
+	}
+
 	/*
 	 * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts,
 	 * 1998 G3 Series PowerBooks have 128, 
@@ -433,6 +461,10 @@
 	/* disable all interrupts in all controllers */
 	for (i = 0; i * 32 < max_irqs; ++i)
 		out_le32(&pmac_irq_hw[i]->enable, 0);
+	/* mark level interrupts */
+	for (i = 0; i < max_irqs; i++)
+		if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
+			irq_desc[i].status = IRQ_LEVEL;
 	
 	/* get interrupt line of secondary interrupt controller */
 	if (irq_cascade >= 0) {
@@ -474,7 +506,7 @@
 	out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]);
 	if (max_real_irqs > 32)
 		out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]);
-	(void)in_le32(&pmac_irq_hw[0]->flag);
+	(void)in_le32(&pmac_irq_hw[0]->event);
 	/* make sure mask gets to controller before we return to caller */
 	mb();
         (void)in_le32(&pmac_irq_hw[0]->enable);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- linux.orig/arch/ppc/kernel/pmac_setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_setup.c 1.43 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.pmac_setup.c 1.45 12/01/01 20:09:06 benh
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -61,7 +61,6 @@
 #include <asm/pci-bridge.h>
 #include <asm/ohare.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
 #include <asm/machdep.h>
 #include <asm/keyboard.h>
 #include <asm/dma.h>
@@ -69,6 +68,7 @@
 #include <asm/cputable.h>
 #include <asm/btext.h>
 
+#include <asm/pmac_feature.h>
 #include <asm/time.h>
 #include "local_irq.h"
 #include "pmac_pic.h"
@@ -83,6 +83,10 @@
 extern void pmac_calibrate_decr(void);
 extern void pmac_pcibios_fixup(void);
 extern void pmac_find_bridges(void);
+extern int pmac_ide_check_base(ide_ioreg_t base);
+extern ide_ioreg_t pmac_ide_get_base(int index);
+extern void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+	ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq);
 
 extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
 extern int mackbd_getkeycode(unsigned int scancode);
@@ -436,7 +440,8 @@
 #endif
 #ifdef CONFIG_PMAC_PBOOK
 	media_bay_init();
-#endif	
+#endif
+	pmac_feature_late_init();
 }
 
 #ifdef CONFIG_SCSI
@@ -604,14 +609,11 @@
 static int __pmac
 pmac_ide_check_region(ide_ioreg_t from, unsigned int extent)
 {
-	/*
-	 * We only do the check_region if `from' looks like a genuine
-	 * I/O port number.  If it actually refers to a memory-mapped
-	 * register, it should be OK.
-	 */
-	if (from < ~_IO_BASE)
-		return check_region(from, extent);
-	return 0;
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return 0;
+#endif
+	return check_region(from, extent);
 }
 
 static void __pmac
@@ -619,24 +621,31 @@
 			unsigned int extent,
 			const char *name)
 {
-	if (from < ~_IO_BASE)
-		request_region(from, extent, name);
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return;
+#endif
+	request_region(from, extent, name);
 }
 
 static void __pmac
 pmac_ide_release_region(ide_ioreg_t from,
 			unsigned int extent)
 {
-	if (from < ~_IO_BASE)
-		release_region(from, extent);
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
+	if (pmac_ide_check_base(from) >= 0)
+		return;
+#endif
+	release_region(from, extent);
 }
 
+#ifndef CONFIG_BLK_DEV_IDE_PMAC
 /*
  * This is only used if we have a PCI IDE controller, not
  * for the IDE controller in the ohare/paddington/heathrow/keylargo.
  */
 static void __pmac
-pmac_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
+pmac_ide_pci_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port,
 		ide_ioreg_t ctrl_port, int *irq)
 {
 	ide_ioreg_t reg = data_port;
@@ -648,7 +657,8 @@
 	}
 	hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
 }
-#endif
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 
 /*
  * Read in a property describing some pieces of memory.
@@ -806,14 +816,21 @@
 
 	ppc_md.find_end_of_memory = pmac_find_end_of_memory;
 
+	ppc_md.feature_call   = pmac_do_feature_call;
+
 	select_adb_keyboard();
 
-#if defined(CONFIG_BLK_DEV_IDE) && defined(CONFIG_BLK_DEV_IDE_PMAC)
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
         ppc_ide_md.ide_check_region	= pmac_ide_check_region;
         ppc_ide_md.ide_request_region	= pmac_ide_request_region;
         ppc_ide_md.ide_release_region	= pmac_ide_release_region;
+#ifdef CONFIG_BLK_DEV_IDE_PMAC
         ppc_ide_md.ide_init_hwif	= pmac_ide_init_hwif_ports;
-#endif /* CONFIG_BLK_DEV_IDE && CONFIG_BLK_DEV_IDE_PMAC */
+        ppc_ide_md.default_io_base	= pmac_ide_get_base;
+#else /* CONFIG_BLK_DEV_IDE_PMAC */
+        ppc_ide_md.ide_init_hwif	= pmac_ide_pci_init_hwif_ports;
+#endif /* CONFIG_BLK_DEV_IDE_PMAC */
+#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */
 
 #ifdef CONFIG_BOOTX_TEXT
 	ppc_md.progress = pmac_progress;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_smp.c linux/arch/ppc/kernel/pmac_smp.c
--- linux.orig/arch/ppc/kernel/pmac_smp.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_smp.c	Wed Dec 26 16:28:34 2001
@@ -43,7 +43,8 @@
 #include <asm/prom.h>
 #include <asm/smp.h>
 #include <asm/residual.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/time.h>
 #include <asm/gemini.h>
 
@@ -442,7 +443,7 @@
 	flush_icache_range((unsigned long) vector, (unsigned long) vector + 4);
 	
 	/* Put some life in our friend */
-	feature_core99_kick_cpu(nr);
+	pmac_call_feature(PMAC_FTR_RESET_CPU, NULL, nr, 0);
 	
 	/* FIXME: We wait a bit for the CPU to take the exception, I should
 	 * instead wait for the entry code to set something for me. Well,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/pmac_time.c linux/arch/ppc/kernel/pmac_time.c
--- linux.orig/arch/ppc/kernel/pmac_time.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/pmac_time.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.pmac_time.c 1.16 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.pmac_time.c 1.19 12/04/01 01:24:51 benh
  */
 /*
  * Support for periodic interrupts (100 per second) and for getting
@@ -110,11 +110,11 @@
 			return 0;
 		while (!req.complete)
 			pmu_poll();
-		if (req.reply_len != 5)
+		if (req.reply_len != 4)
 			printk(KERN_ERR "pmac_get_rtc_time: got %d byte reply\n",
 			       req.reply_len);
-		now = (req.reply[1] << 24) + (req.reply[2] << 16)
-			+ (req.reply[3] << 8) + req.reply[4];
+		now = (req.reply[0] << 24) + (req.reply[1] << 16)
+			+ (req.reply[2] << 8) + req.reply[3];
 		return now - RTC_OFFSET;
 #endif /* CONFIG_ADB_PMU */
 	default: ;
@@ -228,10 +228,6 @@
 	case PBOOK_WAKE:
 		write_lock_irqsave(&xtime_lock, flags);
 		xtime.tv_sec = pmac_get_rtc_time() + time_diff;
-		set_dec(tb_ticks_per_jiffy);
-		/* No currently-supported powerbook has a 601,
-		   so use get_tbl, not native  */
-		last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
 		xtime.tv_usec = 0;
 		last_rtc_update = xtime.tv_sec;
 		write_unlock_irqrestore(&xtime_lock, flags);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/ppc8260_pic.c linux/arch/ppc/kernel/ppc8260_pic.c
--- linux.orig/arch/ppc/kernel/ppc8260_pic.c	Tue May 22 00:04:47 2001
+++ linux/arch/ppc/kernel/ppc8260_pic.c	Thu Feb  7 19:19:49 2002
@@ -85,6 +85,22 @@
 	sipnr[word] = 1 << (31 - bit);
 }
 
+static void m8260_end_irq(unsigned int irq_nr)
+{
+	int	bit, word;
+	volatile uint	*simr;
+
+	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
+
+		bit = irq_to_siubit[irq_nr];
+		word = irq_to_siureg[irq_nr];
+
+		simr = &(immr->im_intctl.ic_simrh);
+		ppc_cached_irq_mask[word] |= (1 << (31 - bit));
+		simr[word] = ppc_cached_irq_mask[word];
+	}
+}
+
 struct hw_interrupt_type ppc8260_pic = {
 	" 8260 SIU  ",
 	NULL,
@@ -92,6 +108,7 @@
 	m8260_unmask_irq,
 	m8260_mask_irq,
 	m8260_mask_and_ack,
+	m8260_end_irq,
 	0
 };
 
@@ -106,6 +123,9 @@
          * to get the irq number.         */
         bits = immr->im_intctl.ic_sivec;
         irq = bits >> 26;
+
+	if (irq == 0)
+		return(-1);
 #if 0
         irq += ppc8260_pic.irq_offset;
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/ppc8xx_pic.c linux/arch/ppc/kernel/ppc8xx_pic.c
--- linux.orig/arch/ppc/kernel/ppc8xx_pic.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/ppc8xx_pic.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ppc8xx_pic.c 1.10 05/17/01 18:14:21 cort
+ * BK Id: SCCS/s.ppc8xx_pic.c 1.13 12/01/01 17:19:48 trini
  */
 #include <linux/config.h>
 #include <linux/stddef.h>
@@ -44,6 +44,21 @@
 						ppc_cached_irq_mask[word];
 }
 
+static void m8xx_end_irq(unsigned int irq_nr)
+{
+	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
+		int bit, word;
+
+		bit = irq_nr & 0x1f;
+		word = irq_nr >> 5;
+
+		ppc_cached_irq_mask[word] |= (1 << (31-bit));
+		((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = 
+			ppc_cached_irq_mask[word];
+	}
+}
+
+
 static void m8xx_mask_and_ack(unsigned int irq_nr)
 {
 	int	bit, word;
@@ -64,6 +79,7 @@
 	m8xx_unmask_irq,
 	m8xx_mask_irq,
 	m8xx_mask_and_ack,
+	m8xx_end_irq,
 	0
 };
 
@@ -97,19 +113,26 @@
 #endif
 
 
+/*
+ * We either return a valid interrupt or -1 if there is nothing pending
+ */
 int
 m8xx_get_irq(struct pt_regs *regs)
 {
 	int irq;
-        unsigned long bits = 0;
 
-        /* For MPC8xx, read the SIVEC register and shift the bits down
-         * to get the irq number.         */
-        bits = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec;
-        irq = bits >> 26;
-#if 0
-        irq += ppc8xx_pic.irq_offset;
-#endif
+	/* For MPC8xx, read the SIVEC register and shift the bits down
+	 * to get the irq number.
+	 */
+	irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26;
+
+	/*
+	 * When we read the sivec without an interrupt to process, we will 
+	 * get back SIU_LEVEL7.  In this case, return -1
+	 */
+	if (irq == SIU_LEVEL7)
+		return -1;
+
 	return irq;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- linux.orig/arch/ppc/kernel/ppc_ksyms.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Mon Jan 21 18:49:12 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ppc_ksyms.c 1.59 11/04/01 22:58:20 paulus
+ * BK Id: SCCS/s.ppc_ksyms.c 1.63 01/20/02 23:53:11 benh
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -36,7 +36,7 @@
 #include <asm/system.h>
 #include <asm/pci-bridge.h>
 #include <asm/irq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/dma.h>
 #include <asm/machdep.h>
 #include <asm/hw_irq.h>
@@ -247,6 +247,8 @@
 EXPORT_SYMBOL(machine_is_compatible);
 EXPORT_SYMBOL(find_all_nodes);
 EXPORT_SYMBOL(get_property);
+EXPORT_SYMBOL(request_OF_resource);
+EXPORT_SYMBOL(release_OF_resource);
 EXPORT_SYMBOL(pci_bus_io_base);
 EXPORT_SYMBOL(pci_bus_io_base_phys);
 EXPORT_SYMBOL(pci_bus_mem_base_phys);
@@ -257,16 +259,6 @@
 EXPORT_SYMBOL(pci_phys_to_bus);
 EXPORT_SYMBOL(pci_bus_to_phys);
 EXPORT_SYMBOL(pmac_newworld);
-EXPORT_SYMBOL(feature_set);
-EXPORT_SYMBOL(feature_clear);
-EXPORT_SYMBOL(feature_test);
-EXPORT_SYMBOL(feature_set_gmac_power);
-EXPORT_SYMBOL(feature_gmac_phy_reset);
-EXPORT_SYMBOL(feature_set_usb_power);
-EXPORT_SYMBOL(feature_set_firewire_power);
-EXPORT_SYMBOL(feature_set_firewire_cable_power);
-EXPORT_SYMBOL(feature_set_modem_power);
-EXPORT_SYMBOL(feature_set_airport_power);
 #endif /* defined(CONFIG_ALL_PPC) */
 #if defined(CONFIG_BOOTX_TEXT)
 EXPORT_SYMBOL(btext_update_display);
@@ -293,6 +285,7 @@
 EXPORT_SYMBOL_NOVERS(memmove);
 EXPORT_SYMBOL_NOVERS(memscan);
 EXPORT_SYMBOL_NOVERS(memcmp);
+EXPORT_SYMBOL_NOVERS(memchr);
 
 EXPORT_SYMBOL(abs);
 
@@ -319,7 +312,9 @@
 EXPORT_SYMBOL(get_wchan);
 EXPORT_SYMBOL(console_drivers);
 #ifdef CONFIG_XMON
+extern void xmon_printf(char *fmt, ...);
 EXPORT_SYMBOL(xmon);
+EXPORT_SYMBOL(xmon_printf);
 #endif
 EXPORT_SYMBOL(__up);
 EXPORT_SYMBOL(__down);
@@ -361,3 +356,8 @@
 extern long *ret_from_intercept;
 EXPORT_SYMBOL(ret_from_intercept);
 EXPORT_SYMBOL(cur_cpu_spec);
+#if defined(CONFIG_ALL_PPC)
+extern unsigned long agp_special_page;
+EXPORT_SYMBOL_NOVERS(agp_special_page);
+#endif /* defined(CONFIG_ALL_PPC) */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- linux.orig/arch/ppc/kernel/prep_pci.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/prep_pci.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prep_pci.c 1.31 10/05/01 17:48:18 trini
+ * BK Id: SCCS/s.prep_pci.c 1.33 12/20/01 15:36:12 trini
  */
 /*
  * PReP pci functions.
@@ -1190,6 +1190,27 @@
 }
 
 static void __init
+prep_pcibios_after_init(void)
+{
+	struct pci_dev *dev;
+	
+	/* If there is a WD 90C, reset the IO BAR to 0x0 (it started that 
+	 * way, but the PCI layer relocated it because it thought 0x0 was
+	 * invalid for a BAR).
+	 * If you don't do this, the card's VGA base will be <IO BAR>+0xc0000
+	 * instead of 0xc0000. vgacon.c (for example) is completely unaware of
+	 * this little quirk.
+	 */
+	dev = pci_find_device(PCI_VENDOR_ID_WD, PCI_DEVICE_ID_WD_90C, NULL);
+	if (dev) {
+		dev->resource[1].end -= dev->resource[1].start;
+		dev->resource[1].start = 0;
+		/* tell the hardware */
+		pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0x0);
+	}
+}
+
+static void __init
 prep_init_resource(struct resource *res, unsigned long start,
 		   unsigned long end, int flags)
 {
@@ -1246,4 +1267,5 @@
 	}
 
 	ppc_md.pcibios_fixup = prep_pcibios_fixup;
+	ppc_md.pcibios_after_init = prep_pcibios_after_init;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- linux.orig/arch/ppc/kernel/prep_setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/prep_setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prep_setup.c 1.44 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.prep_setup.c 1.47 12/19/01 09:45:54 trini
  */
 /*
  *  linux/arch/ppc/kernel/setup.c
@@ -189,7 +189,7 @@
 
 no_l2:
 #ifdef CONFIG_PREP_RESIDUAL
-	if (res->ResidualLength == 0) {
+	if (res->ResidualLength != 0) {
 		/* print info about SIMMs */
 		seq_printf(m, "simms\t\t: ");
 		for (i = 0; (res->ActualNumMemories) && (i < MAX_MEMS); i++) {
@@ -645,7 +645,7 @@
 static int __prep
 prep_get_irq(struct pt_regs *regs)
 {
-	return i8259_irq(smp_processor_id());
+	return i8259_irq();
 }		
 
 static void __init
@@ -657,7 +657,7 @@
 		openpic_init(1, NUM_8259_INTERRUPTS, 0, -1);
 	for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ )
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init();	
+	i8259_init(0xbffffff0); /* PCI interrupt ack address for MPC105 and 106 */
 }
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- linux.orig/arch/ppc/kernel/prom.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/prom.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prom.c 1.42 09/08/01 15:47:42 paulus
+ * BK Id: SCCS/s.prom.c 1.48 12/19/01 10:50:58 paulus
  */
 /*
  * Procedures for interfacing to the Open Firmware PROM on
@@ -19,6 +19,9 @@
 #include <linux/version.h>
 #include <linux/threads.h>
 #include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
 
 #include <asm/sections.h>
 #include <asm/prom.h>
@@ -34,6 +37,7 @@
 #include <asm/bitops.h>
 #include <asm/bootinfo.h>
 #include <asm/btext.h>
+#include <asm/pci-bridge.h>
 #include "open_pic.h"
 
 #ifdef CONFIG_FB
@@ -371,8 +375,8 @@
 		btext_drawstring(RELOC(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"));
 		btext_flushscreen();
 	}
-#endif	/* CONFIG_BOOTX_TEXT */	
-		
+#endif	/* CONFIG_BOOTX_TEXT */
+
 	/* New BootX enters kernel with MMU off, i/os are not allowed
 	   here. This hack will have been done by the boostrap anyway.
 	*/
@@ -388,7 +392,7 @@
 			out_le32((unsigned *)0x80880008, 1);	/* XXX */
 		}
 	}
-		
+
 	/* Move klimit to enclose device tree, args, ramdisk, etc... */
 	if (bi->version < 5) {
 		space = bi->deviceTreeOffset + bi->deviceTreeSize;
@@ -411,7 +415,7 @@
 		     ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
 			x = *(volatile unsigned long *)ptr;
 	}
-		
+
 #ifdef CONFIG_BOOTX_TEXT
 	/*
 	 * Note that after we call prepare_disp_BAT, we can't do
@@ -620,7 +624,7 @@
 		phys = 0;
 	else {
  	    if ((int) call_prom(RELOC("getprop"), 4, 1, RELOC(prom_chosen),
-			    RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) {	
+			    RELOC("mmu"), &prom_mmu, sizeof(prom_mmu)) <= 0) {
 		prom_print(RELOC(" no MMU found\n"));
 	    } else {
 		int nargs;
@@ -653,7 +657,8 @@
 	call_prom(RELOC("quiesce"), 0, 0);
 
 #ifdef CONFIG_BOOTX_TEXT
-	btext_prepare_BAT();
+	if (RELOC(prom_disp_node) != 0)
+		btext_prepare_BAT();
 #endif
 
 	prom_print(RELOC("returning "));
@@ -793,7 +798,7 @@
 			for (i=1; i<RELOC(prom_num_displays); i++) {
 				RELOC(prom_display_paths[i-1]) = RELOC(prom_display_paths[i]);
 				RELOC(prom_display_nodes[i-1]) = RELOC(prom_display_nodes[i]);
-			}	
+			}
 			if (--RELOC(prom_num_displays) > 0)
 				RELOC(prom_disp_node) = RELOC(prom_display_nodes[0]);
 			else
@@ -880,7 +885,7 @@
 		}
 	}
 	/* kludge for valkyrie */
-	if (strcmp(name, RELOC("valkyrie")) == 0) 
+	if (strcmp(name, RELOC("valkyrie")) == 0)
 		address += 0x1000;
 
 	btext_setup_display(width, height, depth, pitch, address);
@@ -1111,10 +1116,15 @@
 	np->name = get_property(np, "name", 0);
 	np->type = get_property(np, "device_type", 0);
 
+	if (!np->name)
+		np->name = "<NULL>";
+	if (!np->type)
+		np->type = "<NULL>";
+
 	/* get the device addresses and interrupts */
-	if (ifunc != NULL) {
+	if (ifunc != NULL)
 		mem_start = ifunc(np, mem_start, naddrc, nsizec);
-	}
+
 	if (use_of_interrupt_tree)
 		mem_start = finish_node_interrupts(np, mem_start);
 
@@ -1126,11 +1136,18 @@
 	if (ip != NULL)
 		nsizec = *ip;
 
-	/* the f50 sets the name to 'display' and 'compatible' to what we
-	 * expect for the name -- Cort
+	/* 
+	 * The F50 sets the name to 'display' and 'compatible' to what we
+	 * expect for the name. -- Cort
+	 *
+	 * But sometimes you get a 'display' name for non-OF cards, and thus
+	 * no compatible property.  And very rarely we won't have a name
+	 * property either. -- Tom
 	 */
 	if (!strcmp(np->name, "display"))
 		np->name = get_property(np, "compatible", 0);
+	if (!np->name)
+		np->name = get_property(np, "name", 0);
 
 	if (np->parent == NULL)
 		ifunc = interpret_root_props;
@@ -1145,6 +1162,8 @@
 		ifunc = interpret_macio_props;
 	else if (!strcmp(np->type, "isa"))
 		ifunc = interpret_isa_props;
+	else if (!strcmp(np->name, "uni-n"))
+		ifunc = interpret_root_props;
 	else if (!((ifunc == interpret_dbdma_props
 		    || ifunc == interpret_macio_props)
 		   && (!strcmp(np->type, "escc")
@@ -1508,7 +1527,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct reg_property)) >= 0) {
-			adr[i].space = 0;
+			adr[i].space = 2;
 			adr[i].address = rp[i].address + base_address;
 			adr[i].size = rp[i].size;
 			++i;
@@ -1544,14 +1563,13 @@
 	struct reg_property *rp;
 	struct address_range *adr;
 	unsigned long base_address;
-	int i, l, keylargo, *ip;
+	int i, l, *ip;
 	struct device_node *db;
 
 	base_address = 0;
 	for (db = np->parent; db != NULL; db = db->parent) {
 		if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
 			base_address = db->addrs[0].address;
-			keylargo = device_is_compatible(db, "Keylargo");
 			break;
 		}
 	}
@@ -1561,7 +1579,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct reg_property)) >= 0) {
-			adr[i].space = 0;
+			adr[i].space = 2;
 			adr[i].address = rp[i].address + base_address;
 			adr[i].size = rp[i].size;
 			++i;
@@ -1616,7 +1634,7 @@
 
 	if (use_of_interrupt_tree)
 		return mem_start;
- 
+
 	ip = (int *) get_property(np, "interrupts", &l);
 	if (ip != 0) {
 		np->intrs = (struct interrupt_info *) mem_start;
@@ -1645,7 +1663,7 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= rpsize) >= 0) {
-			adr[i].space = (naddrc >= 2? rp[naddrc-2]: 0);
+			adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2);
 			adr[i].address = rp[naddrc - 1];
 			adr[i].size = rp[naddrc + nsizec - 1];
 			++i;
@@ -1786,7 +1804,7 @@
 machine_is_compatible(const char *compat)
 {
 	struct device_node *root;
-	
+
 	root = find_path_device("/");
 	if (root == 0)
 		return 0;
@@ -1870,10 +1888,176 @@
 {
 	struct property **next = &np->properties;
 
-	prop->next = NULL;	
+	prop->next = NULL;
 	while (*next)
 		next = &(*next)->next;
 	*next = prop;
+}
+
+/* I quickly hacked that one, check against spec ! */
+static inline unsigned long __openfirmware
+bus_space_to_resource_flags(unsigned int bus_space)
+{
+	u8 space = (bus_space >> 24) & 0xf;
+	if (space == 0)
+		space = 0x02;
+	if (space == 0x02)
+		return IORESOURCE_MEM;
+	else if (space == 0x01)
+		return IORESOURCE_IO;
+	else {
+		printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
+		    	bus_space);
+		return 0;
+	}
+}
+
+static struct resource* __openfirmware
+find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range)
+{
+	unsigned long mask;
+	int i;
+	
+	/* Check this one */ 
+	mask = bus_space_to_resource_flags(range->space);
+	for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
+		if ((pdev->resource[i].flags & mask) == mask &&
+			pdev->resource[i].start <= range->address &&
+			pdev->resource[i].end > range->address) {
+				if ((range->address + range->size - 1) > pdev->resource[i].end) {
+					/* Add better message */
+					printk(KERN_WARNING "PCI/OF resource overlap !\n");
+					return NULL;
+				}
+				break;
+			}
+	}
+	if (i == DEVICE_COUNT_RESOURCE)
+		return NULL;
+	return &pdev->resource[i];
+}
+
+/*
+ * Request an OF device resource. Currently handles child of PCI devices,
+ * or other nodes attached to the root node. Ultimately, put some
+ * link to resources in the OF node.
+ * WARNING: out_resource->name should be initialized before calling this
+ * function.
+ */
+struct resource* __openfirmware
+request_OF_resource(struct device_node* node, int index, const char* name_postfix)
+{
+	struct pci_dev* pcidev;
+	u8 pci_bus, pci_devfn;
+	unsigned long iomask;
+	struct device_node* nd;
+	struct resource* parent;
+	struct resource *res = NULL;
+	int nlen, plen;
+
+	if (index >= node->n_addrs)
+		goto fail;
+
+	/* Sanity check on bus space */
+	iomask = bus_space_to_resource_flags(node->addrs[index].space);
+	if (iomask & IORESOURCE_MEM)
+		parent = &iomem_resource;
+	else if (iomask & IORESOURCE_IO)
+		parent = &ioport_resource;
+	else
+		goto fail;
+		
+	/* Find a PCI parent if any */
+	nd = node;
+	pcidev = NULL;
+	while(nd) {
+		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+			pcidev = pci_find_slot(pci_bus, pci_devfn);
+		if (pcidev) break;
+		nd = nd->parent;
+	}
+	if (pcidev)
+		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+	if (!parent) {
+		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
+			node->name);
+		goto fail;
+	}
+
+	res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL);
+	if (!res)
+		goto fail;
+	nlen = strlen(node->name);
+	plen = name_postfix ? strlen(name_postfix) : 0;
+	res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
+	if (res->name) {
+		strcpy((char *)res->name, node->name);
+		if (plen)
+			strcpy((char *)res->name+nlen, name_postfix);
+	}
+	return res;
+fail:
+	return NULL;
+}
+
+int __openfirmware
+release_OF_resource(struct device_node* node, int index)
+{
+	struct pci_dev* pcidev;
+	u8 pci_bus, pci_devfn;
+	unsigned long iomask;
+	struct device_node* nd;
+	struct resource* parent;
+	struct resource *res = NULL;
+
+	if (index >= node->n_addrs)
+		return -EINVAL;
+
+	/* Sanity check on bus space */
+	iomask = bus_space_to_resource_flags(node->addrs[index].space);
+	if (iomask & IORESOURCE_MEM)
+		parent = &iomem_resource;
+	else if (iomask & IORESOURCE_IO)
+		parent = &ioport_resource;
+	else
+		return -EINVAL;
+		
+	/* Find a PCI parent if any */
+	nd = node;
+	pcidev = NULL;
+	while(nd) {
+		if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+			pcidev = pci_find_slot(pci_bus, pci_devfn);
+		if (pcidev) break;
+		nd = nd->parent;
+	}
+	if (pcidev)
+		parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+	if (!parent) {
+		printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
+			node->name);
+		return -ENODEV;
+	}
+
+	/* Find us in the parent */
+	res = parent->child;
+	while (res) {
+		if (res->start == node->addrs[index].address &&
+		    res->end == (res->start + node->addrs[index].size - 1))
+		    	break;
+		res = res->sibling;
+	}
+	if (!res)
+		return -ENODEV;
+
+	if (res->name) {
+		kfree(res->name);
+		res->name = NULL;
+	}
+	release_resource(res);
+	kfree(res);
+
+	return 0;
 }
 
 #if 0
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- linux.orig/arch/ppc/kernel/setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/setup.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.setup.c 1.65 11/18/01 20:57:25 trini
+ * BK Id: SCCS/s.setup.c 1.67 12/01/01 20:09:07 benh
  */
 /*
  * Common prep/pmac/chrp boot and setup code.
@@ -43,9 +43,9 @@
 #include <asm/bootx.h>
 #include <asm/btext.h>
 #include <asm/machdep.h>
-#include <asm/feature.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
+#include <asm/pmac_feature.h>
 
 extern void platform_init(unsigned long r3, unsigned long r4,
 		unsigned long r5, unsigned long r6, unsigned long r7);
@@ -547,8 +547,13 @@
 	loops_per_jiffy = 500000000 / HZ;
 
 #ifdef CONFIG_ALL_PPC
-	feature_init();
-#endif
+	/* This could be called "early setup arch", it must be done
+	 * now because xmon need it
+	 */
+	if (_machine == _MACH_Pmac)
+		pmac_feature_init();	/* New cool way */
+#endif /* CONFIG_ALL_PPC */
+
 #ifdef CONFIG_XMON
 	xmon_map_scc();
 	if (strstr(cmd_line, "xmon"))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/sleep.S linux/arch/ppc/kernel/sleep.S
--- linux.orig/arch/ppc/kernel/sleep.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/sleep.S	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.sleep.S 1.13 08/19/01 22:23:04 paulus
+ * BK Id: SCCS/s.sleep.S 1.18 12/02/01 12:38:54 benh
  */
 /*
  * This file contains sleep low-level functions for PowerBook G3.
@@ -16,6 +16,7 @@
 #include "ppc_asm.tmpl"
 #include <asm/processor.h>
 #include <asm/page.h>
+#include <asm/cputable.h>
 
 #define MAGIC	0x4c617273	/* 'Lars' */
 
@@ -37,8 +38,15 @@
 #define SL_IBAT3	0x58
 #define SL_TB		0x60
 #define SL_HID0		0x68
-#define SL_R2		0x6c
-#define SL_R12		0x70	/* r12 to r31 */
+#define SL_HID1		0x6c
+#define SL_MSSCR0	0x70
+#define SL_MSSSR0	0x74
+#define SL_ICTRL	0x78
+#define SL_LDSTCR	0x7c
+#define SL_LDSTDB	0x80
+#define SL_R2		0x84
+#define SL_CR		0x88
+#define SL_R12		0x8c	/* r12 to r31 */
 #define SL_SIZE		(SL_R12 + 80)
 
 #define tophys(rd,rs)   addis   rd,rs,-KERNELBASE@h     
@@ -56,6 +64,8 @@
 	mflr	r0
 	stw	r0,4(r1)
 	stwu	r1,-SL_SIZE(r1)
+	mfcr	r0
+	stw	r0,SL_CR(r1)
 	stw	r2,SL_R2(r1)
 	stmw	r12,SL_R12(r1)
 
@@ -121,7 +131,31 @@
 	/* Save HID0 */
 	mfspr	r4,HID0
 	stw	r4,SL_HID0(r1)
-	
+
+	/* Save 7400/7410/7450 specific registers */
+	mfspr	r3,PVR
+	srwi	r3,r3,16
+	cmpli	cr0,r3,0x8000
+	cmpli	cr1,r3,0x000c
+	cmpli	cr2,r3,0x800c
+	cror	4*cr1+eq,4*cr1+eq,4*cr2+eq
+	cror	4*cr0+eq,4*cr0+eq,4*cr1+eq
+	bne	1f
+	mfspr	r4,SPRN_MSSCR0
+	stw	r4,SL_MSSCR0(r1)
+	mfspr	r4,SPRN_MSSSR0
+	stw	r4,SL_MSSSR0(r1)
+	/* Save 7450 specific registers */
+	beq	cr1,1f
+	mfspr	r4,HID1
+	stw	r4,SL_HID1(r1)
+	mfspr	r4,SPRN_ICTRL
+	stw	r4,SL_ICTRL(r1)
+	mfspr	r4,SPRN_LDSTCR
+	stw	r4,SL_LDSTCR(r1)
+	mfspr	r4,SPRN_LDSTDB
+	stw	r4,SL_LDSTDB(r1)
+1:
 	/* The ROM can wake us up via 2 different vectors:
 	 *  - On wallstreet & lombard, we must write a magic
 	 *    value 'Lars' at address 4 and a pointer to a
@@ -257,6 +291,19 @@
 	mtspr	HID0,r3
 	sync
 	
+	/* Restore the kernel's segment registers before
+	 * we do any r1 memory access as we are not sure they
+	 * are in a sane state above the first 256Mb region
+	 */
+	li	r0,16		/* load up segment register values */
+	mtctr	r0		/* for context 0 */
+	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
+	li	r4,0
+3:	mtsrin	r3,r4
+	addi	r3,r3,0x111	/* increment VSID */
+	addis	r4,r4,0x1000	/* address of next segment */
+	bdnz	3b
+	
 	/* Restore the remaining bits of the HID0 register. */
 	subi	r1,r1,SL_PC
 	lwz	r3,SL_HID0(r1)
@@ -266,17 +313,52 @@
 	sync
 	isync
 
-	/* Restore the kernel's segment registers, the
-	   BATs, and SDR1.  Then we can turn on the MMU. */
-	li	r0,16		/* load up segment register values */
-	mtctr	r0		/* for context 0 */
-	lis	r3,0x2000	/* Ku = 1, VSID = 0 */
+	/* Restore 7400/7410/7450 specific registers */
+	mfspr	r3,PVR
+	srwi	r3,r3,16
+	cmpli	cr0,r3,0x8000
+	cmpli	cr1,r3,0x000c
+	cmpli	cr2,r3,0x800c
+	cror	4*cr1+eq,4*cr1+eq,4*cr2+eq
+	cror	4*cr0+eq,4*cr0+eq,4*cr1+eq
+	bne	1f
+	lwz	r4,SL_MSSCR0(r1)
+	sync
+	mtspr	SPRN_MSSCR0,r4
+	sync
+	isync
+	lwz	r4,SL_MSSSR0(r1)
+	sync
+	mtspr	SPRN_MSSSR0,r4
+	sync
+	isync
+	bne	cr2,1f
 	li	r4,0
-3:	mtsrin	r3,r4
-	addi	r3,r3,0x111	/* increment VSID */
-	addis	r4,r4,0x1000	/* address of next segment */
-	bdnz	3b
-	
+	mtspr	SPRN_L2CR2,r4
+	/* Restore 7450 specific registers */
+	beq	cr1,1f
+	lwz	r4,SL_HID1(r1)
+	sync
+	mtspr	HID1,r4
+	isync
+	sync
+	lwz	r4,SPRN_ICTRL(r1)
+	sync
+	mtspr	SPRN_ICTRL,r4
+	isync
+	sync
+	lwz	r4,SPRN_LDSTCR(r1)
+	sync
+	mtspr	SPRN_LDSTCR,r4
+	isync
+	sync
+	lwz	r4,SL_LDSTDB(r1)
+	sync
+	mtspr	SPRN_LDSTDB,r4
+	isync
+	sync
+1:
+	/* Restore the BATs, and SDR1.  Then we can turn on the MMU. */
 	lwz	r4,SL_SDR1(r1)
 	mtsdr1	r4
 	lwz	r4,SL_SPRG0(r1)
@@ -344,6 +426,8 @@
 	mttbl	r4
 
 	/* Restore the callee-saved registers and return */
+	lwz	r0,SL_CR(r1)
+	mtcr	r0
 	lwz	r2,SL_R2(r1)
 	lmw	r12,SL_R12(r1)
 	addi	r1,r1,SL_SIZE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- linux.orig/arch/ppc/kernel/time.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.time.c 1.26 10/05/01 08:29:42 trini
+ * BK Id: SCCS/s.time.c 1.29 12/11/01 11:40:45 trini
  */
 /*
  * Common time routines among all ppc machines.
@@ -352,15 +352,12 @@
         	tz.tz_dsttime = 0;
         	do_sys_settimeofday(NULL, &tz);
         }
-
-       do_get_fast_time = do_gettimeofday;
 }
 
-#define TICK_SIZE tick
-#define FEBRUARY	2
-#define	STARTOFTIME	1970
-#define SECDAY		86400L
-#define SECYR		(SECDAY * 365)
+#define FEBRUARY		2
+#define	STARTOFTIME		1970
+#define SECDAY			86400L
+#define SECYR			(SECDAY * 365)
 #define	leapyear(year)		((year) % 4 == 0)
 #define	days_in_year(a) 	(leapyear(a) ? 366 : 365)
 #define	days_in_month(a) 	(month_days[(a) - 1])
@@ -369,55 +366,12 @@
 	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 };
 
-/*
- * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
- */
-void GregorianDay(struct rtc_time * tm)
-{
-	int leapsToDate;
-	int lastYear;
-	int day;
-	int MonthOffset[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
-
-	lastYear=tm->tm_year-1;
-
-	/*
-	 * Number of leap corrections to apply up to end of last year
-	 */
-	leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
-
-	/*
-	 * This year is a leap year if it is divisible by 4 except when it is
-	 * divisible by 100 unless it is divisible by 400
-	 *
-	 * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
-	 */
-	if((tm->tm_year%4==0) &&
-	   ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
-	   (tm->tm_mon>2))
-	{
-		/*
-		 * We are past Feb. 29 in a leap year
-		 */
-		day=1;
-	}
-	else
-	{
-		day=0;
-	}
-
-	day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] +
-		   tm->tm_mday;
-
-	tm->tm_wday=day%7;
-}
-
 void to_tm(int tim, struct rtc_time * tm)
 {
-	register int    i;
-	register long   hms, day;
+	register int i;
+	register long hms, day, gday;
 
-	day = tim / SECDAY;
+	gday = day = tim / SECDAY;
 	hms = tim % SECDAY;
 
 	/* Hours, minutes, seconds are easy */
@@ -442,9 +396,9 @@
 	tm->tm_mday = day + 1;
 
 	/*
-	 * Determine the day of week
+	 * Determine the day of week. Jan. 1, 1970 was a Thursday.
 	 */
-	GregorianDay(tm);
+	tm->tm_wday = (gday + 4) % 7;
 }
 
 /* Auxiliary function to compute scaling factors */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/kernel/xics.c linux/arch/ppc/kernel/xics.c
--- linux.orig/arch/ppc/kernel/xics.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/kernel/xics.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.xics.c 1.5 05/17/01 18:14:22 cort
+ * BK Id: SCCS/s.xics.c 1.8 12/19/01 09:48:40 trini
  */
 /*
  * arch/ppc/kernel/xics.c
@@ -157,7 +157,7 @@
 	vec &= 0x00ffffff;
 	/* for sanity, this had better be < NR_IRQS - 16 */
 	if( vec == XICS_IRQ_8259_CASCADE )
-		irq = i8259_irq(cpu);
+		irq = i8259_poll();
 	else if( vec == XICS_IRQ_SPURIOUS )
 		irq = -1;
 	else
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- linux.orig/arch/ppc/mm/init.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/mm/init.c	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.init.c 1.36 09/22/01 14:03:09 paulus
+ * BK Id: SCCS/s.init.c 1.40 01/25/02 15:15:24 benh
  */
 /*
  *  PowerPC version 
@@ -49,8 +49,6 @@
 #include "mem_pieces.h"
 #include "mmu_decl.h"
 
-#define MAX_LOW_MEM	(0xF0000000UL - KERNELBASE)
-
 mmu_gather_t mmu_gathers[NR_CPUS];
 
 void *end_of_DRAM;
@@ -62,6 +60,9 @@
 int boot_mapsize;
 unsigned long totalram_pages;
 unsigned long totalhigh_pages;
+#ifdef CONFIG_ALL_PPC
+unsigned long agp_special_page;
+#endif
 
 extern char _end[];
 extern char etext[], _stext[];
@@ -85,7 +86,7 @@
 char *klimit = _end;
 struct mem_pieces phys_avail;
 
-extern char *sysmap; 
+extern char *sysmap;
 extern unsigned long sysmap_size;
 
 /*
@@ -97,8 +98,6 @@
 
 /* max amount of RAM to use */
 unsigned long __max_memory;
-/* max amount of low RAM to map in */
-unsigned long __max_low_memory = MAX_LOW_MEM;
 
 int do_check_pgt_cache(int low, int high)
 {
@@ -313,12 +312,7 @@
 	if (__max_memory && total_memory > __max_memory)
 		total_memory = __max_memory;
 	total_lowmem = total_memory;
-	if (total_lowmem > __max_low_memory) {
-		total_lowmem = __max_low_memory;
-#ifndef CONFIG_HIGHMEM
-		total_memory = total_lowmem;
-#endif /* CONFIG_HIGHMEM */
-	}
+	adjust_total_lowmem();	
 	end_of_DRAM = __va(total_lowmem);
 	set_phys_avail(total_lowmem);
 
@@ -352,9 +346,10 @@
 		ppc_md.progress("MMU:exit", 0x211);
 
 #ifdef CONFIG_BOOTX_TEXT
-	/* Must be done last, or ppc_md.progress will die */
-	if (have_of)
-		map_boot_text();
+	/* By default, we are no longer mapped */
+	boot_text_mapped = 0;	
+	/* Must be done last, or ppc_md.progress will die. */
+	map_boot_text();
 #endif
 }
 
@@ -474,20 +469,22 @@
 	}
 #endif /* CONFIG_BLK_DEV_INITRD */
 
-#if defined(CONFIG_ALL_PPC)	
+#if defined(CONFIG_ALL_PPC)
 	/* mark the RTAS pages as reserved */
 	if ( rtas_data )
 		for (addr = (ulong)__va(rtas_data);
 		     addr < PAGE_ALIGN((ulong)__va(rtas_data)+rtas_size) ;
 		     addr += PAGE_SIZE)
 			SetPageReserved(virt_to_page(addr));
+	if (agp_special_page)
+		SetPageReserved(virt_to_page(agp_special_page));
 #endif /* defined(CONFIG_ALL_PPC) */
 	if ( sysmap )
 		for (addr = (unsigned long)sysmap;
 		     addr < PAGE_ALIGN((unsigned long)sysmap+sysmap_size) ;
 		     addr += PAGE_SIZE)
 			SetPageReserved(virt_to_page(addr));
-	
+
 	for (addr = PAGE_OFFSET; addr < (unsigned long)end_of_DRAM;
 	     addr += PAGE_SIZE) {
 		if (!PageReserved(virt_to_page(addr)))
@@ -526,6 +523,10 @@
 	if (sysmap)
 		printk("System.map loaded at 0x%08x for debugger, size: %ld bytes\n",
 			(unsigned int)sysmap, sysmap_size);
+#if defined(CONFIG_ALL_PPC)
+	if (agp_special_page)
+		printk(KERN_INFO "AGP special page: 0x%08lx\n", agp_special_page);
+#endif /* defined(CONFIG_ALL_PPC) */
 	mem_init_done = 1;
 }
 
@@ -572,6 +573,20 @@
 	/* remove the sysmap pages from the available memory */
 	if (sysmap)
 		mem_pieces_remove(&phys_avail, __pa(sysmap), sysmap_size, 1);
+	/* Because of some uninorth weirdness, we need a page of
+	 * memory as high as possible (it must be outside of the
+	 * bus address seen as the AGP aperture). It will be used
+	 * by the r128 DRM driver
+	 * 
+	 * FIXME: We need to make sure that page doesn't overlap any of the\
+	 * above. This could be done by improving mem_pieces_find to be able
+	 * to do a backward search from the end of the list.
+	 */
+	if (_machine == _MACH_Pmac && find_devices("uni-north-agp")) {
+		agp_special_page = (total_memory - PAGE_SIZE);
+		mem_pieces_remove(&phys_avail, agp_special_page, PAGE_SIZE, 0);	
+		agp_special_page = (unsigned long)__va(agp_special_page);
+	}
 #endif /* CONFIG_ALL_PPC */
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/mm/mmu_decl.h linux/arch/ppc/mm/mmu_decl.h
--- linux.orig/arch/ppc/mm/mmu_decl.h	Sat Nov  3 01:43:54 2001
+++ linux/arch/ppc/mm/mmu_decl.h	Mon Feb  4 18:47:24 2002
@@ -24,7 +24,8 @@
  */
 
 extern void mapin_ram(void);
-extern void bat_mapin_ram(void);
+extern void bat_mapin_ram(unsigned long bat2, unsigned long bat3);
+extern void adjust_total_lowmem(void);
 extern int map_page(unsigned long va, unsigned long pa, int flags);
 extern void setbat(int index, unsigned long virt, unsigned long phys,
 		   unsigned int size, int flags);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/mm/pgtable.c linux/arch/ppc/mm/pgtable.c
--- linux.orig/arch/ppc/mm/pgtable.c	Tue Oct  2 16:12:44 2001
+++ linux/arch/ppc/mm/pgtable.c	Mon Feb  4 18:59:24 2002
@@ -41,6 +41,11 @@
 unsigned long ioremap_bot;
 int io_bat_index;
 
+/* Maximum 768Mb of lowmem. On SMP, this value will be
+ * trimmed down to whatever can be covered by BATs though.
+ */
+#define MAX_LOW_MEM	0x30000000
+
 #ifndef CONFIG_SMP
 struct pgtable_cache_struct quicklists;
 #endif
@@ -49,6 +54,10 @@
 #define HAVE_BATS	1
 #endif
 
+#ifdef HAVE_BATS
+static unsigned long __bat2, __bat3;
+#endif
+
 extern char etext[], _stext[];
 
 #ifdef HAVE_BATS
@@ -186,6 +195,65 @@
 	return err;
 }
 
+void __init
+adjust_total_lowmem(void)
+{
+	unsigned long max_low_mem = MAX_LOW_MEM;
+	
+#ifdef HAVE_BATS
+	unsigned long bat_max = 0x10000000;
+	unsigned long align;
+	unsigned long ram = total_lowmem;
+	int is601 = 0;
+	
+	/* 601s have smaller BATs */
+	if (PVR_VER(mfspr(PVR)) == 1) {
+		bat_max = 0x00800000;
+		is601 = 1;
+	}
+
+	/* Make sure we don't map a block larger than the
+	   smallest alignment of the physical address. */
+	/* alignment of ram_phys_base */
+	align = ~(ram_phys_base-1) & ram_phys_base;
+	/* set BAT block size to MIN(max_size, align) */
+	if (align && align < bat_max)
+		bat_max = align;
+
+	/* Calculate BAT values */	
+	__bat2 = 1UL << __ilog2(ram);
+	if (__bat2 > bat_max)
+		__bat2 = bat_max;
+	ram -= __bat2;
+	if (ram) {
+		__bat3 = 1UL << __ilog2(ram);
+		if (__bat3 > bat_max)
+			__bat3 = bat_max;
+		ram -= __bat3;
+	}
+
+	printk(KERN_INFO "Memory BAT mapping: BAT2=%ldMb, BAT3=%ldMb, residual: %ldMb\n",
+		__bat2 >> 20, __bat3 >> 20, ram >> 20);
+
+	/* On SMP, we limit the lowmem to the area mapped with BATs.
+	 * We also assume nobody will do SMP with 601s
+	 */
+#ifdef CONFIG_SMP
+	if (!is601)
+		max_low_mem = __bat2 + __bat3;
+#endif /* CONFIG_SMP */
+
+#endif /* HAVE_BATS */
+	if (total_lowmem > max_low_mem) {
+		total_lowmem = max_low_mem;
+#ifndef CONFIG_HIGHMEM
+		printk(KERN_INFO "Warning, memory limited to %ld Mb, use CONFIG_HIGHMEM"
+			" to reach %ld Mb\n", max_low_mem >> 20, total_lowmem >> 20);
+		total_memory = total_lowmem;
+#endif /* CONFIG_HIGHMEM */
+	}
+}
+
 /*
  * Map in all of physical memory starting at KERNELBASE.
  */
@@ -195,7 +263,7 @@
 
 #ifdef HAVE_BATS
 	if (!__map_without_bats)
-		bat_mapin_ram();
+		bat_mapin_ram(__bat2, __bat3);
 #endif /* HAVE_BATS */
 
 	v = KERNELBASE;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/mm/ppc_mmu.c linux/arch/ppc/mm/ppc_mmu.c
--- linux.orig/arch/ppc/mm/ppc_mmu.c	Mon Nov 26 13:29:17 2001
+++ linux/arch/ppc/mm/ppc_mmu.c	Mon Feb  4 18:47:24 2002
@@ -86,37 +86,16 @@
 	return 0;
 }
 
-void __init bat_mapin_ram(void)
+void __init bat_mapin_ram(unsigned long bat2, unsigned long bat3)
 {
-	unsigned long tot, bl, done;
-	unsigned long max_size = (256<<20);
-	unsigned long align;
-
-	/* Set up BAT2 and if necessary BAT3 to cover RAM. */
-
-	/* Make sure we don't map a block larger than the
-	   smallest alignment of the physical address. */
-	/* alignment of ram_phys_base */
-	align = ~(ram_phys_base-1) & ram_phys_base;
-	/* set BAT block size to MIN(max_size, align) */
-	if (align && align < max_size)
-		max_size = align;
-
+	unsigned long tot, done;
+	
 	tot = total_lowmem;
-	for (bl = 128<<10; bl < max_size; bl <<= 1) {
-		if (bl * 2 > tot)
-			break;
-	}
-
-	setbat(2, KERNELBASE, ram_phys_base, bl, _PAGE_KERNEL);
+	setbat(2, KERNELBASE, ram_phys_base, bat2, _PAGE_KERNEL);
 	done = (unsigned long)bat_addrs[2].limit - KERNELBASE + 1;
-	if ((done < tot) && !bat_addrs[3].limit) {
-		/* use BAT3 to cover a bit more */
+	if ((done < tot) && !bat_addrs[3].limit && bat3) {
 		tot -= done;
-		for (bl = 128<<10; bl < max_size; bl <<= 1)
-			if (bl * 2 > tot)
-				break;
-		setbat(3, KERNELBASE+done, ram_phys_base+done, bl, 
+		setbat(3, KERNELBASE+done, ram_phys_base+done, bat3, 
 		       _PAGE_KERNEL);
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/nonstdio.h linux/arch/ppc/xmon/nonstdio.h
--- linux.orig/arch/ppc/xmon/nonstdio.h	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/xmon/nonstdio.h	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.nonstdio.h 1.5 05/17/01 18:14:23 cort
+ * BK Id: SCCS/s.nonstdio.h 1.8 12/01/01 20:09:07 benh
  */
 typedef int	FILE;
 extern FILE *xmon_stdin, *xmon_stdout;
@@ -21,5 +21,6 @@
 extern void xmon_printf(const char *, ...);
 extern void xmon_fprintf(void *, const char *, ...);
 extern void xmon_sprintf(char *, const char *, ...);
+extern void xmon_puts(char*);
 
 #define perror(s)	printf("%s: no files!\n", (s))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/start.c linux/arch/ppc/xmon/start.c
--- linux.orig/arch/ppc/xmon/start.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/xmon/start.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.start.c 1.16 08/20/01 22:17:58 paulus
+ * BK Id: SCCS/s.start.c 1.18 12/01/01 20:09:07 benh
  */
 /*
  * Copyright (C) 1996 Paul Mackerras.
@@ -13,9 +13,11 @@
 #include <linux/pmu.h>
 #include <linux/cuda.h>
 #include <linux/kernel.h>
+#include <linux/errno.h>
 #include <asm/prom.h>
 #include <asm/bootx.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/processor.h>
 #include <asm/delay.h>
 #include <asm/btext.h>
@@ -32,7 +34,7 @@
 static int use_screen;
 static int via_modem;
 static int xmon_use_sccb;
-static struct device_node *macio_node;
+static struct device_node *channel_node;
 
 #define TB_SPEED	25000000
 
@@ -99,6 +101,7 @@
 				np = np->sibling;
 			if (np != NULL) {
 				/* XXX should parse this properly */
+				channel_node = np;
 				slots = get_property(np, "slot-names", &l);
 				if (slots != NULL && l >= 10
 				    && strcmp(slots+4, "Modem") == 0)
@@ -126,10 +129,8 @@
 		RXRDY = 1;
 		
 		np = find_devices("mac-io");
-		if (np && np->n_addrs) {
-			macio_node = np;
+		if (np && np->n_addrs)
 			addr = np->addrs[0].address + 0x13020;
-		}
 		base = (volatile unsigned char *) ioremap(addr & PAGE_MASK, PAGE_SIZE);
 		sccc = base + (addr & ~PAGE_MASK);
 		sccd = sccc + 0x10;
@@ -349,12 +350,19 @@
 	{
 		int i, x;
 
-		if (macio_node != 0)
-			feature_set(macio_node, FEATURE_Serial_enable);
-		if (via_modem && macio_node != 0) {
+		if (channel_node != 0)
+			pmac_call_feature(
+				PMAC_FTR_SCC_ENABLE,
+				channel_node,
+				PMAC_SCC_ASYNC | PMAC_SCC_FLAG_XMON, 1);
+			printk(KERN_INFO "Serial port locked ON by debugger !\n");
+		if (via_modem && channel_node != 0) {
 			unsigned int t0;
 
-			feature_set(macio_node, FEATURE_Modem_power);
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				channel_node, 0, 1);
+			printk(KERN_INFO "Modem powered up by debugger !\n");
 			t0 = readtb();
 			while (readtb() - t0 < 3*TB_SPEED)
 				eieio();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/subr_prf.c linux/arch/ppc/xmon/subr_prf.c
--- linux.orig/arch/ppc/xmon/subr_prf.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/xmon/subr_prf.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.subr_prf.c 1.5 05/17/01 18:14:23 cort
+ * BK Id: SCCS/s.subr_prf.c 1.8 12/01/01 20:09:07 benh
  */
 /*
  * Written by Cort Dougan to replace the version originally used
@@ -51,3 +51,8 @@
 	va_end(ap);
 }
 
+void
+xmon_puts(char *s)
+{
+	xmon_write(stdout, s, strlen(s));
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/ppc/xmon/xmon.c linux/arch/ppc/xmon/xmon.c
--- linux.orig/arch/ppc/xmon/xmon.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/ppc/xmon/xmon.c	Wed Dec 26 16:28:34 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.xmon.c 1.16 09/22/01 15:25:10 trini
+ * BK Id: SCCS/s.xmon.c 1.18 12/01/01 20:09:07 benh
  */
 /*
  * Routines providing a simple monitor for use on the PowerMac.
@@ -90,6 +90,7 @@
 static void write_spr(int, unsigned);
 static void super_regs(void);
 static void print_sysmap(void);
+static void sysmap_lookup(void);
 static void remove_bpts(void);
 static void insert_bpts(void);
 static struct bpt *at_breakpoint(unsigned pc);
@@ -98,10 +99,7 @@
 #ifdef CONFIG_SMP
 static void cpu_cmd(void);
 #endif /* CONFIG_SMP */
-#if 0 /* Makes compile with -Wall */
-static char *pretty_print_addr(unsigned long addr);
-static char *lookup_name(unsigned long addr);
-#endif
+static int pretty_print_addr(unsigned long addr);
 static void csum(void);
 
 extern int print_insn_big_powerpc(FILE *, unsigned long, unsigned);
@@ -112,6 +110,8 @@
 
 extern void xmon_enter(void);
 extern void xmon_leave(void);
+extern char* xmon_find_symbol(unsigned long addr, unsigned long* saddr);
+extern unsigned long xmon_symbol_to_addr(char* symbol);
 
 #define GETWORD(v)	(((v)[0] << 24) + ((v)[1] << 16) + ((v)[2] << 8) + (v)[3])
 
@@ -121,6 +121,7 @@
 #define isalnum(c)	(('0' <= (c) && (c) <= '9') \
 			 || ('a' <= (c) && (c) <= 'z') \
 			 || ('A' <= (c) && (c) <= 'Z'))
+#define isspace(c)	(c == ' ' || c == '\t' || c == 10 || c == 13 || c == 0)
 
 static char *help_string = "\
 Commands:\n\
@@ -138,6 +139,8 @@
   r	print registers\n\
   S	print special registers\n\
   t	print backtrace\n\
+  la	lookup address in system.map\n\
+  ls	lookup symbol in system.map\n\
   x	exit monitor\n\
 ";
 
@@ -147,6 +150,19 @@
 
 static struct pt_regs *xmon_regs[NR_CPUS];
 
+extern inline void sync(void)
+{
+	asm volatile("sync; isync");
+}
+
+extern inline void __delay(unsigned int loops)
+{
+	if (loops != 0)
+		__asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
+				     "r" (loops) : "ctr");
+}
+
+
 void
 xmon(struct pt_regs *excp)
 {
@@ -398,6 +414,9 @@
 		case 'd':
 			dump();
 			break;
+		case 'l':
+			sysmap_lookup();
+			break;
 		case 'r':
 			if (excp != NULL)
 				prregs(excp);	/* print regs */
@@ -464,8 +483,8 @@
 	if (cmd == 'i') {
 		/* interrupt other cpu(s) */
 		cpu = MSG_ALL_BUT_SELF;
-		scanhex(&cpu);
-		smp_send_xmon_break(cpu);
+		if (scanhex(&cpu))
+			smp_send_xmon_break(cpu);
 		return;
 	}
 	termch = cmd;
@@ -547,8 +566,10 @@
 	unsigned short fcs;
 	unsigned char v;
 
-	scanhex(&adrs);
-	scanhex(&ncsum);
+	if (!scanhex(&adrs))
+		return;
+	if (!scanhex(&ncsum))
+		return;
 	fcs = 0xffff;
 	for (i = 0; i < ncsum; ++i) {
 		if (mread(adrs+i, &v, 1) == 0) {
@@ -580,6 +601,11 @@
 			mode = 6;
 		else
 			termch = cmd;
+		cmd = inchar();
+		if (cmd == 'p')
+			mode &= ~4;
+		else
+			termch = cmd;
 		dabr.address = 0;
 		dabr.count = 0;
 		dabr.enabled = scanhex(&dabr.address);
@@ -588,11 +614,16 @@
 			dabr.address = (dabr.address & ~7) | mode;
 		break;
 	case 'i':
+		cmd = inchar();
+		if (cmd == 'p')
+			mode = 2;
+		else
+			mode = 3;
 		iabr.address = 0;
 		iabr.count = 0;
 		iabr.enabled = scanhex(&iabr.address);
 		if (iabr.enabled)
-			iabr.address |= 3;
+			iabr.address |= mode;
 		scanhex(&iabr.count);
 		break;
 #endif
@@ -625,6 +656,8 @@
 					printf("r");
 				if (dabr.address & 2)
 					printf("w");
+				if (dabr.address & 4)
+					printf("p");
 				printf("]\n");
 			}
 			if (iabr.enabled)
@@ -674,7 +707,8 @@
 	for (; sp != 0; sp = stack[0]) {
 		if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
 			break;
-		printf("%x ", stack[1]);
+		pretty_print_addr(stack[1]);
+		printf(" ");
 		if (stack[1] == (unsigned) &ret_from_intercept
 		    || stack[1] == (unsigned) &ret_from_except
 		    || stack[1] == (unsigned) &ret_from_syscall_1
@@ -688,8 +722,8 @@
 			if (mread(sp, stack, sizeof(stack)) != sizeof(stack))
 				break;
 		}
+		printf("\n");
 	}
-	printf("\n");
 }
 
 int
@@ -707,10 +741,11 @@
 #ifdef CONFIG_SMP
 	printf("cpu %d: ", smp_processor_id());
 #endif /* CONFIG_SMP */
-	printf("vector: %x at pc = %x",
-	       fp->trap, fp->nip);
-	printf(", lr = %x, msr = %x, sp = %x [%x]\n",
-	       fp->link, fp->msr, fp->gpr[1], fp);
+	printf("vector: %x at pc = ", fp->trap);
+	pretty_print_addr(fp->nip);
+	printf(", lr = ");
+	pretty_print_addr(fp->link);
+	printf("\nmsr = %x, sp = %x [%x]\n", fp->msr, fp->gpr[1], fp);
 	if (fp->trap == 0x300 || fp->trap == 0x600)
 		printf("dar = %x, dsisr = %x\n", fp->dar, fp->dsisr);
 	if (current)
@@ -795,8 +830,16 @@
 print_sysmap(void)
 {
 	extern char *sysmap;
-	if ( sysmap )
-		printf("System.map: \n%s", sysmap);
+	if ( sysmap ) {
+		printf("System.map: \n");
+		if( setjmp(bus_error_jmp) == 0 ) {
+			debugger_fault_handler = handle_fault;
+			sync();
+			xmon_puts(sysmap);
+			sync();
+		}
+		debugger_fault_handler = 0;
+	}
 	else
 		printf("No System.map\n");
 }
@@ -1028,17 +1071,6 @@
 /*
  * Stuff for reading and writing memory safely
  */
-extern inline void sync(void)
-{
-	asm volatile("sync; isync");
-}
-
-extern inline void __delay(unsigned int loops)
-{
-	if (loops != 0)
-		__asm__ __volatile__("mtctr %0; 1: bdnz 1b" : :
-				     "r" (loops) : "ctr");
-}
 
 int
 mread(unsigned adrs, void *buf, int size)
@@ -1565,6 +1597,24 @@
 		}
 		printf("invalid register name '%%%s'\n", regname);
 		return 0;
+	} else if (c == '$') {
+		static char symname[64];
+		int i;
+		for (i=0; i<63; i++) {
+			c = inchar();
+			if (isspace(c)) {
+				termch = c;
+				break;
+			}
+			symname[i] = c;
+		}
+		symname[i++] = 0;
+		*vp = xmon_symbol_to_addr(symname);
+		if (!(*vp)) {
+			printf("unknown symbol\n");
+			return 0;
+		}
+		return 1;
 	}
 
 	d = hexdigit(c);
@@ -1652,38 +1702,169 @@
 	lineptr = str;
 }
 
-#if 0 /* Makes compile with -Wall */
-static char *pretty_print_addr(unsigned long addr)
+void
+sysmap_lookup(void)
 {
-	printf("%08x", addr);
-	if ( lookup_name(addr) )
-		printf(" %s", lookup_name(addr) );
-	return NULL;
+	int type = inchar();
+	unsigned addr;
+	static char tmp[64];
+	char* cur;
+
+	extern char *sysmap;
+	extern unsigned long sysmap_size;
+	if ( !sysmap || !sysmap_size )
+		return;
+
+	switch(type) {
+		case 'a':
+			if (scanhex(&addr)) {
+				pretty_print_addr(addr);
+				printf("\n");
+			}
+			termch = 0;
+			break;
+		case 's':
+			getstring(tmp, 64);
+			if( setjmp(bus_error_jmp) == 0 ) {
+				debugger_fault_handler = handle_fault;
+				sync();
+				cur = sysmap;
+				do {
+					cur = strstr(cur, tmp);
+					if (cur) {
+						static char res[64];
+						char *p, *d;
+						p = cur;
+						while(p > sysmap && *p != 10)
+							p--;
+						if (*p == 10) p++;
+						d = res;
+						while(*p && p < (sysmap + sysmap_size) && *p != 10)
+							*(d++) = *(p++);
+						*(d++) = 0;
+						printf("%s\n", res);
+						cur++;
+					}
+				} while (cur);
+				sync();
+			}
+			debugger_fault_handler = 0;
+			termch = 0;
+			break;
+	}
 }
-#endif
 
-#if 0 /* Makes compile with -Wall */
-static char *lookup_name(unsigned long addr)
+static int
+pretty_print_addr(unsigned long addr)
 {
+	char *sym;
+	unsigned long saddr;
+	
+	printf("%08x", addr);
+	sym = xmon_find_symbol(addr, &saddr);
+	if (sym)
+		printf(" (%s+0x%x)", sym, addr-saddr);
+	return (sym != 0);
+}
+
+char*
+xmon_find_symbol(unsigned long addr, unsigned long* saddr)
+{
+	static char rbuffer[64];
+	char *p, *ep, *limit;
+	unsigned long prev, next;
+	char* psym;
+
 	extern char *sysmap;
 	extern unsigned long sysmap_size;
-	char *c = sysmap;
-	unsigned long cmp;
 	if ( !sysmap || !sysmap_size )
 		return NULL;
-return NULL;	
-#if 0
-	cmp = simple_strtoul(c, &c, 8);
-	/* XXX crap, we don't want the whole of the rest of the map - paulus */
-	strcpy( last, strsep( &c, "\n"));
-	while ( c < (sysmap+sysmap_size) )
-	{
-		cmp = simple_strtoul(c, &c, 8);
-		if ( cmp < addr )
-			break;
-		strcpy( last, strsep( &c, "\n"));
+	
+	prev = 0;
+	psym = NULL;
+	p = sysmap;
+	limit = p + sysmap_size;
+	if( setjmp(bus_error_jmp) == 0 ) {
+		debugger_fault_handler = handle_fault;
+		sync();
+		do {
+			next = simple_strtoul(p, &p, 16);
+			if (next > addr && prev <= addr) {
+				if (!psym)
+					goto bail;
+				ep = rbuffer;
+				p = psym;
+				while(*p && p < limit && *p == 32)
+					p++;
+				while(*p && p < limit && *p != 10 && (ep - rbuffer) < 63)
+					*(ep++) = *(p++);
+				*(ep++) = 0;
+				if (saddr)
+					*saddr = prev;
+				debugger_fault_handler = 0;
+				return rbuffer;
+			}
+			prev = next;
+			psym = p;
+			while(*p && p < limit && *p != 10)
+				p++;
+			if (*p) p++;
+		} while(*p && p < limit && next);
+bail:
+		sync();
 	}
-	return last;
-#endif	
+	debugger_fault_handler = 0;
+	return NULL;
 }
-#endif
+
+unsigned long
+xmon_symbol_to_addr(char* symbol)
+{
+	char *p, *cur;
+	char *match;
+	int goodness = 0;
+	int result = 0;
+	
+	extern char *sysmap;
+	extern unsigned long sysmap_size;
+	if ( !sysmap || !sysmap_size )
+		return 0;
+
+	if( setjmp(bus_error_jmp) == 0 ) {
+		debugger_fault_handler = handle_fault;
+		sync();
+		cur = sysmap;
+		while(cur) {
+			cur = strstr(cur, symbol);
+			if (cur) {
+				int gd = 1;
+
+				/* best match if equal, better match if
+				 * begins with
+				 */
+				if (cur == sysmap || *(cur-1) == ' ') {
+					gd++;
+					if (cur[strlen(symbol)] == 10)
+						gd++;
+				}
+				if (gd > goodness) {
+					match = cur;
+					goodness = gd;
+					if (gd == 3)
+						break;
+				}
+				cur++;
+			}
+		}	
+		if (goodness) {
+			p = match;
+			while(p > sysmap && *p != 10)
+				p--;
+			if (*p == 10) p++;
+			result = simple_strtoul(p, &p, 16);
+		}
+		sync();
+	}
+	debugger_fault_handler = 0;
+	return result;
+}		
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/Makefile linux/arch/s390/Makefile
--- linux.orig/arch/s390/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/Makefile	Fri Dec 21 16:25:30 2001
@@ -65,3 +65,6 @@
 
 archdep:
 	@$(MAKEBOOT) dep
+
+install: vmlinux
+	@$(MAKEBOOT) BOOTIMAGE=image install
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/boot/Makefile linux/arch/s390/boot/Makefile
--- linux.orig/arch/s390/boot/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/boot/Makefile	Fri Dec 21 16:25:30 2001
@@ -35,3 +35,6 @@
 clean:
 	rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
 
+install: $(CONFIGURE) $(BOOTIMAGE)
+	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/config.in linux/arch/s390/config.in
--- linux.orig/arch/s390/config.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/config.in	Fri Dec 21 16:25:30 2001
@@ -67,9 +67,9 @@
 comment 'Kernel hacking'
 
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-if [ "$CONFIG_CTC" = "y" ]; then
-  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
-fi
+#if [ "$CONFIG_CTC" = "y" ]; then
+#  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
+#fi
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/defconfig linux/arch/s390/defconfig
--- linux.orig/arch/s390/defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/defconfig	Fri Dec 21 16:25:30 2001
@@ -7,7 +7,7 @@
 CONFIG_UID16=y
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_GENERIC_BUST_SPINLOCK=n
+# CONFIG_GENERIC_BUST_SPINLOCK is not set
 CONFIG_ARCH_S390=y
 
 #
@@ -103,8 +103,8 @@
 #
 # S/390 tape hardware support
 #
-CONFIG_S390_TAPE_3490=y
-CONFIG_S390_TAPE_3480=y
+CONFIG_S390_TAPE_3490=m
+CONFIG_S390_TAPE_3480=m
 
 #
 # Network device drivers
@@ -150,6 +150,7 @@
 # CONFIG_IPV6_NETLINK is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
 
 #
 #  
@@ -180,12 +181,12 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
-# CONFIG_CMS_FS is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_JBD is not set
 # CONFIG_JBD_DEBUG is not set
@@ -203,7 +204,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
-# CONFIG_FREEVXFS_FS is not set
+# CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_NTFS_RW is not set
 # CONFIG_HPFS_FS is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/debug.c linux/arch/s390/kernel/debug.c
--- linux.orig/arch/s390/kernel/debug.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/debug.c	Fri Dec 21 16:25:30 2001
@@ -228,8 +228,10 @@
 	strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1)));
 	rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0;
 	memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
+#ifdef CONFIG_PROC_FS
 	memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS *
 		sizeof(struct proc_dir_entry*));
+#endif /* CONFIG_PROC_FS */
 	atomic_set(&(rc->ref_count), 0);
 
 	return rc;
@@ -346,8 +348,10 @@
 	if (!db_info)
 		return;
 	if (atomic_dec_and_test(&db_info->ref_count)) {
+#ifdef DEBUG
 		printk(KERN_INFO "debug: freeing debug area %p (%s)\n",
 		       db_info, db_info->name);
+#endif
 		for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
 			if (db_info->views[i] != NULL)
 				debug_delete_proc_dir_entry
@@ -541,14 +545,18 @@
 	debug_info_snapshot = debug_info_copy(debug_info);
 
 	if(!debug_info_snapshot){
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: debug_info_copy failed (out of mem)\n");
+#endif
 		rc = -ENOMEM;
 		goto out;
 	}
 
 	if ((file->private_data =
 	     kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) {
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: kmalloc failed\n");
+#endif
 		debug_info_free(debug_info_snapshot);	
 		rc = -ENOMEM;
 		goto out;
@@ -602,6 +610,7 @@
 {
 	struct proc_dir_entry *rc = NULL;
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	const char *fn = name;
 	int len;
@@ -634,6 +643,7 @@
 #endif
 
       out:
+#endif /* CONFIG_PROC_FS */
 	return rc;
 }
 
@@ -646,12 +656,14 @@
     (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) 
 {
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	proc_unregister(root, proc_entry->low_ino);
 	kfree(proc_entry);
 #else
 	remove_proc_entry(proc_entry->name, root);
 #endif
+#endif /* CONFIG_PROC_FS */
 }
 
 /*
@@ -677,9 +689,11 @@
 		goto out;
 	debug_register_view(rc, &debug_level_view);
         debug_register_view(rc, &debug_flush_view);
+#ifdef DEBUG
 	printk(KERN_INFO
 	       "debug: reserved %d areas of %d pages for debugging %s\n",
 	       nr_areas, 1 << page_order, rc->name);
+#endif
       out:
         if (rc == NULL){
 		printk(KERN_ERR "debug: debug_register failed for %s\n",name);
@@ -699,7 +713,9 @@
 	if (!id)
 		goto out;
 	down(&debug_lock);
+#ifdef DEBUG
 	printk(KERN_INFO "debug: unregistering %s\n", id->name);
+#endif
 	debug_info_put(id);
 	up(&debug_lock);
 
@@ -906,11 +922,13 @@
 
 	down(&debug_lock);
 	if (!initialized) {
+#ifdef CONFIG_PROC_FS
 		debug_proc_root_entry =
 		    debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT,
 						S_IFDIR | S_IRUGO | S_IXUGO
 						| S_IWUSR | S_IWGRP, NULL,
 						NULL);
+#endif /* CONFIG_PROC_FS */
 		printk(KERN_INFO "debug: Initialization complete\n");
 		initialized = 1;
 	}
@@ -1271,7 +1289,9 @@
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
 #endif
+#ifdef CONFIG_PROC_FS
 	debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry);
+#endif /* CONFIG_PROC_FS */
 	return;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/entry.S linux/arch/s390/kernel/entry.S
--- linux.orig/arch/s390/kernel/entry.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/entry.S	Fri Dec 21 16:25:30 2001
@@ -79,7 +79,7 @@
 sigpending   =  8
 need_resched = 24
 tsk_ptrace   = 28
-processor    = 56
+processor    = 52
 
 /*
  * Base Address of this Module --- saved in __LC_ENTRY_BASE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/init_task.c linux/arch/s390/kernel/init_task.c
--- linux.orig/arch/s390/kernel/init_task.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/init_task.c	Fri Dec 21 16:25:30 2001
@@ -12,7 +12,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/process.c linux/arch/s390/kernel/process.c
--- linux.orig/arch/s390/kernel/process.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/process.c	Fri Dec 21 16:25:30 2001
@@ -75,173 +75,24 @@
 	}
 }
 
-/*
-  As all the register will only be made displayable to the root
-  user ( via printk ) or checking if the uid of the user is 0 from
-  the /proc filesystem please god this will be secure enough DJB.
-  The lines are given one at a time so as not to chew stack space in
-  printk on a crash & also for the proc filesystem when you get
-  0 returned you know you've got all the lines
- */
-
-static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs)
-{
-	int linelen=0;
-	int regno,chaincnt;
-	u32 backchain,prev_backchain,endchain;
-	u32 ksp = 0;
-	char *mode = "???";
-
-	enum
-	{
-		sp_linefeed,
-		sp_psw,
-		sp_ksp,
-		sp_gprs,
-		sp_gprs1,
-		sp_gprs2,
-		sp_gprs3,
-		sp_gprs4,
-		sp_acrs,
-		sp_acrs1,
-		sp_acrs2,
-		sp_acrs3,
-		sp_acrs4,
-		sp_kern_backchain,
-		sp_kern_backchain1
-	};
-
-	if (task)
-		ksp = task->thread.ksp;
-	if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE))
-		ksp = regs->gprs[15];
-
-	if (regs)
-		mode = (regs->psw.mask & PSW_PROBLEM_STATE)?
-		       "User" : "Kernel";
-
-	switch(line)
-	{
-	case sp_linefeed: 
-		linelen=sprintf(buff,"\n");
-		break;
-	case sp_psw:
-		if(regs)
-			linelen=sprintf(buff, "%s PSW:    %08lx %08lx    %s\n", mode,
-				(unsigned long) regs->psw.mask,
-				(unsigned long) regs->psw.addr,
-				print_tainted());
-		else
-			linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n");
-		break;
-	case sp_ksp:
-		linelen=sprintf(&buff[linelen],
-				"task: %08x ksp: %08x pt_regs: %08x\n",
-				(addr_t)task, (addr_t)ksp, (addr_t)regs);
-		break;
-	case sp_gprs:
-		if(regs)
-			linelen=sprintf(buff, "%s GPRS:\n", mode);
-		break;
-	case sp_gprs1 ... sp_gprs4:
-		if(regs)
-		{
-			regno=(line-sp_gprs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->gprs[regno], 
-					regs->gprs[regno+1],
-					regs->gprs[regno+2],
-					regs->gprs[regno+3]);
-		}
-		break;
-	case sp_acrs:
-		if(regs)
-			linelen=sprintf(buff, "%s ACRS:\n", mode);
-		break;	
-        case sp_acrs1 ... sp_acrs4:
-		if(regs)
-		{
-			regno=(line-sp_acrs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->acrs[regno],
-					regs->acrs[regno+1],
-					regs->acrs[regno+2],
-					regs->acrs[regno+3]);
-		}
-		break;
-	case sp_kern_backchain:
-		if (regs && (regs->psw.mask & PSW_PROBLEM_STATE))
-			break;	
-		if (ksp)
-			linelen=sprintf(buff, "Kernel BackChain  CallChain\n");
-		break;
-	default:
-		if (ksp)
-		{
-			
-			backchain=ksp&PSW_ADDR_MASK;
-			endchain=((backchain&(-8192))+8192);
-			prev_backchain=backchain-1;
-			line-=sp_kern_backchain1;
-			for(chaincnt=0;;chaincnt++)
-			{
-				if((backchain==0)||(backchain>=endchain)
-				   ||(chaincnt>=8)||(prev_backchain>=backchain))
-					break;
-				if(chaincnt==line)
-				{
-					linelen+=sprintf(&buff[linelen],"       %08x   [<%08lx>]\n",
-							 backchain,
-							 *(u32 *)(backchain+56)&PSW_ADDR_MASK);
-					break;
-				}
-				prev_backchain=backchain;
-				backchain=(*((u32 *)backchain))&PSW_ADDR_MASK;
-			}
-		}
-	}
-	return(linelen);
-}
-
+extern void show_registers(struct pt_regs *regs);
+extern void show_trace(unsigned long *sp);
 
 void show_regs(struct pt_regs *regs)
 {
-	char buff[80];
-	int i, line;
+	struct task_struct *tsk = current;
 
-        printk("CPU:    %d\n",smp_processor_id());
-        printk("Process %s (pid: %d, stackpage=%08X)\n",
-                current->comm, current->pid, 4096+(addr_t)current);
-	
-	for (line = 0; sprintf_regs(line, buff, current, regs); line++)
-		printk(buff);
-
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
-	{
-		printk("User Code:\n");
-		memset(buff, 0, 20);
-		copy_from_user(buff,
-			       (char *) (regs->psw.addr & PSW_ADDR_MASK), 20);
-		for (i = 0; i < 20; i++)
-			printk("%02x ", buff[i]);
-		printk("\n");
-	}
+        printk("CPU:    %d    %s\n", tsk->processor, print_tainted());
+        printk("Process %s (pid: %d, task: %08lx, ksp: %08x)\n",
+	       current->comm, current->pid, (unsigned long) tsk,
+	       tsk->thread.ksp);
+
+	show_registers(regs);
+	/* Show stack backtrace if pt_regs is from kernel mode */
+	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+		show_trace((unsigned long *) regs->gprs[15]);
 }
 
-char *task_show_regs(struct task_struct *task, char *buffer)
-{
-	int line, len;
-
-	for (line = 0; ; line++)
-	{
-		len = sprintf_regs(line, buffer, task, task->thread.regs);
-		if (!len) break;
-		buffer += len;
-	}
-	return buffer;
-}
-
-
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
         int clone_arg = flags | CLONE_VM;
@@ -301,16 +152,10 @@
             unsigned long gprs[10];    /* gprs 6 -15                       */
             unsigned long fprs[4];     /* fpr 4 and 6                      */
             unsigned long empty[4];
-#if CONFIG_REMOTE_DEBUG
-	    struct gdb_pt_regs childregs;
-#else
             struct pt_regs childregs;
-#endif
           } *frame;
 
         frame = (struct stack_frame *) (2*PAGE_SIZE + (unsigned long) p) -1;
-        frame = (struct stack_frame *) (((unsigned long) frame)&-8L);
-        p->thread.regs = (struct pt_regs *)&frame->childregs;
         p->thread.ksp = (unsigned long) frame;
         memcpy(&frame->childregs,regs,sizeof(struct pt_regs));
         frame->childregs.gprs[15] = new_stackp;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/ptrace.c linux/arch/s390/kernel/ptrace.c
--- linux.orig/arch/s390/kernel/ptrace.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/ptrace.c	Fri Dec 21 16:25:30 2001
@@ -41,7 +41,7 @@
 
 void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = task->thread.regs;
+	struct pt_regs *regs = __KSTK_PTREGS(task);
 	per_struct *per_info=
 			(per_struct *)&task->thread.per_info;
 
@@ -155,7 +155,7 @@
 		mask=0xffffffff;
 		if(useraddr<PT_FPC)
 		{
-			realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
+			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
 			if(useraddr<PT_PSWMASK)
 			{
 				copymax=PT_PSWMASK;
@@ -217,7 +217,6 @@
 {
 	struct task_struct *child;
 	int ret = -EPERM;
-	unsigned long flags;
 	unsigned long tmp;
 	int copied;
 	ptrace_area   parea; 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/s390_ksyms.c linux/arch/s390/kernel/s390_ksyms.c
--- linux.orig/arch/s390/kernel/s390_ksyms.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/s390_ksyms.c	Fri Dec 21 16:25:30 2001
@@ -18,8 +18,9 @@
 EXPORT_SYMBOL_NOVERS(_oi_bitmap);
 EXPORT_SYMBOL_NOVERS(_ni_bitmap);
 EXPORT_SYMBOL_NOVERS(_zb_findmap);
-EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup);
-EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup);
+EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
+EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
+EXPORT_SYMBOL_NOVERS(__clear_user_asm);
 
 /*
  * semaphore ops
@@ -35,6 +36,7 @@
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(memscan);
 EXPORT_SYMBOL_NOVERS(strlen);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strcmp);
@@ -57,5 +59,3 @@
 EXPORT_SYMBOL(console_mode);
 EXPORT_SYMBOL(console_device);
 EXPORT_SYMBOL_NOVERS(do_call_softirq);
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/setup.c linux/arch/s390/kernel/setup.c
--- linux.orig/arch/s390/kernel/setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/setup.c	Fri Dec 21 16:25:30 2001
@@ -439,6 +439,7 @@
 	lowcore->kernel_stack = ((__u32) &init_task_union) + 8192;
 	lowcore->async_stack = (__u32)
 		__alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0) + 8192;
+	lowcore->jiffy_timer = -1LL;
 	set_prefix((__u32) lowcore);
         cpu_init();
         boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
@@ -485,15 +486,16 @@
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
         struct cpuinfo_S390 *cpuinfo;
-	unsigned n = v;
+	unsigned long n = (unsigned long) v - 1;
 
-	if (!n--) {
+	if (!n) {
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 			       "# processors    : %i\n"
 			       "bogomips per cpu: %lu.%02lu\n",
 			       smp_num_cpus, loops_per_jiffy/(500000/HZ),
 			       (loops_per_jiffy/(5000/HZ))%100);
-	} else if (cpu_online_map & (1 << n)) {
+	} 
+	if (cpu_online_map & (1 << n)) {
 		cpuinfo = &safe_get_cpu_lowcore(n).cpu_data;
 		seq_printf(m, "processor %i: "
 			       "version = %02X,  "
@@ -508,7 +510,7 @@
 
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
-	return *pos <= NR_CPUS ? (void)(*pos+1) : NULL;
+	return *pos <= NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
 }
 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/smp.c linux/arch/s390/kernel/smp.c
--- linux.orig/arch/s390/kernel/smp.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/smp.c	Fri Dec 21 16:25:30 2001
@@ -49,9 +49,6 @@
 static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
 int              smp_num_cpus;
 struct _lowcore *lowcore_ptr[NR_CPUS];
-unsigned int     prof_multiplier[NR_CPUS];
-unsigned int     prof_old_multiplier[NR_CPUS];
-unsigned int     prof_counter[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
 static atomic_t  smp_commenced = ATOMIC_INIT(0);
@@ -473,7 +470,7 @@
 /*
  *      Activate a secondary processor.
  */
-extern void init_100hz_timer(void);
+extern void init_cpu_timer(void);
 extern int pfault_init(void);
 extern int pfault_token(void);
 
@@ -486,8 +483,8 @@
         /* Wait for completion of smp startup */
         while (!atomic_read(&smp_commenced))
                 /* nothing */ ;
-        /* init per CPU 100 hz timer */
-        init_100hz_timer();
+        /* init per CPU timer */
+        init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
@@ -540,7 +537,7 @@
 
         cpu_lowcore=&get_cpu_lowcore(cpu);
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (idle->thread.ksp | 8191) + 1;
+	cpu_lowcore->kernel_stack = (__u32) idle + 8192;
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctl 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -591,15 +588,7 @@
         
         /*
          *      Initialize the logical to physical CPU number mapping
-         *      and the per-CPU profiling counter/multiplier
          */
-        
-        for (i = 0; i < NR_CPUS; i++) {
-                prof_counter[i] = 1;
-                prof_old_multiplier[i] = 1;
-                prof_multiplier[i] = 1;
-        }
-
         print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data);
 
         for(i = 0; i < smp_num_cpus; i++)
@@ -646,58 +635,6 @@
 int setup_profiling_timer(unsigned int multiplier)
 {
         return 0;
-}
-
-/*
- * Local timer interrupt handler. It does both profiling and
- * process statistics/rescheduling.
- *
- * We do profiling in every local tick, statistics/rescheduling
- * happen only every 'profiling multiplier' ticks. The default
- * multiplier is 1 and it can be changed by writing the new multiplier
- * value into /proc/profile.
- */
-
-void smp_local_timer_interrupt(struct pt_regs * regs)
-{
-	int user = (user_mode(regs) != 0);
-        int cpu = smp_processor_id();
-
-        /*
-         * The profiling function is SMP safe. (nothing can mess
-         * around with "current", and the profiling counters are
-         * updated with atomic operations). This is especially
-         * useful with a profiling multiplier != 1
-         */
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-
-        if (!--prof_counter[cpu]) {
-
-                /*
-                 * The multiplier may have changed since the last time we got
-                 * to this point as a result of the user writing to
-                 * /proc/profile.  In this case we need to adjust the APIC
-                 * timer accordingly.
-                 *
-                 * Interrupts are already masked off at this point.
-                 */
-                prof_counter[cpu] = prof_multiplier[cpu];
-                if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
-			/* FIXME setup_APIC_timer(calibration_result/prof_counter[cpu]
-			   ); */
-                  prof_old_multiplier[cpu] = prof_counter[cpu];
-                }
-
-                /*
-                 * After doing the above, we need to make like
-                 * a normal interrupt - otherwise timer interrupts
-                 * ignore the global interrupt lock, which is the
-                 * WrongThing (tm) to do.
-                 */
-
-		update_process_times(user);
-        }
 }
 
 EXPORT_SYMBOL(lowcore_ptr);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/time.c linux/arch/s390/kernel/time.c
--- linux.orig/arch/s390/kernel/time.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -33,19 +33,18 @@
 
 #include <asm/irq.h>
 
-
 /* change this if you have some constant time drift */
-#define USECS_PER_JIFFY ((signed long)1000000/HZ)
-#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12)
+#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
+#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
 #define TICK_SIZE tick
 
-static uint64_t init_timer_cc, last_timer_cc;
+static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(uint64_t todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timeval *xtime)
 {
         const int high_bit = 0x80000000L;
         const int c_f4240 = 0xf4240L;
@@ -79,13 +78,15 @@
 		      : "cc", "memory", "2", "3", "4" );
 }
 
-unsigned long do_gettimeoffset(void) 
+static inline unsigned long do_gettimeoffset(void) 
 {
-	__u64 timer_cc;
+	__u64 now;
 
-	asm volatile ("STCK %0" : "=m" (timer_cc));
-        /* We require the offset from the previous interrupt */
-        return ((unsigned long)((timer_cc - last_timer_cc)>>12));
+	asm ("STCK %0" : "=m" (now));
+        now = (now - init_timer_cc) >> 12;
+	/* We require the offset from the latest update of xtime */
+	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
+	return (unsigned long) now;
 }
 
 /*
@@ -95,15 +96,10 @@
 {
 	unsigned long flags;
 	unsigned long usec, sec;
-	unsigned long lost_ticks;
 
 	read_lock_irqsave(&xtime_lock, flags);
-	lost_ticks = jiffies - wall_jiffies;
-	usec = do_gettimeoffset();
-	if (lost_ticks)
-		usec +=(USECS_PER_JIFFY*lost_ticks);
 	sec = xtime.tv_sec;
-	usec += xtime.tv_usec;
+	usec = xtime.tv_usec + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -149,51 +145,31 @@
 extern __u16 boot_cpu_addr;
 #endif
 
-void do_timer_interrupt(struct pt_regs *regs, __u16 error_code)
+static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, 0);
 
-        /*
-         * reset timer to 10ms minus time already elapsed
-         * since timer-interrupt pending
-         */
+	/*
+	 * set clock comparator for next tick
+	 */
+        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+
 #ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) {
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
 		write_lock(&xtime_lock);
-		last_timer_cc = S390_lowcore.jiffy_timer_cc;
-	}
-#else
-        last_timer_cc = S390_lowcore.jiffy_timer_cc;
-#endif
-        /* set clock comparator */
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
 
-/*
- * In the SMP case we use the local timer interrupt to do the
- * profiling, except when we simulate SMP mode on a uniprocessor
- * system, in that case we have to call the local interrupt handler.
- */
-#ifdef CONFIG_SMP
-        /* when SMP, do smp_local_timer_interrupt for *all* CPUs,
-           but only do the rest for the boot CPU */
-        smp_local_timer_interrupt(regs);
-#else
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-#endif
+	update_process_times(user_mode(regs));
 
-#ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr)
-#endif
-	{
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
 		do_timer(regs);
-#ifdef CONFIG_SMP
 		write_unlock(&xtime_lock);
-#endif
 	}
+#else
+	do_timer(regs);
+#endif
 
 	irq_exit(cpu, 0);
 }
@@ -201,19 +177,17 @@
 /*
  * Start the clock comparator on the current CPU
  */
-static long cr0 __attribute__ ((aligned (8)));
-
-void init_100hz_timer(void)
+void init_cpu_timer(void)
 {
+	unsigned long cr0;
+
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
-        /* set clock comparator */
-        /* read the TOD clock */
-        asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc));
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
+	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
+	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
 }
 
 /*
@@ -222,6 +196,7 @@
  */
 void __init time_init(void)
 {
+        __u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -241,15 +216,16 @@
                 printk("time_init: TOD clock stopped/non-operational\n");
                 break;
         }
+
+	/* set xtime */
+        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+                      (0x3c26700LL*1000000*4096);
+        tod_to_timeval(set_time_cc, &xtime);
+
         /* request the 0x1004 external interrupt */
-        if (register_external_interrupt(0x1004, do_timer_interrupt) != 0)
-                panic("Couldn't request external interrupts 0x1004");
-        init_100hz_timer();
-        init_timer_cc = S390_lowcore.jiffy_timer_cc;
-        init_timer_cc -= 0x8126d60e46000000LL -
-                         (0x3c26700LL*1000000*4096);
-        tod_to_timeval(init_timer_cc, &xtime);
+        if (register_external_interrupt(0x1004, do_comparator_interrupt) != 0)
+                panic("Couldn't request external interrupt 0x1004");
 
-	/* Set do_get_fast_time function pointer.  */
-	do_get_fast_time = do_gettimeofday;
+        /* init CPU timer */
+        init_cpu_timer();
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/kernel/traps.c linux/arch/s390/kernel/traps.c
--- linux.orig/arch/s390/kernel/traps.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/kernel/traps.c	Fri Dec 21 16:25:30 2001
@@ -26,15 +26,13 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/atomic.h>
 #include <asm/mathemu.h>
-#if CONFIG_REMOTE_DEBUG
-#include <asm/gdb-stub.h>
-#endif
 #include <asm/cpcmd.h>
 #include <asm/s390_ext.h>
 
@@ -60,6 +58,203 @@
 extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
 #endif
 
+int kstack_depth_to_print = 12;
+
+/*
+ * If the address is either in the .text section of the
+ * kernel, or in the vmalloc'ed module regions, it *may* 
+ * be the address of a calling routine
+ */
+extern char _stext, _etext;
+
+#ifdef CONFIG_MODULES
+
+extern struct module *module_list;
+extern struct module kernel_module;
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	int retval = 0;
+	struct module *mod;
+
+	if (addr >= (unsigned long) &_stext &&
+	    addr <= (unsigned long) &_etext)
+		return 1;
+
+	for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+		/* mod_bound tests for addr being inside the vmalloc'ed
+		 * module area. Of course it'd be better to test only
+		 * for the .text subset... */
+		if (mod_bound(addr, 0, mod)) {
+			retval = 1;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+#else
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	return (addr >= (unsigned long) &_stext &&
+		addr <= (unsigned long) &_etext);
+}
+
+#endif
+
+void show_trace(unsigned long * stack)
+{
+	unsigned long backchain, low_addr, high_addr, ret_addr;
+	int i;
+
+	if (!stack)
+		stack = (unsigned long*)&stack;
+
+	printk("Call Trace: ");
+	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
+	/* Skip the first frame (biased stack) */
+	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	/* Print up to 8 lines */
+	for (i = 0; i < 8; i++) {
+		if (backchain < low_addr || backchain >= high_addr)
+			break;
+		ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_MASK;
+		if (!kernel_text_address(ret_addr))
+			break;
+		if (i && ((i % 6) == 0))
+			printk("\n   ");
+		printk("[<%08lx>] ", ret_addr);
+		low_addr = backchain;
+		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+	}
+	printk("\n");
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/*
+	 * We can't print the backtrace of a running process. It is
+	 * unreliable at best and can cause kernel oopses.
+	 */
+	if (task_has_cpu(tsk))
+		return;
+	show_trace((unsigned long *) tsk->thread.ksp);
+}
+
+void show_stack(unsigned long *sp)
+{
+	unsigned long *stack;
+	int i;
+
+	// debugging aid: "show_stack(NULL);" prints the
+	// back trace for this cpu.
+
+	if(sp == NULL)
+		sp = (unsigned long*) &sp;
+
+	stack = sp;
+	for (i = 0; i < kstack_depth_to_print; i++) {
+		if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
+			break;
+		if (i && ((i % 8) == 0))
+			printk("\n       ");
+		printk("%08lx ", *stack++);
+	}
+	printk("\n");
+	show_trace(sp);
+}
+
+void show_registers(struct pt_regs *regs)
+{
+	mm_segment_t old_fs;
+	char *mode;
+	int i;
+
+	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	printk("%s PSW : %08lx %08lx\n",
+	       mode, (unsigned long) regs->psw.mask,
+	       (unsigned long) regs->psw.addr);
+	printk("%s GPRS: %08x %08x %08x %08x\n", mode,
+	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
+	printk("%s ACRS: %08x %08x %08x %08x\n", mode,
+	       regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]);
+
+	/*
+	 * Print the first 20 byte of the instruction stream at the
+	 * time of the fault.
+	 */
+	old_fs = get_fs();
+	if (regs->psw.mask & PSW_PROBLEM_STATE)
+		set_fs(USER_DS);
+	else
+		set_fs(KERNEL_DS);
+	printk("%s Code: ", mode);
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (__get_user(c, (char *)(regs->psw.addr + i))) {
+			printk(" Bad PSW.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	set_fs(old_fs);
+
+	printk("\n");
+}	
+
+/* This is called from fs/proc/array.c */
+char *task_show_regs(struct task_struct *task, char *buffer)
+{
+	struct pt_regs *regs;
+
+	regs = __KSTK_PTREGS(task);
+	buffer += sprintf(buffer, "task: %08lx, ksp: %08x\n",
+			  (unsigned long) task, task->thread.ksp);
+	buffer += sprintf(buffer, "User PSW : %08lx %08lx\n",
+			  (unsigned long) regs->psw.mask, 
+			  (unsigned long) regs->psw.addr);
+	buffer += sprintf(buffer, "User GPRS: %08x %08x %08x %08x\n",
+			  regs->gprs[0], regs->gprs[1],
+			  regs->gprs[2], regs->gprs[3]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[4], regs->gprs[5],
+			  regs->gprs[6], regs->gprs[7]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[8], regs->gprs[9],
+			  regs->gprs[10], regs->gprs[11]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->gprs[12], regs->gprs[13],
+			  regs->gprs[14], regs->gprs[15]);
+	buffer += sprintf(buffer, "User ACRS: %08x %08x %08x %08x\n",
+			  regs->acrs[0], regs->acrs[1],
+			  regs->acrs[2], regs->acrs[3]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[4], regs->acrs[5],
+			  regs->acrs[6], regs->acrs[7]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[8], regs->acrs[9],
+			  regs->acrs[10], regs->acrs[11]);
+	buffer += sprintf(buffer, "           %08x %08x %08x %08x\n",
+			  regs->acrs[12], regs->acrs[13],
+			  regs->acrs[14], regs->acrs[15]);
+	return buffer;
+}
+
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
 
 void die(const char * str, struct pt_regs * regs, long err)
@@ -145,7 +340,7 @@
 #if CONFIG_REMOTE_DEBUG
 		if(gdb_stub_initialised)
 		{
-			gdb_stub_handle_exception((struct gdb_pt_regs *)regs,signal);
+			gdb_stub_handle_exception(regs, signal);
 			return 0;
 		}
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/lib/uaccess.S linux/arch/s390/lib/uaccess.S
--- linux.orig/arch/s390/lib/uaccess.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/lib/uaccess.S	Fri Dec 21 16:25:30 2001
@@ -6,46 +6,92 @@
  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
- *  These functions have a non-standard call interface
+ *  These functions have standard call interface
  */
 
 #include <asm/lowcore.h>
 
         .text
         .align 4
-        .globl __copy_from_user_fixup
-__copy_from_user_fixup:
-        l     1,__LC_PGM_OLD_PSW+4
-        sll   4,1
-        srl   4,1
-0:      lhi   3,-4096
-        sll   3,1
-        srl   3,1
-        n     3,__LC_TRANS_EXC_ADDR
-        sr    3,4
-        bm    4(1)
-1:      mvcle 2,4,0
-        b     4(1)
+        .globl __copy_from_user_asm
+__copy_from_user_asm:
+	lr	%r5,%r3
+	sacf	512
+0:	mvcle	%r2,%r4,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r5
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r3,-4096
+        sll	%r3,1
+        srl	%r3,1
+        n	%r3,__LC_TRANS_EXC_ADDR
+        sr	%r3,%r4
+        bm	4(%r1)
+3:      mvcle	%r2,%r4,0
+        b	4(%r1)
         .section __ex_table,"a"
-	.long 1b,0b
+	.long	0b,1b
+	.long	3b,2b
         .previous
 
         .align 4
         .text
-        .globl __copy_to_user_fixup
-__copy_to_user_fixup:
-        l     1,__LC_PGM_OLD_PSW+4
-        sll   4,1
-        srl   4,1
-0:      lhi   5,-4096
-        sll   5,1
-        srl   5,1
-        n     5,__LC_TRANS_EXC_ADDR
-        sr    5,4
-        bm    4(1)
-1:      mvcle 4,2,0
-        b     4(1)
+        .globl __copy_to_user_asm
+__copy_to_user_asm:
+	lr	%r5,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r3
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r5,-4096
+        sll	%r5,1
+        srl	%r5,1
+        n	%r5,__LC_TRANS_EXC_ADDR
+        sr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .long 1b,0b
+	.long   0b,1b
+        .long	3b,2b
+        .previous
+
+        .align 4
+        .text
+        .globl __clear_user_asm
+__clear_user_asm:
+	lr	%r4,%r2
+	lr	%r5,%r3
+	sr	%r2,%r2
+	sr	%r3,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lr	%r2,%r3
+	br	%r14
+1:	l	%r1,__LC_PGM_OLD_PSW+4
+        sll	%r4,1
+        srl	%r4,1
+2:      lhi	%r5,-4096
+        sll	%r5,1
+        srl	%r5,1
+        n	%r5,__LC_TRANS_EXC_ADDR
+        sr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
+        .section __ex_table,"a"
+	.long   0b,1b
+        .long	3b,2b
         .previous
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/math-emu/math.c linux/arch/s390/math-emu/math.c
--- linux.orig/arch/s390/math-emu/math.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/math-emu/math.c	Fri Dec 21 16:25:30 2001
@@ -96,7 +96,7 @@
                         return SIGSEGV; \
         } while (0)
 
-static void display_emulation_not_implemented(char *instr)
+static void display_emulation_not_implemented(struct pt_regs *regs, char *instr)
 {
         struct pt_regs *regs;
         __u16 *location;
@@ -105,7 +105,6 @@
         if(sysctl_ieee_emulation_warnings)
 #endif
         {
-                regs = current->thread.regs;
                 location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc);
                 printk("%s ieee fpu instruction not emulated "
                        "process name: %s pid: %d \n",
@@ -116,10 +115,9 @@
         }
 }
 
-static inline void emu_set_CC (int cc)
+static inline void emu_set_CC (struct pt_regs *regs, int cc)
 {
-        current->thread.regs->psw.mask = 
-		(current->thread.regs->psw.mask & 0xFFFFCFFF) | ((cc&3) << 12);
+        regs->psw.mask = (regs->psw.mask & 0xFFFFCFFF) | ((cc&3) << 12);
 }
 
 /*
@@ -129,24 +127,24 @@
  *  2 : Result is greater than zero
  *  3 : Result is NaN or INF
  */
-static inline void emu_set_CC_cs(int class, int sign)
+static inline void emu_set_CC_cs(struct pt_regs *regs, int class, int sign)
 {
         switch (class) {
         case FP_CLS_NORMAL:
         case FP_CLS_INF:
-                emu_set_CC(sign ? 1 : 2);
+                emu_set_CC(regs, sign ? 1 : 2);
                 break;
         case FP_CLS_ZERO:
-                emu_set_CC(0);
+                emu_set_CC(regs, 0);
                 break;
         case FP_CLS_NAN:
-                emu_set_CC(3);
+                emu_set_CC(regs, 3);
                 break;
         }
 }
 
 /* Add long double */
-static int emu_axbr (int rx, int ry) {
+static int emu_axbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -163,12 +161,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Add double */
-static int emu_adbr (int rx, int ry) {
+static int emu_adbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -178,12 +176,12 @@
         FP_UNPACK_DP(DB, &current->thread.fp_regs.fprs[ry].d);
         FP_ADD_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Add double */
-static int emu_adb (int rx, double *val) {
+static int emu_adb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -193,12 +191,12 @@
         FP_UNPACK_DP(DB, val);
         FP_ADD_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Add float */
-static int emu_aebr (int rx, int ry) {
+static int emu_aebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -208,12 +206,12 @@
         FP_UNPACK_SP(SB, &current->thread.fp_regs.fprs[ry].f);
         FP_ADD_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Add float */
-static int emu_aeb (int rx, float *val) {
+static int emu_aeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -223,12 +221,12 @@
         FP_UNPACK_SP(SB, val);
         FP_ADD_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Compare long double */
-static int emu_cxbr (int rx, int ry) {
+static int emu_cxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB);
 	mathemu_ldcv cvt;
         int IR;
@@ -244,12 +242,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare double */
-static int emu_cdbr (int rx, int ry) {
+static int emu_cdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         int IR;
 
@@ -260,12 +258,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare double */
-static int emu_cdb (int rx, double *val) {
+static int emu_cdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         int IR;
 
@@ -276,12 +274,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare float */
-static int emu_cebr (int rx, int ry) {
+static int emu_cebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         int IR;
 
@@ -292,12 +290,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare float */
-static int emu_ceb (int rx, float *val) {
+static int emu_ceb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         int IR;
 
@@ -308,12 +306,12 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         return 0;
 }
 
 /* Compare and signal long double */
-static int emu_kxbr (int rx, int ry) {
+static int emu_kxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -330,14 +328,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal double */
-static int emu_kdbr (int rx, int ry) {
+static int emu_kdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         FP_DECL_EX;
         int IR;
@@ -349,14 +347,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal double */
-static int emu_kdb (int rx, double *val) {
+static int emu_kdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB);
         FP_DECL_EX;
         int IR;
@@ -368,14 +366,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal float */
-static int emu_kebr (int rx, int ry) {
+static int emu_kebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         FP_DECL_EX;
         int IR;
@@ -387,14 +385,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Compare and signal float */
-static int emu_keb (int rx, float *val) {
+static int emu_keb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB);
         FP_DECL_EX;
         int IR;
@@ -406,14 +404,14 @@
          * IR == -1 if DA < DB, IR == 0 if DA == DB,
          * IR == 1 if DA > DB and IR == 3 if unorderded
          */
-        emu_set_CC((IR == -1) ? 1 : (IR == 1) ? 2 : IR);
+        emu_set_CC(regs, (IR == -1) ? 1 : (IR == 1) ? 2 : IR);
         if (IR == 3)
                 FP_SET_EXCEPTION (FP_EX_INVALID);
         return _fex;
 }
 
 /* Convert from fixed long double */
-static int emu_cxfbr (int rx, int ry) {
+static int emu_cxfbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -421,7 +419,7 @@
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_Q(QR, si, 32, int);
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
@@ -430,35 +428,35 @@
 }
 
 /* Convert from fixed double */
-static int emu_cdfbr (int rx, int ry) {
+static int emu_cdfbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DR);
         FP_DECL_EX;
         __s32 si;
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_D(DR, si, 32, int);
         FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
         return _fex;
 }
 
 /* Convert from fixed float */
-static int emu_cefbr (int rx, int ry) {
+static int emu_cefbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SR);
         FP_DECL_EX;
         __s32 si;
         int mode;
 
 	mode = current->thread.fp_regs.fpc & 3;
-        si = current->thread.regs->gprs[ry];
+        si = regs->gprs[ry];
         FP_FROM_INT_S(SR, si, 32, int);
         FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
         return _fex;
 }
 
 /* Convert to fixed long double */
-static int emu_cfxbr (int rx, int ry, int mask) {
+static int emu_cfxbr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_Q(QA);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -475,13 +473,13 @@
         cvt.w.low = current->thread.fp_regs.fprs[ry+2].ui;
         FP_UNPACK_QP(QA, &cvt.ld);
         FP_TO_INT_ROUND_Q(si, QA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(QA_c, QA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, QA_c, QA_s);
         return _fex;
 }
 
 /* Convert to fixed double */
-static int emu_cfdbr (int rx, int ry, int mask) {
+static int emu_cfdbr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_D(DA);
         FP_DECL_EX;
         __s32 si;
@@ -495,13 +493,13 @@
 		mode = mask - 4;
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
         FP_TO_INT_ROUND_D(si, DA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(DA_c, DA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, DA_c, DA_s);
         return _fex;
 }
 
 /* Convert to fixed float */
-static int emu_cfebr (int rx, int ry, int mask) {
+static int emu_cfebr (struct pt_regs *regs, int rx, int ry, int mask) {
         FP_DECL_S(SA);
         FP_DECL_EX;
         __s32 si;
@@ -515,13 +513,13 @@
 		mode = mask - 4;
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
         FP_TO_INT_ROUND_S(si, SA, 32, 1);
-        current->thread.regs->gprs[rx] = si;
-        emu_set_CC_cs(SA_c, SA_s);
+        regs->gprs[rx] = si;
+        emu_set_CC_cs(regs, SA_c, SA_s);
         return _fex;
 }
 
 /* Divide long double */
-static int emu_dxbr (int rx, int ry) {
+static int emu_dxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -542,7 +540,7 @@
 }
 
 /* Divide double */
-static int emu_ddbr (int rx, int ry) {
+static int emu_ddbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -556,7 +554,7 @@
 }
 
 /* Divide double */
-static int emu_ddb (int rx, double *val) {
+static int emu_ddb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -570,7 +568,7 @@
 }
 
 /* Divide float */
-static int emu_debr (int rx, int ry) {
+static int emu_debr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -584,7 +582,7 @@
 }
 
 /* Divide float */
-static int emu_deb (int rx, float *val) {
+static int emu_deb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -598,25 +596,25 @@
 }
 
 /* Divide to integer double */
-static int emu_didbr (int rx, int ry, int mask) {
-        display_emulation_not_implemented("didbr");
+static int emu_didbr (struct pt_regs *regs, int rx, int ry, int mask) {
+        display_emulation_not_implemented(regs, "didbr");
         return 0;
 }
 
 /* Divide to integer float */
-static int emu_diebr (int rx, int ry, int mask) {
-        display_emulation_not_implemented("diebr");
+static int emu_diebr (struct pt_regs *regs, int rx, int ry, int mask) {
+        display_emulation_not_implemented(regs, "diebr");
         return 0;
 }
 
 /* Extract fpc */
-static int emu_efpc (int rx, int ry) {
-        current->thread.regs->gprs[rx] = current->thread.fp_regs.fpc;
+static int emu_efpc (struct pt_regs *regs, int rx, int ry) {
+        regs->gprs[rx] = current->thread.fp_regs.fpc;
         return 0;
 }
 
 /* Load and test long double */
-static int emu_ltxbr (int rx, int ry) {
+static int emu_ltxbr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
 	mathemu_ldcv cvt;
         FP_DECL_Q(QA);
@@ -627,36 +625,36 @@
         FP_UNPACK_QP(QA, &cvt.ld);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
         fp_regs->fprs[rx+2].ui = fp_regs->fprs[ry+2].ui;
-        emu_set_CC_cs(QA_c, QA_s);
+        emu_set_CC_cs(regs, QA_c, QA_s);
         return _fex;
 }
 
 /* Load and test double */
-static int emu_ltdbr (int rx, int ry) {
+static int emu_ltdbr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_D(DA);
         FP_DECL_EX;
 
         FP_UNPACK_DP(DA, &fp_regs->fprs[ry].d);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
-        emu_set_CC_cs(DA_c, DA_s);
+        emu_set_CC_cs(regs, DA_c, DA_s);
         return _fex;
 }
 
 /* Load and test double */
-static int emu_ltebr (int rx, int ry) {
+static int emu_ltebr (struct pt_regs *regs, int rx, int ry) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_S(SA);
         FP_DECL_EX;
 
         FP_UNPACK_SP(SA, &fp_regs->fprs[ry].f);
         fp_regs->fprs[rx].ui = fp_regs->fprs[ry].ui;
-        emu_set_CC_cs(SA_c, SA_s);
+        emu_set_CC_cs(regs, SA_c, SA_s);
         return _fex;
 }
 
 /* Load complement long double */
-static int emu_lcxbr (int rx, int ry) {
+static int emu_lcxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -670,12 +668,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load complement double */
-static int emu_lcdbr (int rx, int ry) {
+static int emu_lcdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -684,12 +682,12 @@
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
 	FP_NEG_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load complement float */
-static int emu_lcebr (int rx, int ry) {
+static int emu_lcebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -698,12 +696,12 @@
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
 	FP_NEG_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load floating point integer long double */
-static int emu_fixbr (int rx, int ry, int mask) {
+static int emu_fixbr (struct pt_regs *regs, int rx, int ry, int mask) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_Q(QA);
         FP_DECL_EX;
@@ -728,7 +726,7 @@
 }
 
 /* Load floating point integer double */
-static int emu_fidbr (int rx, int ry, int mask) {
+static int emu_fidbr (struct pt_regs *regs, int rx, int ry, int mask) {
 	/* FIXME: rounding mode !! */
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_D(DA);
@@ -749,7 +747,7 @@
 }
 
 /* Load floating point integer float */
-static int emu_fiebr (int rx, int ry, int mask) {
+static int emu_fiebr (struct pt_regs *regs, int rx, int ry, int mask) {
         s390_fp_regs *fp_regs = &current->thread.fp_regs;
         FP_DECL_S(SA);
         FP_DECL_EX;
@@ -769,7 +767,7 @@
 }
 
 /* Load lengthened double to long double */
-static int emu_lxdbr (int rx, int ry) {
+static int emu_lxdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -785,7 +783,7 @@
 }
 
 /* Load lengthened double to long double */
-static int emu_lxdb (int rx, double *val) {
+static int emu_lxdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -801,7 +799,7 @@
 }
 
 /* Load lengthened float to long double */
-static int emu_lxebr (int rx, int ry) {
+static int emu_lxebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -817,7 +815,7 @@
 }
 
 /* Load lengthened float to long double */
-static int emu_lxeb (int rx, float *val) {
+static int emu_lxeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -833,7 +831,7 @@
 }
 
 /* Load lengthened float to double */
-static int emu_ldebr (int rx, int ry) {
+static int emu_ldebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -846,7 +844,7 @@
 }
 
 /* Load lengthened float to double */
-static int emu_ldeb (int rx, float *val) {
+static int emu_ldeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -859,7 +857,7 @@
 }
 
 /* Load negative long double */
-static int emu_lnxbr (int rx, int ry) {
+static int emu_lnxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -880,12 +878,12 @@
 		current->thread.fp_regs.fprs[rx+2].ui =
 			current->thread.fp_regs.fprs[ry+2].ui;
 	}
-	emu_set_CC_cs(QR_c, QR_s);
+	emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load negative double */
-static int emu_lndbr (int rx, int ry) {
+static int emu_lndbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -898,12 +896,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(DR_c, DR_s);
+	emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load negative float */
-static int emu_lnebr (int rx, int ry) {
+static int emu_lnebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -916,12 +914,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(SR_c, SR_s);
+	emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load positive long double */
-static int emu_lpxbr (int rx, int ry) {
+static int emu_lpxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -942,12 +940,12 @@
 		current->thread.fp_regs.fprs[rx+2].ui =
 			current->thread.fp_regs.fprs[ry+2].ui;
 	}
-	emu_set_CC_cs(QR_c, QR_s);
+	emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Load positive double */
-static int emu_lpdbr (int rx, int ry) {
+static int emu_lpdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -960,12 +958,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(DR_c, DR_s);
+	emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Load positive float */
-static int emu_lpebr (int rx, int ry) {
+static int emu_lpebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -978,12 +976,12 @@
 	} else
 		current->thread.fp_regs.fprs[rx].ui =
 			current->thread.fp_regs.fprs[ry].ui;
-	emu_set_CC_cs(SR_c, SR_s);
+	emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Load rounded long double to double */
-static int emu_ldxbr (int rx, int ry) {
+static int emu_ldxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_D(DR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -999,7 +997,7 @@
 }
 
 /* Load rounded long double to float */
-static int emu_lexbr (int rx, int ry) {
+static int emu_lexbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_S(SR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1015,7 +1013,7 @@
 }
 
 /* Load rounded double to float */
-static int emu_ledbr (int rx, int ry) {
+static int emu_ledbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_S(SR);
 	FP_DECL_EX;
         int mode;
@@ -1028,7 +1026,7 @@
 }
 
 /* Multiply long double */
-static int emu_mxbr (int rx, int ry) {
+static int emu_mxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1049,7 +1047,7 @@
 }
 
 /* Multiply double */
-static int emu_mdbr (int rx, int ry) {
+static int emu_mdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1063,7 +1061,7 @@
 }
 
 /* Multiply double */
-static int emu_mdb (int rx, double *val) {
+static int emu_mdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1077,7 +1075,7 @@
 }
 
 /* Multiply double to long double */
-static int emu_mxdbr (int rx, int ry) {
+static int emu_mxdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
 	FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1096,7 +1094,7 @@
 }
 
 /* Multiply double to long double */
-static int emu_mxdb (int rx, long double *val) {
+static int emu_mxdb (struct pt_regs *regs, int rx, long double *val) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1115,7 +1113,7 @@
 }
 
 /* Multiply float */
-static int emu_meebr (int rx, int ry) {
+static int emu_meebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1129,7 +1127,7 @@
 }
 
 /* Multiply float */
-static int emu_meeb (int rx, float *val) {
+static int emu_meeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1143,7 +1141,7 @@
 }
 
 /* Multiply float to double */
-static int emu_mdebr (int rx, int ry) {
+static int emu_mdebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -1159,7 +1157,7 @@
 }
 
 /* Multiply float to double */
-static int emu_mdeb (int rx, float *val) {
+static int emu_mdeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
 	FP_DECL_EX;
         int mode;
@@ -1175,7 +1173,7 @@
 }
 
 /* Multiply and add double */
-static int emu_madbr (int rx, int ry, int rz) {
+static int emu_madbr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1191,7 +1189,7 @@
 }
 
 /* Multiply and add double */
-static int emu_madb (int rx, double *val, int rz) {
+static int emu_madb (struct pt_regs *regs, int rx, double *val, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1207,7 +1205,7 @@
 }
 
 /* Multiply and add float */
-static int emu_maebr (int rx, int ry, int rz) {
+static int emu_maebr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1223,7 +1221,7 @@
 }
 
 /* Multiply and add float */
-static int emu_maeb (int rx, float *val, int rz) {
+static int emu_maeb (struct pt_regs *regs, int rx, float *val, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1239,7 +1237,7 @@
 }
 
 /* Multiply and subtract double */
-static int emu_msdbr (int rx, int ry, int rz) {
+static int emu_msdbr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1255,7 +1253,7 @@
 }
 
 /* Multiply and subtract double */
-static int emu_msdb (int rx, double *val, int rz) {
+static int emu_msdb (struct pt_regs *regs, int rx, double *val, int rz) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DC); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1271,7 +1269,7 @@
 }
 
 /* Multiply and subtract float */
-static int emu_msebr (int rx, int ry, int rz) {
+static int emu_msebr (struct pt_regs *regs, int rx, int ry, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1287,7 +1285,7 @@
 }
 
 /* Multiply and subtract float */
-static int emu_mseb (int rx, float *val, int rz) {
+static int emu_mseb (struct pt_regs *regs, int rx, float *val, int rz) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SC); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1303,10 +1301,10 @@
 }
 
 /* Set floating point control word */
-static int emu_sfpc (int rx, int ry) {
+static int emu_sfpc (struct pt_regs *regs, int rx, int ry) {
         __u32 temp;
 
-        temp = current->thread.regs->gprs[rx];
+        temp = regs->gprs[rx];
         if ((temp & ~FPC_VALID_MASK) != 0)
 		return SIGILL;
 	current->thread.fp_regs.fpc = temp;
@@ -1314,7 +1312,7 @@
 }
 
 /* Square root long double */
-static int emu_sqxbr (int rx, int ry) {
+static int emu_sqxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1328,12 +1326,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Square root double */
-static int emu_sqdbr (int rx, int ry) {
+static int emu_sqdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1342,12 +1340,12 @@
         FP_UNPACK_DP(DA, &current->thread.fp_regs.fprs[ry].d);
 	FP_SQRT_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Square root double */
-static int emu_sqdb (int rx, double *val) {
+static int emu_sqdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1356,12 +1354,12 @@
         FP_UNPACK_DP(DA, val);
 	FP_SQRT_D(DR, DA);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Square root float */
-static int emu_sqebr (int rx, int ry) {
+static int emu_sqebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1370,12 +1368,12 @@
         FP_UNPACK_SP(SA, &current->thread.fp_regs.fprs[ry].f);
 	FP_SQRT_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Square root float */
-static int emu_sqeb (int rx, float *val) {
+static int emu_sqeb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1384,12 +1382,12 @@
         FP_UNPACK_SP(SA, val);
 	FP_SQRT_S(SR, SA);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Subtract long double */
-static int emu_sxbr (int rx, int ry) {
+static int emu_sxbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_Q(QA); FP_DECL_Q(QB); FP_DECL_Q(QR);
         FP_DECL_EX;
 	mathemu_ldcv cvt;
@@ -1406,12 +1404,12 @@
         FP_PACK_QP(&cvt.ld, QR);
         current->thread.fp_regs.fprs[rx].ui = cvt.w.high;
         current->thread.fp_regs.fprs[rx+2].ui = cvt.w.low;
-        emu_set_CC_cs(QR_c, QR_s);
+        emu_set_CC_cs(regs, QR_c, QR_s);
         return _fex;
 }
 
 /* Subtract double */
-static int emu_sdbr (int rx, int ry) {
+static int emu_sdbr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1421,12 +1419,12 @@
         FP_UNPACK_DP(DB, &current->thread.fp_regs.fprs[ry].d);
         FP_SUB_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Subtract double */
-static int emu_sdb (int rx, double *val) {
+static int emu_sdb (struct pt_regs *regs, int rx, double *val) {
         FP_DECL_D(DA); FP_DECL_D(DB); FP_DECL_D(DR);
         FP_DECL_EX;
         int mode;
@@ -1436,12 +1434,12 @@
         FP_UNPACK_DP(DB, val);
         FP_SUB_D(DR, DA, DB);
 	FP_PACK_DP(&current->thread.fp_regs.fprs[rx].d, DR);
-        emu_set_CC_cs(DR_c, DR_s);
+        emu_set_CC_cs(regs, DR_c, DR_s);
         return _fex;
 }
 
 /* Subtract float */
-static int emu_sebr (int rx, int ry) {
+static int emu_sebr (struct pt_regs *regs, int rx, int ry) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1451,12 +1449,12 @@
         FP_UNPACK_SP(SB, &current->thread.fp_regs.fprs[ry].f);
         FP_SUB_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Subtract float */
-static int emu_seb (int rx, float *val) {
+static int emu_seb (struct pt_regs *regs, int rx, float *val) {
         FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
         FP_DECL_EX;
         int mode;
@@ -1466,12 +1464,12 @@
         FP_UNPACK_SP(SB, val);
         FP_SUB_S(SR, SA, SB);
 	FP_PACK_SP(&current->thread.fp_regs.fprs[rx].f, SR);
-        emu_set_CC_cs(SR_c, SR_s);
+        emu_set_CC_cs(regs, SR_c, SR_s);
         return _fex;
 }
 
 /* Test data class long double */
-static int emu_tcxb (int rx, long val) {
+static int emu_tcxb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_Q(QA);
 	mathemu_ldcv cvt;
 	int bit;
@@ -1500,12 +1498,12 @@
 	}
 	if (!QA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
 /* Test data class double */
-static int emu_tcdb (int rx, long val) {
+static int emu_tcdb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_D(DA);
 	int bit;
 
@@ -1531,12 +1529,12 @@
 	}
 	if (!DA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
 /* Test data class float */
-static int emu_tceb (int rx, long val) {
+static int emu_tceb (struct pt_regs *regs, int rx, long val) {
         FP_DECL_S(SA);
 	int bit;
 
@@ -1562,7 +1560,7 @@
 	}
 	if (!SA_s)
 		bit++;
-	emu_set_CC(((__u32) val >> bit) & 1);
+	emu_set_CC(regs, ((__u32) val >> bit) & 1);
         return 0;
 }
 
@@ -1666,8 +1664,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *,int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
                 emu_load_regd(opcode[3] & 15);
@@ -1677,8 +1676,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(opcode[3] & 15);
 		break;
@@ -1686,8 +1686,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 emu_load_rege(opcode[3] & 15);
 		break;
@@ -1699,8 +1700,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
                 emu_load_regd(opcode[3] & 15);
@@ -1711,8 +1713,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_regd((opcode[2] >> 4) & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(opcode[3] & 15);
@@ -1722,8 +1725,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
                 emu_load_rege((opcode[2] >> 4) & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 emu_load_rege(opcode[3] & 15);
@@ -1732,21 +1736,24 @@
                 /* call the emulation function */
                 if (opcode[3] & 0x20)
 			return SIGILL;
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
         case 8: /* RRE format, cdfbr instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
 		break;
         case 9: /* RRE format, cefbr instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
 		break;
         case 10: /* RRF format, cfxbr instruction */
@@ -1758,8 +1765,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 11: /* RRF format, cfdbr instruction */
                 if ((opcode[2] & 128) == 128 || (opcode[2] & 96) == 32)
@@ -1767,8 +1775,9 @@
 			return SIGILL;
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 12: /* RRF format, cfebr instruction */
                 if ((opcode[2] & 128) == 128 || (opcode[2] & 96) == 32)
@@ -1776,8 +1785,9 @@
 			return SIGILL;
                 emu_store_rege(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15, opcode[2] >> 4);
 		break;
         case 13: /* RRE format, ldxbr & mdxbr instruction */
                 /* double store but long double load */
@@ -1786,8 +1796,9 @@
                 emu_store_regd((opcode[3] >> 4) & 15);
                 emu_store_regd(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
@@ -1798,8 +1809,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 emu_load_regd(((opcode[3] >> 4) & 15) + 2);
 		break;
@@ -1808,8 +1820,9 @@
                 emu_store_rege((opcode[3] >> 4) & 15);
                 emu_store_rege(opcode[3]  & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
 		break;
         case 16: /* RRE format, ldxbr instruction */
@@ -1819,8 +1832,9 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_regd((opcode[3] >> 4) & 15);
                 break;
         case 17: /* RRE format, ldxbr instruction */
@@ -1830,22 +1844,25 @@
                 emu_store_regd(opcode[3] & 15);
                 emu_store_regd((opcode[3] & 15) + 2);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 break;
         case 18: /* RRE format, ledbr instruction */
                 /* double store but float load */
                 emu_store_regd(opcode[3] & 15);
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 emu_load_rege((opcode[3] >> 4) & 15);
                 break;
         case 19: /* RRE format, efpc & sfpc instruction */
                 /* call the emulation function */
-                _fex = ((int (*)(int, int)) jump_table[opcode[1]])
-                        (opcode[3] >> 4, opcode[3] & 15);
+                _fex = ((int (*)(struct pt_regs *, int, int))
+			jump_table[opcode[1]])
+                        (regs, opcode[3] >> 4, opcode[3] & 15);
                 break;
         default: /* invalid operation */
                 return SIGILL;
@@ -1904,8 +1921,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, double *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1918,8 +1936,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_rege((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1933,8 +1952,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *, int)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp, opcode[4] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, double *, int))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp, opcode[4] >> 4);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1948,8 +1968,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *, int)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp, opcode[4] >> 4);
+                _fex = ((int (*)(struct pt_regs *, int, float *, int))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp, opcode[4] >> 4);
                 emu_load_rege((opcode[4] >> 4) & 15);
                 break;
         }
@@ -1965,8 +1986,9 @@
                 dxb = (__u64 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_copy_from_user(&temp, dxb, 8);
                 /* call the emulation function */
-                _fex = ((int (*)(int, double *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (double *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, double *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (double *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 emu_load_regd(((opcode[1] >> 4) & 15) + 2);
                 break;
@@ -1981,8 +2003,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 break;
         }
@@ -1998,8 +2021,9 @@
                 dxb = (__u32 *) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 mathemu_get_user(temp, dxb);
                 /* call the emulation function */
-                _fex = ((int (*)(int, float *)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, (float *) &temp);
+                _fex = ((int (*)(struct pt_regs *, int, float *))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, (float *) &temp);
                 emu_load_regd((opcode[1] >> 4) & 15);
                 emu_load_regd(((opcode[1] >> 4) & 15) + 2);
                 break;
@@ -2012,8 +2036,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         case 9: /* RXE format, RX address used as int value */ {
@@ -2024,8 +2049,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         case 10: /* RXE format, RX address used as int value */ {
@@ -2039,8 +2065,9 @@
                 opc = *((__u32 *) opcode);
                 dxb = (__u64) calc_addr(regs, opc >> 16, opc >> 12, opc);
                 /* call the emulation function */
-                _fex = ((int (*)(int, long)) jump_table[opcode[5]])
-                        (opcode[1] >> 4, dxb);
+                _fex = ((int (*)(struct pt_regs *, int, long))
+			jump_table[opcode[5]])
+                        (regs, opcode[1] >> 4, dxb);
                 break;
         }
         default: /* invalid operation */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/vmlinux-shared.lds linux/arch/s390/vmlinux-shared.lds
--- linux.orig/arch/s390/vmlinux-shared.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/vmlinux-shared.lds	Thu Jan 10 20:08:20 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) }
   .kstrtab : { *(.kstrtab) }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390/vmlinux.lds linux/arch/s390/vmlinux.lds
--- linux.orig/arch/s390/vmlinux.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390/vmlinux.lds	Thu Jan 10 20:08:20 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/Makefile linux/arch/s390x/Makefile
--- linux.orig/arch/s390x/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/Makefile	Fri Dec 21 16:25:30 2001
@@ -55,6 +55,9 @@
 image: vmlinux 
 	@$(MAKEBOOT) image
 
+install: vmlinux
+	@$(MAKEBOOT) BOOTIMAGE=image install
+
 archclean:
 	@$(MAKEBOOT) clean
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/boot/Makefile linux/arch/s390x/boot/Makefile
--- linux.orig/arch/s390x/boot/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/boot/Makefile	Fri Dec 21 16:25:30 2001
@@ -35,3 +35,5 @@
 clean:
 	rm -f image listing iplfba.boot ipleckd.boot ipldump.boot
 
+install: $(CONFIGURE) $(BOOTIMAGE)
+	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map $(TOPDIR)/Kerntypes "$(INSTALL_PATH)"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/config.in linux/arch/s390x/config.in
--- linux.orig/arch/s390x/config.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/config.in	Fri Dec 21 16:25:30 2001
@@ -71,9 +71,9 @@
 comment 'Kernel hacking'
 
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
-if [ "$CONFIG_CTC" = "y" ]; then
-  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
-fi
+#if [ "$CONFIG_CTC" = "y" ]; then
+#  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
+#fi
 bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
 endmenu
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/defconfig linux/arch/s390x/defconfig
--- linux.orig/arch/s390x/defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/defconfig	Fri Dec 21 16:25:30 2001
@@ -6,7 +6,7 @@
 # CONFIG_MCA is not set
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
-CONFIG_GENERIC_BUST_SPINLOCK=n
+# CONFIG_GENERIC_BUST_SPINLOCK is not set
 CONFIG_ARCH_S390=y
 CONFIG_ARCH_S390X=y
 
@@ -103,8 +103,8 @@
 #
 # S/390 tape hardware support
 #
-CONFIG_S390_TAPE_3490=y
-CONFIG_S390_TAPE_3480=y
+CONFIG_S390_TAPE_3490=m
+CONFIG_S390_TAPE_3480=m
 
 #
 # Network device drivers
@@ -150,6 +150,7 @@
 # CONFIG_IPV6_NETLINK is not set
 # CONFIG_KHTTPD is not set
 # CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
 
 #
 #  
@@ -180,12 +181,12 @@
 # CONFIG_AUTOFS4_FS is not set
 # CONFIG_REISERFS_FS is not set
 # CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
 # CONFIG_ADFS_FS is not set
 # CONFIG_ADFS_FS_RW is not set
 # CONFIG_AFFS_FS is not set
 # CONFIG_HFS_FS is not set
 # CONFIG_BFS_FS is not set
-# CONFIG_CMS_FS is not set
 # CONFIG_EXT3_FS is not set
 # CONFIG_JBD is not set
 # CONFIG_JBD_DEBUG is not set
@@ -203,7 +204,7 @@
 # CONFIG_JOLIET is not set
 # CONFIG_ZISOFS is not set
 # CONFIG_MINIX_FS is not set
-# CONFIG_FREEVXFS_FS is not set
+# CONFIG_VXFS_FS is not set
 # CONFIG_NTFS_FS is not set
 # CONFIG_NTFS_RW is not set
 # CONFIG_HPFS_FS is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/debug.c linux/arch/s390x/kernel/debug.c
--- linux.orig/arch/s390x/kernel/debug.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/debug.c	Fri Dec 21 16:25:30 2001
@@ -228,8 +228,10 @@
 	strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1)));
 	rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0;
 	memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
+#ifdef CONFIG_PROC_FS
 	memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS *
 		sizeof(struct proc_dir_entry*));
+#endif /* CONFIG_PROC_FS */
 	atomic_set(&(rc->ref_count), 0);
 
 	return rc;
@@ -346,8 +348,10 @@
 	if (!db_info)
 		return;
 	if (atomic_dec_and_test(&db_info->ref_count)) {
+#ifdef DEBUG
 		printk(KERN_INFO "debug: freeing debug area %p (%s)\n",
 		       db_info, db_info->name);
+#endif
 		for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
 			if (db_info->views[i] != NULL)
 				debug_delete_proc_dir_entry
@@ -541,14 +545,18 @@
 	debug_info_snapshot = debug_info_copy(debug_info);
 
 	if(!debug_info_snapshot){
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: debug_info_copy failed (out of mem)\n");
+#endif
 		rc = -ENOMEM;
 		goto out;
 	}
 
 	if ((file->private_data =
 	     kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) {
+#ifdef DEBUG
 		printk(KERN_ERR "debug_open: kmalloc failed\n");
+#endif
 		debug_info_free(debug_info_snapshot);	
 		rc = -ENOMEM;
 		goto out;
@@ -602,6 +610,7 @@
 {
 	struct proc_dir_entry *rc = NULL;
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	const char *fn = name;
 	int len;
@@ -634,6 +643,7 @@
 #endif
 
       out:
+#endif /* CONFIG_PROC_FS */
 	return rc;
 }
 
@@ -646,12 +656,14 @@
     (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) 
 {
 
+#ifdef CONFIG_PROC_FS
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98))
 	proc_unregister(root, proc_entry->low_ino);
 	kfree(proc_entry);
 #else
 	remove_proc_entry(proc_entry->name, root);
 #endif
+#endif /* CONFIG_PROC_FS */
 }
 
 /*
@@ -677,9 +689,11 @@
 		goto out;
 	debug_register_view(rc, &debug_level_view);
         debug_register_view(rc, &debug_flush_view);
+#ifdef DEBUG
 	printk(KERN_INFO
 	       "debug: reserved %d areas of %d pages for debugging %s\n",
 	       nr_areas, 1 << page_order, rc->name);
+#endif
       out:
         if (rc == NULL){
 		printk(KERN_ERR "debug: debug_register failed for %s\n",name);
@@ -699,7 +713,9 @@
 	if (!id)
 		goto out;
 	down(&debug_lock);
+#ifdef DEBUG
 	printk(KERN_INFO "debug: unregistering %s\n", id->name);
+#endif
 	debug_info_put(id);
 	up(&debug_lock);
 
@@ -906,11 +922,13 @@
 
 	down(&debug_lock);
 	if (!initialized) {
+#ifdef CONFIG_PROC_FS
 		debug_proc_root_entry =
 		    debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT,
 						S_IFDIR | S_IRUGO | S_IXUGO
 						| S_IWUSR | S_IWGRP, NULL,
 						NULL);
+#endif /* CONFIG_PROC_FS */
 		printk(KERN_INFO "debug: Initialization complete\n");
 		initialized = 1;
 	}
@@ -1271,7 +1289,9 @@
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
 #endif
+#ifdef CONFIG_PROC_FS
 	debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry);
+#endif /* CONFIG_PROC_FS */
 	return;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/entry.S linux/arch/s390x/kernel/entry.S
--- linux.orig/arch/s390x/kernel/entry.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/entry.S	Fri Dec 21 16:25:30 2001
@@ -79,7 +79,7 @@
 sigpending   = 16
 need_resched = 32
 tsk_ptrace   = 40
-processor    = 92
+processor    = 88
 
 /*
  * Register usage in interrupt handlers:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/init_task.c linux/arch/s390x/kernel/init_task.c
--- linux.orig/arch/s390x/kernel/init_task.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/init_task.c	Fri Dec 21 16:25:30 2001
@@ -12,7 +12,6 @@
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 
-static struct vm_area_struct init_mmap = INIT_MMAP;
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
 static struct signal_struct init_signals = INIT_SIGNALS;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/ioctl32.c linux/arch/s390x/kernel/ioctl32.c
--- linux.orig/arch/s390x/kernel/ioctl32.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/ioctl32.c	Fri Dec 21 16:25:30 2001
@@ -24,6 +24,7 @@
 #include <linux/route.h>
 #include <linux/ext2_fs.h>
 #include <linux/hdreg.h>
+#include <linux/if_bonding.h>
 #include <asm/types.h>
 #include <asm/uaccess.h>
 #include <asm/dasd.h>
@@ -195,6 +196,58 @@
 out:
 	if(ifc.ifc_buf != NULL)
 		kfree (ifc.ifc_buf);
+	return err;
+}
+
+static int bond_ioctl(unsigned long fd, unsigned int cmd, unsigned long arg)
+{
+	struct ifreq ifr;
+	mm_segment_t old_fs;
+	int err, len;
+	u32 data;
+	
+	if (copy_from_user(&ifr, (struct ifreq32 *)arg, sizeof(struct ifreq32)))
+		return -EFAULT;
+	ifr.ifr_data = (__kernel_caddr_t)get_free_page(GFP_KERNEL);
+	if (!ifr.ifr_data)
+		return -EAGAIN;
+
+	switch (cmd) {
+	case SIOCBONDENSLAVE:
+	case SIOCBONDRELEASE:
+	case SIOCBONDSETHWADDR:
+	case SIOCBONDCHANGEACTIVE:
+		len = IFNAMSIZ * sizeof(char);
+		break;
+	case SIOCBONDSLAVEINFOQUERY:
+		len = sizeof(struct ifslave);
+		break;
+	case SIOCBONDINFOQUERY:
+		len = sizeof(struct ifbond);
+		break;
+	default:
+		err = -EINVAL;
+		goto out;
+	};
+
+	__get_user(data, &(((struct ifreq32 *)arg)->ifr_ifru.ifru_data));
+	if (copy_from_user(ifr.ifr_data, (char *)A(data), len)) {
+		err = -EFAULT;
+		goto out;
+	}
+
+	old_fs = get_fs();
+	set_fs (KERNEL_DS);
+	err = sys_ioctl (fd, cmd, (unsigned long)&ifr);
+	set_fs (old_fs);
+	if (!err) {
+		len = copy_to_user((char *)A(data), ifr.ifr_data, len);
+		if (len)
+			err = -EFAULT;
+	}
+
+out:
+	free_page((unsigned long)ifr.ifr_data);
 	return err;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/linux32.c linux/arch/s390x/kernel/linux32.c
--- linux.orig/arch/s390x/kernel/linux32.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/linux32.c	Fri Dec 21 16:25:30 2001
@@ -897,24 +897,24 @@
 	return sys32_fcntl(fd, cmd, arg);
 }
 
-struct mem_dqblk32 {
+struct dqblk32 {
+    __u32 dqb_bhardlimit;
+    __u32 dqb_bsoftlimit;
+    __u32 dqb_curblocks;
     __u32 dqb_ihardlimit;
     __u32 dqb_isoftlimit;
     __u32 dqb_curinodes;
-    __u32 dqb_bhardlimit;
-    __u32 dqb_bsoftlimit;
-    __u64 dqb_curspace;
     __kernel_time_t32 dqb_btime;
     __kernel_time_t32 dqb_itime;
 };
                                 
-extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr);
+extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr);
 
 asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr)
 {
 	int cmds = cmd >> SUBCMDSHIFT;
 	int err;
-	struct mem_dqblk d;
+	struct dqblk d;
 	mm_segment_t old_fs;
 	char *spec;
 	
@@ -924,32 +924,32 @@
 	case Q_SETQUOTA:
 	case Q_SETUSE:
 	case Q_SETQLIM:
-		if (copy_from_user (&d, (struct mem_dqblk32 *)addr,
-				    sizeof (struct mem_dqblk32)))
+		if (copy_from_user (&d, (struct dqblk32 *)addr,
+				    sizeof (struct dqblk32)))
 			return -EFAULT;
-		d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime;
-		d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime;
+		d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime;
+		d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime;
 		break;
 	default:
 		return sys_quotactl(cmd, special,
-				    id, (__kernel_caddr_t)addr);
+				    id, (caddr_t)addr);
 	}
 	spec = getname (special);
 	err = PTR_ERR(spec);
 	if (IS_ERR(spec)) return err;
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
-	err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d);
+	err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d);
 	set_fs (old_fs);
 	putname (spec);
 	if (err)
 		return err;
 	if (cmds == Q_GETQUOTA) {
 		__kernel_time_t b = d.dqb_btime, i = d.dqb_itime;
-		((struct mem_dqblk32 *)&d)->dqb_itime = i;
-		((struct mem_dqblk32 *)&d)->dqb_btime = b;
-		if (copy_to_user ((struct mem_dqblk32 *)addr, &d,
-				  sizeof (struct mem_dqblk32)))
+		((struct dqblk32 *)&d)->dqb_itime = i;
+		((struct dqblk32 *)&d)->dqb_btime = b;
+		if (copy_to_user ((struct dqblk32 *)addr, &d,
+				  sizeof (struct dqblk32)))
 			return -EFAULT;
 	}
 	return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/process.c linux/arch/s390x/kernel/process.c
--- linux.orig/arch/s390x/kernel/process.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/process.c	Fri Dec 21 16:25:30 2001
@@ -75,171 +75,22 @@
 	}
 }
 
-/*
-  As all the register will only be made displayable to the root
-  user ( via printk ) or checking if the uid of the user is 0 from
-  the /proc filesystem please god this will be secure enough DJB.
-  The lines are given one at a time so as not to chew stack space in
-  printk on a crash & also for the proc filesystem when you get
-  0 returned you know you've got all the lines
- */
-
-static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs)
-{
-	int linelen=0;
-	int regno,chaincnt;
-	u64 backchain,prev_backchain,endchain;
-	u64 ksp = 0;
-	char *mode = "???";
-
-	enum
-	{
-		sp_linefeed,
-		sp_psw,
-		sp_ksp,
-		sp_gprs,
-		sp_gprs1,
-		sp_gprs2,
-		sp_gprs3,
-		sp_gprs4,
-		sp_gprs5,
-		sp_gprs6,
-		sp_gprs7,
-		sp_gprs8,
-		sp_acrs,
-		sp_acrs1,
-		sp_acrs2,
-		sp_acrs3,
-		sp_acrs4,
-		sp_kern_backchain,
-		sp_kern_backchain1
-	};
-
-	if (task)
-		ksp = task->thread.ksp;
-	if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE))
-		ksp = regs->gprs[15];
-
-	if (regs)
-		mode = (regs->psw.mask & PSW_PROBLEM_STATE)?
-		       "User" : "Kernel";
-
-	switch(line)
-	{
-	case sp_linefeed: 
-		linelen=sprintf(buff,"\n");
-		break;
-	case sp_psw:
-		if(regs)
-			linelen=sprintf(buff, "%s PSW:    %016lx %016lx    %s\n", mode,
-				(unsigned long) regs->psw.mask,
-				(unsigned long) regs->psw.addr,
-				print_tainted());
-		else
-			linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n");
-		break;
-	case sp_ksp:
-		linelen=sprintf(&buff[linelen],
-				"task: %016lx ksp: %016lx pt_regs: %016lx\n",
-				(addr_t)task, (addr_t)ksp, (addr_t)regs);
-		break;
-	case sp_gprs:
-		if(regs)
-			linelen=sprintf(buff, "%s GPRS:\n", mode);
-		break;
-	case sp_gprs1 ... sp_gprs8:
-		if(regs)
-		{
-			regno=(line-sp_gprs1)*2;
-			linelen = sprintf(buff,"%016lx  %016lx\n",
-					  regs->gprs[regno],
-					  regs->gprs[regno+1]);
-		}
-		break;
-	case sp_acrs:
-		if(regs)
-			linelen=sprintf(buff, "%s ACRS:\n", mode);
-		break;	
-        case sp_acrs1 ... sp_acrs4:
-		if(regs)
-		{
-			regno=(line-sp_acrs1)*4;
-			linelen=sprintf(buff,"%08x  %08x  %08x  %08x\n",
-					regs->acrs[regno],
-					regs->acrs[regno+1],
-					regs->acrs[regno+2],
-					regs->acrs[regno+3]);
-		}
-		break;
-	case sp_kern_backchain:
-		if (regs && (regs->psw.mask & PSW_PROBLEM_STATE))
-			break;
-		if (ksp)
-			linelen=sprintf(buff, "Kernel BackChain          CallChain\n");
-		break;
-	default:
-		if (ksp)
-		{
-			
-			backchain=ksp&PSW_ADDR_MASK;
-			endchain=((backchain&(-THREAD_SIZE))+THREAD_SIZE);
-			prev_backchain=backchain-1;
-			line-=sp_kern_backchain1;
-			for(chaincnt=0;;chaincnt++)
-			{
-				if((backchain==0)||(backchain>=endchain)
-				   ||(chaincnt>=8)||(prev_backchain>=backchain))
-					break;
-				if(chaincnt==line)
-				{
-					linelen+=sprintf(&buff[linelen],"       %016lx   [<%016lx>]\n",
-							 backchain,
-							 *(u64 *)(backchain+112)&PSW_ADDR_MASK);
-					break;
-				}
-				prev_backchain=backchain;
-				backchain=(*((u64 *)backchain))&PSW_ADDR_MASK;
-			}
-		}
-	}
-	return(linelen);
-}
+extern void show_registers(struct pt_regs *regs);
+extern void show_trace(unsigned long *sp);
 
 void show_regs(struct pt_regs *regs)
 {
-	char buff[80];
-	int i, line;
-
-        printk("CPU:    %d\n",smp_processor_id());
-        printk("Process %s (pid: %d, stackpage=%016lX)\n",
-                current->comm, current->pid, 4096+(addr_t)current);
-	
-	for (line = 0; sprintf_regs(line, buff, current, regs); line++)
-		printk(buff);
-
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
-	{
-		printk("User Code:\n");
-		memset(buff, 0, 20);
-		copy_from_user(buff,
-			       (char *) (regs->psw.addr & PSW_ADDR_MASK), 20);
-		for (i = 0; i < 20; i++)
-			printk("%02x ", buff[i]);
-		printk("\n");
-	}
-}
-
-char *task_show_regs(struct task_struct *task, char *buffer)
-{
-	int line, len;
+	struct task_struct *tsk = current;
 
-	for (line = 0; ; line++)
-	{
-		len = sprintf_regs(line, buffer, task, NULL);
-		if (!len) break;
-		buffer += len;
-	}
-	return buffer;
+        printk("CPU:    %d    %s\n", tsk->processor, print_tainted());
+        printk("Process %s (pid: %d, task: %016lx, ksp: %016lx)\n",
+	       current->comm, current->pid, (unsigned long) tsk,
+	       tsk->thread.ksp);
+
+	show_registers(regs);
+	/* Show stack backtrace if pt_regs is from kernel mode */
+	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+		show_trace((unsigned long *) regs->gprs[15]);
 }
 
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
@@ -301,16 +152,10 @@
             unsigned long gprs[10];    /* gprs 6 -15                       */
             unsigned long fprs[2];     /* fpr 4 and 6                      */
             unsigned long empty[2];
-#if CONFIG_REMOTE_DEBUG
-	    struct gdb_pt_regs childregs;
-#else
             struct pt_regs childregs;
-#endif
           } *frame;
 
         frame = (struct stack_frame *) (4*PAGE_SIZE + (unsigned long) p) -1;
-        frame = (struct stack_frame *) (((unsigned long) frame)&-8L);
-        p->thread.regs = &frame->childregs;
         p->thread.ksp = (unsigned long) frame;
         frame->childregs = *regs;
         frame->childregs.gprs[15] = new_stackp;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/ptrace.c linux/arch/s390x/kernel/ptrace.c
--- linux.orig/arch/s390x/kernel/ptrace.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/ptrace.c	Fri Dec 21 16:25:30 2001
@@ -41,7 +41,7 @@
 
 void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = task->thread.regs;
+	struct pt_regs *regs = __KSTK_PTREGS(task);
 	per_struct *per_info=
 			(per_struct *)&task->thread.per_info;
 
@@ -169,7 +169,7 @@
 		mask=PSW_ADDR_MASK;
 		if(useraddr<PT_FPC)
 		{
-			realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]);
+			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
 			if(useraddr<PT_PSWMASK)
 			{
 				copymax=PT_PSWMASK;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/s390_ksyms.c linux/arch/s390x/kernel/s390_ksyms.c
--- linux.orig/arch/s390x/kernel/s390_ksyms.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/s390_ksyms.c	Fri Dec 21 16:25:30 2001
@@ -21,8 +21,9 @@
 EXPORT_SYMBOL_NOVERS(_oi_bitmap);
 EXPORT_SYMBOL_NOVERS(_ni_bitmap);
 EXPORT_SYMBOL_NOVERS(_zb_findmap);
-EXPORT_SYMBOL_NOVERS(__copy_from_user_fixup);
-EXPORT_SYMBOL_NOVERS(__copy_to_user_fixup);
+EXPORT_SYMBOL_NOVERS(__copy_from_user_asm);
+EXPORT_SYMBOL_NOVERS(__copy_to_user_asm);
+EXPORT_SYMBOL_NOVERS(__clear_user_asm);
 
 /*
  * semaphore ops
@@ -38,6 +39,7 @@
 EXPORT_SYMBOL_NOVERS(memcmp);
 EXPORT_SYMBOL_NOVERS(memset);
 EXPORT_SYMBOL_NOVERS(memmove);
+EXPORT_SYMBOL_NOVERS(memscan);
 EXPORT_SYMBOL_NOVERS(strlen);
 EXPORT_SYMBOL_NOVERS(strchr);
 EXPORT_SYMBOL_NOVERS(strcmp);
@@ -67,4 +69,3 @@
 EXPORT_SYMBOL(console_mode);
 EXPORT_SYMBOL(console_device);
 EXPORT_SYMBOL_NOVERS(do_call_softirq);
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/setup.c linux/arch/s390x/kernel/setup.c
--- linux.orig/arch/s390x/kernel/setup.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/setup.c	Fri Dec 21 16:25:30 2001
@@ -425,9 +425,10 @@
 	lowcore->io_new_psw.mask = _IO_PSW_MASK;
 	lowcore->io_new_psw.addr = (addr_t) &io_int_handler;
 	lowcore->ipl_device = S390_lowcore.ipl_device;
-	lowcore->kernel_stack = ((__u32) &init_task_union) + 16384;
+	lowcore->kernel_stack = ((__u64) &init_task_union) + 16384;
 	lowcore->async_stack = (__u64)
 		__alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384;
+	lowcore->jiffy_timer = -1LL;
 	set_prefix((__u32)(__u64) lowcore);
         cpu_init();
         boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
@@ -474,15 +475,16 @@
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
         struct cpuinfo_S390 *cpuinfo;
-	unsigned n = v;
+	unsigned long n = (unsigned long) v - 1;
 
-	if (!n--) {
+	if (!n) {
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 				"# processors    : %i\n"
 				"bogomips per cpu: %lu.%02lu\n",
 				smp_num_cpus, loops_per_jiffy/(500000/HZ),
 				(loops_per_jiffy/(5000/HZ))%100);
-	} else if (cpu_online_map & (1 << n)) {
+	}
+	if (cpu_online_map & (1 << n)) {
 		cpuinfo = &safe_get_cpu_lowcore(n).cpu_data;
 		seq_printf(m, "processor %i: "
 				"version = %02X,  "
@@ -497,7 +499,7 @@
 
 static void *c_start(struct seq_file *m, loff_t *pos)
 {
-	return *pos <= NR_CPUS ? (void)(*pos+1) : NULL;
+	return *pos <= NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
 }
 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/smp.c linux/arch/s390x/kernel/smp.c
--- linux.orig/arch/s390x/kernel/smp.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/smp.c	Fri Dec 21 16:25:30 2001
@@ -49,9 +49,6 @@
 static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
 int              smp_num_cpus;
 struct _lowcore *lowcore_ptr[NR_CPUS];
-unsigned int     prof_multiplier[NR_CPUS];
-unsigned int     prof_old_multiplier[NR_CPUS];
-unsigned int     prof_counter[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
 static atomic_t  smp_commenced = ATOMIC_INIT(0);
@@ -453,7 +450,7 @@
 /*
  *      Activate a secondary processor.
  */
-extern void init_100hz_timer(void);
+extern void init_cpu_timer(void);
 extern int pfault_init(void);
 
 int __init start_secondary(void *cpuvoid)
@@ -465,8 +462,8 @@
         /* Wait for completion of smp startup */
         while (!atomic_read(&smp_commenced))
                 /* nothing */ ;
-        /* init per CPU 100 hz timer */
-        init_100hz_timer();
+        /* init per CPU timer */
+        init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
@@ -519,7 +516,7 @@
 
         cpu_lowcore=&get_cpu_lowcore(cpu);
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (idle->thread.ksp | 16383) + 1;
+	cpu_lowcore->kernel_stack = (__u64) idle + 16384;
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctg 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -571,15 +568,7 @@
         
         /*
          *      Initialize the logical to physical CPU number mapping
-         *      and the per-CPU profiling counter/multiplier
          */
-        
-        for (i = 0; i < NR_CPUS; i++) {
-                prof_counter[i] = 1;
-                prof_old_multiplier[i] = 1;
-                prof_multiplier[i] = 1;
-        }
-
         print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data);
 
         for(i = 0; i < smp_num_cpus; i++)
@@ -631,56 +620,6 @@
 int setup_profiling_timer(unsigned int multiplier)
 {
         return 0;
-}
-
-/*
- * Local timer interrupt handler. It does both profiling and
- * process statistics/rescheduling.
- *
- * We do profiling in every local tick, statistics/rescheduling
- * happen only every 'profiling multiplier' ticks. The default
- * multiplier is 1 and it can be changed by writing the new multiplier
- * value into /proc/profile.
- */
-
-void smp_local_timer_interrupt(struct pt_regs * regs)
-{
-	int user = (user_mode(regs) != 0);
-        int cpu = smp_processor_id();
-
-        /*
-         * The profiling function is SMP safe. (nothing can mess
-         * around with "current", and the profiling counters are
-         * updated with atomic operations). This is especially
-         * useful with a profiling multiplier != 1
-         */
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-
-        if (!--prof_counter[cpu]) {
-
-                /*
-                 * The multiplier may have changed since the last time we got
-                 * to this point as a result of the user writing to
-                 * /proc/profile.  In this case we need to adjust the APIC
-                 * timer accordingly.
-                 *
-                 * Interrupts are already masked off at this point.
-                 */
-                prof_counter[cpu] = prof_multiplier[cpu];
-                if (prof_counter[cpu] != prof_old_multiplier[cpu]) {
-                  prof_old_multiplier[cpu] = prof_counter[cpu];
-                }
-
-                /*
-                 * After doing the above, we need to make like
-                 * a normal interrupt - otherwise timer interrupts
-                 * ignore the global interrupt lock, which is the
-                 * WrongThing (tm) to do.
-                 */
-
-		update_process_times(user);
-        }
 }
 
 EXPORT_SYMBOL(lowcore_ptr);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/time.c linux/arch/s390x/kernel/time.c
--- linux.orig/arch/s390x/kernel/time.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -33,65 +33,33 @@
 #include <asm/irq.h>
 #include <asm/s390_ext.h>
 
-
 /* change this if you have some constant time drift */
-#define USECS_PER_JIFFY ((signed long)1000000/HZ)
-#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12)
+#define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
+#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
 #define TICK_SIZE tick
 
-static uint64_t init_timer_cc, last_timer_cc;
+static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(uint64_t todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timeval *xtime)
 {
-#if 0
-        const int high_bit = 0x80000000L;
-        const int c_f4240 = 0xf4240L;
-        const int c_7a120 = 0x7a120;
-	/* We have to divide the 64 bit value todval by 4096
-	 * (because the 2^12 bit is the one that changes every 
-         * microsecond) and then split it into seconds and
-         * microseconds. A value of max (2^52-1) divided by
-         * the value 0xF4240 can yield a max result of approx
-         * (2^32.068). Thats to big to fit into a signed int
-	 *   ... hacking time!
-         */
-	asm volatile ("L     2,%1\n\t"
-		      "LR    3,2\n\t"
-		      "SRL   2,12\n\t"
-		      "SLL   3,20\n\t"
-		      "L     4,%O1+4(%R1)\n\t"
-		      "SRL   4,12\n\t"
-		      "OR    3,4\n\t"  /* now R2/R3 contain (todval >> 12) */
-		      "SR    4,4\n\t"
-		      "CL    2,%2\n\t"
-		      "JL    .+12\n\t"
-		      "S     2,%2\n\t"
-		      "L     4,%3\n\t"
-                      "D     2,%4\n\t"
-		      "OR    3,4\n\t"
-		      "ST    2,%O0+4(%R0)\n\t"
-		      "ST    3,%0"
-		      : "=m" (*xtime) : "m" (todval),
-		        "m" (c_7a120), "m" (high_bit), "m" (c_f4240)
-		      : "cc", "memory", "2", "3", "4" );
-#else
-    todval >>= 12;
-    xtime->tv_sec = todval / 1000000;
-    xtime->tv_usec = todval % 1000000;
-#endif
+        todval >>= 12;
+        xtime->tv_sec = todval / 1000000;
+        xtime->tv_usec = todval % 1000000;
 }
 
-unsigned long do_gettimeoffset(void) 
+static inline unsigned long do_gettimeoffset(void) 
 {
-	__u64 timer_cc;
+	__u64 now;
 
-	asm volatile ("STCK %0" : "=m" (timer_cc));
-        /* We require the offset from the previous interrupt */
-        return ((unsigned long)((timer_cc - last_timer_cc)>>12));
+	asm ("STCK %0" : "=m" (now));
+        now = (now - init_timer_cc) >> 12;
+	/* We require the offset from the latest update of xtime */
+	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
+	return (unsigned long) now;
 }
 
 /*
@@ -101,15 +69,10 @@
 {
 	unsigned long flags;
 	unsigned long usec, sec;
-	unsigned long lost_ticks;
 
 	read_lock_irqsave(&xtime_lock, flags);
-	lost_ticks = jiffies - wall_jiffies;
-	usec = do_gettimeoffset();
-	if (lost_ticks)
-		usec +=(USECS_PER_JIFFY*lost_ticks);
 	sec = xtime.tv_sec;
-	usec += xtime.tv_usec;
+	usec = xtime.tv_usec + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -155,51 +118,31 @@
 extern __u16 boot_cpu_addr;
 #endif
 
-void do_timer_interrupt(struct pt_regs *regs, __u16 error_code)
+static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
 
 	irq_enter(cpu, 0);
 
-        /*
-         * reset timer to 10ms minus time already elapsed
-         * since timer-interrupt pending
-         */
+	/*
+	 * set clock comparator for next tick
+	 */
+        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+
 #ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) {
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
 		write_lock(&xtime_lock);
-		last_timer_cc = S390_lowcore.jiffy_timer_cc;
-	}
-#else
-        last_timer_cc = S390_lowcore.jiffy_timer_cc;
-#endif
-        /* set clock comparator */
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
 
-/*
- * In the SMP case we use the local timer interrupt to do the
- * profiling, except when we simulate SMP mode on a uniprocessor
- * system, in that case we have to call the local interrupt handler.
- */
-#ifdef CONFIG_SMP
-        /* when SMP, do smp_local_timer_interrupt for *all* CPUs,
-           but only do the rest for the boot CPU */
-        smp_local_timer_interrupt(regs);
-#else
-        if (!user_mode(regs))
-                s390_do_profile(regs->psw.addr);
-#endif
+	update_process_times(user_mode(regs));
 
-#ifdef CONFIG_SMP
-	if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr)
-#endif
-	{
+	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
 		do_timer(regs);
-#ifdef CONFIG_SMP
 		write_unlock(&xtime_lock);
-#endif
 	}
+#else
+	do_timer(regs);
+#endif
 
 	irq_exit(cpu, 0);
 }
@@ -207,19 +150,17 @@
 /*
  * Start the clock comparator on the current CPU
  */
-static unsigned long cr0 __attribute__ ((aligned (8)));
-
-void init_100hz_timer(void)
+void init_cpu_timer(void)
 {
+	unsigned long cr0;
+
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
-        /* set clock comparator */
-        /* read the TOD clock */
-        asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc));
-        S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc));
+	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
+	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
 }
 
 /*
@@ -228,6 +169,7 @@
  */
 void __init time_init(void)
 {
+        __u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -247,15 +189,16 @@
                 printk("time_init: TOD clock stopped/non-operational\n");
                 break;
         }
+
+	/* set xtime */
+        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+                      (0x3c26700LL*1000000*4096);
+        tod_to_timeval(set_time_cc, &xtime);
+
         /* request the 0x1004 external interrupt */
-        if (register_external_interrupt(0x1004, do_timer_interrupt) != 0)
-                panic("Couldn't request external interrupts 0x1004");
-        init_100hz_timer();
-        init_timer_cc = S390_lowcore.jiffy_timer_cc;
-        init_timer_cc -= 0x8126d60e46000000LL -
-                         (0x3c26700LL*1000000*4096);
-        tod_to_timeval(init_timer_cc, &xtime);
+        if (register_external_interrupt(0x1004, do_comparator_interrupt) != 0)
+                panic("Couldn't request external interrupt 0x1004");
 
-	/* Set do_get_fast_time function pointer.  */
-	do_get_fast_time = do_gettimeofday;
+        /* init CPU timer */
+        init_cpu_timer();
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/traps.c linux/arch/s390x/kernel/traps.c
--- linux.orig/arch/s390x/kernel/traps.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/traps.c	Fri Dec 21 16:25:30 2001
@@ -26,6 +26,7 @@
 #include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/module.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -58,6 +59,203 @@
 extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code);
 #endif
 
+int kstack_depth_to_print = 20;
+
+/*
+ * If the address is either in the .text section of the
+ * kernel, or in the vmalloc'ed module regions, it *may* 
+ * be the address of a calling routine
+ */
+extern char _stext, _etext;
+
+#ifdef CONFIG_MODULES
+
+extern struct module *module_list;
+extern struct module kernel_module;
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	int retval = 0;
+	struct module *mod;
+
+	if (addr >= (unsigned long) &_stext &&
+	    addr <= (unsigned long) &_etext)
+		return 1;
+
+	for (mod = module_list; mod != &kernel_module; mod = mod->next) {
+		/* mod_bound tests for addr being inside the vmalloc'ed
+		 * module area. Of course it'd be better to test only
+		 * for the .text subset... */
+		if (mod_bound(addr, 0, mod)) {
+			retval = 1;
+			break;
+		}
+	}
+
+	return retval;
+}
+
+#else
+
+static inline int kernel_text_address(unsigned long addr)
+{
+	return (addr >= (unsigned long) &_stext &&
+		addr <= (unsigned long) &_etext);
+}
+
+#endif
+
+void show_trace(unsigned long * stack)
+{
+	unsigned long backchain, low_addr, high_addr, ret_addr;
+	int i;
+
+	if (!stack)
+		stack = (unsigned long*)&stack;
+
+	printk("Call Trace: ");
+	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
+	/* Skip the first frame (biased stack) */
+	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	/* Print up to 8 lines */
+	for (i = 0; i < 8; i++) {
+		if (backchain < low_addr || backchain >= high_addr)
+			break;
+		ret_addr = *((unsigned long *) (backchain+112)) & PSW_ADDR_MASK;
+		if (!kernel_text_address(ret_addr))
+			break;
+		if (i && ((i % 3) == 0))
+			printk("\n   ");
+		printk("[<%016lx>] ", ret_addr);
+		low_addr = backchain;
+		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+	}
+	printk("\n");
+}
+
+void show_trace_task(struct task_struct *tsk)
+{
+	/*
+	 * We can't print the backtrace of a running process. It is
+	 * unreliable at best and can cause kernel oopses.
+	 */
+	if (task_has_cpu(tsk))
+		return;
+	show_trace((unsigned long *) tsk->thread.ksp);
+}
+
+void show_stack(unsigned long *sp)
+{
+	unsigned long *stack;
+	int i;
+
+	// debugging aid: "show_stack(NULL);" prints the
+	// back trace for this cpu.
+
+	if (sp == NULL)
+		sp = (unsigned long*) &sp;
+
+	stack = sp;
+	for (i = 0; i < kstack_depth_to_print; i++) {
+		if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
+			break;
+		if (i && ((i % 4) == 0))
+			printk("\n       ");
+		printk("%016lx ", *stack++);
+	}
+	printk("\n");
+	show_trace(sp);
+}
+
+void show_registers(struct pt_regs *regs)
+{
+	mm_segment_t old_fs;
+	char *mode;
+	int i;
+
+	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	printk("%s PSW : %016lx %016lx\n",
+	       mode, (unsigned long) regs->psw.mask,
+	       (unsigned long) regs->psw.addr);
+	printk("%s GPRS: %016lx %016lx %016lx %016lx\n", mode,
+	       regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
+	printk("           %016lx %016lx %016lx %016lx\n",
+	       regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
+	printk("%s ACRS: %08x %08x %08x %08x\n", mode,
+	       regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]);
+	printk("           %08x %08x %08x %08x\n",
+	       regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]);
+
+	/*
+	 * Print the first 20 byte of the instruction stream at the
+	 * time of the fault.
+	 */
+	old_fs = get_fs();
+	if (regs->psw.mask & PSW_PROBLEM_STATE)
+		set_fs(USER_DS);
+	else
+		set_fs(KERNEL_DS);
+	printk("%s Code: ", mode);
+	for (i = 0; i < 20; i++) {
+		unsigned char c;
+		if (__get_user(c, (char *)(regs->psw.addr + i))) {
+			printk(" Bad PSW.");
+			break;
+		}
+		printk("%02x ", c);
+	}
+	set_fs(old_fs);
+
+	printk("\n");
+}	
+
+/* This is called from fs/proc/array.c */
+char *task_show_regs(struct task_struct *task, char *buf)
+{
+	struct pt_regs *regs;
+
+	regs = __KSTK_PTREGS(task);
+	buf += sprintf(buf, "task: %016lx, ksp: %016lx\n",
+		       (unsigned long) task, task->thread.ksp);
+	buf += sprintf(buf, "User PSW : %016lx %016lx\n",
+		       (unsigned long) regs->psw.mask, 
+		       (unsigned long) regs->psw.addr);
+	buf += sprintf(buf, "User GPRS: %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[0], regs->gprs[1],
+		       regs->gprs[2], regs->gprs[3]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[4], regs->gprs[5], 
+		       regs->gprs[6], regs->gprs[7]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[8], regs->gprs[9],
+		       regs->gprs[10], regs->gprs[11]);
+	buf += sprintf(buf, "           %016lx %016lx %016lx %016lx\n",
+		       regs->gprs[12], regs->gprs[13],
+		       regs->gprs[14], regs->gprs[15]);
+	buf += sprintf(buf, "User ACRS: %08x %08x %08x %08x\n",
+		       regs->acrs[0], regs->acrs[1],
+		       regs->acrs[2], regs->acrs[3]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[4], regs->acrs[5],
+		       regs->acrs[6], regs->acrs[7]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[8], regs->acrs[9],
+		       regs->acrs[10], regs->acrs[11]);
+	buf += sprintf(buf, "           %08x %08x %08x %08x\n",
+		       regs->acrs[12], regs->acrs[13],
+		       regs->acrs[14], regs->acrs[15]);
+	return buf;
+}
+
 spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
 
 void die(const char * str, struct pt_regs * regs, long err)
@@ -142,7 +340,7 @@
 #if CONFIG_REMOTE_DEBUG
 		if(gdb_stub_initialised)
 		{
-			gdb_stub_handle_exception((gdb_pt_regs *)regs,signal);
+			gdb_stub_handle_exception(regs, signal);
 			return 0;
 		}
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/kernel/wrapper32.S linux/arch/s390x/kernel/wrapper32.S
--- linux.orig/arch/s390x/kernel/wrapper32.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/kernel/wrapper32.S	Fri Dec 21 16:25:30 2001
@@ -120,8 +120,8 @@
 	lgfr	%r2,%r2			# long
 	lgfr	%r3,%r3			# long
 	llgtr	%r4,%r4			# long
-	lgfr	%r5,%r5			# long
-	jg	sys32_ptrace		# branch to system call
+	llgfr	%r5,%r5			# long
+	jg	sys_ptrace		# branch to system call
 
 	.globl  sys32_alarm_wrapper 
 sys32_alarm_wrapper:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/lib/uaccess.S linux/arch/s390x/lib/uaccess.S
--- linux.orig/arch/s390x/lib/uaccess.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/lib/uaccess.S	Fri Dec 21 16:25:30 2001
@@ -6,40 +6,82 @@
  *    Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
- *  These functions have a non-standard call interface
+ *  These functions have standard call interface
  */
 
 #include <asm/lowcore.h>
 
         .text
         .align 4
-        .globl __copy_from_user_fixup
-__copy_from_user_fixup:
-        lg    1,__LC_PGM_OLD_PSW+8
-0:      lghi  3,-4096
-        ng    3,__LC_TRANS_EXC_ADDR
-        sgr   3,4
-        bm    4(1)
-1:      mvcle 2,4,0
-        b     4(1)
+        .globl __copy_from_user_asm
+__copy_from_user_asm:
+	lgr	%r5,%r3
+	sacf	512
+0:	mvcle	%r2,%r4,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r5
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:	lghi	%r3,-4096
+        ng	%r3,__LC_TRANS_EXC_ADDR
+        sgr	%r3,%r4
+        bm	4(%r1)
+3:      mvcle	%r2,%r4,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .align 8
-	.quad 1b,0b
+        .align	8
+	.quad	0b,1b
+	.quad	3b,2b
         .previous
 
         .align 4
         .text
-        .globl __copy_to_user_fixup
-__copy_to_user_fixup:
-        lg    1,__LC_PGM_OLD_PSW+8
-0:      lghi  5,-4096
-        ng    5,__LC_TRANS_EXC_ADDR
-        sgr   5,4
-        bm    4(1)
-1:      mvcle 4,2,0
-        b     4(1)
+        .globl __copy_to_user_asm
+__copy_to_user_asm:
+	lgr	%r5,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r3
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:      lghi	%r5,-4096
+        ng	%r5,__LC_TRANS_EXC_ADDR
+        sgr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
         .section __ex_table,"a"
-        .align 8
-        .quad 1b,0b
+        .align	8
+        .quad	0b,1b
+        .quad	3b,2b
         .previous
 
+        .align 4
+        .text
+        .globl __clear_user_asm
+__clear_user_asm:
+	lgr	%r4,%r2
+	lgr	%r5,%r3
+	sgr	%r2,%r2
+	sgr	%r3,%r3
+	sacf	512
+0:	mvcle	%r4,%r2,0
+	jo	0b
+	sacf	0
+	lgr	%r2,%r5
+	br	%r14
+1:	lg	%r1,__LC_PGM_OLD_PSW+8
+2:      lghi	%r5,-4096
+        ng	%r5,__LC_TRANS_EXC_ADDR
+        sgr	%r5,%r4
+        bm	4(%r1)
+3:      mvcle	%r4,%r2,0
+        b	4(%r1)
+        .section __ex_table,"a"
+        .align	8
+        .quad	0b,1b
+        .quad	3b,2b
+        .previous
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/vmlinux-shared.lds linux/arch/s390x/vmlinux-shared.lds
--- linux.orig/arch/s390x/vmlinux-shared.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/vmlinux-shared.lds	Thu Jan 10 20:08:20 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) }
   .kstrtab : { *(.kstrtab) }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/s390x/vmlinux.lds linux/arch/s390x/vmlinux.lds
--- linux.orig/arch/s390x/vmlinux.lds	Tue Jan 22 17:51:22 2002
+++ linux/arch/s390x/vmlinux.lds	Thu Jan 10 20:08:20 2002
@@ -13,7 +13,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0700
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/config.in linux/arch/sh/config.in
--- linux.orig/arch/sh/config.in	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/config.in	Fri Dec 21 18:00:38 2001
@@ -189,7 +189,7 @@
    if [ "$CONFIG_PCI_GODIRECT" = "y" -o "$CONFIG_PCI_GOANY" = "y" ]; then
       define_bool CONFIG_PCI_DIRECT y
    fi
-   define_bool CONFIG_SH_PCIDMA_NONCOHERENT n
+   bool 'Cache and PCI noncoherent' CONFIG_SH_PCIDMA_NONCOHERENT n
 fi
 
 source drivers/pci/Config.in
@@ -280,9 +280,9 @@
 #
 source drivers/input/Config.in
 
-if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
-   source drivers/maple/Config.in
-fi
+# if [ "$CONFIG_SH_DREAMCAST" = "y" ]; then
+#    source drivers/maple/Config.in
+# fi
 
 mainmenu_option next_comment
 comment 'Character devices'
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/io_7751se.c linux/arch/sh/kernel/io_7751se.c
--- linux.orig/arch/sh/kernel/io_7751se.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/kernel/io_7751se.c	Fri Dec 21 18:00:38 2001
@@ -17,7 +17,7 @@
 #include <asm/hitachi_7751se.h>
 #include <asm/addrspace.h>
 
-#include <asm/pci.h>
+#include <linux/pci.h>
 #include <asm/pci-sh7751.h>
 
 #if 0
@@ -70,7 +70,7 @@
 	else
 		return (volatile __u16 *) (PA_SUPERIO + (port << 1));
 #endif
-	maybebadio(name,port);
+	maybebadio(name,(unsigned long)port);
 	return (volatile __u16*)port;
 }
 
@@ -276,6 +276,7 @@
 /* ISA page descriptor.  */
 static __u32 sh_isa_memmap[256];
 
+#if 0
 static int
 sh_isa_mmap(__u32 start, __u32 length, __u32 offset)
 {
@@ -286,12 +287,11 @@
 
 	idx = start >> 12;
 	sh_isa_memmap[idx] = 0xb8000000 + (offset &~ 0xfff);
-#if 0
 	printk("sh_isa_mmap: start %x len %x offset %x (idx %x paddr %x)\n",
 	       start, length, offset, idx, sh_isa_memmap[idx]);
-#endif
 	return 0;
 }
+#endif
 
 unsigned long
 sh7751se_isa_port2addr(unsigned long offset)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/pci-7751se.c linux/arch/sh/kernel/pci-7751se.c
--- linux.orig/arch/sh/kernel/pci-7751se.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/kernel/pci-7751se.c	Fri Dec 21 18:00:38 2001
@@ -37,7 +37,6 @@
  */
 int __init pcibios_init_platform(void)
 {
-   unsigned long data;
    unsigned long bcr1, wcr1, wcr2, wcr3, mcr;
    unsigned short bcr2;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/rtc.c linux/arch/sh/kernel/rtc.c
--- linux.orig/arch/sh/kernel/rtc.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/kernel/rtc.c	Fri Dec 21 18:00:38 2001
@@ -46,7 +46,7 @@
 	} while ((ctrl_inb(RCR1) & RCR1_CF) != 0);
 
 #if RTC_BIT_INVERTED != 0
-	/* Work around to avoid reading correct value. */
+	/* Work around to avoid reading incorrect value. */
 	if (sec128 == RTC_BIT_INVERTED) {
 		schedule_timeout(1);
 		goto again;
@@ -81,12 +81,18 @@
 		goto again;
 	}
 
+#if RTC_BIT_INVERTED != 0
+	if ((sec128 & RTC_BIT_INVERTED))
+		sec--;
+#endif
+
 	tv->tv_sec = mktime(yr100 * 100 + yr, mon, day, hr, min, sec);
-	tv->tv_usec = ((sec128 ^ RTC_BIT_INVERTED) * 1000000) / 128;
+	tv->tv_usec = (sec128 * 1000000) / 128;
 }
 
-static int set_rtc_time(unsigned long nowtime)
+int sh_rtc_settimeofday(const struct timeval *tv)
 {
+	unsigned long nowtime = tv->tv_sec;
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
 
@@ -122,13 +128,4 @@
 	ctrl_outb(RCR2_RTCEN|RCR2_START, RCR2);  /* Start RTC */
 
 	return retval;
-}
-
-int sh_rtc_settimeofday(const struct timeval *tv)
-{
-#if RTC_BIT_INVERTED != 0
-	/* This is not accurate, but better than nothing. */
-	schedule_timeout(HZ/2);
-#endif
-	return set_rtc_time(tv->tv_sec);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/sh_ksyms.c linux/arch/sh/kernel/sh_ksyms.c
--- linux.orig/arch/sh/kernel/sh_ksyms.c	Sat Sep  8 19:29:09 2001
+++ linux/arch/sh/kernel/sh_ksyms.c	Wed Feb  6 21:06:42 2002
@@ -39,8 +39,6 @@
 /* Networking helper routines. */
 EXPORT_SYMBOL(csum_partial_copy);
 
-EXPORT_SYMBOL(simple_strtol);
-
 EXPORT_SYMBOL(strtok);
 EXPORT_SYMBOL(strpbrk);
 EXPORT_SYMBOL(strstr);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/kernel/traps.c linux/arch/sh/kernel/traps.c
--- linux.orig/arch/sh/kernel/traps.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/kernel/traps.c	Fri Dec 21 18:00:38 2001
@@ -560,3 +560,8 @@
 		}
 	}
 }
+
+void show_trace_task(struct task_struct *tsk)
+{
+	printk("Backtrace not yet implemented for SH.\n");
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sh/vmlinux.lds.S linux/arch/sh/vmlinux.lds.S
--- linux.orig/arch/sh/vmlinux.lds.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/sh/vmlinux.lds.S	Thu Jan 10 20:08:20 2002
@@ -23,7 +23,6 @@
 	*(.fixup)
 	*(.gnu.warning)
 	} = 0x0009
-  .text.lock : { *(.text.lock) }	/* out-of-line lock text */
   .rodata : { *(.rodata) *(.rodata.*) }
   .kstrtab : { *(.kstrtab) }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/defconfig linux/arch/sparc/defconfig
--- linux.orig/arch/sparc/defconfig	Fri Dec 21 17:41:53 2001
+++ linux/arch/sparc/defconfig	Thu Jan 17 19:37:52 2002
@@ -373,6 +373,7 @@
 # CONFIG_NLS_CODEPAGE_949 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
 # CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ISO8859_1 is not set
 # CONFIG_NLS_ISO8859_2 is not set
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/check_asm.sh linux/arch/sparc/kernel/check_asm.sh
--- linux.orig/arch/sparc/kernel/check_asm.sh	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/kernel/check_asm.sh	Tue Jan 15 19:08:06 2002
@@ -1,12 +1,12 @@
 #!/bin/sh
 case $1 in
   -printf)
-    sed -n -e '/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+    sed -n -e '/^#/d;/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
 /g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/printf ("#define AOFF_'$2'_\0	0x%08x\\n", check_asm_data[i++]); printf("#define ASIZ_'$2'_\0	0x%08x\\n", check_asm_data[i++]);/' >> $4
     echo "printf (\"#define ASIZ_$2\\t0x%08x\\n\", check_asm_data[i++]);" >> $4
   ;;
   -data)
-    sed -n -e '/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+    sed -n -e '/^#/d;/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
 /g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/	((char *)\&((struct '$2'_struct *)0)->\0) - ((char *)((struct '$2'_struct *)0)),	sizeof(((struct '$2'_struct *)0)->\0),/' >> $4
     echo "	sizeof(struct $2_struct)," >> $4
   ;;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/devices.c linux/arch/sparc/kernel/devices.c
--- linux.orig/arch/sparc/kernel/devices.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/kernel/devices.c	Tue Jan 15 19:08:06 2002
@@ -4,9 +4,10 @@
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
  */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/threads.h>
-#include <linux/config.h>
+#include <linux/string.h>
 #include <linux/init.h>
 
 #include <asm/page.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/ebus.c linux/arch/sparc/kernel/ebus.c
--- linux.orig/arch/sparc/kernel/ebus.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/kernel/ebus.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: ebus.c,v 1.18 2001/11/08 04:41:33 davem Exp $
+/* $Id: ebus.c,v 1.18.2.2 2002/01/05 01:12:31 davem Exp $
  * ebus.c: PCI to EBus bridge device.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/init_task.c linux/arch/sparc/kernel/init_task.c
--- linux.orig/arch/sparc/kernel/init_task.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/kernel/init_task.c	Tue Jan 15 19:08:06 2002
@@ -14,6 +14,6 @@
  * If this is not aligned on a 8k boundry, then you should change code
  * in etrap.S which assumes it.
  */
-union task_union init_task_union
-	__attribute__((__section__(".text"))) =
+__asm__(".section \".text\",#alloc\n");
+union task_union init_task_union =
 		{ INIT_TASK(init_task_union.task) };
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/pcic.c linux/arch/sparc/kernel/pcic.c
--- linux.orig/arch/sparc/kernel/pcic.c	Mon Feb 19 03:49:44 2001
+++ linux/arch/sparc/kernel/pcic.c	Mon Feb  4 16:55:13 2002
@@ -1,4 +1,4 @@
-/* $Id: pcic.c,v 1.22 2001/02/13 01:16:43 davem Exp $
+/* $Id: pcic.c,v 1.22.2.1 2002/01/23 14:35:45 davem Exp $
  * pcic.c: Sparc/PCI controller support
  *
  * Copyright (C) 1998 V. Roganov and G. Raiko
@@ -759,7 +759,6 @@
 	unsigned long v;
 	int timer_irq, irq;
 
-	do_get_fast_time = pci_do_gettimeofday;
 	/* A hack until do_gettimeofday prototype is moved to arch specific headers
 	   and btfixupped. Patch do_gettimeofday with ba pci_do_gettimeofday; nop */
 	((unsigned int *)do_gettimeofday)[0] = 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/time.c linux/arch/sparc/kernel/time.c
--- linux.orig/arch/sparc/kernel/time.c	Tue Oct 30 23:08:11 2001
+++ linux/arch/sparc/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.59 2001/10/30 04:54:21 davem Exp $
+/* $Id: time.c,v 1.59.2.1 2002/01/23 14:35:45 davem Exp $
  * linux/arch/sparc/kernel/time.c
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -371,7 +371,6 @@
 	struct intersil *iregs;
 #endif
 
-	do_get_fast_time = do_gettimeofday;
 	BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM);
 	btfixup();
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/kernel/unaligned.c linux/arch/sparc/kernel/unaligned.c
--- linux.orig/arch/sparc/kernel/unaligned.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/kernel/unaligned.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: unaligned.c,v 1.22 2000/04/29 08:05:21 anton Exp $
+/* $Id: unaligned.c,v 1.22.2.1 2001/12/21 00:52:47 davem Exp $
  * unaligned.c: Unaligned load/store trap handling with special
  *              cases for the kernel to do them more quickly.
  *
@@ -224,7 +224,7 @@
 	"or	%%l1, %%g7, %%g7\n\t"						\
 	"st	%%g7, [%0 + 4]\n"						\
 "0:\n\n\t"									\
-	".section __ex_table\n\t"						\
+	".section __ex_table,#alloc\n\t"					\
 	".word	4b, " #errh "\n\t"						\
 	".word	5b, " #errh "\n\t"						\
 	".word	6b, " #errh "\n\t"						\
@@ -277,7 +277,7 @@
 "16:\t"	"stb	%%l2, [%0]\n"							\
 "17:\t"	"stb	%%l1, [%0 + 1]\n"						\
 "0:\n\n\t"									\
-	".section __ex_table\n\t"						\
+	".section __ex_table,#alloc\n\t"					\
 	".word	4b, " #errh "\n\t"						\
 	".word	5b, " #errh "\n\t"						\
 	".word	6b, " #errh "\n\t"						\
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc/prom/ranges.c linux/arch/sparc/prom/ranges.c
--- linux.orig/arch/sparc/prom/ranges.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc/prom/ranges.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: ranges.c,v 1.14 1999/10/06 19:28:54 zaitcev Exp $
+/* $Id: ranges.c,v 1.14.2.1 2001/12/19 00:16:21 davem Exp $
  * ranges.c: Handle ranges in newer proms for obio/sbus.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -16,26 +16,25 @@
 int num_obio_ranges;
 
 /* Adjust register values based upon the ranges parameters. */
-void
+static void
 prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
 		 struct linux_prom_ranges *rangep, int nranges)
 {
 	int regc, rngc;
 
-	for(regc=0; regc < nregs; regc++) {
-		for(rngc=0; rngc < nranges; rngc++)
-			if(regp[regc].which_io == rangep[rngc].ot_child_space &&
-			   regp[regc].phys_addr >= rangep[rngc].ot_child_base &&
-			   regp[regc].phys_addr + regp[regc].reg_size <= rangep[rngc].ot_child_base + rangep[rngc].or_size)
+	for (regc = 0; regc < nregs; regc++) {
+		for (rngc = 0; rngc < nranges; rngc++)
+			if (regp[regc].which_io == rangep[rngc].ot_child_space)
 				break; /* Fount it */
-		if(rngc==nranges) /* oops */
+		if (rngc == nranges) /* oops */
 			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
 		regp[regc].which_io = rangep[rngc].ot_parent_space;
+		regp[regc].phys_addr -= rangep[rngc].ot_child_base;
 		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
 	}
 }
 
-void
+static void
 prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
 		   struct linux_prom_ranges *ranges2, int nranges2)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- linux.orig/arch/sparc64/defconfig	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/defconfig	Thu Jan 17 19:37:52 2002
@@ -483,6 +483,7 @@
 # CONFIG_8139TOO_PIO is not set
 # CONFIG_8139TOO_TUNE_TWISTER is not set
 # CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_NEW_RX_RESET is not set
 CONFIG_SIS900=m
 CONFIG_EPIC100=m
 CONFIG_SUNDANCE=m
@@ -683,6 +684,7 @@
 # CONFIG_NLS_CODEPAGE_949 is not set
 # CONFIG_NLS_CODEPAGE_874 is not set
 # CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
 # CONFIG_NLS_CODEPAGE_1251 is not set
 # CONFIG_NLS_ISO8859_1 is not set
 # CONFIG_NLS_ISO8859_2 is not set
@@ -785,6 +787,8 @@
 CONFIG_USB_OV511=m
 CONFIG_USB_PWC=m
 CONFIG_USB_SE401=m
+CONFIG_USB_STV680=m
+CONFIG_USB_VICAM=m
 CONFIG_USB_DSBR=m
 CONFIG_USB_DABUSB=m
 
@@ -813,6 +817,7 @@
 CONFIG_USB_SERIAL_EMPEG=m
 CONFIG_USB_SERIAL_FTDI_SIO=m
 CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
 # CONFIG_USB_SERIAL_IR is not set
 CONFIG_USB_SERIAL_EDGEPORT=m
 CONFIG_USB_SERIAL_KEYSPAN_PDA=m
@@ -826,6 +831,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
 # CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
 CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_KLSI=m
 CONFIG_USB_SERIAL_PL2303=m
 CONFIG_USB_SERIAL_CYBERJACK=m
 CONFIG_USB_SERIAL_XIRCOM=m
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/central.c linux/arch/sparc64/kernel/central.c
--- linux.orig/arch/sparc64/kernel/central.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/central.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: central.c,v 1.14 2000/09/21 06:25:14 anton Exp $
+/* $Id: central.c,v 1.14.2.1 2001/12/19 00:16:21 davem Exp $
  * central.c: Central FHC driver for Sunfire/Starfire/Wildfire.
  *
  * Copyright (C) 1997, 1999 David S. Miller (davem@redhat.com)
@@ -67,6 +67,7 @@
 		if (rngc == nranges) /* oops */
 			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
 		regp[regc].which_io = rangep[rngc].ot_parent_space;
+		regp[regc].phys_addr -= rangep[rngc].ot_child_base;
 		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/check_asm.sh linux/arch/sparc64/kernel/check_asm.sh
--- linux.orig/arch/sparc64/kernel/check_asm.sh	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/check_asm.sh	Tue Jan 15 19:08:06 2002
@@ -1,12 +1,12 @@
 #!/bin/sh
 case $1 in
   -printf)
-    sed -n -e '/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+    sed -n -e '/^#/d;/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
 /g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/printf ("#define AOFF_'$2'_\0	0x%08x\\n", check_asm_data[i++]); printf("#define ASIZ_'$2'_\0	0x%08x\\n", check_asm_data[i++]);/' >> $4
     echo "printf (\"#define ASIZ_$2\\t0x%08x\\n\", check_asm_data[i++]);" >> $4
   ;;
   -data)
-    sed -n -e '/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
+    sed -n -e '/^#/d;/struct[ 	]*'$2'_struct[ 	]*{/,/};/p' < $3 | sed '/struct[ 	]*'$2'_struct[ 	]*{/d;/:[0-9]*[ 	]*;/d;/^[ 	]*$/d;/};/d;s/^[ 	]*//;s/volatile[ 	]*//;s/\(unsigned\|signed\|struct\)[ 	]*//;s/\(\[\|__attribute__\).*;[ 	]*$//;s/(\*//;s/)(.*)//;s/;[ 	]*$//;s/^[^ 	]*[ 	]*//;s/,/\
 /g' | sed 's/^[ 	*]*//;s/[ 	]*$//;s/^.*$/	((char *)\&((struct '$2'_struct *)0)->\0) - ((char *)((struct '$2'_struct *)0)),	sizeof(((struct '$2'_struct *)0)->\0),/' >> $4
     echo "	sizeof(struct $2_struct)," >> $4
   ;;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/ioctl32.c linux/arch/sparc64/kernel/ioctl32.c
--- linux.orig/arch/sparc64/kernel/ioctl32.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/ioctl32.c	Thu Jan 17 19:37:52 2002
@@ -1,4 +1,4 @@
-/* $Id: ioctl32.c,v 1.133 2001/11/14 06:14:29 davem Exp $
+/* $Id: ioctl32.c,v 1.133.2.2 2002/01/14 09:49:29 davem Exp $
  * ioctl32.c: Conversion between 32bit and 64bit native ioctls.
  *
  * Copyright (C) 1997-2000  Jakub Jelinek  (jakub@redhat.com)
@@ -472,6 +472,7 @@
 		return -ENODEV;
 
 	strcpy(ifr32.ifr_name, dev->name);
+	dev_put(dev);
 
 	err = copy_to_user((struct ifreq32 *)arg, &ifr32, sizeof(struct ifreq32));
 	return (err ? -EFAULT : 0);
@@ -1580,12 +1581,17 @@
 	}
 
 	err |= __get_user(sbp32, &sg_io32->sbp);
-	sg_io64.sbp = kmalloc(64, GFP_KERNEL);
+	sg_io64.sbp = kmalloc(sg_io64.mx_sb_len, GFP_KERNEL);
 	if (!sg_io64.sbp) {
 		err = -ENOMEM;
 		goto out;
 	}
-	memset(sg_io64.sbp, 0, 64);
+	if (copy_from_user(sg_io64.sbp,
+			   (void *) A(sbp32),
+			   sg_io64.mx_sb_len)) {
+		err = -EFAULT;
+		goto out;
+	}
 
 	err |= __get_user(dxferp32, &sg_io32->dxferp);
 	if (sg_io64.iovec_count) {
@@ -1633,7 +1639,7 @@
 	err |= __put_user(sg_io64.resid, &sg_io32->resid);
 	err |= __put_user(sg_io64.duration, &sg_io32->duration);
 	err |= __put_user(sg_io64.info, &sg_io32->info);
-	err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, 64);
+	err |= copy_to_user((void *)A(sbp32), sg_io64.sbp, sg_io64.mx_sb_len);
 	if (sg_io64.dxferp) {
 		if (sg_io64.iovec_count)
 			err |= copy_back_sg_iovec(&sg_io64, dxferp32);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/iommu_common.c linux/arch/sparc64/kernel/iommu_common.c
--- linux.orig/arch/sparc64/kernel/iommu_common.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/iommu_common.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: iommu_common.c,v 1.6 2001/10/09 02:24:33 davem Exp $
+/* $Id: iommu_common.c,v 1.6.2.1 2001/12/11 22:47:27 davem Exp $
  * iommu_common.c: UltraSparc SBUS/PCI common iommu code.
  *
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -66,7 +66,9 @@
 
 	daddr = dma_sg->dma_address;
 	sglen = sg->length;
-	sgaddr = (unsigned long) sg->address;
+	sgaddr = (unsigned long) (sg->address ?
+				  sg->address :
+				  page_address(sg->page) + sg->offset);
 	while (dlen > 0) {
 		unsigned long paddr;
 
@@ -116,7 +118,9 @@
 		sg++;
 		if (--nents <= 0)
 			break;
-		sgaddr = (unsigned long) sg->address;
+		sgaddr = (unsigned long) (sg->address ?
+					  sg->address :
+					  page_address(sg->page) + sg->offset);
 		sglen = sg->length;
 	}
 	if (dlen < 0) {
@@ -197,14 +201,21 @@
 	unsigned long prev;
 	u32 dent_addr, dent_len;
 
-	prev  = (unsigned long) sg->address;
+	prev  = (unsigned long) (sg->address ?
+				 sg->address :
+				 page_address(sg->page) + sg->offset);
 	prev += (unsigned long) (dent_len = sg->length);
-	dent_addr = (u32) ((unsigned long)sg->address & (IO_PAGE_SIZE - 1UL));
+	dent_addr = (u32) ((unsigned long)(sg->address ?
+					   sg->address :
+					   page_address(sg->page) + sg->offset)
+			   & (IO_PAGE_SIZE - 1UL));
 	while (--nents) {
 		unsigned long addr;
 
 		sg++;
-		addr = (unsigned long) sg->address;
+		addr = (unsigned long) (sg->address ?
+					sg->address :
+					page_address(sg->page) + sg->offset);
 		if (! VCONTIG(prev, addr)) {
 			dma_sg->dma_address = dent_addr;
 			dma_sg->dma_length = dent_len;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/iommu_common.h linux/arch/sparc64/kernel/iommu_common.h
--- linux.orig/arch/sparc64/kernel/iommu_common.h	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/iommu_common.h	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: iommu_common.h,v 1.4 2001/10/09 02:24:33 davem Exp $
+/* $Id: iommu_common.h,v 1.4.2.1 2001/12/11 22:47:27 davem Exp $
  * iommu_common.h: UltraSparc SBUS/PCI common iommu declarations.
  *
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -6,8 +6,9 @@
 
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
 
-#include <asm/page.h>
 #include <asm/iommu.h>
 #include <asm/scatterlist.h>
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/pci_common.c linux/arch/sparc64/kernel/pci_common.c
--- linux.orig/arch/sparc64/kernel/pci_common.c	Wed Aug 15 02:57:28 2001
+++ linux/arch/sparc64/kernel/pci_common.c	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/* $Id: pci_common.c,v 1.27 2001/08/12 13:18:22 davem Exp $
+/* $Id: pci_common.c,v 1.27.2.2 2002/02/01 00:56:44 davem Exp $
  * pci_common.c: PCI controller common support.
  *
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -183,6 +183,17 @@
 		pcp->prom_name[err] = 0;
 	else
 		pcp->prom_name[0] = 0;
+
+	err = prom_getproperty(device_prom_node,
+			       "assigned-addresses",
+			       (char *)pcp->prom_assignments,
+			       sizeof(pcp->prom_assignments));
+	if (err == 0 || err == -1)
+		pcp->num_prom_assignments = 0;
+	else
+		pcp->num_prom_assignments =
+			(err / sizeof(pcp->prom_assignments[0]));
+
 	if (strcmp(pcp->prom_name, "ebus") == 0) {
 		struct linux_prom_ebus_ranges erng[PROM_PCIRNG_MAX];
 		int iter;
@@ -208,16 +219,6 @@
 			ap->size_lo = ep->size;
 		}
 		pcp->num_prom_assignments = err;
-	} else {
-		err = prom_getproperty(device_prom_node,
-				       "assigned-addresses",
-				       (char *)pcp->prom_assignments,
-				       sizeof(pcp->prom_assignments));
-		if (err == 0 || err == -1)
-			pcp->num_prom_assignments = 0;
-		else
-			pcp->num_prom_assignments =
-				(err / sizeof(pcp->prom_assignments[0]));
 	}
 
 	fixup_obp_assignments(pdev, pcp);
@@ -668,6 +669,20 @@
 	unsigned int prom_irq;
 	int prom_node = pcp->prom_node;
 	int err;
+
+	/* If this is an empty EBUS device, sometimes OBP fails to
+	 * give it a valid fully specified interrupts property.
+	 * The EBUS hooked up to SunHME on PCI I/O boards of
+	 * Ex000 systems is one such case.
+	 *
+	 * The interrupt is not important so just ignore it.
+	 */
+	if (pdev->vendor == PCI_VENDOR_ID_SUN &&
+	    pdev->device == PCI_DEVICE_ID_SUN_EBUS &&
+	    !prom_getchild(prom_node)) {
+		pdev->irq = 0;
+		return;
+	}
 
 	err = prom_getproperty(prom_node, "interrupts",
 			       (char *)&prom_irq, sizeof(prom_irq));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/pci_psycho.c linux/arch/sparc64/kernel/pci_psycho.c
--- linux.orig/arch/sparc64/kernel/pci_psycho.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/pci_psycho.c	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/* $Id: pci_psycho.c,v 1.30 2001/11/14 13:17:56 davem Exp $
+/* $Id: pci_psycho.c,v 1.30.2.2 2002/02/01 00:57:47 davem Exp $
  * pci_psycho.c: PSYCHO/U2P specific PCI controller support.
  *
  * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu)
@@ -1480,8 +1480,7 @@
 {
 	unsigned int busrange[2];
 	struct pci_pbm_info *pbm;
-	char namebuf[64];
-	int err, len;
+	int err;
 
 	if (is_pbm_a) {
 		pbm = &p->pbm_A;
@@ -1490,13 +1489,7 @@
 		pbm->mem_space.start = p->controller_regs + PSYCHO_MEMSPACE_A;
 	} else {
 		pbm = &p->pbm_B;
-		pbm->pci_first_slot = 1;
-		len = prom_getproperty(prom_root_node, "name",
-				       namebuf, sizeof(namebuf));
-		if (len > 0) {
-			if (!strcmp(namebuf, "SUNW,Ultra-1-Engine"))
-				pbm->pci_first_slot = 2;
-		}
+		pbm->pci_first_slot = 2;
 		pbm->io_space.start = p->controller_regs + PSYCHO_IOSPACE_B;
 		pbm->mem_space.start = p->controller_regs + PSYCHO_MEMSPACE_B;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/power.c linux/arch/sparc64/kernel/power.c
--- linux.orig/arch/sparc64/kernel/power.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/power.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: power.c,v 1.9 2001/06/08 02:28:22 davem Exp $
+/* $Id: power.c,v 1.9.2.1 2001/12/11 01:57:49 davem Exp $
  * power.c: Power management driver.
  *
  * Copyright (C) 1999 David S. Miller (davem@redhat.com)
@@ -61,7 +61,7 @@
 	sprintf(current->comm, "powerd");
 
 again:
-	while(button_pressed == 0) {
+	while (button_pressed == 0) {
 		spin_lock_irq(&current->sigmask_lock);
 		flush_signals(current);
 		spin_unlock_irq(&current->sigmask_lock);
@@ -98,16 +98,19 @@
 found:
 	power_reg = (unsigned long)ioremap(edev->resource[0].start, 0x4);
 	printk("power: Control reg at %016lx ... ", power_reg);
-	if (kernel_thread(powerd, 0, CLONE_FS) < 0) {
-		printk("Failed to start power daemon.\n");
-		return;
-	}
-	printk("powerd running.\n");
-	if (edev->irqs[0] != 0) {
+	if (edev->irqs[0] != PCI_IRQ_NONE) {
+		if (kernel_thread(powerd, 0, CLONE_FS) < 0) {
+			printk("Failed to start power daemon.\n");
+			return;
+		}
+		printk("powerd running.\n");
+
 		if (request_irq(edev->irqs[0],
 				power_handler, SA_SHIRQ, "power",
 				(void *) power_reg) < 0)
 			printk("power: Error, cannot register IRQ handler.\n");
+	} else {
+		printk("not using powerd.\n");
 	}
 }
 #endif /* CONFIG_PCI */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/process.c linux/arch/sparc64/kernel/process.c
--- linux.orig/arch/sparc64/kernel/process.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/process.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/*  $Id: process.c,v 1.125 2001/11/17 00:10:48 davem Exp $
+/*  $Id: process.c,v 1.125.2.1 2001/12/18 19:40:17 davem Exp $
  *  arch/sparc64/kernel/process.c
  *
  *  Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -471,6 +471,7 @@
 		csp += STACK_BIAS;
 		psp += STACK_BIAS;
 		__get_user(fp, &(((struct reg_window *)psp)->ins[6]));
+		fp += STACK_BIAS;
 	} else
 		__get_user(fp, &(((struct reg_window32 *)psp)->ins[6]));
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/rtrap.S linux/arch/sparc64/kernel/rtrap.S
--- linux.orig/arch/sparc64/kernel/rtrap.S	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/rtrap.S	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: rtrap.S,v 1.57 2001/12/06 00:16:11 davem Exp $
+/* $Id: rtrap.S,v 1.57.2.1 2001/12/24 04:32:33 davem Exp $
  * rtrap.S: Preparing for return from trap on Sparc V9.
  *
  * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
@@ -40,8 +40,7 @@
 __handle_user_windows:
 		call			fault_in_user_windows
 		 wrpr			%g0, RTRAP_PSTATE, %pstate
-		ba,pt			%xcc, __handle_user_windows_continue
-		 wrpr			%g0, RTRAP_PSTATE_IRQOFF, %pstate
+		wrpr			%g0, RTRAP_PSTATE_IRQOFF, %pstate
 		/* Redo sched+sig checks */
 		ldx			[%g6 + AOFF_task_need_resched], %l0
 		brz,pt			%l0, 1f
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/smp.c linux/arch/sparc64/kernel/smp.c
--- linux.orig/arch/sparc64/kernel/smp.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/smp.c	Tue Feb  5 17:28:24 2002
@@ -218,7 +218,7 @@
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
 
-	while (!smp_processors_ready)
+	while (!smp_threads_ready)
 		membar("#LoadLoad");
 }
 
@@ -269,7 +269,7 @@
 			continue;
 
 		if ((cpucount + 1) == max_cpus)
-			break;
+			goto ignorecpu;
 		if (cpu_present_map & (1UL << i)) {
 			unsigned long entry = (unsigned long)(&sparc64_cpu_startup);
 			unsigned long cookie = (unsigned long)(&cpu_new_task);
@@ -314,13 +314,15 @@
 			}
 		}
 		if (!callin_flag) {
+ignorecpu:
 			cpu_present_map &= ~(1UL << i);
 			__cpu_number_map[i] = -1;
 		}
 	}
 	cpu_new_task = NULL;
 	if (cpucount == 0) {
-		printk("Error: only one processor found.\n");
+		if (max_cpus != 1)
+			printk("Error: only one processor found.\n");
 		cpu_present_map = (1UL << smp_processor_id());
 	} else {
 		unsigned long bogosum = 0;
@@ -676,6 +678,39 @@
 			atomic_inc(&dcpage_flushes_xcall);
 #endif
 		}
+	}
+}
+
+void flush_dcache_page_all(struct mm_struct *mm, struct page *page)
+{
+	if (smp_processors_ready) {
+		unsigned long mask = cpu_present_map & ~(1UL << smp_processor_id());
+		u64 data0;
+
+#ifdef CONFIG_DEBUG_DCFLUSH
+		atomic_inc(&dcpage_flushes);
+#endif
+		if (mask == 0UL)
+			goto flush_self;
+		if (tlb_type == spitfire) {
+			data0 = ((u64)&xcall_flush_dcache_page_spitfire);
+			if (page->mapping != NULL)
+				data0 |= ((u64)1 << 32);
+			spitfire_xcall_deliver(data0,
+					       __pa(page->virtual),
+					       (u64) page->virtual,
+					       mask);
+		} else {
+			data0 = ((u64)&xcall_flush_dcache_page_cheetah);
+			cheetah_xcall_deliver(data0,
+					      __pa(page->virtual),
+					      0, mask);
+		}
+#ifdef CONFIG_DEBUG_DCFLUSH
+		atomic_inc(&dcpage_flushes_xcall);
+#endif
+	flush_self:
+		__local_flush_dcache_page(page);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/sys_sparc.c linux/arch/sparc64/kernel/sys_sparc.c
--- linux.orig/arch/sparc64/kernel/sys_sparc.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/kernel/sys_sparc.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: sys_sparc.c,v 1.55 2001/11/29 22:52:03 davem Exp $
+/* $Id: sys_sparc.c,v 1.55.2.1 2001/12/21 04:58:23 davem Exp $
  * linux/arch/sparc64/kernel/sys_sparc.c
  *
  * This file contains various random system calls that
@@ -40,12 +40,15 @@
 	return PAGE_SIZE;
 }
 
-#define COLOUR_ALIGN(addr)	(((addr)+SHMLBA-1)&~(SHMLBA-1))
+#define COLOUR_ALIGN(addr,pgoff)		\
+	((((addr)+SHMLBA-1)&~(SHMLBA-1)) +	\
+	 (((pgoff)<<PAGE_SHIFT) & (SHMLBA-1)))
 
 unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	struct vm_area_struct * vmm;
 	unsigned long task_size = TASK_SIZE;
+	int do_color_align;
 
 	if (flags & MAP_FIXED) {
 		/* We do not accept a shared mapping if it would violate
@@ -63,11 +66,14 @@
 	if (!addr)
 		addr = TASK_UNMAPPED_BASE;
 
-	if (flags & MAP_SHARED)
-		addr = COLOUR_ALIGN(addr);
+	do_color_align = 0;
+	if (filp || (flags & MAP_SHARED))
+		do_color_align = 1;
+
+	if (do_color_align)
+		addr = COLOUR_ALIGN(addr, pgoff);
 	else
 		addr = PAGE_ALIGN(addr);
-
 	task_size -= len;
 
 	for (vmm = find_vma(current->mm, addr); ; vmm = vmm->vm_next) {
@@ -81,8 +87,8 @@
 		if (!vmm || addr + len <= vmm->vm_start)
 			return addr;
 		addr = vmm->vm_end;
-		if (flags & MAP_SHARED)
-			addr = COLOUR_ALIGN(addr);
+		if (do_color_align)
+			addr = COLOUR_ALIGN(addr, pgoff);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/kernel/time.c linux/arch/sparc64/kernel/time.c
--- linux.orig/arch/sparc64/kernel/time.c	Fri Dec 21 17:41:53 2001
+++ linux/arch/sparc64/kernel/time.c	Mon Feb  4 16:55:13 2002
@@ -1,4 +1,4 @@
-/* $Id: time.c,v 1.41 2001/11/20 18:24:55 kanoj Exp $
+/* $Id: time.c,v 1.41.2.1 2002/01/23 14:35:45 davem Exp $
  * time.c: UltraSparc timer and TOD clock support.
  *
  * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
@@ -329,8 +329,6 @@
 	unsigned long dregs = 0UL;
 #endif
 	u8 tmp;
-
-	do_get_fast_time = do_gettimeofday;
 
 	if (!mregs && !dregs) {
 		prom_printf("Something wrong, clock regs not mapped yet.\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/lib/U3copy_from_user.S linux/arch/sparc64/lib/U3copy_from_user.S
--- linux.orig/arch/sparc64/lib/U3copy_from_user.S	Thu Nov  9 23:57:41 2000
+++ linux/arch/sparc64/lib/U3copy_from_user.S	Thu Jan 17 19:37:52 2002
@@ -1,4 +1,4 @@
-/* $Id: U3copy_from_user.S,v 1.3 2000/11/01 09:29:19 davem Exp $
+/* $Id: U3copy_from_user.S,v 1.3.2.1 2002/01/15 07:17:47 davem Exp $
  * U3memcpy.S: UltraSparc-III optimized copy from userspace.
  *
  * Copyright (C) 1999, 2000 David S. Miller (davem@redhat.com)
@@ -13,7 +13,7 @@
 	.section .fixup;		\
 	.align 4;			\
 99:	VISExitHalf;			\
-	ba,pt %xcc, U3cfu_fixup;	\
+	ba U3cfu_fixup;			\
 	 a, b, %o1;			\
 	.section __ex_table;		\
 	.align 4;			\
@@ -25,7 +25,7 @@
 	.section .fixup;		\
 	.align 4;			\
 99:	VISExitHalf;			\
-	ba,pt %xcc, U3cfu_fixup;	\
+	ba U3cfu_fixup;			\
 	 a, b, %o1;			\
 	.section __ex_table;		\
 	.align 4;			\
@@ -39,7 +39,7 @@
 99:	VISExitHalf;			\
 	and %o2, (0x40 - 1), %o1;	\
 	add %o1, %o4, %o1;		\
-	ba,pt %xcc, U3cfu_fixup;	\
+	ba U3cfu_fixup;			\
 	 add %o1, 0x1c0, %o1;		\
 	.section __ex_table;		\
 	.align 4;			\
@@ -54,7 +54,7 @@
 	and %o2, (0x40 - 1), %o1;	\
 	sll %g3, 6, %g3;		\
 	add %o1, 0x80, %o1;		\
-	ba,pt %xcc, U3cfu_fixup;	\
+	ba U3cfu_fixup;			\
 	 add %o1, %g3, %o1;		\
 	.section __ex_table;		\
 	.align 4;			\
@@ -68,7 +68,7 @@
 99:	VISExitHalf;			\
 	and %o2, (0x40 - 1), %o1;	\
 	add %o1, 0x40, %o1;		\
-	ba,pt %xcc, U3cfu_fixup;	\
+	ba U3cfu_fixup;			\
 	 add %o1, %g3, %o1;		\
 	.section __ex_table;		\
 	.align 4;			\
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/arch/sparc64/solaris/timod.c linux/arch/sparc64/solaris/timod.c
--- linux.orig/arch/sparc64/solaris/timod.c	Tue Jan 22 17:51:22 2002
+++ linux/arch/sparc64/solaris/timod.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: timod.c,v 1.16 2001/09/18 22:29:06 davem Exp $
+/* $Id: timod.c,v 1.16.2.1 2001/12/18 22:15:25 davem Exp $
  * timod.c: timod emulation.
  *
  * Copyright (C) 1998 Patrik Rak (prak3264@ss1000.ms.mff.cuni.cz)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/Makefile linux/drivers/Makefile
--- linux.orig/drivers/Makefile	Sun Nov 11 18:09:32 2001
+++ linux/drivers/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the Linux kernel device drivers.
 #
-# 15 Sep 2000, Christoph Hellwig <hch@caldera.de>
+# 15 Sep 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/atm/firestream.c linux/drivers/atm/firestream.c
--- linux.orig/drivers/atm/firestream.c	Fri Dec 21 17:41:53 2001
+++ linux/drivers/atm/firestream.c	Wed Feb 13 17:37:40 2002
@@ -1530,7 +1530,7 @@
 	fs_dprintk (FS_DEBUG_QUEUE, "Added %d entries. \n", n);
 }
 
-static void __exit free_queue (struct fs_dev *dev, struct queue *txq)
+static void __devexit free_queue (struct fs_dev *dev, struct queue *txq)
 {
 	func_enter ();
 
@@ -1546,7 +1546,7 @@
 	func_exit ();
 }
 
-static void __exit free_freepool (struct fs_dev *dev, struct freepool *fp)
+static void __devexit free_freepool (struct fs_dev *dev, struct freepool *fp)
 {
 	func_enter ();
 
@@ -2088,7 +2088,7 @@
 #endif 
 */
 
-const static struct pci_device_id firestream_pci_tbl[] __devinitdata = {
+static struct pci_device_id firestream_pci_tbl[] __devinitdata = {
 	{ PCI_VENDOR_ID_FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS50, 
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, FS_IS50},
 	{ PCI_VENDOR_ID_FUJITSU_ME, PCI_DEVICE_ID_FUJITSU_FS155, 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/atm/iphase.c linux/drivers/atm/iphase.c
--- linux.orig/drivers/atm/iphase.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/atm/iphase.c	Mon Jan 14 18:53:53 2002
@@ -1777,8 +1777,9 @@
         memset((caddr_t)ia_vcc, 0, sizeof(*ia_vcc));
         if (vcc->qos.txtp.max_sdu > 
                          (iadev->tx_buf_sz - sizeof(struct cpcs_trailer))){
-           printk("IA:  SDU size over the configured SDU size %d\n",
-                                                          iadev->tx_buf_sz);
+           printk("IA:  SDU size over (%d) the configured SDU size %d\n",
+		  vcc->qos.txtp.max_sdu,iadev->tx_buf_sz);
+	   INPH_IA_VCC(vcc) = NULL;  
            kfree(ia_vcc);
            return -EINVAL; 
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/Makefile linux/drivers/block/Makefile
--- linux.orig/drivers/block/Makefile	Sun Sep  9 19:00:55 2001
+++ linux/drivers/block/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the kernel block device drivers.
 #
-# 12 June 2000, Christoph Hellwig <schch@pe.tu-clausthal.de>
+# 12 June 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 # 
 # Note : at this point, these files are compiled on all systems.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/blkpg.c linux/drivers/block/blkpg.c
--- linux.orig/drivers/block/blkpg.c	Sun Nov 11 18:20:21 2001
+++ linux/drivers/block/blkpg.c	Mon Feb  4 17:42:17 2002
@@ -247,7 +247,7 @@
 			if (cmd == BLKGETSIZE)
 				return put_user((unsigned long)ullval, (unsigned long *)arg);
 			else
-				return put_user(ullval, (u64 *)arg);
+				return put_user(ullval << 9, (u64 *)arg);
 #if 0
 		case BLKRRPART: /* Re-read partition tables */
 			if (!capable(CAP_SYS_ADMIN)) 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/cpqarray.c linux/drivers/block/cpqarray.c
--- linux.orig/drivers/block/cpqarray.c	Fri Nov  9 22:28:46 2001
+++ linux/drivers/block/cpqarray.c	Thu Jan 17 20:01:28 2002
@@ -32,6 +32,7 @@
 #include <linux/blkpg.h>
 #include <linux/timer.h>
 #include <linux/proc_fs.h>
+#include <linux/devfs_fs_kernel.h>
 #include <linux/init.h>
 #include <linux/hdreg.h>
 #include <linux/spinlock.h>
@@ -70,6 +71,7 @@
 
 static int nr_ctlr;
 static ctlr_info_t *hba[MAX_CTLR];
+static devfs_handle_t de_arr[MAX_CTLR][NWD];
 
 static int eisa[8];
 
@@ -338,6 +340,7 @@
 
 		del_gendisk(&ida_gendisk[i]);
 	}
+	devfs_unregister(devfs_find_handle(NULL, "ida", 0, 0, 0, 0));
 	remove_proc_entry("cpqarray", proc_root_driver);
 	kfree(ida);
 	kfree(ida_sizes);
@@ -540,6 +543,8 @@
 		ida_gendisk[i].part = ida + (i*256);
 		ida_gendisk[i].sizes = ida_sizes + (i*256);
 		ida_gendisk[i].nr_real = 0; 
+		ida_gendisk[i].de_arr = de_arr[i]; 
+		ida_gendisk[i].fops = &ida_fops; 
 	
 		/* Get on the disk list */
 		add_gendisk(&ida_gendisk[i]);
@@ -1881,6 +1886,14 @@
                 			kfree(id_ldrive);
                 			return;
 
+				}
+				if (!de_arr[ctlr][log_unit]) {
+					char txt[16];
+
+					sprintf(txt, "ida/c%dd%d", ctlr,
+						log_unit);
+					de_arr[ctlr][log_unit] =
+						devfs_mk_dir(NULL, txt, NULL);
 				}
 				info_p->phys_drives =
 				    sense_config_buf->ctlr_phys_drv;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/floppy.c linux/drivers/block/floppy.c
--- linux.orig/drivers/block/floppy.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/block/floppy.c	Mon Jan 14 18:53:53 2002
@@ -3917,7 +3917,7 @@
     {NULL, t360, t1200, t3in+5+8, t3in+5, t3in, t3in};
 
     base_minor = (drive < 4) ? drive : (124 + drive);
-    if (UDP->cmos <= NUMBER(default_drive_params)) {
+    if (UDP->cmos < NUMBER(default_drive_params)) {
 	i = 0;
 	do {
 	    char name[16];
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/genhd.c linux/drivers/block/genhd.c
--- linux.orig/drivers/block/genhd.c	Wed Oct 17 21:46:29 2001
+++ linux/drivers/block/genhd.c	Wed Jan 23 21:13:47 2002
@@ -33,6 +33,7 @@
  *	the only reason this is exported is source compatiblity.
  */
 /*static*/ struct gendisk *gendisk_head;
+static struct gendisk *gendisk_array[MAX_BLKDEV];
 
 EXPORT_SYMBOL(gendisk_head);
 
@@ -65,6 +66,7 @@
 			goto out;
 		}
 	}
+	gendisk_array[gp->major] = gp;
 	gp->next = gendisk_head;
 	gendisk_head = gp;
 out:
@@ -87,6 +89,7 @@
 	struct gendisk **gpp;
 
 	write_lock(&gendisk_lock);
+	gendisk_array[gp->major] = NULL;
 	for (gpp = &gendisk_head; *gpp; gpp = &((*gpp)->next))
 		if (*gpp == gp)
 			break;
@@ -112,11 +115,15 @@
 	int maj = MAJOR(dev);
 
 	read_lock(&gendisk_lock);
+	if ((gp = gendisk_array[maj]))
+		goto out;
+
+	/* This is needed for early 2.4 source compatiblity.  --hch */
 	for (gp = gendisk_head; gp; gp = gp->next)
 		if (gp->major == maj)
 			break;
+out:
 	read_unlock(&gendisk_lock);
-
 	return gp;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux.orig/drivers/block/ll_rw_blk.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/block/ll_rw_blk.c	Mon Jan  7 17:36:23 2002
@@ -694,8 +694,10 @@
 	switch (el_ret) {
 
 		case ELEVATOR_BACK_MERGE:
-			if (!q->back_merge_fn(q, req, bh, max_segments))
+			if (!q->back_merge_fn(q, req, bh, max_segments)) {
+				insert_here = &req->queue;
 				break;
+			}
 			elevator->elevator_merge_cleanup_fn(q, req, count);
 			req->bhtail->b_reqnext = bh;
 			req->bhtail = bh;
@@ -706,8 +708,10 @@
 			goto out;
 
 		case ELEVATOR_FRONT_MERGE:
-			if (!q->front_merge_fn(q, req, bh, max_segments))
+			if (!q->front_merge_fn(q, req, bh, max_segments)) {
+				insert_here = req->queue.prev;
 				break;
+			}
 			elevator->elevator_merge_cleanup_fn(q, req, count);
 			bh->b_reqnext = req->bh;
 			req->bh = bh;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/rd.c linux/drivers/block/rd.c
--- linux.orig/drivers/block/rd.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/block/rd.c	Mon Jan 14 17:42:16 2002
@@ -191,26 +191,44 @@
  *               2000 Transmeta Corp.
  * aops copied from ramfs.
  */
-static int ramdisk_readpage(struct file *file, struct page * page)
+static void ramdisk_updatepage(struct page * page, int need_kmap)
 {
 	if (!Page_Uptodate(page)) {
-		memset(kmap(page), 0, PAGE_CACHE_SIZE);
-		kunmap(page);
+		struct buffer_head *bh = page->buffers;
+		void * address;
+
+		if (need_kmap)
+			kmap(page);
+		address = page_address(page);
+		if (bh) {
+			struct buffer_head *tmp = bh;
+			do {
+				if (!buffer_uptodate(tmp)) {
+					memset(address, 0, tmp->b_size);
+					mark_buffer_uptodate(tmp, 1);
+				}
+				address += tmp->b_size;
+				tmp = tmp->b_this_page;
+			} while (tmp != bh);
+		} else
+			memset(address, 0, PAGE_CACHE_SIZE);
+		if (need_kmap)
+			kunmap(page);
 		flush_dcache_page(page);
 		SetPageUptodate(page);
 	}
+}
+
+static int ramdisk_readpage(struct file *file, struct page * page)
+{
+	ramdisk_updatepage(page, 1);
 	UnlockPage(page);
 	return 0;
 }
 
 static int ramdisk_prepare_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
-	if (!Page_Uptodate(page)) {
-		void *addr = page_address(page);
-		memset(addr, 0, PAGE_CACHE_SIZE);
-		flush_dcache_page(page);
-		SetPageUptodate(page);
-	}
+	ramdisk_updatepage(page, 0);
 	SetPageDirty(page);
 	return 0;
 }
@@ -233,44 +251,40 @@
 	unsigned long index;
 	int offset, size, err;
 
-	err = -EIO;
 	err = 0;
 	mapping = rd_bdev[minor]->bd_inode->i_mapping;
 
+	/* writing a buffer cache not uptodate must not clear it */
+	if (sbh->b_page->mapping == mapping) {
+		if (rw == WRITE) {
+			mark_buffer_uptodate(sbh, 1);
+			SetPageDirty(sbh->b_page);
+		}
+		goto out;
+	}
+
 	index = sbh->b_rsector >> (PAGE_CACHE_SHIFT - 9);
 	offset = (sbh->b_rsector << 9) & ~PAGE_CACHE_MASK;
 	size = sbh->b_size;
 
 	do {
 		int count;
-		struct page ** hash;
 		struct page * page;
 		char * src, * dst;
-		int unlock = 0;
 
 		count = PAGE_CACHE_SIZE - offset;
 		if (count > size)
 			count = size;
 		size -= count;
 
-		hash = page_hash(mapping, index);
-		page = __find_get_page(mapping, index, hash);
+		page = grab_cache_page(mapping, index);
 		if (!page) {
-			page = grab_cache_page(mapping, index);
 			err = -ENOMEM;
-			if (!page)
-				goto out;
-			err = 0;
-
-			if (!Page_Uptodate(page)) {
-				memset(kmap(page), 0, PAGE_CACHE_SIZE);
-				kunmap(page);
-				SetPageUptodate(page);
-			}
-
-			unlock = 1;
+			goto out;
 		}
 
+		ramdisk_updatepage(page, 1);
+
 		index++;
 
 		if (rw == READ) {
@@ -294,8 +308,7 @@
 		} else {
 			SetPageDirty(page);
 		}
-		if (unlock)
-			UnlockPage(page);
+		UnlockPage(page);
 		__free_page(page);
 	} while (size);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/block/swim3.c linux/drivers/block/swim3.c
--- linux.orig/drivers/block/swim3.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/block/swim3.c	Wed Dec 26 16:29:22 2001
@@ -29,14 +29,15 @@
 #include <asm/prom.h>
 #include <asm/uaccess.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 
 #define MAJOR_NR	FLOPPY_MAJOR
 #include <linux/blk.h>
 #include <linux/devfs_fs_kernel.h>
 
 static int floppy_blocksizes[2] = {512,512};
-static int floppy_sizes[2] = {2880,2880};
+static int floppy_sizes[2] = {1440,1440};
 
 #define MAX_FLOPPIES	2
 
@@ -444,9 +445,9 @@
 		++cp;
 		init_dma(cp, OUTPUT_MORE, CURRENT->buffer, 512);
 		++cp;
-		init_dma(cp, OUTPUT_MORE, write_postamble, sizeof(write_postamble));
+		init_dma(cp, OUTPUT_LAST, write_postamble, sizeof(write_postamble));
 	} else {
-		init_dma(cp, INPUT_MORE, CURRENT->buffer, n * 512);
+		init_dma(cp, INPUT_LAST, CURRENT->buffer, n * 512);
 	}
 	++cp;
 	out_le16(&cp->command, DBDMA_STOP);
@@ -678,7 +679,10 @@
 			break;
 		dr = fs->dma;
 		cp = fs->dma_cmd;
-		st_le32(&dr->control, RUN << 16);
+		/* We must wait a bit for dbdma to complete */
+		for (n=0; (in_le32(&dr->status) & ACTIVE) && n < 1000; n++)
+			udelay(10);
+		DBDMA_DO_STOP(dr);
 		out_8(&sw->intr_enable, 0);
 		out_8(&sw->control_bic, WRITE_SECTORS | DO_ACTION);
 		out_8(&sw->select, RELAX);
@@ -1070,9 +1074,14 @@
 		return -EINVAL;
 	}
 
+	if (!request_OF_resource(swim, 0, NULL)) {
+		printk(KERN_INFO "swim3: can't request IO resource !\n");
+		return -EINVAL;
+	}
+
 	mediabay = (strcasecmp(swim->parent->type, "media-bay") == 0) ? swim->parent : NULL;
 	if (mediabay == NULL)
-		feature_set(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 1);
 	
 	memset(fs, 0, sizeof(*fs));
 	fs->state = idle;
@@ -1094,14 +1103,14 @@
 
 	if (request_irq(fs->swim3_intr, swim3_interrupt, 0, "SWIM3", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3\n", fs->swim3_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 /*
 	if (request_irq(fs->dma_intr, fd_dma_interrupt, 0, "SWIM3-dma", fs)) {
 		printk(KERN_ERR "Couldn't get irq %d for SWIM3 DMA",
 		       fs->dma_intr);
-		feature_clear(swim, FEATURE_SWIM3_enable);
+		pmac_call_feature(PMAC_FTR_SWIM3_ENABLE, swim, 0, 0);
 		return -EBUSY;
 	}
 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/Config.in linux/drivers/char/Config.in
--- linux.orig/drivers/char/Config.in	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/Config.in	Wed Jan  9 21:56:59 2002
@@ -219,7 +219,18 @@
    bool '  Serverworks LE/HE support' CONFIG_AGP_SWORKS
 fi
 
-source drivers/char/drm/Config.in
+bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM
+if [ "$CONFIG_DRM" = "y" ]; then
+   bool '  Build drivers for old (XFree 4.0) DRM' CONFIG_DRM_OLD
+   if [ "$CONFIG_DRM_OLD" = "y" ]; then
+      comment 'DRM 4.0 drivers'
+      source drivers/char/drm-4.0/Config.in
+   else
+      comment 'DRM 4.1 drivers'
+      define_bool CONFIG_DRM_NEW y
+      source drivers/char/drm/Config.in
+   fi
+fi
 
 if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
    source drivers/char/pcmcia/Config.in
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/Makefile linux/drivers/char/Makefile
--- linux.orig/drivers/char/Makefile	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/Makefile	Wed Jan  9 21:56:59 2002
@@ -25,7 +25,7 @@
 			misc.o pty.o random.o selection.o serial.o \
 			sonypi.o tty_io.o tty_ioctl.o generic_serial.o
 
-mod-subdirs	:=	joystick ftape drm pcmcia
+mod-subdirs	:=	joystick ftape drm drm-4.0 pcmcia
 
 list-multi	:=	
 
@@ -200,7 +200,8 @@
 obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
 
 subdir-$(CONFIG_FTAPE) += ftape
-subdir-$(CONFIG_DRM) += drm
+subdir-$(CONFIG_DRM_OLD) += drm-4.0
+subdir-$(CONFIG_DRM_NEW) += drm
 subdir-$(CONFIG_PCMCIA) += pcmcia
 subdir-$(CONFIG_AGP) += agp
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/advantechwdt.c linux/drivers/char/advantechwdt.c
--- linux.orig/drivers/char/advantechwdt.c	Thu Sep 13 22:21:32 2001
+++ linux/drivers/char/advantechwdt.c	Thu Jan 17 22:00:03 2002
@@ -64,6 +64,7 @@
 #define WDT_START 0x443
 
 #define WD_TIMO 60		/* 1 minute */
+static int wd_margin = WD_TIMO;
 
 /*
  *	Kernel methods.
@@ -73,7 +74,7 @@
 advwdt_ping(void)
 {
 	/* Write a watchdog value */
-	outb_p(WD_TIMO, WDT_START);
+	outb_p(wd_margin, WDT_START);
 }
 
 static ssize_t
@@ -100,8 +101,9 @@
 advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	  unsigned long arg)
 {
+	int new_margin;
 	static struct watchdog_info ident = {
-		WDIOF_KEEPALIVEPING, 1, "Advantech WDT"
+		WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 1, "Advantech WDT"
 	};
 	
 	switch (cmd) {
@@ -117,6 +119,19 @@
 
 	case WDIOC_KEEPALIVE:
 	  advwdt_ping();
+	  break;
+
+	case WDIOC_SETTIMEOUT:
+	  if (get_user(new_margin, (int *)arg))
+		  return -EFAULT;
+	  if ((new_margin < 1) || (new_margin > 63))
+		  return -EINVAL;
+	  wd_margin = new_margin;
+	  advwdt_ping();
+	  /* Fall */
+
+	case WDIOC_GETTIMEOUT:
+	  return put_user(wd_margin, (int *)arg);
 	  break;
 
 	default:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/agp/agp.h linux/drivers/char/agp/agp.h
--- linux.orig/drivers/char/agp/agp.h	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/agp/agp.h	Mon Jan  7 14:18:35 2002
@@ -179,6 +179,9 @@
 #ifndef PCI_DEVICE_ID_INTEL_820_0
 #define PCI_DEVICE_ID_INTEL_820_0       0x2500
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_820_UP_0
+#define PCI_DEVICE_ID_INTEL_820_UP_0    0x2501
+#endif
 #ifndef PCI_DEVICE_ID_INTEL_840_0
 #define PCI_DEVICE_ID_INTEL_840_0		0x1a21
 #endif
@@ -189,7 +192,7 @@
 #define PCI_DEVICE_ID_INTEL_850_0     0x2530
 #endif
 #ifndef PCI_DEVICE_ID_INTEL_860_0
-#define PCI_DEVICE_ID_INTEL_860_0	0x2532
+#define PCI_DEVICE_ID_INTEL_860_0     0x2531
 #endif
 #ifndef PCI_DEVICE_ID_INTEL_810_DC100_0
 #define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122
@@ -275,6 +278,9 @@
 #define I830_RDRAM_CHANNEL_TYPE    0x03010
 #define I830_RDRAM_ND(x)           (((x) & 0x20) >> 5)
 #define I830_RDRAM_DDT(x)          (((x) & 0x18) >> 3)
+
+/* This one is for I830MP w. an external graphic card */
+#define INTEL_I830_ERRSTS          0x92
 
 /* intel i820 registers */
 #define INTEL_I820_RDCR     0x51
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/agp/agpgart_be.c linux/drivers/char/agp/agpgart_be.c
--- linux.orig/drivers/char/agp/agpgart_be.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/agp/agpgart_be.c	Wed Jan 23 19:52:23 2002
@@ -1587,6 +1587,40 @@
 	return 0;
 }
 
+static int intel_830mp_configure(void)
+{
+       u32 temp;
+       u16 temp2;
+       aper_size_info_8 *current_size;
+
+       current_size = A_SIZE_8(agp_bridge.current_size);
+
+       /* aperture size */
+       pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE,
+                             current_size->size_value);
+
+       /* address to map to */
+       pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp);
+       agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK);
+
+       /* attbase - aperture base */
+       pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE,
+                              agp_bridge.gatt_bus_addr);
+
+       /* agpctrl */
+       pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000);
+
+       /* gmch */
+       pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2);
+       pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG,
+                             temp2 | (1 << 9));
+       /* clear any possible AGP-related error conditions */
+       pci_write_config_word(agp_bridge.dev, INTEL_I830_ERRSTS, 0x1c);
+       return 0;
+}
+       
+
+
 static int intel_840_configure(void)
 {
 	u32 temp;
@@ -1715,6 +1749,7 @@
 	return 0;
 }
 
+
 static unsigned long intel_mask_memory(unsigned long addr, int type)
 {
 	/* Memory type is ignored */
@@ -1755,6 +1790,14 @@
 	{4, 1024, 0, 63}
 };
 
+static aper_size_info_8 intel_830mp_sizes[4] = 
+{
+  {256, 65536, 6, 0},
+  {128, 32768, 5, 32},
+  {64, 16384, 4, 48},
+  {32, 8192, 3, 56}
+};
+
 static int __init intel_generic_setup (struct pci_dev *pdev)
 {
 	agp_bridge.masks = intel_generic_masks;
@@ -1789,6 +1832,7 @@
 }
 
 
+
 static int __init intel_820_setup (struct pci_dev *pdev)
 {
        agp_bridge.masks = intel_generic_masks;
@@ -1800,7 +1844,7 @@
        agp_bridge.needs_scratch_page = FALSE;
        agp_bridge.configure = intel_820_configure;
        agp_bridge.fetch_size = intel_8xx_fetch_size;
-       agp_bridge.cleanup = intel_cleanup;
+       agp_bridge.cleanup = intel_820_cleanup;
        agp_bridge.tlb_flush = intel_820_tlbflush;
        agp_bridge.mask_memory = intel_mask_memory;
        agp_bridge.agp_enable = agp_generic_agp_enable;
@@ -1813,12 +1857,46 @@
        agp_bridge.free_by_type = agp_generic_free_by_type;
        agp_bridge.agp_alloc_page = agp_generic_alloc_page;
        agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+       agp_bridge.suspend = agp_generic_suspend;
+       agp_bridge.resume = agp_generic_resume;
+       agp_bridge.cant_use_aperture = 0;
 
        return 0;
 
        (void) pdev; /* unused */
 }
 
+static int __init intel_830mp_setup (struct pci_dev *pdev)
+{
+       agp_bridge.masks = intel_generic_masks;
+       agp_bridge.num_of_masks = 1;
+       agp_bridge.aperture_sizes = (void *) intel_830mp_sizes;
+       agp_bridge.size_type = U8_APER_SIZE;
+       agp_bridge.num_aperture_sizes = 4;
+       agp_bridge.dev_private_data = NULL;
+       agp_bridge.needs_scratch_page = FALSE;
+       agp_bridge.configure = intel_830mp_configure;
+       agp_bridge.fetch_size = intel_8xx_fetch_size;
+       agp_bridge.cleanup = intel_8xx_cleanup;
+       agp_bridge.tlb_flush = intel_8xx_tlbflush;
+       agp_bridge.mask_memory = intel_mask_memory;
+       agp_bridge.agp_enable = agp_generic_agp_enable;
+       agp_bridge.cache_flush = global_cache_flush;
+       agp_bridge.create_gatt_table = agp_generic_create_gatt_table;
+       agp_bridge.free_gatt_table = agp_generic_free_gatt_table;
+       agp_bridge.insert_memory = agp_generic_insert_memory;
+       agp_bridge.remove_memory = agp_generic_remove_memory;
+       agp_bridge.alloc_by_type = agp_generic_alloc_by_type;
+       agp_bridge.free_by_type = agp_generic_free_by_type;
+       agp_bridge.agp_alloc_page = agp_generic_alloc_page;
+       agp_bridge.agp_destroy_page = agp_generic_destroy_page;
+       agp_bridge.suspend = agp_generic_suspend;
+       agp_bridge.resume = agp_generic_resume;
+       agp_bridge.cant_use_aperture = 0;
+       return 0;
+
+       (void) pdev; /* unused */
+}
 
 static int __init intel_840_setup (struct pci_dev *pdev)
 {
@@ -3572,12 +3650,18 @@
 		"Intel",
 		"i820",
 		intel_820_setup },
+        { PCI_DEVICE_ID_INTEL_820_UP_0,
+                PCI_VENDOR_ID_INTEL,
+                INTEL_I820,
+                "Intel",
+                 "i820",
+                 intel_820_setup },
 	{ PCI_DEVICE_ID_INTEL_830_M_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I830_M,
 		"Intel",
 		"i830M",
-		intel_generic_setup },
+		intel_830mp_setup },
 	{ PCI_DEVICE_ID_INTEL_840_0,
 		PCI_VENDOR_ID_INTEL,
 		INTEL_I840,
@@ -3904,11 +3988,13 @@
 			}
 
 			if (i810_dev == NULL) {
-				printk(KERN_ERR PFX "Detected an "
-					   "Intel 830M, but could not find the"
-					   " secondary device.\n");
-				agp_bridge.type = NOT_SUPPORTED;
-				return -ENODEV;
+                                /* 
+                                 * We probably have a I830MP chipset
+                                 * with an external graphics
+                                 * card. It will be initialized later 
+                                 */
+				agp_bridge.type = INTEL_I830_M;
+				break;
 			}
 			printk(KERN_INFO PFX "Detected an Intel "
 				   "830M Chipset.\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/cyclades.c linux/drivers/char/cyclades.c
--- linux.orig/drivers/char/cyclades.c	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/cyclades.c	Tue Jan  8 16:18:53 2002
@@ -4965,6 +4965,8 @@
   uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
   uclong		Ze_phys0[NR_CARDS], Ze_phys2[NR_CARDS];
   unsigned char         Ze_irq[NR_CARDS];
+  struct resource *resource;
+  unsigned long res_start, res_len;
 
         for (i = 0; i < NR_CARDS; i++) {
                 /* look for a Cyclades card by vendor and device id */
@@ -4992,8 +4994,8 @@
 
 		device_id &= ~PCI_DEVICE_ID_MASK;
 
-    if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
-	   || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
+	if ((device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo)
+	    || (device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi)){
 #ifdef CY_PCI_DEBUG
             printk("Cyclom-Y/PCI (bus=0x0%x, pci_id=0x%x, ",
 		pdev->bus->number, pdev->devfn);
@@ -5012,7 +5014,14 @@
 		/* Although we don't use this I/O region, we should
 		   request it from the kernel anyway, to avoid problems
 		   with other drivers accessing it. */
-		request_region(cy_pci_phys1, CyPCI_Yctl, "Cyclom-Y");
+		resource = request_region(cy_pci_phys1, CyPCI_Yctl, "Cyclom-Y");
+		if (resource == NULL) {
+		    printk(KERN_ERR "cyclades: failed to allocate IO "
+				    "resource at 0x%lx\n", cy_pci_phys1);
+		    continue;
+		}
+		res_start = cy_pci_phys1;
+		res_len = CyPCI_Yctl;
 
 #if defined(__alpha__)
                 if (device_id  == PCI_DEVICE_ID_CYCLOM_Y_Lo) { /* below 1M? */
@@ -5083,6 +5092,10 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = cy_pci_nchan/4;
+		cy_card[j].resource = resource;
+		cy_card[j].res_start = res_start;
+		cy_card[j].res_len = res_len;
+		resource = NULL;	/* For next card */
 
                 /* enable interrupts in the PCI interface */
 		plx_ver = cy_readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
@@ -5118,7 +5131,7 @@
 		    cy_pci_nchan, cy_next_channel);
 
                 cy_next_channel += cy_pci_nchan;
-    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
+	}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo){
 	    /* print message */
 		printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
 		    pdev->bus->number, pdev->devfn);
@@ -5128,7 +5141,7 @@
 		    (ulong)cy_pci_phys2, (ulong)cy_pci_phys0);
 	    printk("Cyclades-Z/PCI not supported for low addresses\n");
 	    break;
-    }else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
+	}else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi){
 #ifdef CY_PCI_DEBUG
             printk("Cyclades-Z/PCI (bus=0x0%x, pci_id=0x%x, ",
 	        pdev->bus->number, pdev->devfn);
@@ -5163,6 +5176,15 @@
 		   request it from the kernel anyway, to avoid problems
 		   with other drivers accessing it. */
 		request_region(cy_pci_phys1, CyPCI_Zctl, "Cyclades-Z");
+		resource = request_region(cy_pci_phys1, CyPCI_Zctl, 
+					  "Cyclades-Z");
+		if (resource == NULL) {
+		    printk(KERN_ERR "cyclades: failed to allocate IO "
+				    "resource at 0x%lx\n", cy_pci_phys1);
+		    continue;
+		}
+		res_start = cy_pci_phys1;
+		res_len = CyPCI_Zctl;
 
 		if (mailbox == ZE_V1) {
 		    cy_pci_addr2 = (ulong)ioremap(cy_pci_phys2, CyPCI_Ze_win);
@@ -5261,6 +5283,10 @@
                 cy_card[j].bus_index = 1;
                 cy_card[j].first_line = cy_next_channel;
                 cy_card[j].num_chips = -1;
+		cy_card[j].resource = resource;
+		cy_card[j].res_start = res_start;
+		cy_card[j].res_len = res_len;
+		resource = NULL;	/* For next card */
 
                 /* print message */
 #ifdef CONFIG_CYZ_INTR
@@ -5279,7 +5305,7 @@
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
                 cy_next_channel += cy_pci_nchan;
-    }
+	    }
         }
 
         for (; ZeIndex != 0 && i < NR_CARDS; i++) {
@@ -5787,6 +5813,10 @@
 #endif /* CONFIG_CYZ_INTR */
 	    )
 		free_irq(cy_card[i].irq, &cy_card[i]);
+	    if (cy_card[i].resource) {
+		cy_card[i].resource = NULL;
+		release_region(cy_card[i].res_start, cy_card[i].res_len);
+	    }
         }
     }
     if (tmp_buf) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm/Config.in linux/drivers/char/drm/Config.in
--- linux.orig/drivers/char/drm/Config.in	Tue Jan 22 17:51:22 2002
+++ linux/drivers/char/drm/Config.in	Mon Jan 14 18:53:53 2002
@@ -5,13 +5,10 @@
 # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
 #
 
-bool 'Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)' CONFIG_DRM
-if [ "$CONFIG_DRM" != "n" ]; then
-    tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
-    tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
-    tristate '  ATI Rage 128' CONFIG_DRM_R128
-    dep_tristate '  ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP
-    dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
-    dep_tristate '  Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP
-    dep_tristate '  SiS' CONFIG_DRM_SIS $CONFIG_AGP
-fi
+tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
+#tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
+tristate '  ATI Rage 128' CONFIG_DRM_R128
+dep_tristate '  ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP
+dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
+dep_tristate '  Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP
+dep_tristate '  SiS' CONFIG_DRM_SIS $CONFIG_AGP
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm/sis_drv.c linux/drivers/char/drm/sis_drv.c
--- linux.orig/drivers/char/drm/sis_drv.c	Fri Dec 21 17:41:53 2001
+++ linux/drivers/char/drm/sis_drv.c	Mon Feb  4 19:11:15 2002
@@ -40,12 +40,12 @@
 #define DRIVER_PATCHLEVEL  0
 
 #define DRIVER_IOCTLS \
-        [DRM_IOCTL_NR(SIS_IOCTL_FB_ALLOC)]   = { sis_fb_alloc,	  1, 1 }, \
-        [DRM_IOCTL_NR(SIS_IOCTL_FB_FREE)]    = { sis_fb_free,	  1, 1 }, \
+        [DRM_IOCTL_NR(SIS_IOCTL_FB_ALLOC)]   = { sis_fb_alloc,	  1, 0 }, \
+        [DRM_IOCTL_NR(SIS_IOCTL_FB_FREE)]    = { sis_fb_free,	  1, 0 }, \
         /* AGP Memory Management */					  \
-        [DRM_IOCTL_NR(SIS_IOCTL_AGP_INIT)]   = { sisp_agp_init,	  1, 1 }, \
-        [DRM_IOCTL_NR(SIS_IOCTL_AGP_ALLOC)]  = { sisp_agp_alloc,  1, 1 }, \
-        [DRM_IOCTL_NR(SIS_IOCTL_AGP_FREE)]   = { sisp_agp_free,	  1, 1 }
+        [DRM_IOCTL_NR(SIS_IOCTL_AGP_INIT)]   = { sisp_agp_init,	  1, 0 }, \
+        [DRM_IOCTL_NR(SIS_IOCTL_AGP_ALLOC)]  = { sisp_agp_alloc,  1, 0 }, \
+        [DRM_IOCTL_NR(SIS_IOCTL_AGP_FREE)]   = { sisp_agp_free,	  1, 0 }
 #if 0 /* these don't appear to be defined */
 	/* SIS Stereo */						 
 	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]    = { sis_control,	  1, 1 }, 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/Config.in linux/drivers/char/drm-4.0/Config.in
--- linux.orig/drivers/char/drm-4.0/Config.in	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/Config.in	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,13 @@
+#
+# drm device configuration
+#
+# This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.0 (old version).
+#
+
+tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM40_TDFX
+tristate '  3dlabs GMX 2000' CONFIG_DRM40_GAMMA
+dep_tristate '  ATI Rage 128' CONFIG_DRM40_R128 $CONFIG_AGP
+dep_tristate '  ATI Radeon' CONFIG_DRM40_RADEON $CONFIG_AGP
+dep_tristate '  Intel I810' CONFIG_DRM40_I810 $CONFIG_AGP
+dep_tristate '  Matrox G200/G400/G450' CONFIG_DRM40_MGA $CONFIG_AGP
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/Makefile linux/drivers/char/drm-4.0/Makefile
--- linux.orig/drivers/char/drm-4.0/Makefile	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/Makefile	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,60 @@
+#
+# Makefile for the drm device driver.  This driver provides support for
+# the Direct Rendering Infrastructure (DRI) in XFree86 4.x.
+#
+
+O_TARGET	:= drm.o
+
+
+list-multi  := drmlib.o gamma.o tdfx.o r128.o ffb.o mga.o i810.o
+gamma-objs  := gamma_drv.o  gamma_dma.o
+tdfx-objs   := tdfx_drv.o                 tdfx_context.o
+r128-objs   := r128_drv.o   r128_cce.o    r128_context.o r128_bufs.o r128_state.o
+ffb-objs    := ffb_drv.o                  ffb_context.o
+mga-objs    := mga_drv.o    mga_dma.o     mga_context.o  mga_bufs.o  mga_state.o
+i810-objs   := i810_drv.o   i810_dma.o    i810_context.o i810_bufs.o
+radeon-objs := radeon_drv.o radeon_cp.o   radeon_context.o radeon_bufs.o radeon_state.o
+
+drmlib-objs	:= init.o memory.o proc.o auth.o context.o drawable.o bufs.o \
+			lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o
+
+ifneq ($(CONFIG_AGP),)
+ifneq ($(CONFIG_AGP),n)
+drmlib-objs 	+= agpsupport.o
+endif
+endif
+
+obj-$(CONFIG_DRM40_GAMMA)	+= gamma.o	drmlib.o
+obj-$(CONFIG_DRM40_TDFX)	+= tdfx.o	drmlib.o
+obj-$(CONFIG_DRM40_R128)	+= r128.o	drmlib.o
+obj-$(CONFIG_DRM40_RADEON)	+= radeon.o	drmlib.o
+obj-$(CONFIG_DRM40_FFB)		+= ffb.o	drmlib.o
+obj-$(CONFIG_DRM40_MGA)		+= mga.o	drmlib.o
+obj-$(CONFIG_DRM40_I810)	+= i810.o	drmlib.o
+
+
+include $(TOPDIR)/Rules.make
+
+drmlib.o: $(drmlib-objs)
+	$(LD) -r -o $@ $(drmlib-objs)
+
+gamma.o: $(gamma-objs)
+	$(LD) -r -o $@ $(gamma-objs)
+
+tdfx.o: $(tdfx-objs)
+	$(LD) -r -o $@ $(tdfx-objs)
+
+mga.o: $(mga-objs)
+	$(LD) -r -o $@ $(mga-objs)
+
+i810.o: $(i810-objs)
+	$(LD) -r -o $@ $(i810-objs)
+
+r128.o: $(r128-objs)
+	$(LD) -r -o $@ $(r128-objs)
+
+radeon.o: $(radeon-objs)
+	$(LD) -r -o $@ $(radeon-objs)
+
+ffb.o: $(ffb-objs)
+	$(LD) -r -o $@ $(ffb-objs)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/README.drm linux/drivers/char/drm-4.0/README.drm
--- linux.orig/drivers/char/drm-4.0/README.drm	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/README.drm	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,46 @@
+************************************************************
+* For the very latest on DRI development, please see:      *
+*     http://dri.sourceforge.net/                          *
+************************************************************
+
+The Direct Rendering Manager (drm) is a device-independent kernel-level
+device driver that provides support for the XFree86 Direct Rendering
+Infrastructure (DRI).
+
+The DRM supports the Direct Rendering Infrastructure (DRI) in four major
+ways:
+
+    1. The DRM provides synchronized access to the graphics hardware via
+       the use of an optimized two-tiered lock.
+
+    2. The DRM enforces the DRI security policy for access to the graphics
+       hardware by only allowing authenticated X11 clients access to
+       restricted regions of memory.
+
+    3. The DRM provides a generic DMA engine, complete with multiple
+       queues and the ability to detect the need for an OpenGL context
+       switch.
+
+    4. The DRM is extensible via the use of small device-specific modules
+       that rely extensively on the API exported by the DRM module.
+
+
+Documentation on the DRI is available from:
+    http://precisioninsight.com/piinsights.html
+
+For specific information about kernel-level support, see:
+
+    The Direct Rendering Manager, Kernel Support for the Direct Rendering
+    Infrastructure
+    http://precisioninsight.com/dr/drm.html
+
+    Hardware Locking for the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/locking.html
+
+    A Security Analysis of the Direct Rendering Infrastructure
+    http://precisioninsight.com/dr/security.html
+
+************************************************************
+* For the very latest on DRI development, please see:      *
+*     http://dri.sourceforge.net/                          *
+************************************************************
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/agpsupport.c linux/drivers/char/drm-4.0/agpsupport.c
--- linux.orig/drivers/char/drm-4.0/agpsupport.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/agpsupport.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,339 @@
+/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*-
+ * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/module.h>
+#if LINUX_VERSION_CODE < 0x020400
+#include "agpsupport-pre24.h"
+#else
+#define DRM_AGP_GET (drm_agp_t *)inter_module_get("drm_agp")
+#define DRM_AGP_PUT inter_module_put("drm_agp")
+#endif
+
+static const drm_agp_t *drm_agp = NULL;
+
+int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	agp_kern_info    *kern;
+	drm_agp_info_t   info;
+
+	if (!dev->agp->acquired || !drm_agp->copy_info) return -EINVAL;
+
+	kern                   = &dev->agp->agp_info;
+	info.agp_version_major = kern->version.major;
+	info.agp_version_minor = kern->version.minor;
+	info.mode              = kern->mode;
+	info.aperture_base     = kern->aper_base;
+	info.aperture_size     = kern->aper_size * 1024 * 1024;
+	info.memory_allowed    = kern->max_memory << PAGE_SHIFT;
+	info.memory_used       = kern->current_memory << PAGE_SHIFT;
+	info.id_vendor         = kern->device->vendor;
+	info.id_device         = kern->device->device;
+
+	if (copy_to_user((drm_agp_info_t *)arg, &info, sizeof(info)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int              retcode;
+
+	if (dev->agp->acquired || !drm_agp->acquire) return -EINVAL;
+	if ((retcode = drm_agp->acquire())) return retcode;
+	dev->agp->acquired = 1;
+	return 0;
+}
+
+int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+
+	if (!dev->agp->acquired || !drm_agp->release) return -EINVAL;
+	drm_agp->release();
+	dev->agp->acquired = 0;
+	return 0;
+	
+}
+
+void _drm_agp_release(void)
+{
+	if (drm_agp->release) drm_agp->release();
+}
+
+int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_mode_t   mode;
+
+	if (!dev->agp->acquired || !drm_agp->enable) return -EINVAL;
+
+	if (copy_from_user(&mode, (drm_agp_mode_t *)arg, sizeof(mode)))
+		return -EFAULT;
+	
+	dev->agp->mode    = mode.mode;
+	drm_agp->enable(mode.mode);
+	dev->agp->base    = dev->agp->agp_info.aper_base;
+	dev->agp->enabled = 1;
+	return 0;
+}
+
+int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	agp_memory       *memory;
+	unsigned long    pages;
+	u32 		 type;
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS)))
+		return -ENOMEM;
+   
+   	memset(entry, 0, sizeof(*entry));
+
+	pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE;
+	type = (u32) request.type;
+
+	if (!(memory = drm_alloc_agp(pages, type))) {
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -ENOMEM;
+	}
+	
+	entry->handle    = (unsigned long)memory->memory;
+	entry->memory    = memory;
+	entry->bound     = 0;
+	entry->pages     = pages;
+	entry->prev      = NULL;
+	entry->next      = dev->agp->memory;
+	if (dev->agp->memory) dev->agp->memory->prev = entry;
+	dev->agp->memory = entry;
+
+	request.handle   = entry->handle;
+        request.physical = memory->physical;
+
+	if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) {
+		dev->agp->memory       = entry->next;
+		dev->agp->memory->prev = NULL;
+		drm_free_agp(memory, pages);
+		drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		return -EFAULT;
+	}
+	return 0;
+}
+
+static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev,
+					   unsigned long handle)
+{
+	drm_agp_mem_t *entry;
+
+	for (entry = dev->agp->memory; entry; entry = entry->next) {
+		if (entry->handle == handle) return entry;
+	}
+	return NULL;
+}
+
+int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (!entry->bound) return -EINVAL;
+	return drm_unbind_agp(entry->memory);
+}
+
+int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	 = filp->private_data;
+	drm_device_t	  *dev	 = priv->dev;
+	drm_agp_binding_t request;
+	drm_agp_mem_t     *entry;
+	int               retcode;
+	int               page;
+	
+	if (!dev->agp->acquired || !drm_agp->bind_memory) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_binding_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) return -EINVAL;
+	page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE;
+	if ((retcode = drm_bind_agp(entry->memory, page))) return retcode;
+	entry->bound = dev->agp->base + (page << PAGE_SHIFT);
+	DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", 
+		  dev->agp->base, entry->bound);
+	return 0;
+}
+
+int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_agp_buffer_t request;
+	drm_agp_mem_t    *entry;
+	
+	if (!dev->agp->acquired) return -EINVAL;
+	if (copy_from_user(&request, (drm_agp_buffer_t *)arg, sizeof(request)))
+		return -EFAULT;
+	if (!(entry = drm_agp_lookup_entry(dev, request.handle)))
+		return -EINVAL;
+	if (entry->bound) drm_unbind_agp(entry->memory);
+   
+	if (entry->prev) entry->prev->next = entry->next;
+	else             dev->agp->memory  = entry->next;
+	if (entry->next) entry->next->prev = entry->prev;
+	drm_free_agp(entry->memory, entry->pages);
+	drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+	return 0;
+}
+
+drm_agp_head_t *drm_agp_init(void)
+{
+	drm_agp_head_t *head         = NULL;
+
+	drm_agp = DRM_AGP_GET;
+	if (drm_agp) {
+		if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS)))
+			return NULL;
+		memset((void *)head, 0, sizeof(*head));
+		drm_agp->copy_info(&head->agp_info);
+		if (head->agp_info.chipset == NOT_SUPPORTED) {
+			drm_free(head, sizeof(*head), DRM_MEM_AGPLISTS);
+			return NULL;
+		}
+		head->memory = NULL;
+		switch (head->agp_info.chipset) {
+		case INTEL_GENERIC:	head->chipset = "Intel";         break;
+		case INTEL_LX:		head->chipset = "Intel 440LX";   break;
+		case INTEL_BX:		head->chipset = "Intel 440BX";   break;
+		case INTEL_GX:		head->chipset = "Intel 440GX";   break;
+		case INTEL_I810:	head->chipset = "Intel i810";    break;
+
+#if LINUX_VERSION_CODE >= 0x020400
+		case INTEL_I840:	head->chipset = "Intel i840";    break;
+#endif
+
+		case VIA_GENERIC:	head->chipset = "VIA";           break;
+		case VIA_VP3:		head->chipset = "VIA VP3";       break;
+		case VIA_MVP3:		head->chipset = "VIA MVP3";      break;
+
+#if LINUX_VERSION_CODE >= 0x020400
+		case VIA_MVP4:		head->chipset = "VIA MVP4";      break;
+		case VIA_APOLLO_KX133:	head->chipset = "VIA Apollo KX133"; 
+			break;
+		case VIA_APOLLO_KT133:	head->chipset = "VIA Apollo KT133"; 
+			break;
+#endif
+
+		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
+			break;
+		case SIS_GENERIC:	head->chipset = "SiS";           break;
+		case AMD_GENERIC:	head->chipset = "AMD";           break;
+		case AMD_IRONGATE:	head->chipset = "AMD Irongate";  break;
+		case ALI_GENERIC:	head->chipset = "ALi";           break;
+		case ALI_M1541: 	head->chipset = "ALi M1541";     break;
+		case ALI_M1621: 	head->chipset = "ALi M1621";	 break;
+		case ALI_M1631: 	head->chipset = "ALi M1631";	 break;
+		case ALI_M1632: 	head->chipset = "ALi M1632";	 break;
+		case ALI_M1641: 	head->chipset = "ALi M1641";	 break;
+		case ALI_M1647: 	head->chipset = "ALi M1647";	 break;
+		case ALI_M1651: 	head->chipset = "ALi M1651";	 break;
+		case SVWRKS_GENERIC:	head->chipset = "Serverworks Generic";
+			break;
+		case SVWRKS_HE: 	head->chipset = "Serverworks HE"; break;
+		case SVWRKS_LE: 	head->chipset = "Serverworks LE"; break;
+
+		default:		head->chipset = "Unknown";       break;
+		}
+		DRM_INFO("AGP %d.%d on %s @ 0x%08lx %ZuMB\n",
+			 head->agp_info.version.major,
+			 head->agp_info.version.minor,
+			 head->chipset,
+			 head->agp_info.aper_base,
+			 head->agp_info.aper_size);
+	}
+	return head;
+}
+
+void drm_agp_uninit(void)
+{
+	DRM_AGP_PUT;
+	drm_agp = NULL;
+}
+
+agp_memory *drm_agp_allocate_memory(size_t pages, u32 type)
+{
+	if (!drm_agp->allocate_memory) return NULL;
+	return drm_agp->allocate_memory(pages, type);
+}
+
+int drm_agp_free_memory(agp_memory *handle)
+{
+	if (!handle || !drm_agp->free_memory) return 0;
+	drm_agp->free_memory(handle);
+	return 1;
+}
+
+int drm_agp_bind_memory(agp_memory *handle, off_t start)
+{
+	if (!handle || !drm_agp->bind_memory) return -EINVAL;
+	return drm_agp->bind_memory(handle, start);
+}
+
+int drm_agp_unbind_memory(agp_memory *handle)
+{
+	if (!handle || !drm_agp->unbind_memory) return -EINVAL;
+	return drm_agp->unbind_memory(handle);
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/auth.c linux/drivers/char/drm-4.0/auth.c
--- linux.orig/drivers/char/drm-4.0/auth.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/auth.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,162 @@
+/* auth.c -- IOCTLs for authentication -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_hash_magic(drm_magic_t magic)
+{
+	return magic & (DRM_HASH_SIZE-1);
+}
+
+static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_file_t	  *retval = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash	  = drm_hash_magic(magic);
+
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
+		if (pt->magic == magic) {
+			retval = pt->priv;
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+	return retval;
+}
+
+int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic)
+{
+	int		  hash;
+	drm_magic_entry_t *entry;
+	
+	DRM_DEBUG("%d\n", magic);
+	
+	hash	     = drm_hash_magic(magic);
+	entry	     = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
+	if (!entry) return -ENOMEM;
+	entry->magic = magic;
+	entry->priv  = priv;
+	entry->next  = NULL;
+
+	down(&dev->struct_sem);
+	if (dev->magiclist[hash].tail) {
+		dev->magiclist[hash].tail->next = entry;
+		dev->magiclist[hash].tail	= entry;
+	} else {
+		dev->magiclist[hash].head	= entry;
+		dev->magiclist[hash].tail	= entry;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_remove_magic(drm_device_t *dev, drm_magic_t magic)
+{
+	drm_magic_entry_t *prev = NULL;
+	drm_magic_entry_t *pt;
+	int		  hash;
+	
+	DRM_DEBUG("%d\n", magic);
+	hash = drm_hash_magic(magic);
+	
+	down(&dev->struct_sem);
+	for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
+		if (pt->magic == magic) {
+			if (dev->magiclist[hash].head == pt) {
+				dev->magiclist[hash].head = pt->next;
+			}
+			if (dev->magiclist[hash].tail == pt) {
+				dev->magiclist[hash].tail = prev;
+			}
+			if (prev) {
+				prev->next = pt->next;
+			}
+			up(&dev->struct_sem);
+			return 0;
+		}
+	}
+	up(&dev->struct_sem);
+
+	drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+	
+	return -EINVAL;
+}
+
+int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	static drm_magic_t sequence = 0;
+	static spinlock_t  lock	    = SPIN_LOCK_UNLOCKED;
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+
+				/* Find unique magic */
+	if (priv->magic) {
+		auth.magic = priv->magic;
+	} else {
+		do {
+			spin_lock(&lock);
+			if (!sequence) ++sequence; /* reserve 0 */
+			auth.magic = sequence++;
+			spin_unlock(&lock);
+		} while (drm_find_file(dev, auth.magic));
+		priv->magic = auth.magic;
+		drm_add_magic(dev, priv, auth.magic);
+	}
+	
+	DRM_DEBUG("%u\n", auth.magic);
+	if (copy_to_user((drm_auth_t *)arg, &auth, sizeof(auth)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	   *priv    = filp->private_data;
+	drm_device_t	   *dev	    = priv->dev;
+	drm_auth_t	   auth;
+	drm_file_t	   *file;
+
+	if (copy_from_user(&auth, (drm_auth_t *)arg, sizeof(auth)))
+		return -EFAULT;
+	DRM_DEBUG("%u\n", auth.magic);
+	if ((file = drm_find_file(dev, auth.magic))) {
+		file->authenticated = 1;
+		drm_remove_magic(dev, auth.magic);
+		return 0;
+	}
+	return -EINVAL;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/bufs.c linux/drivers/char/drm-4.0/bufs.c
--- linux.orig/drivers/char/drm-4.0/bufs.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/bufs.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,543 @@
+/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "linux/un.h"
+
+				/* Compute order.  Can be made faster. */
+int drm_order(unsigned long size)
+{
+	int	      order;
+	unsigned long tmp;
+
+	for (order = 0, tmp = size; tmp >>= 1; ++order);
+	if (size & ~(1 << order)) ++order;
+	return order;
+}
+
+int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map;
+	
+	if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */
+
+	map	     = drm_alloc(sizeof(*map), DRM_MEM_MAPS);
+	if (!map) return -ENOMEM;
+	if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EFAULT;
+	}
+
+	DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
+		  map->offset, map->size, map->type);
+	if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) {
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+	map->mtrr   = -1;
+	map->handle = 0;
+
+	switch (map->type) {
+	case _DRM_REGISTERS:
+	case _DRM_FRAME_BUFFER:
+#ifndef __sparc__
+		if (map->offset + map->size < map->offset
+		    || map->offset < virt_to_phys(high_memory)) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -EINVAL;
+		}
+#endif
+#ifdef CONFIG_MTRR
+		if (map->type == _DRM_FRAME_BUFFER
+		    || (map->flags & _DRM_WRITE_COMBINING)) {
+			map->mtrr = mtrr_add(map->offset, map->size,
+					     MTRR_TYPE_WRCOMB, 1);
+		}
+#endif
+		map->handle = drm_ioremap(map->offset, map->size);
+		break;
+			
+
+	case _DRM_SHM:
+		map->handle = (void *)drm_alloc_pages(drm_order(map->size)
+						      - PAGE_SHIFT,
+						      DRM_MEM_SAREA);
+		DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size),
+			  map->handle);
+		if (!map->handle) {
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+			return -ENOMEM;
+		}
+		map->offset = (unsigned long)map->handle;
+		if (map->flags & _DRM_CONTAINS_LOCK) {
+			dev->lock.hw_lock = map->handle; /* Pointer to lock */
+		}
+		break;
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	case _DRM_AGP:
+		map->offset = map->offset + dev->agp->base;
+		break;
+#endif
+	default:
+		drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		return -EINVAL;
+	}
+
+	down(&dev->struct_sem);
+	if (dev->maplist) {
+		++dev->map_count;
+		dev->maplist = drm_realloc(dev->maplist,
+					   (dev->map_count-1)
+					   * sizeof(*dev->maplist),
+					   dev->map_count
+					   * sizeof(*dev->maplist),
+					   DRM_MEM_MAPS);
+	} else {
+		dev->map_count = 1;
+		dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist),
+					 DRM_MEM_MAPS);
+	}
+	dev->maplist[dev->map_count-1] = map;
+	up(&dev->struct_sem);
+
+	if (copy_to_user((drm_map_t *)arg, map, sizeof(*map)))
+		return -EFAULT;
+	if (map->type != _DRM_SHM) {
+		if (copy_to_user(&((drm_map_t *)arg)->handle,
+				 &map->offset,
+				 sizeof(map->offset)))
+			return -EFAULT;
+	}		
+	return 0;
+}
+
+int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		return -EINVAL;
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		down_write(&current->mm->mmap_sem);
+		virtual = do_mmap(filp, 0, dma->byte_count,
+				  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+		up_write(&current->mm->mmap_sem);
+		if (virtual > -1024UL) {
+				/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/context.c linux/drivers/char/drm-4.0/context.c
--- linux.orig/drivers/char/drm-4.0/context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,321 @@
+/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx)
+{
+	DRM_DEBUG("\n");
+	
+	if (atomic_read(&q->use_count) != 1
+	    || atomic_read(&q->finalization)
+	    || atomic_read(&q->block_count)) {
+		DRM_ERROR("New queue is already in use: u%d f%d b%d\n",
+			  atomic_read(&q->use_count),
+			  atomic_read(&q->finalization),
+			  atomic_read(&q->block_count));
+	}
+		  
+	atomic_set(&q->finalization,  0);
+	atomic_set(&q->block_count,   0);
+	atomic_set(&q->block_read,    0);
+	atomic_set(&q->block_write,   0);
+	atomic_set(&q->total_queued,  0);
+	atomic_set(&q->total_flushed, 0);
+	atomic_set(&q->total_locks,   0);
+
+	init_waitqueue_head(&q->write_queue);
+	init_waitqueue_head(&q->read_queue);
+	init_waitqueue_head(&q->flush_queue);
+
+	q->flags = ctx->flags;
+
+	drm_waitlist_create(&q->waitlist, dev->dma->buf_count);
+
+	return 0;
+}
+
+
+/* drm_alloc_queue:
+PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not
+	disappear (so all deallocation must be done after IOCTLs are off)
+     2) dev->queue_count < dev->queue_slots
+     3) dev->queuelist[i].use_count == 0 and
+	dev->queuelist[i].finalization == 0 if i not in use 
+POST: 1) dev->queuelist[i].use_count == 1
+      2) dev->queue_count < dev->queue_slots */
+		
+static int drm_alloc_queue(drm_device_t *dev)
+{
+	int	    i;
+	drm_queue_t *queue;
+	int	    oldslots;
+	int	    newslots;
+				/* Check for a free queue */
+	for (i = 0; i < dev->queue_count; i++) {
+		atomic_inc(&dev->queuelist[i]->use_count);
+		if (atomic_read(&dev->queuelist[i]->use_count) == 1
+		    && !atomic_read(&dev->queuelist[i]->finalization)) {
+			DRM_DEBUG("%d (free)\n", i);
+			return i;
+		}
+		atomic_dec(&dev->queuelist[i]->use_count);
+	}
+				/* Allocate a new queue */
+	
+	queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES);
+	if(queue == NULL)
+		return -ENOMEM;	
+
+	memset(queue, 0, sizeof(*queue));
+	down(&dev->struct_sem);
+	atomic_set(&queue->use_count, 1);
+	
+	++dev->queue_count;
+	if (dev->queue_count >= dev->queue_slots) {
+		oldslots = dev->queue_slots * sizeof(*dev->queuelist);
+		if (!dev->queue_slots) dev->queue_slots = 1;
+		dev->queue_slots *= 2;
+		newslots = dev->queue_slots * sizeof(*dev->queuelist);
+
+		dev->queuelist = drm_realloc(dev->queuelist,
+					     oldslots,
+					     newslots,
+					     DRM_MEM_QUEUES);
+		if (!dev->queuelist) {
+			up(&dev->struct_sem);
+			DRM_DEBUG("out of memory\n");
+			return -ENOMEM;
+		}
+	}
+	dev->queuelist[dev->queue_count-1] = queue;
+	
+	up(&dev->struct_sem);
+	DRM_DEBUG("%d (new)\n", dev->queue_count - 1);
+	return dev->queue_count - 1;
+}
+
+int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Init kernel's context and get a new one. */
+		drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+		ctx.handle = drm_alloc_queue(dev);
+	}
+	drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx);
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+
+	if (DRM_BUFCOUNT(&q->waitlist)) {
+		atomic_dec(&q->use_count);
+		return -EBUSY;
+	}
+	
+	q->flags = ctx.flags;
+	
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+		
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	ctx.flags = q->flags;
+	atomic_dec(&q->use_count);
+	
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return drm_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	drm_queue_t	*q;
+	drm_buf_t	*buf;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	
+	if (ctx.handle >= dev->queue_count) return -EINVAL;
+	q = dev->queuelist[ctx.handle];
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+				/* No longer in use */
+		atomic_dec(&q->use_count);
+		return -EINVAL;
+	}
+	
+	atomic_inc(&q->finalization); /* Mark queue in finalization state */
+	atomic_sub(2, &q->use_count); /* Mark queue as unused (pending
+					 finalization) */
+
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) {
+			clear_bit(0, &dev->interrupt_flag);
+			return -EINTR;
+		}
+	}
+				/* Remove queued buffers */
+	while ((buf = drm_waitlist_get(&q->waitlist))) {
+		drm_free_buffer(dev, buf);
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	
+				/* Wakeup blocked processes */
+	wake_up_interruptible(&q->read_queue);
+	wake_up_interruptible(&q->write_queue);
+	wake_up_interruptible(&q->flush_queue);
+	
+				/* Finalization over.  Queue is made
+				   available when both use_count and
+				   finalization become 0, which won't
+				   happen until all the waiting processes
+				   stop waiting. */
+	atomic_dec(&q->finalization);
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/ctxbitmap.c linux/drivers/char/drm-4.0/ctxbitmap.c
--- linux.orig/drivers/char/drm-4.0/ctxbitmap.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/ctxbitmap.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,85 @@
+/* ctxbitmap.c -- Context bitmap management -*- linux-c -*-
+ * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com
+ * 
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle)
+{
+	if (ctx_handle < 0) goto failed;
+
+	if (ctx_handle < DRM_MAX_CTXBITMAP) {
+		clear_bit(ctx_handle, dev->ctx_bitmap);
+		return;
+	}
+failed:
+       	DRM_ERROR("Attempt to free invalid context handle: %d\n",
+		  ctx_handle);
+       	return;
+}
+
+int drm_ctxbitmap_next(drm_device_t *dev)
+{
+	int bit;
+
+	bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP);
+	if (bit < DRM_MAX_CTXBITMAP) {
+		set_bit(bit, dev->ctx_bitmap);
+	   	DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit);
+		return bit;
+	}
+	return -1;
+}
+
+int drm_ctxbitmap_init(drm_device_t *dev)
+{
+	int i;
+   	int temp;
+
+	dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, 
+						      DRM_MEM_CTXBITMAP);
+	if(dev->ctx_bitmap == NULL) {
+		return -ENOMEM;
+	}
+	memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE);
+	for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+		temp = drm_ctxbitmap_next(dev);
+	   	DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp);
+	}
+
+	return 0;
+}
+
+void drm_ctxbitmap_cleanup(drm_device_t *dev)
+{
+	drm_free((void *)dev->ctx_bitmap, PAGE_SIZE,
+		 DRM_MEM_CTXBITMAP);
+}
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/dma.c linux/drivers/char/drm-4.0/dma.c
--- linux.orig/drivers/char/drm-4.0/dma.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/dma.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,546 @@
+/* dma.c -- DMA IOCTL and function support -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinuxa.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+void drm_dma_setup(drm_device_t *dev)
+{
+	int i;
+	
+	if (!(dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER))) {
+                printk(KERN_ERR "drm_dma_setup: can't drm_alloc dev->dma");
+                return;
+        }       
+	memset(dev->dma, 0, sizeof(*dev->dma));
+	for (i = 0; i <= DRM_MAX_ORDER; i++)
+		memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0]));
+}
+
+void drm_dma_takedown(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma = dev->dma;
+	int		  i, j;
+
+	if (!dma) return;
+	
+				/* Clear dma buffers */
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].seg_count) {
+			DRM_DEBUG("order %d: buf_count = %d,"
+				  " seg_count = %d\n",
+				  i,
+				  dma->bufs[i].buf_count,
+				  dma->bufs[i].seg_count);
+			for (j = 0; j < dma->bufs[i].seg_count; j++) {
+				drm_free_pages(dma->bufs[i].seglist[j],
+					       dma->bufs[i].page_order,
+					       DRM_MEM_DMA);
+			}
+			drm_free(dma->bufs[i].seglist,
+				 dma->bufs[i].seg_count
+				 * sizeof(*dma->bufs[0].seglist),
+				 DRM_MEM_SEGS);
+		}
+	   	if(dma->bufs[i].buf_count) {
+		   	for(j = 0; j < dma->bufs[i].buf_count; j++) {
+			   if(dma->bufs[i].buflist[j].dev_private) {
+			      drm_free(dma->bufs[i].buflist[j].dev_private,
+				       dma->bufs[i].buflist[j].dev_priv_size,
+				       DRM_MEM_BUFS);
+			   }
+			}
+		   	drm_free(dma->bufs[i].buflist,
+				 dma->bufs[i].buf_count *
+				 sizeof(*dma->bufs[0].buflist),
+				 DRM_MEM_BUFS);
+		   	drm_freelist_destroy(&dma->bufs[i].freelist);
+		}
+	}
+	
+	if (dma->buflist) {
+		drm_free(dma->buflist,
+			 dma->buf_count * sizeof(*dma->buflist),
+			 DRM_MEM_BUFS);
+	}
+
+	if (dma->pagelist) {
+		drm_free(dma->pagelist,
+			 dma->page_count * sizeof(*dma->pagelist),
+			 DRM_MEM_PAGES);
+	}
+	drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER);
+	dev->dma = NULL;
+}
+
+#if DRM_DMA_HISTOGRAM
+/* This is slow, but is useful for debugging. */
+int drm_histogram_slot(unsigned long count)
+{
+	int value = DRM_DMA_HISTOGRAM_INITIAL;
+	int slot;
+
+	for (slot = 0;
+	     slot < DRM_DMA_HISTOGRAM_SLOTS;
+	     ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) {
+		if (count < value) return slot;
+	}
+	return DRM_DMA_HISTOGRAM_SLOTS - 1;
+}
+
+void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf)
+{
+	cycles_t queued_to_dispatched;
+	cycles_t dispatched_to_completed;
+	cycles_t completed_to_freed;
+	int	 q2d, d2c, c2f, q2c, q2f;
+	
+	if (buf->time_queued) {
+		queued_to_dispatched	= (buf->time_dispatched
+					   - buf->time_queued);
+		dispatched_to_completed = (buf->time_completed
+					   - buf->time_dispatched);
+		completed_to_freed	= (buf->time_freed
+					   - buf->time_completed);
+
+		q2d = drm_histogram_slot(queued_to_dispatched);
+		d2c = drm_histogram_slot(dispatched_to_completed);
+		c2f = drm_histogram_slot(completed_to_freed);
+
+		q2c = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed);
+		q2f = drm_histogram_slot(queued_to_dispatched
+					 + dispatched_to_completed
+					 + completed_to_freed);
+		
+		atomic_inc(&dev->histo.total);
+		atomic_inc(&dev->histo.queued_to_dispatched[q2d]);
+		atomic_inc(&dev->histo.dispatched_to_completed[d2c]);
+		atomic_inc(&dev->histo.completed_to_freed[c2f]);
+		
+		atomic_inc(&dev->histo.queued_to_completed[q2c]);
+		atomic_inc(&dev->histo.queued_to_freed[q2f]);
+
+	}
+	buf->time_queued     = 0;
+	buf->time_dispatched = 0;
+	buf->time_completed  = 0;
+	buf->time_freed	     = 0;
+}
+#endif
+
+void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if (!buf) return;
+	
+	buf->waiting  = 0;
+	buf->pending  = 0;
+	buf->pid      = 0;
+	buf->used     = 0;
+#if DRM_DMA_HISTOGRAM
+	buf->time_completed = get_cycles();
+#endif
+	if (waitqueue_active(&buf->dma_wait)) {
+		wake_up_interruptible(&buf->dma_wait);
+	} else {
+				/* If processes are waiting, the last one
+				   to wake will put the buffer on the free
+				   list.  If no processes are waiting, we
+				   put the buffer on the freelist here. */
+		drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf);
+	}
+}
+
+void drm_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+	for (i = 0; i < dma->buf_count; i++) {
+		if (dma->buflist[i]->pid == pid) {
+			switch (dma->buflist[i]->list) {
+			case DRM_LIST_NONE:
+				drm_free_buffer(dev, dma->buflist[i]);
+				break;
+			case DRM_LIST_WAIT:
+				dma->buflist[i]->list = DRM_LIST_RECLAIM;
+				break;
+			default:
+				/* Buffer already on hardware. */
+				break;
+			}
+		}
+	}
+}
+
+int drm_context_switch(drm_device_t *dev, int old, int new)
+{
+	char	    buf[64];
+	drm_queue_t *q;
+
+	atomic_inc(&dev->total_ctx);
+
+	if (test_and_set_bit(0, &dev->context_flag)) {
+		DRM_ERROR("Reentering -- FIXME\n");
+		return -EBUSY;
+	}
+
+#if DRM_DMA_HISTOGRAM
+	dev->ctx_start = get_cycles();
+#endif
+	
+	DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+	if (new >= dev->queue_count) {
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (new == dev->last_context) {
+		clear_bit(0, &dev->context_flag);
+		return 0;
+	}
+	
+	q = dev->queuelist[new];
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) == 1) {
+		atomic_dec(&q->use_count);
+		clear_bit(0, &dev->context_flag);
+		return -EINVAL;
+	}
+
+	if (drm_flags & DRM_FLAG_NOCTX) {
+		drm_context_switch_complete(dev, new);
+	} else {
+		sprintf(buf, "C %d %d\n", old, new);
+		drm_write_string(dev, buf);
+	}
+	
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+int drm_context_switch_complete(drm_device_t *dev, int new)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+	dev->last_switch  = jiffies;
+	
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("Lock isn't held after context switch\n");
+	}
+
+	if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("Cannot free lock\n");
+		}
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+						      - dev->ctx_start)]);
+		   
+#endif
+	clear_bit(0, &dev->context_flag);
+	wake_up_interruptible(&dev->context_wait);
+	
+	return 0;
+}
+
+void drm_clear_next_buffer(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+	
+	dma->next_buffer = NULL;
+	if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) {
+		wake_up_interruptible(&dma->next_queue->flush_queue);
+	}
+	dma->next_queue	 = NULL;
+}
+
+
+int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long))
+{
+	int	   i;
+	int	   candidate = -1;
+	int	   j	     = jiffies;
+
+	if (!dev) {
+		DRM_ERROR("No device\n");
+		return -1;
+	}
+	if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) {
+				/* This only happens between the time the
+				   interrupt is initialized and the time
+				   the queues are initialized. */
+		return -1;
+	}
+
+				/* Doing "while locked" DMA? */
+	if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) {
+		return DRM_KERNEL_CONTEXT;
+	}
+
+				/* If there are buffers on the last_context
+				   queue, and we have not been executing
+				   this context very long, continue to
+				   execute this context. */
+	if (dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j
+	    && DRM_WAITCOUNT(dev, dev->last_context)) {
+		return dev->last_context;
+	}
+
+				/* Otherwise, find a candidate */
+	for (i = dev->last_checked + 1; i < dev->queue_count; i++) {
+		if (DRM_WAITCOUNT(dev, i)) {
+			candidate = dev->last_checked = i;
+			break;
+		}
+	}
+
+	if (candidate < 0) {
+		for (i = 0; i < dev->queue_count; i++) {
+			if (DRM_WAITCOUNT(dev, i)) {
+				candidate = dev->last_checked = i;
+				break;
+			}
+		}
+	}
+
+	if (wrapper
+	    && candidate >= 0
+	    && candidate != dev->last_context
+	    && dev->last_switch <= j
+	    && dev->last_switch + DRM_TIME_SLICE > j) {
+		if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) {
+			del_timer(&dev->timer);
+			dev->timer.function = wrapper;
+			dev->timer.data	    = (unsigned long)dev;
+			dev->timer.expires  = dev->last_switch+DRM_TIME_SLICE;
+			add_timer(&dev->timer);
+		}
+		return -1;
+	}
+
+	return candidate;
+}
+
+
+int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d)
+{
+	int		  i;
+	drm_queue_t	  *q;
+	drm_buf_t	  *buf;
+	int		  idx;
+	int		  while_locked = 0;
+	drm_device_dma_t  *dma = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+	DRM_DEBUG("%d\n", d->send_count);
+
+	if (d->flags & _DRM_DMA_WHILE_LOCKED) {
+		int context = dev->lock.hw_lock->lock;
+		
+		if (!_DRM_LOCK_IS_HELD(context)) {
+			DRM_ERROR("No lock held during \"while locked\""
+				  " request\n");
+			return -EINVAL;
+		}
+		if (d->context != _DRM_LOCKING_CONTEXT(context)
+		    && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) {
+			DRM_ERROR("Lock held by %d while %d makes"
+				  " \"while locked\" request\n",
+				  _DRM_LOCKING_CONTEXT(context),
+				  d->context);
+			return -EINVAL;
+		}
+		q = dev->queuelist[DRM_KERNEL_CONTEXT];
+		while_locked = 1;
+	} else {
+		q = dev->queuelist[d->context];
+	}
+
+
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->block_write)) {
+		add_wait_queue(&q->write_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!atomic_read(&q->block_write)) break;
+			schedule();
+			if (signal_pending(current)) {
+				atomic_dec(&q->use_count);
+				remove_wait_queue(&q->write_queue, &entry);
+				return -EINTR;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->write_queue, &entry);
+	}
+	
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Process %d using buffer %d on list %d\n",
+				  current->pid, buf->idx, buf->list);
+		}
+		buf->used	  = d->send_sizes[i];
+		buf->while_locked = while_locked;
+		buf->context	  = d->context;
+		if (!buf->used) {
+			DRM_ERROR("Queueing 0 length buffer\n");
+		}
+		if (buf->pending) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		if (buf->waiting) {
+			atomic_dec(&q->use_count);
+			DRM_ERROR("Queueing waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			return -EINVAL;
+		}
+		buf->waiting = 1;
+		if (atomic_read(&q->use_count) == 1
+		    || atomic_read(&q->finalization)) {
+			drm_free_buffer(dev, buf);
+		} else {
+			drm_waitlist_put(&q->waitlist, buf);
+			atomic_inc(&q->total_queued);
+		}
+	}
+	atomic_dec(&q->use_count);
+	
+	return 0;
+}
+
+static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d,
+					int order)
+{
+	int		  i;
+	drm_buf_t	  *buf;
+	drm_device_dma_t  *dma = dev->dma;
+	
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = drm_freelist_get(&dma->bufs[order].freelist,
+				       d->flags & _DRM_DMA_WAIT);
+		if (!buf) break;
+		if (buf->pending || buf->waiting) {
+			DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n",
+				  buf->idx,
+				  buf->pid,
+				  buf->waiting,
+				  buf->pending);
+		}
+		buf->pid     = current->pid;
+		if (copy_to_user(&d->request_indices[i],
+				 &buf->idx,
+				 sizeof(buf->idx)))
+			return -EFAULT;
+
+		if (copy_to_user(&d->request_sizes[i],
+				 &buf->total,
+				 sizeof(buf->total)))
+			return -EFAULT;
+
+		++d->granted_count;
+	}
+	return 0;
+}
+
+
+int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma)
+{
+	int		  order;
+	int		  retcode = 0;
+	int		  tmp_order;
+	
+	order = drm_order(dma->request_size);
+
+	dma->granted_count = 0;
+	retcode		   = drm_dma_get_buffers_of_order(dev, dma, order);
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_SMALLER_OK)) {
+		for (tmp_order = order - 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order >= DRM_MIN_ORDER;
+		     --tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+
+	if (dma->granted_count < dma->request_count
+	    && (dma->flags & _DRM_DMA_LARGER_OK)) {
+		for (tmp_order = order + 1;
+		     !retcode
+			     && dma->granted_count < dma->request_count
+			     && tmp_order <= DRM_MAX_ORDER;
+		     ++tmp_order) {
+			
+			retcode = drm_dma_get_buffers_of_order(dev, dma,
+							       tmp_order);
+		}
+	}
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/drawable.c linux/drivers/char/drm-4.0/drawable.c
--- linux.orig/drivers/char/drm-4.0/drawable.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/drawable.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,51 @@
+/* drawable.c -- IOCTLs for drawables -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_draw_t draw;
+
+	draw.handle = 0;	/* NOOP */
+	DRM_DEBUG("%d\n", draw.handle);
+	if (copy_to_user((drm_draw_t *)arg, &draw, sizeof(draw)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	return 0;		/* NOOP */
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/drm.h linux/drivers/char/drm-4.0/drm.h
--- linux.orig/drivers/char/drm-4.0/drm.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/drm.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,414 @@
+/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ * Acknowledgements:
+ * Dec 1999, Richard Henderson <rth@twiddle.net>, move to generic cmpxchg.
+ *
+ */
+
+#ifndef _DRM_H_
+#define _DRM_H_
+
+#include <linux/config.h>
+#if defined(__linux__)
+#include <asm/ioctl.h>		/* For _IO* macros */
+#define DRM_IOCTL_NR(n)	     _IOC_NR(n)
+#elif defined(__FreeBSD__)
+#include <sys/ioccom.h>
+#define DRM_IOCTL_NR(n)	     ((n) & 0xff)
+#endif
+
+#define DRM_PROC_DEVICES "/proc/devices"
+#define DRM_PROC_MISC	 "/proc/misc"
+#define DRM_PROC_DRM	 "/proc/drm"
+#define DRM_DEV_DRM	 "/dev/drm"
+#define DRM_DEV_MODE	 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP)
+#define DRM_DEV_UID	 0
+#define DRM_DEV_GID	 0
+
+
+#define DRM_NAME	"drm"	  /* Name in kernel, /dev, and /proc	    */
+#define DRM_MIN_ORDER	5	  /* At least 2^5 bytes = 32 bytes	    */
+#define DRM_MAX_ORDER	22	  /* Up to 2^22 bytes = 4MB		    */
+#define DRM_RAM_PERCENT 10	  /* How much system ram can we lock?	    */
+
+#define _DRM_LOCK_HELD	0x80000000 /* Hardware lock is held		    */
+#define _DRM_LOCK_CONT	0x40000000 /* Hardware lock is contended	    */
+#define _DRM_LOCK_IS_HELD(lock)	   ((lock) & _DRM_LOCK_HELD)
+#define _DRM_LOCK_IS_CONT(lock)	   ((lock) & _DRM_LOCK_CONT)
+#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+typedef unsigned long drm_handle_t;
+typedef unsigned int  drm_context_t;
+typedef unsigned int  drm_drawable_t;
+typedef unsigned int  drm_magic_t;
+
+/* Warning: If you change this structure, make sure you change
+ * XF86DRIClipRectRec in the server as well */
+
+typedef struct drm_clip_rect {
+           unsigned short x1;
+           unsigned short y1;
+           unsigned short x2;
+           unsigned short y2;
+} drm_clip_rect_t;
+
+/* Seperate include files for the i810/mga/r128 specific structures */
+#include "mga_drm.h"
+#include "i810_drm.h"
+#include "r128_drm.h"
+#include "radeon_drm.h"
+#ifdef CONFIG_DRM40_SIS
+#include "sis_drm.h"
+#endif
+
+typedef struct drm_version {
+	int    version_major;	  /* Major version			    */
+	int    version_minor;	  /* Minor version			    */
+	int    version_patchlevel;/* Patch level			    */
+	size_t name_len;	  /* Length of name buffer		    */
+	char   *name;		  /* Name of driver			    */
+	size_t date_len;	  /* Length of date buffer		    */
+	char   *date;		  /* User-space buffer to hold date	    */
+	size_t desc_len;	  /* Length of desc buffer		    */
+	char   *desc;		  /* User-space buffer to hold desc	    */
+} drm_version_t;
+
+typedef struct drm_unique {
+	size_t unique_len;	  /* Length of unique			    */
+	char   *unique;		  /* Unique name for driver instantiation   */
+} drm_unique_t;
+
+typedef struct drm_list {
+	int		 count;	  /* Length of user-space structures	    */
+	drm_version_t	 *version;
+} drm_list_t;
+
+typedef struct drm_block {
+	int		 unused;
+} drm_block_t;
+
+typedef struct drm_control {
+	enum {
+		DRM_ADD_COMMAND,
+		DRM_RM_COMMAND,
+		DRM_INST_HANDLER,
+		DRM_UNINST_HANDLER
+	}		 func;
+	int		 irq;
+} drm_control_t;
+
+typedef enum drm_map_type {
+	_DRM_FRAME_BUFFER = 0,	  /* WC (no caching), no core dump	    */
+	_DRM_REGISTERS	  = 1,	  /* no caching, no core dump		    */
+	_DRM_SHM	  = 2,	  /* shared, cached			    */
+	_DRM_AGP          = 3	  /* AGP/GART                               */
+} drm_map_type_t;
+
+typedef enum drm_map_flags {
+	_DRM_RESTRICTED	     = 0x01, /* Cannot be mapped to user-virtual    */
+	_DRM_READ_ONLY	     = 0x02,
+	_DRM_LOCKED	     = 0x04, /* shared, cached, locked		    */
+	_DRM_KERNEL	     = 0x08, /* kernel requires access		    */
+	_DRM_WRITE_COMBINING = 0x10, /* use write-combining if available    */
+	_DRM_CONTAINS_LOCK   = 0x20  /* SHM page that contains lock	    */
+} drm_map_flags_t;
+
+typedef struct drm_map {
+	unsigned long	offset;	 /* Requested physical address (0 for SAREA)*/
+	unsigned long	size;	 /* Requested physical size (bytes)	    */
+	drm_map_type_t	type;	 /* Type of memory to map		    */
+	drm_map_flags_t flags;	 /* Flags				    */
+	void		*handle; /* User-space: "Handle" to pass to mmap    */
+				 /* Kernel-space: kernel-virtual address    */
+	int		mtrr;	 /* MTRR slot used			    */
+				 /* Private data			    */
+} drm_map_t;
+
+typedef enum drm_lock_flags {
+	_DRM_LOCK_READY	     = 0x01, /* Wait until hardware is ready for DMA */
+	_DRM_LOCK_QUIESCENT  = 0x02, /* Wait until hardware quiescent	     */
+	_DRM_LOCK_FLUSH	     = 0x04, /* Flush this context's DMA queue first */
+	_DRM_LOCK_FLUSH_ALL  = 0x08, /* Flush all DMA queues first	     */
+				/* These *HALT* flags aren't supported yet
+				   -- they will be used to support the
+				   full-screen DGA-like mode. */
+	_DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues   */
+	_DRM_HALT_CUR_QUEUES = 0x20  /* Halt all current queues		     */
+} drm_lock_flags_t;
+
+typedef struct drm_lock {
+	int		 context;
+	drm_lock_flags_t flags;
+} drm_lock_t;
+
+typedef enum drm_dma_flags {	      /* These values *MUST* match xf86drm.h */
+				      /* Flags for DMA buffer dispatch	     */
+	_DRM_DMA_BLOCK	      = 0x01, /* Block until buffer dispatched.
+					 Note, the buffer may not yet have
+					 been processed by the hardware --
+					 getting a hardware lock with the
+					 hardware quiescent will ensure
+					 that the buffer has been
+					 processed.			     */
+	_DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held	     */
+	_DRM_DMA_PRIORITY     = 0x04, /* High priority dispatch		     */
+
+				      /* Flags for DMA buffer request	     */
+	_DRM_DMA_WAIT	      = 0x10, /* Wait for free buffers		     */
+	_DRM_DMA_SMALLER_OK   = 0x20, /* Smaller-than-requested buffers ok   */
+	_DRM_DMA_LARGER_OK    = 0x40  /* Larger-than-requested buffers ok    */
+} drm_dma_flags_t;
+
+typedef struct drm_buf_desc {
+	int	      count;	 /* Number of buffers of this size	     */
+	int	      size;	 /* Size in bytes			     */
+	int	      low_mark;	 /* Low water mark			     */
+	int	      high_mark; /* High water mark			     */
+	enum {
+		_DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA  */
+		_DRM_AGP_BUFFER = 0x02  /* Buffer is in agp space            */
+	}	      flags;
+	unsigned long agp_start; /* Start address of where the agp buffers
+				  * are in the agp aperture */
+} drm_buf_desc_t;
+
+typedef struct drm_buf_info {
+	int	       count;	/* Entries in list			     */
+	drm_buf_desc_t *list;
+} drm_buf_info_t;
+
+typedef struct drm_buf_free {
+	int	       count;
+	int	       *list;
+} drm_buf_free_t;
+
+typedef struct drm_buf_pub {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	void		  *address;    /* Address of buffer		     */
+} drm_buf_pub_t;
+
+typedef struct drm_buf_map {
+	int	      count;	/* Length of buflist			    */
+	void	      *virtual;	/* Mmaped area in user-virtual		    */
+	drm_buf_pub_t *list;	/* Buffer information			    */
+} drm_buf_map_t;
+
+typedef struct drm_dma {
+				/* Indices here refer to the offset into
+				   buflist in drm_buf_get_t.  */
+	int		context;	  /* Context handle		    */
+	int		send_count;	  /* Number of buffers to send	    */
+	int		*send_indices;	  /* List of handles to buffers	    */
+	int		*send_sizes;	  /* Lengths of data to send	    */
+	drm_dma_flags_t flags;		  /* Flags			    */
+	int		request_count;	  /* Number of buffers requested    */
+	int		request_size;	  /* Desired size for buffers	    */
+	int		*request_indices; /* Buffer information		    */
+	int		*request_sizes;
+	int		granted_count;	  /* Number of buffers granted	    */
+} drm_dma_t;
+
+typedef enum {
+	_DRM_CONTEXT_PRESERVED = 0x01,
+	_DRM_CONTEXT_2DONLY    = 0x02
+} drm_ctx_flags_t;
+
+typedef struct drm_ctx {
+	drm_context_t	handle;
+	drm_ctx_flags_t flags;
+} drm_ctx_t;
+
+typedef struct drm_ctx_res {
+	int		count;
+	drm_ctx_t	*contexts;
+} drm_ctx_res_t;
+
+typedef struct drm_draw {
+	drm_drawable_t	handle;
+} drm_draw_t;
+
+typedef struct drm_auth {
+	drm_magic_t	magic;
+} drm_auth_t;
+
+typedef struct drm_irq_busid {
+	int irq;
+	int busnum;
+	int devnum;
+	int funcnum;
+} drm_irq_busid_t;
+
+typedef struct drm_agp_mode {
+	unsigned long mode;
+} drm_agp_mode_t;
+
+				/* For drm_agp_alloc -- allocated a buffer */
+typedef struct drm_agp_buffer {
+	unsigned long size;	/* In bytes -- will round to page boundary */
+	unsigned long handle;	/* Used for BIND/UNBIND ioctls */
+	unsigned long type;     /* Type of memory to allocate  */
+        unsigned long physical; /* Physical used by i810       */
+} drm_agp_buffer_t;
+
+				/* For drm_agp_bind */
+typedef struct drm_agp_binding {
+	unsigned long handle;   /* From drm_agp_buffer */
+	unsigned long offset;	/* In bytes -- will round to page boundary */
+} drm_agp_binding_t;
+
+typedef struct drm_agp_info {
+	int            agp_version_major;
+	int            agp_version_minor;
+	unsigned long  mode;
+	unsigned long  aperture_base;  /* physical address */
+	unsigned long  aperture_size;  /* bytes */
+	unsigned long  memory_allowed; /* bytes */
+	unsigned long  memory_used;
+
+				/* PCI information */
+	unsigned short id_vendor;
+	unsigned short id_device;
+} drm_agp_info_t;
+
+#define DRM_IOCTL_BASE			'd'
+#define DRM_IO(nr)			_IO(DRM_IOCTL_BASE,nr)
+#define DRM_IOR(nr,size)		_IOR(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOW(nr,size)		_IOW(DRM_IOCTL_BASE,nr,size)
+#define DRM_IOWR(nr,size)		_IOWR(DRM_IOCTL_BASE,nr,size)
+
+
+#define DRM_IOCTL_VERSION		DRM_IOWR(0x00, drm_version_t)
+#define DRM_IOCTL_GET_UNIQUE		DRM_IOWR(0x01, drm_unique_t)
+#define DRM_IOCTL_GET_MAGIC		DRM_IOR( 0x02, drm_auth_t)
+#define DRM_IOCTL_IRQ_BUSID		DRM_IOWR(0x03, drm_irq_busid_t)
+
+#define DRM_IOCTL_SET_UNIQUE		DRM_IOW( 0x10, drm_unique_t)
+#define DRM_IOCTL_AUTH_MAGIC		DRM_IOW( 0x11, drm_auth_t)
+#define DRM_IOCTL_BLOCK			DRM_IOWR(0x12, drm_block_t)
+#define DRM_IOCTL_UNBLOCK		DRM_IOWR(0x13, drm_block_t)
+#define DRM_IOCTL_CONTROL		DRM_IOW( 0x14, drm_control_t)
+#define DRM_IOCTL_ADD_MAP		DRM_IOWR(0x15, drm_map_t)
+#define DRM_IOCTL_ADD_BUFS		DRM_IOWR(0x16, drm_buf_desc_t)
+#define DRM_IOCTL_MARK_BUFS		DRM_IOW( 0x17, drm_buf_desc_t)
+#define DRM_IOCTL_INFO_BUFS		DRM_IOWR(0x18, drm_buf_info_t)
+#define DRM_IOCTL_MAP_BUFS		DRM_IOWR(0x19, drm_buf_map_t)
+#define DRM_IOCTL_FREE_BUFS		DRM_IOW( 0x1a, drm_buf_free_t)
+
+#define DRM_IOCTL_ADD_CTX		DRM_IOWR(0x20, drm_ctx_t)
+#define DRM_IOCTL_RM_CTX		DRM_IOWR(0x21, drm_ctx_t)
+#define DRM_IOCTL_MOD_CTX		DRM_IOW( 0x22, drm_ctx_t)
+#define DRM_IOCTL_GET_CTX		DRM_IOWR(0x23, drm_ctx_t)
+#define DRM_IOCTL_SWITCH_CTX		DRM_IOW( 0x24, drm_ctx_t)
+#define DRM_IOCTL_NEW_CTX		DRM_IOW( 0x25, drm_ctx_t)
+#define DRM_IOCTL_RES_CTX		DRM_IOWR(0x26, drm_ctx_res_t)
+#define DRM_IOCTL_ADD_DRAW		DRM_IOWR(0x27, drm_draw_t)
+#define DRM_IOCTL_RM_DRAW		DRM_IOWR(0x28, drm_draw_t)
+#define DRM_IOCTL_DMA			DRM_IOWR(0x29, drm_dma_t)
+#define DRM_IOCTL_LOCK			DRM_IOW( 0x2a, drm_lock_t)
+#define DRM_IOCTL_UNLOCK		DRM_IOW( 0x2b, drm_lock_t)
+#define DRM_IOCTL_FINISH		DRM_IOW( 0x2c, drm_lock_t)
+
+#define DRM_IOCTL_AGP_ACQUIRE		DRM_IO(  0x30)
+#define DRM_IOCTL_AGP_RELEASE		DRM_IO(  0x31)
+#define DRM_IOCTL_AGP_ENABLE		DRM_IOW( 0x32, drm_agp_mode_t)
+#define DRM_IOCTL_AGP_INFO		DRM_IOR( 0x33, drm_agp_info_t)
+#define DRM_IOCTL_AGP_ALLOC		DRM_IOWR(0x34, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_FREE		DRM_IOW( 0x35, drm_agp_buffer_t)
+#define DRM_IOCTL_AGP_BIND		DRM_IOW( 0x36, drm_agp_binding_t)
+#define DRM_IOCTL_AGP_UNBIND		DRM_IOW( 0x37, drm_agp_binding_t)
+
+/* Mga specific ioctls */
+#define DRM_IOCTL_MGA_INIT		DRM_IOW( 0x40, drm_mga_init_t)
+#define DRM_IOCTL_MGA_SWAP		DRM_IOW( 0x41, drm_mga_swap_t)
+#define DRM_IOCTL_MGA_CLEAR		DRM_IOW( 0x42, drm_mga_clear_t)
+#define DRM_IOCTL_MGA_ILOAD		DRM_IOW( 0x43, drm_mga_iload_t)
+#define DRM_IOCTL_MGA_VERTEX		DRM_IOW( 0x44, drm_mga_vertex_t)
+#define DRM_IOCTL_MGA_FLUSH		DRM_IOW( 0x45, drm_lock_t )
+#define DRM_IOCTL_MGA_INDICES		DRM_IOW( 0x46, drm_mga_indices_t)
+#define DRM_IOCTL_MGA_BLIT		DRM_IOW( 0x47, drm_mga_blit_t)
+
+/* I810 specific ioctls */
+#define DRM_IOCTL_I810_INIT		DRM_IOW( 0x40, drm_i810_init_t)
+#define DRM_IOCTL_I810_VERTEX		DRM_IOW( 0x41, drm_i810_vertex_t)
+#define DRM_IOCTL_I810_CLEAR		DRM_IOW( 0x42, drm_i810_clear_t)
+#define DRM_IOCTL_I810_FLUSH		DRM_IO(  0x43)
+#define DRM_IOCTL_I810_GETAGE		DRM_IO(  0x44)
+#define DRM_IOCTL_I810_GETBUF		DRM_IOWR(0x45, drm_i810_dma_t)
+#define DRM_IOCTL_I810_SWAP		DRM_IO(  0x46)
+#define DRM_IOCTL_I810_COPY		DRM_IOW( 0x47, drm_i810_copy_t)
+#define DRM_IOCTL_I810_DOCOPY		DRM_IO(  0x48)
+
+/* Rage 128 specific ioctls */
+#define DRM_IOCTL_R128_INIT		DRM_IOW( 0x40, drm_r128_init_t)
+#define DRM_IOCTL_R128_CCE_START	DRM_IO(  0x41)
+#define DRM_IOCTL_R128_CCE_STOP		DRM_IOW( 0x42, drm_r128_cce_stop_t)
+#define DRM_IOCTL_R128_CCE_RESET	DRM_IO(  0x43)
+#define DRM_IOCTL_R128_CCE_IDLE		DRM_IO(  0x44)
+#define DRM_IOCTL_R128_RESET		DRM_IO(  0x46)
+#define DRM_IOCTL_R128_SWAP		DRM_IO(  0x47)
+#define DRM_IOCTL_R128_CLEAR		DRM_IOW( 0x48, drm_r128_clear_t)
+#define DRM_IOCTL_R128_VERTEX		DRM_IOW( 0x49, drm_r128_vertex_t)
+#define DRM_IOCTL_R128_INDICES		DRM_IOW( 0x4a, drm_r128_indices_t)
+#define DRM_IOCTL_R128_BLIT		DRM_IOW( 0x4b, drm_r128_blit_t)
+#define DRM_IOCTL_R128_DEPTH		DRM_IOW( 0x4c, drm_r128_depth_t)
+#define DRM_IOCTL_R128_STIPPLE		DRM_IOW( 0x4d, drm_r128_stipple_t)
+#define DRM_IOCTL_R128_PACKET		DRM_IOWR(0x4e, drm_r128_packet_t)
+
+/* Radeon specific ioctls */
+#define DRM_IOCTL_RADEON_CP_INIT	DRM_IOW( 0x40, drm_radeon_init_t)
+#define DRM_IOCTL_RADEON_CP_START	DRM_IO(  0x41)
+#define DRM_IOCTL_RADEON_CP_STOP	DRM_IOW( 0x42, drm_radeon_cp_stop_t)
+#define DRM_IOCTL_RADEON_CP_RESET	DRM_IO(  0x43)
+#define DRM_IOCTL_RADEON_CP_IDLE	DRM_IO(  0x44)
+#define DRM_IOCTL_RADEON_RESET		DRM_IO(  0x45)
+#define DRM_IOCTL_RADEON_FULLSCREEN	DRM_IOW( 0x46, drm_radeon_fullscreen_t)
+#define DRM_IOCTL_RADEON_SWAP		DRM_IO(  0x47)
+#define DRM_IOCTL_RADEON_CLEAR		DRM_IOW( 0x48, drm_radeon_clear_t)
+#define DRM_IOCTL_RADEON_VERTEX		DRM_IOW( 0x49, drm_radeon_vertex_t)
+#define DRM_IOCTL_RADEON_INDICES	DRM_IOW( 0x4a, drm_radeon_indices_t)
+#define DRM_IOCTL_RADEON_BLIT		DRM_IOW( 0x4b, drm_radeon_blit_t)
+#define DRM_IOCTL_RADEON_STIPPLE	DRM_IOW( 0x4c, drm_radeon_stipple_t)
+#define DRM_IOCTL_RADEON_INDIRECT	DRM_IOWR(0x4d, drm_radeon_indirect_t)
+
+#ifdef CONFIG_DRM40_SIS
+/* SiS specific ioctls */
+#define SIS_IOCTL_FB_ALLOC		DRM_IOWR(0x44, drm_sis_mem_t)
+#define SIS_IOCTL_FB_FREE		DRM_IOW( 0x45, drm_sis_mem_t)
+#define SIS_IOCTL_AGP_INIT		DRM_IOWR(0x53, drm_sis_agp_t)
+#define SIS_IOCTL_AGP_ALLOC		DRM_IOWR(0x54, drm_sis_mem_t)
+#define SIS_IOCTL_AGP_FREE		DRM_IOW( 0x55, drm_sis_mem_t)
+#define SIS_IOCTL_FLIP			DRM_IOW( 0x48, drm_sis_flip_t)
+#define SIS_IOCTL_FLIP_INIT		DRM_IO(  0x49)
+#define SIS_IOCTL_FLIP_FINAL		DRM_IO(  0x50)
+#endif
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/drmP.h linux/drivers/char/drm-4.0/drmP.h
--- linux.orig/drivers/char/drm-4.0/drmP.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/drmP.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,836 @@
+/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _DRM_P_H_
+#define _DRM_P_H_
+
+#ifdef __KERNEL__
+#ifdef __alpha__
+/* add include of current.h so that "current" is defined
+ * before static inline funcs in wait.h. Doing this so we
+ * can build the DRM (part of PI DRI). 4/21/2000 S + B */
+#include <asm/current.h>
+#endif /* __alpha__ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/major.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/file.h>
+#include <linux/pci.h>
+#include <linux/wrapper.h>
+#include <linux/version.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>	/* For (un)lock_kernel */
+#include <linux/mm.h>
+#ifdef __alpha__
+#include <asm/pgtable.h> /* For pte_wrprotect */
+#endif
+#include <asm/io.h>
+#include <asm/mman.h>
+#include <asm/uaccess.h>
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+#include <linux/types.h>
+#include <linux/agp_backend.h>
+#endif
+#if LINUX_VERSION_CODE >= 0x020100 /* KERNEL_VERSION(2,1,0) */
+#include <linux/tqueue.h>
+#include <linux/poll.h>
+#endif
+#if LINUX_VERSION_CODE < 0x020400
+#include "compat-pre24.h"
+#endif
+#include "drm.h"
+
+#define DRM_DEBUG_CODE 2	  /* Include debugging code (if > 1, then
+				     also include looping detection. */
+#define DRM_DMA_HISTOGRAM 1	  /* Make histogram of DMA latency. */
+
+#define DRM_HASH_SIZE	      16 /* Size of key hash table		  */
+#define DRM_KERNEL_CONTEXT    0	 /* Change drm_resctx if changed	  */
+#define DRM_RESERVED_CONTEXTS 1	 /* Change drm_resctx if changed	  */
+#define DRM_LOOPING_LIMIT     5000000
+#define DRM_BSZ		      1024 /* Buffer size for /dev/drm? output	  */
+#define DRM_TIME_SLICE	      (HZ/20)  /* Time slice for GLXContexts	  */
+#define DRM_LOCK_SLICE	      1	/* Time slice for lock, in jiffies	  */
+
+#define DRM_FLAG_DEBUG	  0x01
+#define DRM_FLAG_NOCTX	  0x02
+
+#define DRM_MEM_DMA	   0
+#define DRM_MEM_SAREA	   1
+#define DRM_MEM_DRIVER	   2
+#define DRM_MEM_MAGIC	   3
+#define DRM_MEM_IOCTLS	   4
+#define DRM_MEM_MAPS	   5
+#define DRM_MEM_VMAS	   6
+#define DRM_MEM_BUFS	   7
+#define DRM_MEM_SEGS	   8
+#define DRM_MEM_PAGES	   9
+#define DRM_MEM_FILES	  10
+#define DRM_MEM_QUEUES	  11
+#define DRM_MEM_CMDS	  12
+#define DRM_MEM_MAPPINGS  13
+#define DRM_MEM_BUFLISTS  14
+#define DRM_MEM_AGPLISTS  15
+#define DRM_MEM_TOTALAGP  16
+#define DRM_MEM_BOUNDAGP  17
+#define DRM_MEM_CTXBITMAP 18
+
+#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
+
+				/* Backward compatibility section */
+				/* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */
+#ifndef _PAGE_PWT
+#define _PAGE_PWT _PAGE_WT
+#endif
+				/* Wait queue declarations changed in 2.3.1 */
+#ifndef DECLARE_WAITQUEUE
+#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL }
+typedef struct wait_queue *wait_queue_head_t;
+#define init_waitqueue_head(q) *q = NULL;
+#endif
+
+				/* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */
+#ifndef _PAGE_PSE
+#define _PAGE_PSE _PAGE_4M
+#endif
+
+				/* vm_offset changed to vm_pgoff in 2.3.25 */
+#if LINUX_VERSION_CODE < 0x020319
+#define VM_OFFSET(vma) ((vma)->vm_offset)
+#else
+#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+#endif
+
+				/* *_nopage return values defined in 2.3.26 */
+#ifndef NOPAGE_SIGBUS
+#define NOPAGE_SIGBUS 0
+#endif
+#ifndef NOPAGE_OOM
+#define NOPAGE_OOM 0
+#endif
+
+				/* module_init/module_exit added in 2.3.13 */
+#ifndef module_init
+#define module_init(x)  int init_module(void) { return x(); }
+#endif
+#ifndef module_exit
+#define module_exit(x)  void cleanup_module(void) { x(); }
+#endif
+
+				/* Generic cmpxchg added in 2.3.x */
+#ifndef __HAVE_ARCH_CMPXCHG
+				/* Include this here so that driver can be
+                                   used with older kernels. */
+#if defined(__alpha__)
+static __inline__ unsigned long
+__cmpxchg_u32(volatile int *m, int old, int new)
+{
+	unsigned long prev, cmp;
+
+	__asm__ __volatile__(
+	"1:	ldl_l %0,%2\n"
+	"	cmpeq %0,%3,%1\n"
+	"	beq %1,2f\n"
+	"	mov %4,%1\n"
+	"	stl_c %1,%2\n"
+	"	beq %1,3f\n"
+	"2:	mb\n"
+	".subsection 2\n"
+	"3:	br 1b\n"
+	".previous"
+	: "=&r"(prev), "=&r"(cmp), "=m"(*m)
+	: "r"((long) old), "r"(new), "m"(*m));
+
+	return prev;
+}
+
+static __inline__ unsigned long
+__cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
+{
+	unsigned long prev, cmp;
+
+	__asm__ __volatile__(
+	"1:	ldq_l %0,%2\n"
+	"	cmpeq %0,%3,%1\n"
+	"	beq %1,2f\n"
+	"	mov %4,%1\n"
+	"	stq_c %1,%2\n"
+	"	beq %1,3f\n"
+	"2:	mb\n"
+	".subsection 2\n"
+	"3:	br 1b\n"
+	".previous"
+	: "=&r"(prev), "=&r"(cmp), "=m"(*m)
+	: "r"((long) old), "r"(new), "m"(*m));
+
+	return prev;
+}
+
+static __inline__ unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	switch (size) {
+		case 4:
+			return __cmpxchg_u32(ptr, old, new);
+		case 8:
+			return __cmpxchg_u64(ptr, old, new);
+	}
+	return old;
+}
+#define cmpxchg(ptr,o,n)						 \
+  ({									 \
+     __typeof__(*(ptr)) _o_ = (o);					 \
+     __typeof__(*(ptr)) _n_ = (n);					 \
+     (__typeof__(*(ptr))) __cmpxchg((ptr), (unsigned long)_o_,		 \
+				    (unsigned long)_n_, sizeof(*(ptr))); \
+  })
+
+#elif __i386__
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+				      unsigned long new, int size)
+{
+	unsigned long prev;
+	switch (size) {
+	case 1:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 2:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	case 4:
+		__asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2"
+				     : "=a"(prev)
+				     : "q"(new), "m"(*__xg(ptr)), "0"(old)
+				     : "memory");
+		return prev;
+	}
+	return old;
+}
+
+#define cmpxchg(ptr,o,n)						\
+  ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),		\
+				 (unsigned long)(n),sizeof(*(ptr))))
+#endif /* i386 & alpha */
+#endif
+
+				/* Macros to make printk easier */
+#define DRM_ERROR(fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg)
+#define DRM_MEM_ERROR(area, fmt, arg...) \
+	printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \
+	       drm_mem_stats[area].name , ##arg)
+#define DRM_INFO(fmt, arg...)  printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg)
+
+#if DRM_DEBUG_CODE
+#define DRM_DEBUG(fmt, arg...)						  \
+	do {								  \
+		if (drm_flags&DRM_FLAG_DEBUG)				  \
+			printk(KERN_DEBUG				  \
+			       "[" DRM_NAME ":" __FUNCTION__ "] " fmt ,	  \
+			       ##arg);					  \
+	} while (0)
+#else
+#define DRM_DEBUG(fmt, arg...)		 do { } while (0)
+#endif
+
+#define DRM_PROC_LIMIT (PAGE_SIZE-80)
+
+#define DRM_PROC_PRINT(fmt, arg...)	   \
+   len += sprintf(&buf[len], fmt , ##arg); \
+   if (len > DRM_PROC_LIMIT) return len;
+
+#define DRM_PROC_PRINT_RET(ret, fmt, arg...)	    \
+   len += sprintf(&buf[len], fmt , ##arg);	    \
+   if (len > DRM_PROC_LIMIT) { ret; return len; }
+
+				/* Internal types and structures */
+#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define DRM_MIN(a,b) ((a)<(b)?(a):(b))
+#define DRM_MAX(a,b) ((a)>(b)?(a):(b))
+
+#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
+#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
+#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
+
+typedef int drm_ioctl_t(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+typedef struct drm_ioctl_desc {
+	drm_ioctl_t	     *func;
+	int		     auth_needed;
+	int		     root_only;
+} drm_ioctl_desc_t;
+
+typedef struct drm_devstate {
+	pid_t		  owner;	/* X server pid holding x_lock */
+	
+} drm_devstate_t;
+
+typedef struct drm_magic_entry {
+	drm_magic_t	       magic;
+	struct drm_file	       *priv;
+	struct drm_magic_entry *next;
+} drm_magic_entry_t;
+
+typedef struct drm_magic_head {
+	struct drm_magic_entry *head;
+	struct drm_magic_entry *tail;
+} drm_magic_head_t;
+
+typedef struct drm_vma_entry {
+	struct vm_area_struct *vma;
+	struct drm_vma_entry  *next;
+	pid_t		      pid;
+} drm_vma_entry_t;
+
+typedef struct drm_buf {
+	int		  idx;	       /* Index into master buflist	     */
+	int		  total;       /* Buffer size			     */
+	int		  order;       /* log-base-2(total)		     */
+	int		  used;	       /* Amount of buffer in use (for DMA)  */
+	unsigned long	  offset;      /* Byte offset (used internally)	     */
+	void		  *address;    /* Address of buffer		     */
+	unsigned long	  bus_address; /* Bus address of buffer		     */
+	struct drm_buf	  *next;       /* Kernel-only: used for free list    */
+	__volatile__ int  waiting;     /* On kernel DMA queue		     */
+	__volatile__ int  pending;     /* On hardware DMA queue		     */
+	wait_queue_head_t dma_wait;    /* Processes waiting		     */
+	pid_t		  pid;	       /* PID of holding process	     */
+	int		  context;     /* Kernel queue for this buffer	     */
+	int		  while_locked;/* Dispatch this buffer while locked  */
+	enum {
+		DRM_LIST_NONE	 = 0,
+		DRM_LIST_FREE	 = 1,
+		DRM_LIST_WAIT	 = 2,
+		DRM_LIST_PEND	 = 3,
+		DRM_LIST_PRIO	 = 4,
+		DRM_LIST_RECLAIM = 5
+	}		  list;	       /* Which list we're on		     */
+
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  time_queued;	   /* Queued to kernel DMA queue     */
+	cycles_t	  time_dispatched; /* Dispatched to hardware	     */
+	cycles_t	  time_completed;  /* Completed by hardware	     */
+	cycles_t	  time_freed;	   /* Back on freelist		     */
+#endif
+
+	int		  dev_priv_size; /* Size of buffer private stoarge   */
+	void		  *dev_private;  /* Per-buffer private storage       */
+} drm_buf_t;
+
+#if DRM_DMA_HISTOGRAM
+#define DRM_DMA_HISTOGRAM_SLOTS		  9
+#define DRM_DMA_HISTOGRAM_INITIAL	 10
+#define DRM_DMA_HISTOGRAM_NEXT(current)	 ((current)*10)
+typedef struct drm_histogram {
+	atomic_t	  total;
+	
+	atomic_t	  queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS];
+	
+	atomic_t	  dma[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  schedule[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  ctx[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lacq[DRM_DMA_HISTOGRAM_SLOTS];
+	atomic_t	  lhld[DRM_DMA_HISTOGRAM_SLOTS];
+} drm_histogram_t;
+#endif
+
+				/* bufs is one longer than it has to be */
+typedef struct drm_waitlist {
+	int		  count;	/* Number of possible buffers	   */
+	drm_buf_t	  **bufs;	/* List of pointers to buffers	   */
+	drm_buf_t	  **rp;		/* Read pointer			   */
+	drm_buf_t	  **wp;		/* Write pointer		   */
+	drm_buf_t	  **end;	/* End pointer			   */
+	spinlock_t	  read_lock;
+	spinlock_t	  write_lock;
+} drm_waitlist_t;
+
+typedef struct drm_freelist {
+	int		  initialized; /* Freelist in use		   */
+	atomic_t	  count;       /* Number of free buffers	   */
+	drm_buf_t	  *next;       /* End pointer			   */
+	
+	wait_queue_head_t waiting;     /* Processes waiting on free bufs   */
+	int		  low_mark;    /* Low water mark		   */
+	int		  high_mark;   /* High water mark		   */
+	atomic_t	  wfh;	       /* If waiting for high mark	   */
+	spinlock_t        lock;
+} drm_freelist_t;
+
+typedef struct drm_buf_entry {
+	int		  buf_size;
+	int		  buf_count;
+	drm_buf_t	  *buflist;
+	int		  seg_count;
+	int		  page_order;
+	unsigned long	  *seglist;
+
+	drm_freelist_t	  freelist;
+} drm_buf_entry_t;
+
+typedef struct drm_hw_lock {
+	__volatile__ unsigned int lock;
+	char			  padding[60]; /* Pad to cache line */
+} drm_hw_lock_t;
+
+typedef struct drm_file {
+	int		  authenticated;
+	int		  minor;
+	pid_t		  pid;
+	uid_t		  uid;
+	drm_magic_t	  magic;
+	unsigned long	  ioctl_count;
+	struct drm_file	  *next;
+	struct drm_file	  *prev;
+	struct drm_device *dev;
+	int 		  remove_auth_on_close;
+} drm_file_t;
+
+
+typedef struct drm_queue {
+	atomic_t	  use_count;	/* Outstanding uses (+1)	    */
+	atomic_t	  finalization;	/* Finalization in progress	    */
+	atomic_t	  block_count;	/* Count of processes waiting	    */
+	atomic_t	  block_read;	/* Queue blocked for reads	    */
+	wait_queue_head_t read_queue;	/* Processes waiting on block_read  */
+	atomic_t	  block_write;	/* Queue blocked for writes	    */
+	wait_queue_head_t write_queue;	/* Processes waiting on block_write */
+	atomic_t	  total_queued;	/* Total queued statistic	    */
+	atomic_t	  total_flushed;/* Total flushes statistic	    */
+	atomic_t	  total_locks;	/* Total locks statistics	    */
+	drm_ctx_flags_t	  flags;	/* Context preserving and 2D-only   */
+	drm_waitlist_t	  waitlist;	/* Pending buffers		    */
+	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+} drm_queue_t;
+
+typedef struct drm_lock_data {
+	drm_hw_lock_t	  *hw_lock;	/* Hardware lock		   */
+	pid_t		  pid;		/* PID of lock holder (0=kernel)   */
+	wait_queue_head_t lock_queue;	/* Queue of blocked processes	   */
+	unsigned long	  lock_time;	/* Time of last lock in jiffies	   */
+} drm_lock_data_t;
+
+typedef struct drm_device_dma {
+				/* Performance Counters */
+	atomic_t	  total_prio;	/* Total DRM_DMA_PRIORITY	   */
+	atomic_t	  total_bytes;	/* Total bytes DMA'd		   */
+	atomic_t	  total_dmas;	/* Total DMA buffers dispatched	   */
+	
+	atomic_t	  total_missed_dma;  /* Missed drm_do_dma	    */
+	atomic_t	  total_missed_lock; /* Missed lock in drm_do_dma   */
+	atomic_t	  total_missed_free; /* Missed drm_free_this_buffer */
+	atomic_t	  total_missed_sched;/* Missed drm_dma_schedule	    */
+
+	atomic_t	  total_tried;	/* Tried next_buffer		    */
+	atomic_t	  total_hit;	/* Sent next_buffer		    */
+	atomic_t	  total_lost;	/* Lost interrupt		    */
+
+	drm_buf_entry_t	  bufs[DRM_MAX_ORDER+1];
+	int		  buf_count;
+	drm_buf_t	  **buflist;	/* Vector of pointers info bufs	   */
+	int		  seg_count; 
+	int		  page_count;
+	unsigned long	  *pagelist;
+	unsigned long	  byte_count;
+	enum {
+	   _DRM_DMA_USE_AGP = 0x01
+	} flags;
+
+				/* DMA support */
+	drm_buf_t	  *this_buffer;	/* Buffer being sent		   */
+	drm_buf_t	  *next_buffer; /* Selected buffer to send	   */
+	drm_queue_t	  *next_queue;	/* Queue from which buffer selected*/
+	wait_queue_head_t waiting;	/* Processes waiting on free bufs  */
+} drm_device_dma_t;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+typedef struct drm_agp_mem {
+	unsigned long      handle;
+	agp_memory         *memory;
+	unsigned long      bound; /* address */
+	int                pages;
+	struct drm_agp_mem *prev;
+	struct drm_agp_mem *next;
+} drm_agp_mem_t;
+
+typedef struct drm_agp_head {
+	agp_kern_info      agp_info;
+	const char         *chipset;
+	drm_agp_mem_t      *memory;
+	unsigned long      mode;
+	int                enabled;
+	int                acquired;
+	unsigned long      base;
+   	int 		   agp_mtrr;
+} drm_agp_head_t;
+#endif
+
+typedef struct drm_sigdata {
+	int           context;
+	drm_hw_lock_t *lock;
+} drm_sigdata_t;
+
+typedef struct drm_device {
+	const char	  *name;	/* Simple driver name		   */
+	char		  *unique;	/* Unique identifier: e.g., busid  */
+	int		  unique_len;	/* Length of unique field	   */
+	dev_t		  device;	/* Device number for mknod	   */
+	char		  *devname;	/* For /proc/interrupts		   */
+	
+	int		  blocked;	/* Blocked due to VC switch?	   */
+	struct proc_dir_entry *root;	/* Root for this device's entries  */
+
+				/* Locks */
+	spinlock_t	  count_lock;	/* For inuse, open_count, buf_use  */
+	struct semaphore  struct_sem;	/* For others			   */
+
+				/* Usage Counters */
+	int		  open_count;	/* Outstanding files open	   */
+	atomic_t	  ioctl_count;	/* Outstanding IOCTLs pending	   */
+	atomic_t	  vma_count;	/* Outstanding vma areas open	   */
+	int		  buf_use;	/* Buffers in use -- cannot alloc  */
+	atomic_t	  buf_alloc;	/* Buffer allocation in progress   */
+
+				/* Performance Counters */
+	atomic_t	  total_open;
+	atomic_t	  total_close;
+	atomic_t	  total_ioctl;
+	atomic_t	  total_irq;	/* Total interruptions		   */
+	atomic_t	  total_ctx;	/* Total context switches	   */
+	
+	atomic_t	  total_locks;
+	atomic_t	  total_unlocks;
+	atomic_t	  total_contends;
+	atomic_t	  total_sleeps;
+
+				/* Authentication */
+	drm_file_t	  *file_first;
+	drm_file_t	  *file_last;
+	drm_magic_head_t  magiclist[DRM_HASH_SIZE];
+
+				/* Memory management */
+	drm_map_t	  **maplist;	/* Vector of pointers to regions   */
+	int		  map_count;	/* Number of mappable regions	   */
+
+	drm_vma_entry_t	  *vmalist;	/* List of vmas (for debugging)	   */
+	drm_lock_data_t	  lock;		/* Information on hardware lock	   */
+
+				/* DMA queues (contexts) */
+	int		  queue_count;	/* Number of active DMA queues	   */
+	int		  queue_reserved; /* Number of reserved DMA queues */
+	int		  queue_slots;	/* Actual length of queuelist	   */
+	drm_queue_t	  **queuelist;	/* Vector of pointers to DMA queues */
+	drm_device_dma_t  *dma;		/* Optional pointer for DMA support */
+
+				/* Context support */
+	int		  irq;		/* Interrupt used by board	   */
+	__volatile__ long context_flag;	/* Context swapping flag	   */
+	__volatile__ long interrupt_flag; /* Interruption handler flag	   */
+	__volatile__ long dma_flag;	/* DMA dispatch flag		   */
+	struct timer_list timer;	/* Timer for delaying ctx switch   */
+	wait_queue_head_t context_wait; /* Processes waiting on ctx switch */
+	int		  last_checked;	/* Last context checked for DMA	   */
+	int		  last_context;	/* Last current context		   */
+	unsigned long	  last_switch;	/* jiffies at last context switch  */
+	struct tq_struct  tq;
+	cycles_t	  ctx_start;
+	cycles_t	  lck_start;
+#if DRM_DMA_HISTOGRAM
+	drm_histogram_t	  histo;
+#endif
+	
+				/* Callback to X server for context switch
+				   and for heavy-handed reset. */
+	char		  buf[DRM_BSZ]; /* Output buffer		   */
+	char		  *buf_rp;	/* Read pointer			   */
+	char		  *buf_wp;	/* Write pointer		   */
+	char		  *buf_end;	/* End pointer			   */
+	struct fasync_struct *buf_async;/* Processes waiting for SIGIO	   */
+	wait_queue_head_t buf_readers;	/* Processes waiting to read	   */
+	wait_queue_head_t buf_writers;	/* Processes waiting to ctx switch */
+	
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	drm_agp_head_t    *agp;
+#endif
+	unsigned long     *ctx_bitmap;
+	void		  *dev_private;
+	drm_sigdata_t     sigdata; /* For block_all_signals */
+	sigset_t          sigmask;
+} drm_device_t;
+
+
+				/* Internal function definitions */
+
+				/* Misc. support (init.c) */
+extern int	     drm_flags;
+extern void	     drm_parse_options(char *s);
+extern int           drm_cpu_valid(void);
+
+
+				/* Device support (fops.c) */
+extern int	     drm_open_helper(struct inode *inode, struct file *filp,
+				     drm_device_t *dev);
+extern int	     drm_flush(struct file *filp);
+extern int	     drm_release(struct inode *inode, struct file *filp);
+extern int	     drm_fasync(int fd, struct file *filp, int on);
+extern ssize_t	     drm_read(struct file *filp, char *buf, size_t count,
+			      loff_t *off);
+extern int	     drm_write_string(drm_device_t *dev, const char *s);
+extern unsigned int  drm_poll(struct file *filp, struct poll_table_struct *wait);
+
+				/* Mapping support (vm.c) */
+#if LINUX_VERSION_CODE < 0x020317
+extern unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+				   unsigned long address,
+				   int write_access);
+extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+extern unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+					    unsigned long address,
+					    int write_access);
+extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				       unsigned long address,
+				       int write_access);
+#else
+				/* Return type changed in 2.3.23 */
+extern struct page *drm_vm_nopage(struct vm_area_struct *vma,
+				  unsigned long address,
+				  int write_access);
+extern struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+extern struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+					   unsigned long address,
+					   int write_access);
+extern struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+				      unsigned long address,
+				      int write_access);
+#endif
+extern void	     drm_vm_open(struct vm_area_struct *vma);
+extern void	     drm_vm_close(struct vm_area_struct *vma);
+extern int	     drm_mmap_dma(struct file *filp,
+				  struct vm_area_struct *vma);
+extern int	     drm_mmap(struct file *filp, struct vm_area_struct *vma);
+
+
+				/* Proc support (proc.c) */
+extern int	     drm_proc_init(drm_device_t *dev);
+extern int	     drm_proc_cleanup(void);
+
+				/* Memory management support (memory.c) */
+extern void	     drm_mem_init(void);
+extern int	     drm_mem_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+extern void	     *drm_alloc(size_t size, int area);
+extern void	     *drm_realloc(void *oldpt, size_t oldsize, size_t size,
+				  int area);
+extern char	     *drm_strdup(const char *s, int area);
+extern void	     drm_strfree(const char *s, int area);
+extern void	     drm_free(void *pt, size_t size, int area);
+extern unsigned long drm_alloc_pages(int order, int area);
+extern void	     drm_free_pages(unsigned long address, int order,
+				    int area);
+extern void	     *drm_ioremap(unsigned long offset, unsigned long size);
+extern void	     drm_ioremapfree(void *pt, unsigned long size);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+extern agp_memory    *drm_alloc_agp(int pages, u32 type);
+extern int           drm_free_agp(agp_memory *handle, int pages);
+extern int           drm_bind_agp(agp_memory *handle, unsigned int start);
+extern int           drm_unbind_agp(agp_memory *handle);
+#endif
+
+
+				/* Buffer management support (bufs.c) */
+extern int	     drm_order(unsigned long size);
+extern int	     drm_addmap(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_infobufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_markbufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_freebufs(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_mapbufs(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+
+
+				/* Buffer list management support (lists.c) */
+extern int	     drm_waitlist_create(drm_waitlist_t *bl, int count);
+extern int	     drm_waitlist_destroy(drm_waitlist_t *bl);
+extern int	     drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf);
+extern drm_buf_t     *drm_waitlist_get(drm_waitlist_t *bl);
+
+extern int	     drm_freelist_create(drm_freelist_t *bl, int count);
+extern int	     drm_freelist_destroy(drm_freelist_t *bl);
+extern int	     drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl,
+				      drm_buf_t *buf);
+extern drm_buf_t     *drm_freelist_get(drm_freelist_t *bl, int block);
+
+				/* DMA support (gen_dma.c) */
+extern void	     drm_dma_setup(drm_device_t *dev);
+extern void	     drm_dma_takedown(drm_device_t *dev);
+extern void	     drm_free_buffer(drm_device_t *dev, drm_buf_t *buf);
+extern void	     drm_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int	     drm_context_switch(drm_device_t *dev, int old, int new);
+extern int	     drm_context_switch_complete(drm_device_t *dev, int new);
+extern void	     drm_clear_next_buffer(drm_device_t *dev);
+extern int	     drm_select_queue(drm_device_t *dev,
+				      void (*wrapper)(unsigned long));
+extern int	     drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma);
+extern int	     drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma);
+#if DRM_DMA_HISTOGRAM
+extern int	     drm_histogram_slot(unsigned long count);
+extern void	     drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf);
+#endif
+
+
+				/* Misc. IOCTL support (ioctl.c) */
+extern int	     drm_irq_busid(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_getunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_setunique(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Context IOCTL support (context.c) */
+extern int	     drm_resctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_addctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_modctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_getctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_switchctx(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int	     drm_newctx(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_rmctx(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+
+
+				/* Drawable IOCTL support (drawable.c) */
+extern int	     drm_adddraw(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_rmdraw(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+
+
+				/* Authentication IOCTL support (auth.c) */
+extern int	     drm_add_magic(drm_device_t *dev, drm_file_t *priv,
+				   drm_magic_t magic);
+extern int	     drm_remove_magic(drm_device_t *dev, drm_magic_t magic);
+extern int	     drm_getmagic(struct inode *inode, struct file *filp,
+				  unsigned int cmd, unsigned long arg);
+extern int	     drm_authmagic(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+
+
+				/* Locking IOCTL support (lock.c) */
+extern int	     drm_block(struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg);
+extern int	     drm_unblock(struct inode *inode, struct file *filp,
+				 unsigned int cmd, unsigned long arg);
+extern int	     drm_lock_take(__volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_lock_transfer(drm_device_t *dev,
+				       __volatile__ unsigned int *lock,
+				       unsigned int context);
+extern int	     drm_lock_free(drm_device_t *dev,
+				   __volatile__ unsigned int *lock,
+				   unsigned int context);
+extern int	     drm_finish(struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg);
+extern int	     drm_flush_unblock(drm_device_t *dev, int context,
+				       drm_lock_flags_t flags);
+extern int	     drm_flush_block_and_flush(drm_device_t *dev, int context,
+					       drm_lock_flags_t flags);
+extern int           drm_notifier(void *priv);
+
+				/* Context Bitmap support (ctxbitmap.c) */
+extern int	     drm_ctxbitmap_init(drm_device_t *dev);
+extern void	     drm_ctxbitmap_cleanup(drm_device_t *dev);
+extern int	     drm_ctxbitmap_next(drm_device_t *dev);
+extern void	     drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* AGP/GART support (agpsupport.c) */
+extern drm_agp_head_t *drm_agp_init(void);
+extern void           drm_agp_uninit(void);
+extern int            drm_agp_acquire(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern void           _drm_agp_release(void);
+extern int            drm_agp_release(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern int            drm_agp_enable(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_info(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_alloc(struct inode *inode, struct file *filp,
+				    unsigned int cmd, unsigned long arg);
+extern int            drm_agp_free(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern int            drm_agp_unbind(struct inode *inode, struct file *filp,
+				     unsigned int cmd, unsigned long arg);
+extern int            drm_agp_bind(struct inode *inode, struct file *filp,
+				   unsigned int cmd, unsigned long arg);
+extern agp_memory     *drm_agp_allocate_memory(size_t pages, u32 type);
+extern int            drm_agp_free_memory(agp_memory *handle);
+extern int            drm_agp_bind_memory(agp_memory *handle, off_t start);
+extern int            drm_agp_unbind_memory(agp_memory *handle);
+#endif
+#endif
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/ffb_context.c linux/drivers/char/drm-4.0/ffb_context.c
--- linux.orig/drivers/char/drm-4.0/ffb_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/ffb_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,540 @@
+/* $Id: ffb_context.c,v 1.4 2000/08/29 07:01:55 davem Exp $
+ * ffb_context.c: Creator/Creator3D DRI/DRM context switching.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ *
+ * Almost entirely stolen from tdfx_context.c, see there
+ * for authors.
+ */
+
+#include <linux/sched.h>
+#include <asm/upa.h>
+
+#include "drmP.h"
+
+#include "ffb_drv.h"
+
+static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+	int i;
+
+	for (i = 0; i < FFB_MAX_CTXS; i++) {
+		if (fpriv->hw_state[i] == NULL)
+			break;
+	}
+	if (i == FFB_MAX_CTXS)
+		return -1;
+
+	fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL);
+	if (fpriv->hw_state[i] == NULL)
+		return -1;
+
+	fpriv->hw_state[i]->is_2d_only = is_2d_only;
+
+	/* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */
+	return i + 1;
+}
+
+static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		ctx->drawop = upa_readl(&ffb->drawop);
+		ctx->ppc = upa_readl(&ffb->ppc);
+		ctx->wid = upa_readl(&ffb->wid);
+		ctx->fg = upa_readl(&ffb->fg);
+		ctx->bg = upa_readl(&ffb->bg);
+		ctx->xclip = upa_readl(&ffb->xclip);
+		ctx->fbc = upa_readl(&ffb->fbc);
+		ctx->rop = upa_readl(&ffb->rop);
+		ctx->cmp = upa_readl(&ffb->cmp);
+		ctx->matchab = upa_readl(&ffb->matchab);
+		ctx->magnab = upa_readl(&ffb->magnab);
+		ctx->pmask = upa_readl(&ffb->pmask);
+		ctx->xpmask = upa_readl(&ffb->xpmask);
+		ctx->lpat = upa_readl(&ffb->lpat);
+		ctx->fontxy = upa_readl(&ffb->fontxy);
+		ctx->fontw = upa_readl(&ffb->fontw);
+		ctx->fontinc = upa_readl(&ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+
+		for (i = 0; i < 32; i++)
+			ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+		ctx->ucsr = upa_readl(&ffb->ucsr);
+		return;
+	}
+
+	/* Fetch drawop. */
+	ctx->drawop = upa_readl(&ffb->drawop);
+
+	/* If we were saving the vertex registers, this is where
+	 * we would do it.  We would save 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Capture rendering attributes. */
+
+	ctx->ppc = upa_readl(&ffb->ppc);		/* Pixel Processor Control */
+	ctx->wid = upa_readl(&ffb->wid);		/* Current WID */
+	ctx->fg = upa_readl(&ffb->fg);			/* Constant FG color */
+	ctx->bg = upa_readl(&ffb->bg);			/* Constant BG color */
+	ctx->consty = upa_readl(&ffb->consty);		/* Constant Y */
+	ctx->constz = upa_readl(&ffb->constz);		/* Constant Z */
+	ctx->xclip = upa_readl(&ffb->xclip);		/* X plane clip */
+	ctx->dcss = upa_readl(&ffb->dcss);		/* Depth Cue Scale Slope */
+	ctx->vclipmin = upa_readl(&ffb->vclipmin);	/* Primary XY clip, minimum */
+	ctx->vclipmax = upa_readl(&ffb->vclipmax);	/* Primary XY clip, maximum */
+	ctx->vclipzmin = upa_readl(&ffb->vclipzmin);	/* Primary Z clip, minimum */
+	ctx->vclipzmax = upa_readl(&ffb->vclipzmax);	/* Primary Z clip, maximum */
+	ctx->dcsf = upa_readl(&ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	ctx->dcsb = upa_readl(&ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	ctx->dczf = upa_readl(&ffb->dczf);		/* Depth Cue Scale Z Front */
+	ctx->dczb = upa_readl(&ffb->dczb);		/* Depth Cue Scale Z Back */
+	ctx->blendc = upa_readl(&ffb->blendc);		/* Alpha Blend Control */
+	ctx->blendc1 = upa_readl(&ffb->blendc1);	/* Alpha Blend Color 1 */
+	ctx->blendc2 = upa_readl(&ffb->blendc2);	/* Alpha Blend Color 2 */
+	ctx->fbc = upa_readl(&ffb->fbc);		/* Frame Buffer Control */
+	ctx->rop = upa_readl(&ffb->rop);		/* Raster Operation */
+	ctx->cmp = upa_readl(&ffb->cmp);		/* Compare Controls */
+	ctx->matchab = upa_readl(&ffb->matchab);	/* Buffer A/B Match Ops */
+	ctx->matchc = upa_readl(&ffb->matchc);		/* Buffer C Match Ops */
+	ctx->magnab = upa_readl(&ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	ctx->magnc = upa_readl(&ffb->magnc);		/* Buffer C Magnitude Ops */
+	ctx->pmask = upa_readl(&ffb->pmask);		/* RGB Plane Mask */
+	ctx->xpmask = upa_readl(&ffb->xpmask);		/* X Plane Mask */
+	ctx->ypmask = upa_readl(&ffb->ypmask);		/* Y Plane Mask */
+	ctx->zpmask = upa_readl(&ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min);
+	ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max);
+	ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min);
+	ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max);
+	ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min);
+	ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max);
+	ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min);
+	ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max);
+
+	ctx->lpat = upa_readl(&ffb->lpat);		/* Line Pattern */
+	ctx->fontxy = upa_readl(&ffb->fontxy);		/* XY Font Coordinate */
+	ctx->fontw = upa_readl(&ffb->fontw);		/* Font Width */
+	ctx->fontinc = upa_readl(&ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		ctx->dcss1 = upa_readl(&ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		ctx->dcss2 = upa_readl(&ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		ctx->dcss2 = upa_readl(&ffb->dcss3);	/* Depth Cue Scale Slope 3 */
+		ctx->dcs2  = upa_readl(&ffb->dcs2);	/* Depth Cue Scale 2 */
+		ctx->dcs3  = upa_readl(&ffb->dcs3);	/* Depth Cue Scale 3 */
+		ctx->dcs4  = upa_readl(&ffb->dcs4);	/* Depth Cue Scale 4 */
+		ctx->dcd2  = upa_readl(&ffb->dcd2);	/* Depth Cue Depth 2 */
+		ctx->dcd3  = upa_readl(&ffb->dcd3);	/* Depth Cue Depth 3 */
+		ctx->dcd4  = upa_readl(&ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			ctx->stencil = upa_readl(&ffb->stencil);
+			ctx->stencilctl = upa_readl(&ffb->stencilctl);
+		}
+	}
+
+	/* Save the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register. */
+	ctx->ucsr = upa_readl(&ffb->ucsr);
+}
+
+static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx)
+{
+	ffb_fbcPtr ffb = fpriv->regs;
+	struct ffb_hw_context *ctx;
+	int i;
+
+	ctx = fpriv->hw_state[idx - 1];
+	if (idx == 0 || ctx == NULL)
+		return;
+
+	if (ctx->is_2d_only) {
+		/* 2D applications only care about certain pieces
+		 * of state.
+		 */
+		upa_writel(ctx->drawop, &ffb->drawop);
+
+		/* If we were restoring the vertex registers, this is where
+		 * we would do it.  We would restore 32 32-bit words starting
+		 * at ffb->suvtx.
+		 */
+
+		upa_writel(ctx->ppc, &ffb->ppc);
+		upa_writel(ctx->wid, &ffb->wid);
+		upa_writel(ctx->fg,  &ffb->fg);
+		upa_writel(ctx->bg, &ffb->bg);
+		upa_writel(ctx->xclip, &ffb->xclip);
+		upa_writel(ctx->fbc, &ffb->fbc);
+		upa_writel(ctx->rop, &ffb->rop);
+		upa_writel(ctx->cmp, &ffb->cmp);
+		upa_writel(ctx->matchab, &ffb->matchab);
+		upa_writel(ctx->magnab, &ffb->magnab);
+		upa_writel(ctx->pmask, &ffb->pmask);
+		upa_writel(ctx->xpmask, &ffb->xpmask);
+		upa_writel(ctx->lpat, &ffb->lpat);
+		upa_writel(ctx->fontxy, &ffb->fontxy);
+		upa_writel(ctx->fontw, &ffb->fontw);
+		upa_writel(ctx->fontinc, &ffb->fontinc);
+
+		/* stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+
+		for (i = 0; i < 32; i++)
+			upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+		upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+		return;
+	}
+
+	/* Restore drawop. */
+	upa_writel(ctx->drawop, &ffb->drawop);
+
+	/* If we were restoring the vertex registers, this is where
+	 * we would do it.  We would restore 32 32-bit words starting
+	 * at ffb->suvtx.
+	 */
+
+	/* Restore rendering attributes. */
+
+	upa_writel(ctx->ppc, &ffb->ppc);		/* Pixel Processor Control */
+	upa_writel(ctx->wid, &ffb->wid);		/* Current WID */
+	upa_writel(ctx->fg, &ffb->fg);			/* Constant FG color */
+	upa_writel(ctx->bg, &ffb->bg);			/* Constant BG color */
+	upa_writel(ctx->consty, &ffb->consty);		/* Constant Y */
+	upa_writel(ctx->constz, &ffb->constz);		/* Constant Z */
+	upa_writel(ctx->xclip, &ffb->xclip);		/* X plane clip */
+	upa_writel(ctx->dcss, &ffb->dcss);		/* Depth Cue Scale Slope */
+	upa_writel(ctx->vclipmin, &ffb->vclipmin);	/* Primary XY clip, minimum */
+	upa_writel(ctx->vclipmax, &ffb->vclipmax);	/* Primary XY clip, maximum */
+	upa_writel(ctx->vclipzmin, &ffb->vclipzmin);	/* Primary Z clip, minimum */
+	upa_writel(ctx->vclipzmax, &ffb->vclipzmax);	/* Primary Z clip, maximum */
+	upa_writel(ctx->dcsf, &ffb->dcsf);		/* Depth Cue Scale Front Bound */
+	upa_writel(ctx->dcsb, &ffb->dcsb);		/* Depth Cue Scale Back Bound */
+	upa_writel(ctx->dczf, &ffb->dczf);		/* Depth Cue Scale Z Front */
+	upa_writel(ctx->dczb, &ffb->dczb);		/* Depth Cue Scale Z Back */
+	upa_writel(ctx->blendc, &ffb->blendc);		/* Alpha Blend Control */
+	upa_writel(ctx->blendc1, &ffb->blendc1);	/* Alpha Blend Color 1 */
+	upa_writel(ctx->blendc2, &ffb->blendc2);	/* Alpha Blend Color 2 */
+	upa_writel(ctx->fbc, &ffb->fbc);		/* Frame Buffer Control */
+	upa_writel(ctx->rop, &ffb->rop);		/* Raster Operation */
+	upa_writel(ctx->cmp, &ffb->cmp);		/* Compare Controls */
+	upa_writel(ctx->matchab, &ffb->matchab);	/* Buffer A/B Match Ops */
+	upa_writel(ctx->matchc, &ffb->matchc);		/* Buffer C Match Ops */
+	upa_writel(ctx->magnab, &ffb->magnab);		/* Buffer A/B Magnitude Ops */
+	upa_writel(ctx->magnc, &ffb->magnc);		/* Buffer C Magnitude Ops */
+	upa_writel(ctx->pmask, &ffb->pmask);		/* RGB Plane Mask */
+	upa_writel(ctx->xpmask, &ffb->xpmask);		/* X Plane Mask */
+	upa_writel(ctx->ypmask, &ffb->ypmask);		/* Y Plane Mask */
+	upa_writel(ctx->zpmask, &ffb->zpmask);		/* Z Plane Mask */
+
+	/* Auxiliary Clips. */
+	upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min);
+	upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max);
+	upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min);
+	upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max);
+	upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min);
+	upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max);
+	upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min);
+	upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max);
+
+	upa_writel(ctx->lpat, &ffb->lpat);		/* Line Pattern */
+	upa_writel(ctx->fontxy, &ffb->fontxy);		/* XY Font Coordinate */
+	upa_writel(ctx->fontw, &ffb->fontw);		/* Font Width */
+	upa_writel(ctx->fontinc, &ffb->fontinc);	/* Font X/Y Increment */
+
+	/* These registers/features only exist on FFB2 and later chips. */
+	if (fpriv->ffb_type >= ffb2_prototype) {
+		upa_writel(ctx->dcss1, &ffb->dcss1);	/* Depth Cue Scale Slope 1 */
+		upa_writel(ctx->dcss2, &ffb->dcss2);	/* Depth Cue Scale Slope 2 */
+		upa_writel(ctx->dcss3, &ffb->dcss2);	/* Depth Cue Scale Slope 3 */
+		upa_writel(ctx->dcs2, &ffb->dcs2);	/* Depth Cue Scale 2 */
+		upa_writel(ctx->dcs3, &ffb->dcs3);	/* Depth Cue Scale 3 */
+		upa_writel(ctx->dcs4, &ffb->dcs4);	/* Depth Cue Scale 4 */
+		upa_writel(ctx->dcd2, &ffb->dcd2);	/* Depth Cue Depth 2 */
+		upa_writel(ctx->dcd3, &ffb->dcd3);	/* Depth Cue Depth 3 */
+		upa_writel(ctx->dcd4, &ffb->dcd4);	/* Depth Cue Depth 4 */
+
+		/* And stencil/stencilctl only exists on FFB2+ and later
+		 * due to the introduction of 3DRAM-III.
+		 */
+		if (fpriv->ffb_type == ffb2_vertical_plus ||
+		    fpriv->ffb_type == ffb2_horizontal_plus) {
+			/* Unfortunately, there is a hardware bug on
+			 * the FFB2+ chips which prevents a normal write
+			 * to the stencil control register from working
+			 * as it should.
+			 *
+			 * The state controlled by the FFB stencilctl register
+			 * really gets transferred to the per-buffer instances
+			 * of the stencilctl register in the 3DRAM chips.
+			 *
+			 * The bug is that FFB does not update buffer C correctly,
+			 * so we have to do it by hand for them.
+			 */
+
+			/* This will update buffers A and B. */
+			upa_writel(ctx->stencil, &ffb->stencil);
+			upa_writel(ctx->stencilctl, &ffb->stencilctl);
+
+			/* Force FFB to use buffer C 3dram regs. */
+			upa_writel(0x80000000, &ffb->fbc);
+			upa_writel((ctx->stencilctl | 0x80000),
+				   &ffb->rawstencilctl);
+
+			/* Now restore the correct FBC controls. */
+			upa_writel(ctx->fbc, &ffb->fbc);
+		}
+	}
+
+	/* Restore the 32x32 area pattern. */
+	for (i = 0; i < 32; i++)
+		upa_writel(ctx->area_pattern[i], &ffb->pattern[i]);
+
+	/* Finally, stash away the User Constol/Status Register.
+	 * The only state we really preserve here is the picking
+	 * control.
+	 */
+	upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr);
+}
+
+#define FFB_UCSR_FB_BUSY       0x01000000
+#define FFB_UCSR_RP_BUSY       0x02000000
+#define FFB_UCSR_ALL_BUSY      (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY)
+
+static void FFBWait(ffb_fbcPtr ffb)
+{
+	int limit = 100000;
+
+	do {
+		u32 regval = upa_readl(&ffb->ucsr);
+
+		if ((regval & FFB_UCSR_ALL_BUSY) == 0)
+			break;
+	} while (--limit);
+}
+
+int ffb_context_switch(drm_device_t *dev, int old, int new)
+{
+	ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+
+        atomic_inc(&dev->total_ctx);
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context ||
+	    dev->last_context == 0) {
+		dev->last_context = new;
+                return 0;
+	}
+        
+	FFBWait(fpriv->regs);
+	ffb_save_context(fpriv, old);
+	ffb_restore_context(fpriv, old, new);
+	FFBWait(fpriv->regs);
+        
+	dev->last_context = new;
+
+        return 0;
+}
+
+int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY));
+	if (idx < 0)
+		return -ENFILE;
+
+	DRM_DEBUG("%d\n", ctx.handle);
+	ctx.handle = idx;
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0)
+		hwctx->is_2d_only = 0;
+	else
+		hwctx->is_2d_only = 1;
+
+	return 0;
+}
+
+int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	struct ffb_hw_context *hwctx;
+	drm_ctx_t ctx;
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+
+	idx = ctx.handle;
+	if (idx <= 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	hwctx = fpriv->hw_state[idx - 1];
+	if (hwctx == NULL)
+		return -EINVAL;
+
+	if (hwctx->is_2d_only != 0)
+		ctx.flags = _DRM_CONTEXT_2DONLY;
+	else
+		ctx.flags = 0;
+
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+
+	return 0;
+}
+
+int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return ffb_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	return 0;
+}
+
+int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_t	ctx;
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	ffb_dev_priv_t	*fpriv	= (ffb_dev_priv_t *) (dev + 1);
+	int idx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+
+	idx = ctx.handle - 1;
+	if (idx < 0 || idx >= FFB_MAX_CTXS)
+		return -EINVAL;
+
+	if (fpriv->hw_state[idx] != NULL) {
+		kfree(fpriv->hw_state[idx]);
+		fpriv->hw_state[idx] = NULL;
+	}
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/ffb_drv.c linux/drivers/char/drm-4.0/ffb_drv.c
--- linux.orig/drivers/char/drm-4.0/ffb_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/ffb_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,952 @@
+/* $Id: ffb_drv.c,v 1.14 2001/05/24 12:01:47 davem Exp $
+ * ffb_drv.c: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+#include "drmP.h"
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <asm/shmparam.h>
+#include <asm/oplib.h>
+#include <asm/upa.h>
+
+#include "ffb_drv.h"
+
+#define FFB_NAME	"ffb"
+#define FFB_DESC	"Creator/Creator3D"
+#define FFB_DATE	"20000517"
+#define FFB_MAJOR	0
+#define FFB_MINOR	0
+#define FFB_PATCHLEVEL	1
+
+/* Forward declarations. */
+int  ffb_init(void);
+void ffb_cleanup(void);
+static int  ffb_version(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+static int  ffb_open(struct inode *inode, struct file *filp);
+static int  ffb_release(struct inode *inode, struct file *filp);
+static int  ffb_ioctl(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+static int  ffb_lock(struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg);
+static int  ffb_unlock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma);
+static unsigned long ffb_get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
+
+/* From ffb_context.c */
+extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long);
+extern int ffb_context_switch(drm_device_t *, int, int);
+
+static struct file_operations ffb_fops = {
+	owner:			THIS_MODULE,
+	open:			ffb_open,
+	flush:			drm_flush,
+	release:		ffb_release,
+	ioctl:			ffb_ioctl,
+	mmap:			ffb_mmap,
+	read:			drm_read,
+	fasync:			drm_fasync,
+	poll:			drm_poll,
+	get_unmapped_area:	ffb_get_unmapped_area,
+};
+
+/* This is just a template, we make a new copy for each FFB
+ * we discover at init time so that each one gets a unique
+ * misc device minor number.
+ */
+static struct miscdevice ffb_misc = {
+	minor:	MISC_DYNAMIC_MINOR,
+	name:	FFB_NAME,
+	fops:	&ffb_fops,
+};
+
+static drm_ioctl_desc_t ffb_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { ffb_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 }, /* XXX */
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	
+	/* The implementation is currently a nop just like on tdfx.
+	 * Later we can do something more clever. -DaveM
+	 */
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { ffb_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { ffb_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { ffb_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { ffb_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { ffb_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { ffb_resctx,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { ffb_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { ffb_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls)
+
+#ifdef MODULE
+static char *ffb = NULL;
+#endif
+
+MODULE_AUTHOR("David S. Miller (davem@redhat.com)");
+MODULE_DESCRIPTION("Sun Creator/Creator3D DRI");
+MODULE_LICENSE("GPL");
+
+static int ffb_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+	
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+	
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+
+	/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+	
+	/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+	
+	/* Clear map area information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+				drm_ioremapfree(map->handle, map->size);
+				break;
+
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+
+			default:
+				break;
+			};
+
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+	
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+drm_device_t **ffb_dev_table;
+static int ffb_dev_table_size;
+
+static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance)
+{
+	volatile unsigned char *strap_bits;
+	unsigned char val;
+
+	strap_bits = (volatile unsigned char *)
+		(ffb_priv->card_phys_base + 0x00200000UL);
+
+	/* Don't ask, you have to read the value twice for whatever
+	 * reason to get correct contents.
+	 */
+	val = upa_readb(strap_bits);
+	val = upa_readb(strap_bits);
+	switch (val & 0x78) {
+	case (0x0 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb1_prototype;
+		printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance);
+		break;
+	case (0x0 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb1_standard;
+		printk("ffb%d: Detected FFB1\n", instance);
+		break;
+	case (0x0 << 5) | (0x3 << 3):
+		ffb_priv->ffb_type = ffb1_speedsort;
+		printk("ffb%d: Detected FFB1-SpeedSort\n", instance);
+		break;
+	case (0x1 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_prototype;
+		printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance);
+		break;
+	case (0x1 << 5) | (0x1 << 3):
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Detected FFB2/vertical\n", instance);
+		break;
+	case (0x1 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_vertical_plus;
+		printk("ffb%d: Detected FFB2+/vertical\n", instance);
+		break;
+	case (0x2 << 5) | (0x0 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2/horizontal\n", instance);
+		break;
+	case (0x2 << 5) | (0x2 << 3):
+		ffb_priv->ffb_type = ffb2_horizontal;
+		printk("ffb%d: Detected FFB2+/horizontal\n", instance);
+		break;
+	default:
+		ffb_priv->ffb_type = ffb2_vertical;
+		printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val);
+		break;
+	};
+}
+
+static void __init ffb_apply_upa_parent_ranges(int parent, struct linux_prom64_registers *regs)
+{
+	struct linux_prom64_ranges ranges[PROMREG_MAX];
+	char name[128];
+	int len, i;
+
+	prom_getproperty(parent, "name", name, sizeof(name));
+	if (strcmp(name, "upa") != 0)
+		return;
+
+	len = prom_getproperty(parent, "ranges", (void *) ranges, sizeof(ranges));
+	if (len <= 0)
+		return;
+
+	len /= sizeof(struct linux_prom64_ranges);
+	for (i = 0; i < len; i++) {
+		struct linux_prom64_ranges *rng = &ranges[i];
+		u64 phys_addr = regs->phys_addr;
+
+		if (phys_addr >= rng->ot_child_base &&
+		    phys_addr < (rng->ot_child_base + rng->or_size)) {
+			regs->phys_addr -= rng->ot_child_base;
+			regs->phys_addr += rng->ot_parent_base;
+			return;
+		}
+	}
+
+	return;
+}
+
+static int __init ffb_init_one(int prom_node, int parent_node, int instance)
+{
+	struct linux_prom64_registers regs[2*PROMREG_MAX];
+	drm_device_t *dev;
+	ffb_dev_priv_t *ffb_priv;
+	int ret, i;
+
+	dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+
+	memset(dev, 0, sizeof(*dev));
+	spin_lock_init(&dev->count_lock);
+	sema_init(&dev->struct_sem, 1);
+
+	ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+	ffb_priv->prom_node = prom_node;
+	if (prom_getproperty(ffb_priv->prom_node, "reg",
+			     (void *)regs, sizeof(regs)) <= 0) {
+		kfree(dev);
+		return -EINVAL;
+	}
+	ffb_apply_upa_parent_ranges(parent_node, &regs[0]);
+	ffb_priv->card_phys_base = regs[0].phys_addr;
+	ffb_priv->regs = (ffb_fbcPtr)
+		(regs[0].phys_addr + 0x00600000UL);
+	get_ffb_type(ffb_priv, instance);
+	for (i = 0; i < FFB_MAX_CTXS; i++)
+		ffb_priv->hw_state[i] = NULL;
+
+	ffb_dev_table[instance] = dev;
+
+#ifdef MODULE
+	drm_parse_options(ffb);
+#endif
+
+	memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc));
+	ret = misc_register(&ffb_priv->miscdev);
+	if (ret) {
+		ffb_dev_table[instance] = NULL;
+		kfree(dev);
+		return ret;
+	}
+
+	dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor);
+	dev->name = FFB_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n",
+		 FFB_NAME,
+		 FFB_MAJOR,
+		 FFB_MINOR,
+		 FFB_PATCHLEVEL,
+		 FFB_DATE,
+		 ffb_priv->miscdev.minor,
+		 ffb_priv->card_phys_base);
+	
+	return 0;
+}
+
+static int __init ffb_count_siblings(int root)
+{
+	int node, child, count = 0;
+
+	child = prom_getchild(root);
+	for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb"))
+		count++;
+
+	return count;
+}
+
+static int __init ffb_init_dev_table(void)
+{
+	int root, total;
+
+	total = ffb_count_siblings(prom_root_node);
+	root = prom_getchild(prom_root_node);
+	for (root = prom_searchsiblings(root, "upa"); root;
+	     root = prom_searchsiblings(prom_getsibling(root), "upa"))
+		total += ffb_count_siblings(root);
+
+	if (!total)
+		return -ENODEV;
+
+	ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL);
+	if (!ffb_dev_table)
+		return -ENOMEM;
+
+	ffb_dev_table_size = total;
+
+	return 0;
+}
+
+static int __init ffb_scan_siblings(int root, int instance)
+{
+	int node, child;
+
+	child = prom_getchild(root);
+	for (node = prom_searchsiblings(child, "SUNW,ffb"); node;
+	     node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) {
+		ffb_init_one(node, root, instance);
+		instance++;
+	}
+
+	return instance;
+}
+
+int __init ffb_init(void)
+{
+	int root, instance, ret;
+
+	ret = ffb_init_dev_table();
+	if (ret)
+		return ret;
+
+	instance = ffb_scan_siblings(prom_root_node, 0);
+
+	root = prom_getchild(prom_root_node);
+	for (root = prom_searchsiblings(root, "upa"); root;
+	     root = prom_searchsiblings(prom_getsibling(root), "upa"))
+		instance = ffb_scan_siblings(root, instance);
+
+	return 0;
+}
+
+void __exit ffb_cleanup(void)
+{
+	int instance;
+
+	DRM_DEBUG("\n");
+	
+	drm_proc_cleanup();
+	for (instance = 0; instance < ffb_dev_table_size; instance++) {
+		drm_device_t *dev = ffb_dev_table[instance];
+		ffb_dev_priv_t *ffb_priv;
+
+		if (!dev)
+			continue;
+
+		ffb_priv = (ffb_dev_priv_t *) (dev + 1);
+		if (misc_deregister(&ffb_priv->miscdev)) {
+			DRM_ERROR("Cannot unload module\n");
+		} else {
+			DRM_INFO("Module unloaded\n");
+		}
+		ffb_takedown(dev);
+		kfree(dev);
+		ffb_dev_table[instance] = NULL;
+	}
+	kfree(ffb_dev_table);
+	ffb_dev_table = NULL;
+	ffb_dev_table_size = 0;
+}
+
+static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_version_t version;
+	int len, ret;
+
+	ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version));
+	if (ret)
+		return -EFAULT;
+
+	version.version_major		= FFB_MAJOR;
+	version.version_minor		= FFB_MINOR;
+	version.version_patchlevel	= FFB_PATCHLEVEL;
+
+	len = strlen(FFB_NAME);
+	if (len > version.name_len)
+		len = version.name_len;
+	version.name_len = len;
+	if (len && version.name) {
+		ret = copy_to_user(version.name, FFB_NAME, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DATE);
+	if (len > version.date_len)
+		len = version.date_len;
+	version.date_len = len;
+	if (len && version.date) {
+		ret = copy_to_user(version.date, FFB_DATE, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	len = strlen(FFB_DESC);
+	if (len > version.desc_len)
+		len = version.desc_len;
+	version.desc_len = len;
+	if (len && version.desc) {
+		ret = copy_to_user(version.desc, FFB_DESC, len);
+		if (ret)
+			return -EFAULT;
+	}
+
+	ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version));
+	if (ret)
+		ret = -EFAULT;
+
+	return ret;
+}
+
+static int ffb_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+	
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	return 0;
+}
+
+static int ffb_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t *dev;
+	int minor, i;
+	int ret = 0;
+
+	minor = MINOR(inode->i_rdev);
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_dev_priv_t *ffb_priv;
+
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+
+	if (i >= ffb_dev_table_size)
+		return -EINVAL;
+
+	dev = ffb_dev_table[i];
+	if (!dev)
+		return -EINVAL;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	ret = drm_open_helper(inode, filp, dev);
+	if (!ret) {
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return ffb_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return ret;
+}
+
+static int ffb_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev;
+	int ret = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (dev->lock.hw_lock != NULL
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1);
+		int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock);
+		int idx;
+
+		/* We have to free up the rogue hw context state
+		 * holding error or else we will leak it.
+		 */
+		idx = context - 1;
+		if (fpriv->hw_state[idx] != NULL) {
+			kfree(fpriv->hw_state[idx]);
+			fpriv->hw_state[idx] = NULL;
+		}
+	}
+
+	ret = drm_release(inode, filp);
+
+	if (!ret) {
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			ret = ffb_takedown(dev);
+			unlock_kernel();
+			return ret;
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return ret;
+}
+
+static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+	int		 ret;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+	
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= FFB_IOCTL_COUNT) {
+		ret = -EINVAL;
+	} else {
+		ioctl	  = &ffb_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			ret = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			ret = -EACCES;
+		} else {
+			ret = (func)(inode, filp, cmd, arg);
+		}
+	}
+	
+	atomic_dec(&dev->ioctl_count);
+
+	return ret;
+}
+
+static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv	= filp->private_data;
+        drm_device_t      *dev	= priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret	= 0;
+        drm_lock_t        lock;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+	add_wait_queue(&dev->lock.lock_queue, &entry);
+	for (;;) {
+		if (!dev->lock.hw_lock) {
+			/* Device has been unregistered */
+			ret = -EINTR;
+			break;
+		}
+		if (drm_lock_take(&dev->lock.hw_lock->lock,
+				  lock.context)) {
+			dev->lock.pid       = current->pid;
+			dev->lock.lock_time = jiffies;
+			atomic_inc(&dev->total_locks);
+			break;  /* Got lock */
+		}
+                        
+		/* Contention */
+		atomic_inc(&dev->total_sleeps);
+		current->state = TASK_INTERRUPTIBLE;
+		current->policy |= SCHED_YIELD;
+		schedule();
+		if (signal_pending(current)) {
+			ret = -ERESTARTSYS;
+			break;
+		}
+	}
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&dev->lock.lock_queue, &entry);
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (dev->last_context != lock.context)
+			ffb_context_switch(dev, dev->last_context, lock.context);
+	}
+
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+        return ret;
+}
+
+int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+	unsigned int old, new, prev, ctx;
+	int ret;
+
+	ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock));
+	if (ret)
+		return -EFAULT;
+	
+	if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+
+	/* We no longer really hold it, but if we are the next
+	 * agent to request it then we should just be able to
+	 * take it immediately and not eat the ioctl.
+	 */
+	dev->lock.pid = 0;
+	{
+		__volatile__ unsigned int *plock = &dev->lock.hw_lock->lock;
+
+		do {
+			old  = *plock;
+			new  = ctx;
+			prev = cmpxchg(plock, old, new);
+		} while (prev != old);
+	}
+
+	wake_up_interruptible(&dev->lock.lock_queue);
+	
+	unblock_all_signals();
+	return 0;
+}
+
+extern struct vm_operations_struct drm_vm_ops;
+extern struct vm_operations_struct drm_vm_shm_ops;
+extern struct vm_operations_struct drm_vm_shm_lock_ops;
+
+static int ffb_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	ffb_dev_priv_t	*ffb_priv;
+	int		i, minor;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	minor = MINOR(filp->f_dentry->d_inode->i_rdev);
+	ffb_priv = NULL;
+	for (i = 0; i < ffb_dev_table_size; i++) {
+		ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1);
+		if (ffb_priv->miscdev.minor == minor)
+			break;
+	}
+	if (i >= ffb_dev_table_size)
+		return -EINVAL;
+
+	/* We don't support/need dma mappings, so... */
+	if (!VM_OFFSET(vma))
+		return -EINVAL;
+
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long off;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		off = (map->offset & 0xffffffff);
+		if (off == VM_OFFSET(vma))
+			break;
+	}
+
+	if (i >= dev->map_count)
+		return -EINVAL;
+
+	if (!map ||
+	    ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+		return -EPERM;
+
+	if (map->size != (vma->vm_end - vma->vm_start))
+		return -EINVAL;
+
+	/* Set read-only attribute before mappings are created
+	 * so it works for fb/reg maps too.
+	 */
+	if (map->flags & _DRM_READ_ONLY)
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+		/* FALLTHROUGH */
+
+	case _DRM_REGISTERS:
+		/* In order to handle 32-bit drm apps/xserver we
+		 * play a trick.  The mappings only really specify
+		 * the 32-bit offset from the cards 64-bit base
+		 * address, and we just add in the base here.
+		 */
+		vma->vm_flags |= VM_IO;
+		if (io_remap_page_range(vma->vm_start,
+					ffb_priv->card_phys_base + VM_OFFSET(vma),
+					vma->vm_end - vma->vm_start,
+					vma->vm_page_prot, 0))
+			return -EAGAIN;
+
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		if (map->flags & _DRM_CONTAINS_LOCK)
+			vma->vm_ops = &drm_vm_shm_lock_ops;
+		else {
+			vma->vm_ops = &drm_vm_shm_ops;
+			vma->vm_private_data = (void *) map;
+		}
+
+		/* Don't let this area swap.  Change when
+		 * DRM_KERNEL advisory is supported.
+		 */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		return -EINVAL;	/* This should never happen. */
+	};
+
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+	vma->vm_file = filp; /* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+static drm_map_t *ffb_find_map(struct file *filp, unsigned long off)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev;
+	drm_map_t	*map;
+	int		i;
+
+	if (!priv || (dev = priv->dev) == NULL)
+		return NULL;
+
+	for (i = 0; i < dev->map_count; i++) {
+		unsigned long uoff;
+
+		map = dev->maplist[i];
+
+		/* Ok, a little hack to make 32-bit apps work. */
+		uoff = (map->offset & 0xffffffff);
+		if (uoff == off)
+			return map;
+	}
+	return NULL;
+}
+
+static unsigned long ffb_get_unmapped_area(struct file *filp, unsigned long hint, unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	drm_map_t *map = ffb_find_map(filp, pgoff << PAGE_SHIFT);
+	unsigned long addr = -ENOMEM;
+
+	if (!map)
+		return get_unmapped_area(NULL, hint, len, pgoff, flags);
+
+	if (map->type == _DRM_FRAME_BUFFER ||
+	    map->type == _DRM_REGISTERS) {
+#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
+		addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags);
+#else
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+#endif
+	} else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) {
+		unsigned long slack = SHMLBA - PAGE_SIZE;
+
+		addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags);
+		if (!(addr & ~PAGE_MASK)) {
+			unsigned long kvirt = (unsigned long) map->handle;
+
+			if ((kvirt & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) {
+				unsigned long koff, aoff;
+
+				koff = kvirt & (SHMLBA - 1);
+				aoff = addr & (SHMLBA - 1);
+				if (koff < aoff)
+					koff += SHMLBA;
+
+				addr += (koff - aoff);
+			}
+		}
+	} else {
+		addr = get_unmapped_area(NULL, hint, len, pgoff, flags);
+	}
+
+	return addr;
+}
+
+module_init(ffb_init);
+module_exit(ffb_cleanup);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/ffb_drv.h linux/drivers/char/drm-4.0/ffb_drv.h
--- linux.orig/drivers/char/drm-4.0/ffb_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/ffb_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,276 @@
+/* $Id: ffb_drv.h,v 1.1 2000/06/01 04:24:39 davem Exp $
+ * ffb_drv.h: Creator/Creator3D direct rendering driver.
+ *
+ * Copyright (C) 2000 David S. Miller (davem@redhat.com)
+ */
+
+/* Auxilliary clips. */
+typedef struct  {
+	volatile unsigned int min;
+	volatile unsigned int max;
+} ffb_auxclip, *ffb_auxclipPtr;
+
+/* FFB register set. */
+typedef struct _ffb_fbc {
+	/* Next vertex registers, on the right we list which drawops
+	 * use said register and the logical name the register has in
+	 * that context.
+	 */					/* DESCRIPTION		DRAWOP(NAME)	*/
+/*0x00*/unsigned int		pad1[3];	/* Reserved				*/
+/*0x0c*/volatile unsigned int	alpha;		/* ALPHA Transparency			*/
+/*0x10*/volatile unsigned int	red;		/* RED					*/
+/*0x14*/volatile unsigned int	green;		/* GREEN				*/
+/*0x18*/volatile unsigned int	blue;		/* BLUE					*/
+/*0x1c*/volatile unsigned int	z;		/* DEPTH				*/
+/*0x20*/volatile unsigned int	y;		/* Y			triangle(DOYF)	*/
+						/*                      aadot(DYF)	*/
+						/*                      ddline(DYF)	*/
+						/*                      aaline(DYF)	*/
+/*0x24*/volatile unsigned int	x;		/* X			triangle(DOXF)	*/
+						/*                      aadot(DXF)	*/
+						/*                      ddline(DXF)	*/
+						/*                      aaline(DXF)	*/
+/*0x28*/unsigned int		pad2[2];	/* Reserved				*/
+/*0x30*/volatile unsigned int	ryf;		/* Y (alias to DOYF)	ddline(RYF)	*/
+						/*			aaline(RYF)	*/
+						/*			triangle(RYF)	*/
+/*0x34*/volatile unsigned int	rxf;		/* X			ddline(RXF)	*/
+						/*			aaline(RXF)	*/
+						/*			triangle(RXF)	*/
+/*0x38*/unsigned int		pad3[2];	/* Reserved				*/
+/*0x40*/volatile unsigned int	dmyf;		/* Y (alias to DOYF)	triangle(DMYF)	*/
+/*0x44*/volatile unsigned int	dmxf;		/* X			triangle(DMXF)	*/
+/*0x48*/unsigned int		pad4[2];	/* Reserved				*/
+/*0x50*/volatile unsigned int	ebyi;		/* Y (alias to RYI)	polygon(EBYI)	*/
+/*0x54*/volatile unsigned int	ebxi;		/* X			polygon(EBXI)	*/
+/*0x58*/unsigned int		pad5[2];	/* Reserved				*/
+/*0x60*/volatile unsigned int	by;		/* Y			brline(RYI)	*/
+						/*			fastfill(OP)	*/
+						/*			polygon(YI)	*/
+						/*			rectangle(YI)	*/
+						/*			bcopy(SRCY)	*/
+						/*			vscroll(SRCY)	*/
+/*0x64*/volatile unsigned int	bx;		/* X			brline(RXI)	*/
+						/*			polygon(XI)	*/
+						/*			rectangle(XI)	*/
+						/*			bcopy(SRCX)	*/
+						/*			vscroll(SRCX)	*/
+						/*			fastfill(GO)	*/
+/*0x68*/volatile unsigned int	dy;		/* destination Y	fastfill(DSTY)	*/
+						/*			bcopy(DSRY)	*/
+						/*			vscroll(DSRY)	*/
+/*0x6c*/volatile unsigned int	dx;		/* destination X	fastfill(DSTX)	*/
+						/*			bcopy(DSTX)	*/
+						/*			vscroll(DSTX)	*/
+/*0x70*/volatile unsigned int	bh;		/* Y (alias to RYI)	brline(DYI)	*/
+						/*			dot(DYI)	*/
+						/*			polygon(ETYI)	*/
+						/* Height		fastfill(H)	*/
+						/*			bcopy(H)	*/
+						/*			vscroll(H)	*/
+						/* Y count		fastfill(NY)	*/
+/*0x74*/volatile unsigned int	bw;		/* X			dot(DXI)	*/
+						/*			brline(DXI)	*/
+						/*			polygon(ETXI)	*/
+						/*			fastfill(W)	*/
+						/*			bcopy(W)	*/
+						/*			vscroll(W)	*/
+						/*			fastfill(NX)	*/
+/*0x78*/unsigned int		pad6[2];	/* Reserved				*/
+/*0x80*/unsigned int		pad7[32];	/* Reserved				*/
+	
+	/* Setup Unit's vertex state register */
+/*100*/	volatile unsigned int	suvtx;
+/*104*/	unsigned int		pad8[63];	/* Reserved				*/
+	
+	/* Frame Buffer Control Registers */
+/*200*/	volatile unsigned int	ppc;		/* Pixel Processor Control		*/
+/*204*/	volatile unsigned int	wid;		/* Current WID				*/
+/*208*/	volatile unsigned int	fg;		/* FG data				*/
+/*20c*/	volatile unsigned int	bg;		/* BG data				*/
+/*210*/	volatile unsigned int	consty;		/* Constant Y				*/
+/*214*/	volatile unsigned int	constz;		/* Constant Z				*/
+/*218*/	volatile unsigned int	xclip;		/* X Clip				*/
+/*21c*/	volatile unsigned int	dcss;		/* Depth Cue Scale Slope		*/
+/*220*/	volatile unsigned int	vclipmin;	/* Viewclip XY Min Bounds		*/
+/*224*/	volatile unsigned int	vclipmax;	/* Viewclip XY Max Bounds		*/
+/*228*/	volatile unsigned int	vclipzmin;	/* Viewclip Z Min Bounds		*/
+/*22c*/	volatile unsigned int	vclipzmax;	/* Viewclip Z Max Bounds		*/
+/*230*/	volatile unsigned int	dcsf;		/* Depth Cue Scale Front Bound		*/
+/*234*/	volatile unsigned int	dcsb;		/* Depth Cue Scale Back Bound		*/
+/*238*/	volatile unsigned int	dczf;		/* Depth Cue Z Front			*/
+/*23c*/	volatile unsigned int	dczb;		/* Depth Cue Z Back			*/
+/*240*/	unsigned int		pad9;		/* Reserved				*/
+/*244*/	volatile unsigned int	blendc;		/* Alpha Blend Control			*/
+/*248*/	volatile unsigned int	blendc1;	/* Alpha Blend Color 1			*/
+/*24c*/	volatile unsigned int	blendc2;	/* Alpha Blend Color 2			*/
+/*250*/	volatile unsigned int	fbramitc;	/* FB RAM Interleave Test Control	*/
+/*254*/	volatile unsigned int	fbc;		/* Frame Buffer Control			*/
+/*258*/	volatile unsigned int	rop;		/* Raster OPeration			*/
+/*25c*/	volatile unsigned int	cmp;		/* Frame Buffer Compare			*/
+/*260*/	volatile unsigned int	matchab;	/* Buffer AB Match Mask			*/
+/*264*/	volatile unsigned int	matchc;		/* Buffer C(YZ) Match Mask		*/
+/*268*/	volatile unsigned int	magnab;		/* Buffer AB Magnitude Mask		*/
+/*26c*/	volatile unsigned int	magnc;		/* Buffer C(YZ) Magnitude Mask		*/
+/*270*/	volatile unsigned int	fbcfg0;		/* Frame Buffer Config 0		*/
+/*274*/	volatile unsigned int	fbcfg1;		/* Frame Buffer Config 1		*/
+/*278*/	volatile unsigned int	fbcfg2;		/* Frame Buffer Config 2		*/
+/*27c*/	volatile unsigned int	fbcfg3;		/* Frame Buffer Config 3		*/
+/*280*/	volatile unsigned int	ppcfg;		/* Pixel Processor Config		*/
+/*284*/	volatile unsigned int	pick;		/* Picking Control			*/
+/*288*/	volatile unsigned int	fillmode;	/* FillMode				*/
+/*28c*/	volatile unsigned int	fbramwac;	/* FB RAM Write Address Control		*/
+/*290*/	volatile unsigned int	pmask;		/* RGB PlaneMask			*/
+/*294*/	volatile unsigned int	xpmask;		/* X PlaneMask				*/
+/*298*/	volatile unsigned int	ypmask;		/* Y PlaneMask				*/
+/*29c*/	volatile unsigned int	zpmask;		/* Z PlaneMask				*/
+/*2a0*/	ffb_auxclip		auxclip[4]; 	/* Auxilliary Viewport Clip		*/
+	
+	/* New 3dRAM III support regs */
+/*2c0*/	volatile unsigned int	rawblend2;
+/*2c4*/	volatile unsigned int	rawpreblend;
+/*2c8*/	volatile unsigned int	rawstencil;
+/*2cc*/	volatile unsigned int	rawstencilctl;
+/*2d0*/	volatile unsigned int	threedram1;
+/*2d4*/	volatile unsigned int	threedram2;
+/*2d8*/	volatile unsigned int	passin;
+/*2dc*/	volatile unsigned int	rawclrdepth;
+/*2e0*/	volatile unsigned int	rawpmask;
+/*2e4*/	volatile unsigned int	rawcsrc;
+/*2e8*/	volatile unsigned int	rawmatch;
+/*2ec*/	volatile unsigned int	rawmagn;
+/*2f0*/	volatile unsigned int	rawropblend;
+/*2f4*/	volatile unsigned int	rawcmp;
+/*2f8*/	volatile unsigned int	rawwac;
+/*2fc*/	volatile unsigned int	fbramid;
+	
+/*300*/	volatile unsigned int	drawop;		/* Draw OPeration			*/
+/*304*/	unsigned int		pad10[2];	/* Reserved				*/
+/*30c*/	volatile unsigned int	lpat;		/* Line Pattern control			*/
+/*310*/	unsigned int		pad11;		/* Reserved				*/
+/*314*/	volatile unsigned int	fontxy;		/* XY Font coordinate			*/
+/*318*/	volatile unsigned int	fontw;		/* Font Width				*/
+/*31c*/	volatile unsigned int	fontinc;	/* Font Increment			*/
+/*320*/	volatile unsigned int	font;		/* Font bits				*/
+/*324*/	unsigned int		pad12[3];	/* Reserved				*/
+/*330*/	volatile unsigned int	blend2;
+/*334*/	volatile unsigned int	preblend;
+/*338*/	volatile unsigned int	stencil;
+/*33c*/	volatile unsigned int	stencilctl;
+
+/*340*/	unsigned int		pad13[4];	/* Reserved				*/
+/*350*/	volatile unsigned int	dcss1;		/* Depth Cue Scale Slope 1		*/
+/*354*/	volatile unsigned int	dcss2;		/* Depth Cue Scale Slope 2		*/
+/*358*/	volatile unsigned int	dcss3;		/* Depth Cue Scale Slope 3		*/
+/*35c*/	volatile unsigned int	widpmask;
+/*360*/	volatile unsigned int	dcs2;
+/*364*/	volatile unsigned int	dcs3;
+/*368*/	volatile unsigned int	dcs4;
+/*36c*/	unsigned int		pad14;		/* Reserved				*/
+/*370*/	volatile unsigned int	dcd2;
+/*374*/	volatile unsigned int	dcd3;
+/*378*/	volatile unsigned int	dcd4;
+/*37c*/	unsigned int		pad15;		/* Reserved				*/
+/*380*/	volatile unsigned int	pattern[32];	/* area Pattern				*/
+/*400*/	unsigned int		pad16[8];	/* Reserved				*/
+/*420*/	volatile unsigned int	reset;		/* chip RESET				*/
+/*424*/	unsigned int		pad17[247];	/* Reserved				*/
+/*800*/	volatile unsigned int	devid;		/* Device ID				*/
+/*804*/	unsigned int		pad18[63];	/* Reserved				*/
+/*900*/	volatile unsigned int	ucsr;		/* User Control & Status Register	*/
+/*904*/	unsigned int		pad19[31];	/* Reserved				*/
+/*980*/	volatile unsigned int	mer;		/* Mode Enable Register			*/
+/*984*/	unsigned int		pad20[1439];	/* Reserved				*/
+} ffb_fbc, *ffb_fbcPtr;
+
+struct ffb_hw_context {
+	int is_2d_only;
+
+	unsigned int ppc;
+	unsigned int wid;
+	unsigned int fg;
+	unsigned int bg;
+	unsigned int consty;
+	unsigned int constz;
+	unsigned int xclip;
+	unsigned int dcss;
+	unsigned int vclipmin;
+	unsigned int vclipmax;
+	unsigned int vclipzmin;
+	unsigned int vclipzmax;
+	unsigned int dcsf;
+	unsigned int dcsb;
+	unsigned int dczf;
+	unsigned int dczb;
+	unsigned int blendc;
+	unsigned int blendc1;
+	unsigned int blendc2;
+	unsigned int fbc;
+	unsigned int rop;
+	unsigned int cmp;
+	unsigned int matchab;
+	unsigned int matchc;
+	unsigned int magnab;
+	unsigned int magnc;
+	unsigned int pmask;
+	unsigned int xpmask;
+	unsigned int ypmask;
+	unsigned int zpmask;
+	unsigned int auxclip0min;
+	unsigned int auxclip0max;
+	unsigned int auxclip1min;
+	unsigned int auxclip1max;
+	unsigned int auxclip2min;
+	unsigned int auxclip2max;
+	unsigned int auxclip3min;
+	unsigned int auxclip3max;
+	unsigned int drawop;
+	unsigned int lpat;
+	unsigned int fontxy;
+	unsigned int fontw;
+	unsigned int fontinc;
+	unsigned int area_pattern[32];
+	unsigned int ucsr;
+	unsigned int stencil;
+	unsigned int stencilctl;
+	unsigned int dcss1;
+	unsigned int dcss2;
+	unsigned int dcss3;
+	unsigned int dcs2;
+	unsigned int dcs3;
+	unsigned int dcs4;
+	unsigned int dcd2;
+	unsigned int dcd3;
+	unsigned int dcd4;
+	unsigned int mer;
+};
+
+#define FFB_MAX_CTXS	32
+
+enum ffb_chip_type {
+	ffb1_prototype = 0,	/* Early pre-FCS FFB */
+	ffb1_standard,		/* First FCS FFB, 100Mhz UPA, 66MHz gclk */
+	ffb1_speedsort,		/* Second FCS FFB, 100Mhz UPA, 75MHz gclk */
+	ffb2_prototype,		/* Early pre-FCS vertical FFB2 */
+	ffb2_vertical,		/* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk,
+				   75(SingleBuffer)/83(DoubleBuffer) MHz fclk */
+	ffb2_vertical_plus,	/* Second FCS FFB2/vertical, same timings */
+	ffb2_horizontal,	/* First FCS FFB2/horizontal, same timings as FFB2/vert */
+	ffb2_horizontal_plus,	/* Second FCS FFB2/horizontal, same timings */
+	afb_m3,			/* FCS Elite3D, 3 float chips */
+	afb_m6			/* FCS Elite3D, 6 float chips */
+};
+
+typedef struct ffb_dev_priv {
+	/* Misc software state. */
+	int			prom_node;
+	enum ffb_chip_type	ffb_type;
+	u64			card_phys_base;
+	struct miscdevice 	miscdev;
+
+	/* Controller registers. */
+	ffb_fbcPtr		regs;
+
+	/* Context table. */
+	struct ffb_hw_context	*hw_state[FFB_MAX_CTXS];
+} ffb_dev_priv_t;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/fops.c linux/drivers/char/drm-4.0/fops.c
--- linux.orig/drivers/char/drm-4.0/fops.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/fops.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,253 @@
+/* fops.c -- File operations for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include <linux/poll.h>
+
+/* drm_open is called whenever a process opens /dev/drm. */
+
+int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev)
+{
+	kdev_t	     minor = MINOR(inode->i_rdev);
+	drm_file_t   *priv;
+
+	if (filp->f_flags & O_EXCL)   return -EBUSY; /* No exclusive opens */
+	if (!drm_cpu_valid())         return -EINVAL;
+
+	DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor);
+
+	priv		    = drm_alloc(sizeof(*priv), DRM_MEM_FILES);
+	if(priv == NULL)
+		return -ENOMEM;
+	memset(priv, 0, sizeof(*priv));
+
+	filp->private_data  = priv;
+	priv->uid	    = current->euid;
+	priv->pid	    = current->pid;
+	priv->minor	    = minor;
+	priv->dev	    = dev;
+	priv->ioctl_count   = 0;
+	priv->authenticated = capable(CAP_SYS_ADMIN);
+
+	down(&dev->struct_sem);
+	if (!dev->file_last) {
+		priv->next	= NULL;
+		priv->prev	= NULL;
+		dev->file_first = priv;
+		dev->file_last	= priv;
+	} else {
+		priv->next	     = NULL;
+		priv->prev	     = dev->file_last;
+		dev->file_last->next = priv;
+		dev->file_last	     = priv;
+	}
+	up(&dev->struct_sem);
+	
+	return 0;
+}
+
+int drm_flush(struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+	return 0;
+}
+
+/* drm_release is called whenever a process closes /dev/drm*.  Linux calls
+   this only if any mappings have been closed. */
+
+int drm_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock
+	    && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	}
+	drm_reclaim_buffers(dev, priv->pid);
+
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+	
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+	
+	return 0;
+}
+
+int drm_fasync(int fd, struct file *filp, int on)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      retcode;
+	
+	DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device);
+	retcode = fasync_helper(fd, filp, on, &dev->buf_async);
+	if (retcode < 0) return retcode;
+	return 0;
+}
+
+
+/* The drm_read and drm_write_string code (especially that which manages
+   the circular buffer), is based on Alessandro Rubini's LINUX DEVICE
+   DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */
+
+ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev    = priv->dev;
+	int	      left;
+	int	      avail;
+	int	      send;
+	int	      cur;
+
+	DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp);
+	
+	while (dev->buf_rp == dev->buf_wp) {
+		DRM_DEBUG("  sleeping\n");
+		if (filp->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		interruptible_sleep_on(&dev->buf_readers);
+		if (signal_pending(current)) {
+			DRM_DEBUG("  interrupted\n");
+			return -ERESTARTSYS;
+		}
+		DRM_DEBUG("  awake\n");
+	}
+
+	left  = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	avail = DRM_BSZ - left;
+	send  = DRM_MIN(avail, count);
+
+	while (send) {
+		if (dev->buf_wp > dev->buf_rp) {
+			cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp);
+		} else {
+			cur = DRM_MIN(send, dev->buf_end - dev->buf_rp);
+		}
+		if (copy_to_user(buf, dev->buf_rp, cur))
+			return -EFAULT;
+		dev->buf_rp += cur;
+		if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf;
+		send -= cur;
+	}
+	
+	wake_up_interruptible(&dev->buf_writers);
+	return DRM_MIN(avail, count);;
+}
+
+int drm_write_string(drm_device_t *dev, const char *s)
+{
+	int left   = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ;
+	int send   = strlen(s);
+	int count;
+
+	DRM_DEBUG("%d left, %d to send (%p, %p)\n",
+		  left, send, dev->buf_rp, dev->buf_wp);
+	
+	if (left == 1 || dev->buf_wp != dev->buf_rp) {
+		DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n",
+			  left,
+			  dev->buf_wp,
+			  dev->buf_rp);
+	}
+
+	while (send) {
+		if (dev->buf_wp >= dev->buf_rp) {
+			count = DRM_MIN(send, dev->buf_end - dev->buf_wp);
+			if (count == left) --count; /* Leave a hole */
+		} else {
+			count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1);
+		}
+		strncpy(dev->buf_wp, s, count);
+		dev->buf_wp += count;
+		if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf;
+		send -= count;
+	}
+
+#if LINUX_VERSION_CODE < 0x020315 && !defined(KILLFASYNCHASTHREEPARAMETERS)
+	/* The extra parameter to kill_fasync was added in 2.3.21, and is
+           _not_ present in _stock_ 2.2.14 and 2.2.15.  However, some
+           distributions patch 2.2.x kernels to add this parameter.  The
+           Makefile.linux attempts to detect this addition and defines
+           KILLFASYNCHASTHREEPARAMETERS if three parameters are found. */
+	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO);
+#else
+
+				/* Parameter added in 2.3.21. */
+#if LINUX_VERSION_CODE < 0x020400
+	if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO, POLL_IN);
+#else
+				/* Type of first parameter changed in
+                                   Linux 2.4.0-test2... */
+	if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN);
+#endif
+#endif
+	DRM_DEBUG("waking\n");
+	wake_up_interruptible(&dev->buf_readers);
+	return 0;
+}
+
+unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+{
+	drm_file_t   *priv = filp->private_data;
+	drm_device_t *dev  = priv->dev;
+
+	poll_wait(filp, &dev->buf_readers, wait);
+	if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM;
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/gamma_dma.c linux/drivers/char/drm-4.0/gamma_dma.c
--- linux.orig/drivers/char/drm-4.0/gamma_dma.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/gamma_dma.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,836 @@
+/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*-
+ * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+
+/* WARNING!!! MAGIC NUMBER!!!  The number of regions already added to the
+   kernel must be specified here.  Currently, the number is 2.	This must
+   match the order the X server uses for instantiating register regions ,
+   or must be passed in a new ioctl. */
+#define GAMMA_REG(reg)						   \
+	(2							   \
+	 + ((reg < 0x1000)					   \
+	    ? 0							   \
+	    : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3))))
+
+#define GAMMA_OFF(reg)						   \
+	((reg < 0x1000)						   \
+	 ? reg							   \
+	 : ((reg < 0x10000)					   \
+	    ? (reg - 0x1000)					   \
+	    : ((reg < 0x11000)					   \
+	       ? (reg - 0x10000)				   \
+	       : (reg - 0x11000))))
+
+#define GAMMA_BASE(reg)	 ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle)
+#define GAMMA_ADDR(reg)	 (GAMMA_BASE(reg) + GAMMA_OFF(reg))
+#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg)
+#define GAMMA_READ(reg)	 GAMMA_DEREF(reg)
+#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0)
+
+#define GAMMA_BROADCASTMASK    0x9378
+#define GAMMA_COMMANDINTENABLE 0x0c48
+#define GAMMA_DMAADDRESS       0x0028
+#define GAMMA_DMACOUNT	       0x0030
+#define GAMMA_FILTERMODE       0x8c00
+#define GAMMA_GCOMMANDINTFLAGS 0x0c50
+#define GAMMA_GCOMMANDMODE     0x0c40
+#define GAMMA_GCOMMANDSTATUS   0x0c60
+#define GAMMA_GDELAYTIMER      0x0c38
+#define GAMMA_GDMACONTROL      0x0060
+#define GAMMA_GINTENABLE       0x0808
+#define GAMMA_GINTFLAGS	       0x0810
+#define GAMMA_INFIFOSPACE      0x0018
+#define GAMMA_OUTFIFOWORDS     0x0020
+#define GAMMA_OUTPUTFIFO       0x2000
+#define GAMMA_SYNC	       0x8c40
+#define GAMMA_SYNC_TAG	       0x0188
+
+static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address,
+				      unsigned long length)
+{
+	GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address));
+	while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4)
+		;
+	GAMMA_WRITE(GAMMA_DMACOUNT, length / 4);
+}
+
+static inline void gamma_dma_quiescent_single(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_quiescent_dual(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+	while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3)
+		;
+
+	GAMMA_WRITE(GAMMA_BROADCASTMASK, 3);
+
+	GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10);
+	GAMMA_WRITE(GAMMA_SYNC, 0);
+	
+				/* Read from first MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG);
+	
+				/* Read from second MX */
+	do {
+		while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000))
+			;
+	} while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG);
+}
+
+static inline void gamma_dma_ready(drm_device_t *dev)
+{
+	while (GAMMA_READ(GAMMA_DMACOUNT))
+		;
+}
+
+static inline int gamma_dma_is_ready(drm_device_t *dev)
+{
+	return !GAMMA_READ(GAMMA_DMACOUNT);
+}
+
+static void gamma_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+	drm_device_dma_t *dma = dev->dma;
+	
+	atomic_inc(&dev->total_irq);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */
+	GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8);
+	GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001);
+	if (gamma_dma_is_ready(dev)) {
+				/* Free previous buffer */
+		if (test_and_set_bit(0, &dev->dma_flag)) {
+			atomic_inc(&dma->total_missed_free);
+			return;
+		}
+		if (dma->this_buffer) {
+			drm_free_buffer(dev, dma->this_buffer);
+			dma->this_buffer = NULL;
+		}
+		clear_bit(0, &dev->dma_flag);
+
+				/* Dispatch new buffer */
+		queue_task(&dev->tq, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
+	}
+}
+
+/* Only called by gamma_dma_schedule. */
+static int gamma_do_dma(drm_device_t *dev, int locked)
+{
+	unsigned long	 address;
+	unsigned long	 length;
+	drm_buf_t	 *buf;
+	int		 retcode = 0;
+	drm_device_dma_t *dma = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 dma_start, dma_stop;
+#endif
+
+	if (test_and_set_bit(0, &dev->dma_flag)) {
+		atomic_inc(&dma->total_missed_dma);
+		return -EBUSY;
+	}
+	
+#if DRM_DMA_HISTOGRAM
+	dma_start = get_cycles();
+#endif
+
+	if (!dma->next_buffer) {
+		DRM_ERROR("No next_buffer\n");
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	buf	= dma->next_buffer;
+	address = (unsigned long)buf->address;
+	length	= buf->used;
+
+	DRM_DEBUG("context %d, buffer %d (%ld bytes)\n",
+		  buf->context, buf->idx, length);
+
+	if (buf->list == DRM_LIST_RECLAIM) {
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return -EINVAL;
+	}
+
+	if (!length) {
+		DRM_ERROR("0 length buffer\n");
+		drm_clear_next_buffer(dev);
+		drm_free_buffer(dev, buf);
+		clear_bit(0, &dev->dma_flag);
+		return 0;
+	}
+	
+	if (!gamma_dma_is_ready(dev)) {
+		clear_bit(0, &dev->dma_flag);
+		return -EBUSY;
+	}
+
+	if (buf->while_locked) {
+		if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+			DRM_ERROR("Dispatching buffer %d from pid %d"
+				  " \"while locked\", but no lock held\n",
+				  buf->idx, buf->pid);
+		}
+	} else {
+		if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock,
+					      DRM_KERNEL_CONTEXT)) {
+			atomic_inc(&dma->total_missed_lock);
+			clear_bit(0, &dev->dma_flag);
+			return -EBUSY;
+		}
+	}
+
+	if (dev->last_context != buf->context
+	    && !(dev->queuelist[buf->context]->flags
+		 & _DRM_CONTEXT_PRESERVED)) {
+				/* PRE: dev->last_context != buf->context */
+		if (drm_context_switch(dev, dev->last_context, buf->context)) {
+			drm_clear_next_buffer(dev);
+			drm_free_buffer(dev, buf);
+		}
+		retcode = -EBUSY;
+		goto cleanup;
+			
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+	}
+
+	drm_clear_next_buffer(dev);
+	buf->pending	 = 1;
+	buf->waiting	 = 0;
+	buf->list	 = DRM_LIST_PEND;
+#if DRM_DMA_HISTOGRAM
+	buf->time_dispatched = get_cycles();
+#endif
+
+	gamma_dma_dispatch(dev, address, length);
+	drm_free_buffer(dev, dma->this_buffer);
+	dma->this_buffer = buf;
+
+	atomic_add(length, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+
+	if (!buf->while_locked && !dev->context_flag && !locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+cleanup:
+
+	clear_bit(0, &dev->dma_flag);
+
+#if DRM_DMA_HISTOGRAM
+	dma_stop = get_cycles();
+	atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]);
+#endif
+
+	return retcode;
+}
+
+static void gamma_dma_schedule_timer_wrapper(unsigned long dev)
+{
+	gamma_dma_schedule((drm_device_t *)dev, 0);
+}
+
+static void gamma_dma_schedule_tq_wrapper(void *dev)
+{
+	gamma_dma_schedule(dev, 0);
+}
+
+int gamma_dma_schedule(drm_device_t *dev, int locked)
+{
+	int		 next;
+	drm_queue_t	 *q;
+	drm_buf_t	 *buf;
+	int		 retcode   = 0;
+	int		 processed = 0;
+	int		 missed;
+	int		 expire	   = 20;
+	drm_device_dma_t *dma	   = dev->dma;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	 schedule_start;
+#endif
+
+	if (test_and_set_bit(0, &dev->interrupt_flag)) {
+				/* Not reentrant */
+		atomic_inc(&dma->total_missed_sched);
+		return -EBUSY;
+	}
+	missed = atomic_read(&dma->total_missed_sched);
+
+#if DRM_DMA_HISTOGRAM
+	schedule_start = get_cycles();
+#endif
+
+again:
+	if (dev->context_flag) {
+		clear_bit(0, &dev->interrupt_flag);
+		return -EBUSY;
+	}
+	if (dma->next_buffer) {
+				/* Unsent buffer that was previously
+				   selected, but that couldn't be sent
+				   because the lock could not be obtained
+				   or the DMA engine wasn't ready.  Try
+				   again. */
+		atomic_inc(&dma->total_tried);
+		if (!(retcode = gamma_do_dma(dev, locked))) {
+			atomic_inc(&dma->total_hit);
+			++processed;
+		}
+	} else {
+		do {
+			next = drm_select_queue(dev,
+					     gamma_dma_schedule_timer_wrapper);
+			if (next >= 0) {
+				q   = dev->queuelist[next];
+				buf = drm_waitlist_get(&q->waitlist);
+				dma->next_buffer = buf;
+				dma->next_queue	 = q;
+				if (buf && buf->list == DRM_LIST_RECLAIM) {
+					drm_clear_next_buffer(dev);
+					drm_free_buffer(dev, buf);
+				}
+			}
+		} while (next >= 0 && !dma->next_buffer);
+		if (dma->next_buffer) {
+			if (!(retcode = gamma_do_dma(dev, locked))) {
+				++processed;
+			}
+		}
+	}
+
+	if (--expire) {
+		if (missed != atomic_read(&dma->total_missed_sched)) {
+			atomic_inc(&dma->total_lost);
+			if (gamma_dma_is_ready(dev)) goto again;
+		}
+		if (processed && gamma_dma_is_ready(dev)) {
+			atomic_inc(&dma->total_lost);
+			processed = 0;
+			goto again;
+		}
+	}
+	
+	clear_bit(0, &dev->interrupt_flag);
+	
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles()
+							   - schedule_start)]);
+#endif
+	return retcode;
+}
+
+static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d)
+{
+	unsigned long	  address;
+	unsigned long	  length;
+	int		  must_free = 0;
+	int		  retcode   = 0;
+	int		  i;
+	int		  idx;
+	drm_buf_t	  *buf;
+	drm_buf_t	  *last_buf = NULL;
+	drm_device_dma_t  *dma	    = dev->dma;
+	DECLARE_WAITQUEUE(entry, current);
+
+				/* Turn off interrupt handling */
+	while (test_and_set_bit(0, &dev->interrupt_flag)) {
+		schedule();
+		if (signal_pending(current)) return -EINTR;
+	}
+	if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) {
+		while (!drm_lock_take(&dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT)) {
+			schedule();
+			if (signal_pending(current)) {
+				clear_bit(0, &dev->interrupt_flag);
+				return -EINTR;
+			}
+		}
+		++must_free;
+	}
+	atomic_inc(&dma->total_prio);
+
+	for (i = 0; i < d->send_count; i++) {
+		idx = d->send_indices[i];
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  d->send_indices[i], dma->buf_count - 1);
+			continue;
+		}
+		buf = dma->buflist[ idx ];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->list != DRM_LIST_NONE) {
+			DRM_ERROR("Process %d using %d's buffer on list %d\n",
+				  current->pid, buf->pid, buf->list);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+				/* This isn't a race condition on
+				   buf->list, since our concern is the
+				   buffer reclaim during the time the
+				   process closes the /dev/drm? handle, so
+				   it can't also be doing DMA. */
+		buf->list	  = DRM_LIST_PRIO;
+		buf->used	  = d->send_sizes[i];
+		buf->context	  = d->context;
+		buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED;
+		address		  = (unsigned long)buf->address;
+		length		  = buf->used;
+		if (!length) {
+			DRM_ERROR("0 length buffer\n");
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		if (buf->waiting) {
+			DRM_ERROR("Sending waiting buffer:"
+				  " buffer %d, offset %d\n",
+				  d->send_indices[i], i);
+			retcode = -EINVAL;
+			goto cleanup;
+		}
+		buf->pending = 1;
+		
+		if (dev->last_context != buf->context
+		    && !(dev->queuelist[buf->context]->flags
+			 & _DRM_CONTEXT_PRESERVED)) {
+			add_wait_queue(&dev->context_wait, &entry);
+			current->state = TASK_INTERRUPTIBLE;
+				/* PRE: dev->last_context != buf->context */
+			drm_context_switch(dev, dev->last_context,
+					   buf->context);
+				/* POST: we will wait for the context
+				   switch and will dispatch on a later call
+				   when dev->last_context == buf->context.
+				   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+				   TIME! */
+			schedule();
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&dev->context_wait, &entry);
+			if (signal_pending(current)) {
+				retcode = -EINTR;
+				goto cleanup;
+			}
+			if (dev->last_context != buf->context) {
+				DRM_ERROR("Context mismatch: %d %d\n",
+					  dev->last_context,
+					  buf->context);
+			}
+		}
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = get_cycles();
+		buf->time_dispatched = buf->time_queued;
+#endif
+		gamma_dma_dispatch(dev, address, length);
+		atomic_add(length, &dma->total_bytes);
+		atomic_inc(&dma->total_dmas);
+		
+		if (last_buf) {
+			drm_free_buffer(dev, last_buf);
+		}
+		last_buf = buf;
+	}
+
+
+cleanup:
+	if (last_buf) {
+		gamma_dma_ready(dev);
+		drm_free_buffer(dev, last_buf);
+	}
+	
+	if (must_free && !dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+	clear_bit(0, &dev->interrupt_flag);
+	return retcode;
+}
+
+static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	drm_buf_t	  *last_buf = NULL;
+	int		  retcode   = 0;
+	drm_device_dma_t  *dma	    = dev->dma;
+
+	if (d->flags & _DRM_DMA_BLOCK) {
+		last_buf = dma->buflist[d->send_indices[d->send_count-1]];
+		add_wait_queue(&last_buf->dma_wait, &entry);
+	}
+	
+	if ((retcode = drm_dma_enqueue(dev, d))) {
+		if (d->flags & _DRM_DMA_BLOCK)
+			remove_wait_queue(&last_buf->dma_wait, &entry);
+		return retcode;
+	}
+	
+	gamma_dma_schedule(dev, 0);
+	
+	if (d->flags & _DRM_DMA_BLOCK) {
+		DRM_DEBUG("%d waiting\n", current->pid);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!last_buf->waiting && !last_buf->pending)
+				break; /* finished */
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		DRM_DEBUG("%d running\n", current->pid);
+		remove_wait_queue(&last_buf->dma_wait, &entry);
+		if (!retcode
+		    || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) {
+			if (!waitqueue_active(&last_buf->dma_wait)) {
+				drm_free_buffer(dev, last_buf);
+			}
+		}
+		if (retcode) {
+			DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n",
+				  d->context,
+				  last_buf->waiting,
+				  last_buf->pending,
+				  DRM_WAITCOUNT(dev, d->context),
+				  last_buf->idx,
+				  last_buf->list,
+				  last_buf->pid,
+				  current->pid);
+		}
+	}
+	return retcode;
+}
+
+int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_device_dma_t  *dma	    = dev->dma;
+	int		  retcode   = 0;
+	drm_dma_t	  d;
+
+	if (copy_from_user(&d, (drm_dma_t *)arg, sizeof(d)))
+		return -EFAULT;
+	DRM_DEBUG("%d %d: %d send, %d req\n",
+		  current->pid, d.context, d.send_count, d.request_count);
+
+	if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) {
+		DRM_ERROR("Process %d using context %d\n",
+			  current->pid, d.context);
+		return -EINVAL;
+	}
+	if (d.send_count < 0 || d.send_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n",
+			  current->pid, d.send_count, dma->buf_count);
+		return -EINVAL;
+	}
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+			  current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	if (d.send_count) {
+		if (d.flags & _DRM_DMA_PRIORITY)
+			retcode = gamma_dma_priority(dev, &d);
+		else 
+			retcode = gamma_dma_send_buffers(dev, &d);
+	}
+
+	d.granted_count = 0;
+
+	if (!retcode && d.request_count) {
+		retcode = drm_dma_get_buffers(dev, &d);
+	}
+
+	DRM_DEBUG("%d returning, granted = %d\n",
+		  current->pid, d.granted_count);
+	if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d)))
+		return -EFAULT;
+
+	return retcode;
+}
+
+int gamma_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = gamma_dma_schedule_tq_wrapper;
+	dev->tq.data	      = dev;
+
+
+				/* Before installing handler */
+	GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0);
+	GAMMA_WRITE(GAMMA_GDMACONTROL, 0);
+	
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   gamma_dma_service,
+				   0,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+
+				/* After installing handler */
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0x2001);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008);
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	   0x39090);
+	
+	return 0;
+}
+
+int gamma_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+	
+	DRM_DEBUG("%d\n", irq);
+	
+	GAMMA_WRITE(GAMMA_GDELAYTIMER,	    0);
+	GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0);
+	GAMMA_WRITE(GAMMA_GINTENABLE,	    0);
+	free_irq(irq, dev);
+
+	return 0;
+}
+
+
+int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+	
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = gamma_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = gamma_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+	drm_queue_t	  *q;
+#if DRM_DMA_HISTOGRAM
+	cycles_t	  start;
+
+	dev->lck_start = start = get_cycles();
+#endif
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0 || lock.context >= dev->queue_count)
+		return -EINVAL;
+	q = dev->queuelist[lock.context];
+	
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+
+	if (!ret) {
+		if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+		    != lock.context) {
+			long j = jiffies - dev->lock.lock_time;
+
+			if (j > 0 && j <= DRM_LOCK_SLICE) {
+				/* Can't take lock if we just had it and
+				   there is contention. */
+				current->state = TASK_INTERRUPTIBLE;
+				schedule_timeout(j);
+			}
+		}
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				atomic_inc(&q->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+
+	drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */
+	
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_READY)
+			gamma_dma_ready(dev);
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+			if (gamma_found() == 1) {
+				gamma_dma_quiescent_single(dev);
+			} else {
+				gamma_dma_quiescent_dual(dev);
+			}
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+	
+	return ret;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/gamma_drv.c linux/drivers/char/drm-4.0/gamma_drv.c
--- linux.orig/drivers/char/drm-4.0/gamma_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/gamma_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,572 @@
+/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "gamma_drv.h"
+
+#ifndef PCI_DEVICE_ID_3DLABS_GAMMA
+#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008
+#endif
+#ifndef PCI_DEVICE_ID_3DLABS_MX
+#define PCI_DEVICE_ID_3DLABS_MX 0x0006
+#endif
+
+#define GAMMA_NAME	 "gamma"
+#define GAMMA_DESC	 "3dlabs GMX 2000"
+#define GAMMA_DATE	 "20000910"
+#define GAMMA_MAJOR	 1
+#define GAMMA_MINOR	 0
+#define GAMMA_PATCHLEVEL 0
+
+static drm_device_t	      gamma_device;
+
+static struct file_operations gamma_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 gamma_open,
+	flush:	 drm_flush,
+	release: gamma_release,
+	ioctl:	 gamma_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      gamma_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  GAMMA_NAME,
+	fops:  &gamma_fops,
+};
+
+static drm_ioctl_desc_t	      gamma_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { gamma_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]    = { gamma_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]   = { drm_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]  = { drm_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]  = { drm_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]   = { drm_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]  = { drm_freebufs,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { drm_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { drm_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { drm_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { drm_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { drm_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { drm_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	     = { gamma_dma,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { gamma_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { gamma_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+};
+#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls)
+
+#ifdef MODULE
+static char		      *gamma = NULL;
+#endif
+static int 		      devices = 0;
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("3dlabs GMX 2000");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(gamma, "s");
+MODULE_PARM(devices, "i");
+MODULE_PARM_DESC(devices,
+		 "devices=x, where x is the number of MX chips on card\n");
+#ifndef MODULE
+/* gamma_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_options.
+ */
+
+
+static int __init gamma_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("gamma=", gamma_options);
+#endif
+
+static int gamma_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int gamma_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) gamma_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+int gamma_found(void)
+{
+	return devices;
+}
+
+int gamma_find_devices(void)
+{
+	struct pci_dev *d = NULL, *one = NULL, *two = NULL;
+
+	d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d);
+	if (!d) return 0;
+
+	one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d);
+	if (!one) return 0;
+
+	/* Make sure it's on the same card, if not - no MX's found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0;
+
+	two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one);
+	if (!two) return 1;
+
+	/* Make sure it's on the same card, if not - only 1 MX found */
+	if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1;
+
+	/* Two MX's found - we don't currently support more than 2 */
+	return 2;
+}
+
+/* gamma_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init gamma_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(gamma);
+#endif
+	devices = gamma_find_devices();
+	if (devices == 0) return -1;
+
+	if ((retcode = misc_register(&gamma_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor);
+	dev->name   = GAMMA_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n",
+		 GAMMA_NAME,
+		 GAMMA_MAJOR,
+		 GAMMA_MINOR,
+		 GAMMA_PATCHLEVEL,
+		 GAMMA_DATE,
+		 gamma_misc.minor,
+		 devices);
+
+	return 0;
+}
+
+/* gamma_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit gamma_cleanup(void)
+{
+	drm_device_t	      *dev = &gamma_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&gamma_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	gamma_takedown(dev);
+}
+
+module_init(gamma_init);
+module_exit(gamma_cleanup);
+
+
+int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = GAMMA_MAJOR;
+	version.version_minor	   = GAMMA_MINOR;
+	version.version_patchlevel = GAMMA_PATCHLEVEL;
+
+	DRM_COPY(version.name, GAMMA_NAME);
+	DRM_COPY(version.date, GAMMA_DATE);
+	DRM_COPY(version.desc, GAMMA_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int gamma_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &gamma_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return gamma_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int gamma_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return gamma_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= GAMMA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &gamma_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+
+int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	gamma_dma_schedule(dev, 1);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/gamma_drv.h linux/drivers/char/drm-4.0/gamma_drv.h
--- linux.orig/drivers/char/drm-4.0/gamma_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/gamma_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,58 @@
+/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*-
+ * Created: Mon Jan  4 10:05:05 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ * 
+ */
+
+#ifndef _GAMMA_DRV_H_
+#define _GAMMA_DRV_H_
+
+				/* gamma_drv.c */
+extern int  gamma_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_open(struct inode *inode, struct file *filp);
+extern int  gamma_release(struct inode *inode, struct file *filp);
+extern int  gamma_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  gamma_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  gamma_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* gamma_dma.c */
+extern int  gamma_dma_schedule(drm_device_t *dev, int locked);
+extern int  gamma_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  gamma_irq_install(drm_device_t *dev, int irq);
+extern int  gamma_irq_uninstall(drm_device_t *dev);
+extern int  gamma_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  gamma_find_devices(void);
+extern int  gamma_found(void);
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_bufs.c linux/drivers/char/drm-4.0/i810_bufs.c
--- linux.orig/drivers/char/drm-4.0/i810_bufs.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_bufs.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,339 @@
+/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include "linux/un.h"
+
+int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+   
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+	   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+		buf->offset = offset;
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t), 
+					     DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_i810_buf_priv_t);
+	   	memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t));
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+      
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+   
+	atomic_dec(&dev->buf_alloc);
+	dma->flags = _DRM_DMA_USE_AGP;
+	return 0;
+}
+
+int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return i810_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return -EINVAL;
+}
+
+int i810_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	DRM_DEBUG("count = %d\n", count);
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+
+				DRM_DEBUG("%d %d %d %d %d\n",
+					  i,
+					  dma->bufs[i].buf_count,
+					  dma->bufs[i].buf_size,
+					  dma->bufs[i].freelist.low_mark,
+					  dma->bufs[i].freelist.high_mark);
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int i810_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d, %d, %d\n",
+		  request.size, request.low_mark, request.high_mark);
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int i810_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("%d\n", request.count);
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_context.c linux/drivers/char/drm-4.0/i810_context.c
--- linux.orig/drivers/char/drm-4.0/i810_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,212 @@
+/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+
+static int i810_alloc_queue(drm_device_t *dev)
+{
+   	int temp = drm_ctxbitmap_next(dev);
+   	DRM_DEBUG("i810_alloc_queue: %d\n", temp);
+	return temp;
+}
+
+int i810_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                i810_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int i810_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = i810_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the i810 */
+	return 0;
+}
+
+int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return i810_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int i810_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	i810_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int i810_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+   	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_dma.c linux/drivers/char/drm-4.0/i810_dma.c
--- linux.orig/drivers/char/drm-4.0/i810_dma.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_dma.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1437 @@
+/* i810_dma.c -- DMA support for the i810 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "i810_drv.h"
+#include <linux/interrupt.h>	/* For task queue support */
+
+/* in case we don't have a 2.3.99-pre6 kernel or later: */
+#ifndef VM_DONTCOPY
+#define VM_DONTCOPY 0
+#endif
+
+#define I810_BUF_FREE		2
+#define I810_BUF_CLIENT		1
+#define I810_BUF_HARDWARE      	0
+
+#define I810_BUF_UNMAPPED 0
+#define I810_BUF_MAPPED   1
+
+#define I810_REG(reg)		2
+#define I810_BASE(reg)		((unsigned long) \
+				dev->maplist[I810_REG(reg)]->handle)
+#define I810_ADDR(reg)		(I810_BASE(reg) + reg)
+#define I810_DEREF(reg)		*(__volatile__ int *)I810_ADDR(reg)
+#define I810_READ(reg)		I810_DEREF(reg)
+#define I810_WRITE(reg,val) 	do { I810_DEREF(reg) = val; } while (0)
+#define I810_DEREF16(reg)	*(__volatile__ u16 *)I810_ADDR(reg)
+#define I810_READ16(reg)	I810_DEREF16(reg)
+#define I810_WRITE16(reg,val)	do { I810_DEREF16(reg) = val; } while (0)
+
+#define RING_LOCALS	unsigned int outring, ringmask; volatile char *virt;
+
+#define BEGIN_LP_RING(n) do {				\
+	if (I810_VERBOSE)				\
+		DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n",	\
+			  n, __FUNCTION__);		\
+	if (dev_priv->ring.space < n*4) 		\
+		i810_wait_ring(dev, n*4);		\
+	dev_priv->ring.space -= n*4;			\
+	outring = dev_priv->ring.tail;			\
+	ringmask = dev_priv->ring.tail_mask;		\
+	virt = dev_priv->ring.virtual_start;		\
+} while (0)
+
+#define ADVANCE_LP_RING() do {					\
+	if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n");	\
+	dev_priv->ring.tail = outring;				\
+	I810_WRITE(LP_RING + RING_TAIL, outring);		\
+} while(0)
+
+#define OUT_RING(n) do {						\
+	if (I810_VERBOSE) DRM_DEBUG("   OUT_RING %x\n", (int)(n));	\
+	*(volatile unsigned int *)(virt + outring) = n;			\
+	outring += 4;							\
+	outring &= ringmask;						\
+} while (0);
+
+static inline void i810_print_status_page(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+	u32 *temp = (u32 *)dev_priv->hw_status_page;
+   	int i;
+
+   	DRM_DEBUG(  "hw_status: Interrupt Status : %x\n", temp[0]);
+   	DRM_DEBUG(  "hw_status: LpRing Head ptr : %x\n", temp[1]);
+   	DRM_DEBUG(  "hw_status: IRing Head ptr : %x\n", temp[2]);
+      	DRM_DEBUG(  "hw_status: Reserved : %x\n", temp[3]);
+   	DRM_DEBUG(  "hw_status: Driver Counter : %d\n", temp[5]);
+   	for(i = 6; i < dma->buf_count + 6; i++) {
+	   	DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]);
+	}
+}
+
+static drm_buf_t *i810_freelist_get(drm_device_t *dev)
+{
+   	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+   	int 		 used;
+   
+	/* Linear search might not be the best solution */
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+		/* In use is already a pointer */
+	   	used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, 
+			       I810_BUF_CLIENT);
+	   	if(used == I810_BUF_FREE) {
+			return buf;
+		}
+	}
+   	return NULL;
+}
+
+/* This should only be called if the buffer is not sent to the hardware
+ * yet, the hardware updates in use for us once its on the ring buffer.
+ */
+
+static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	int used;
+   
+   	/* In use is already a pointer */
+   	used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE);
+   	if(used != I810_BUF_CLIENT) {
+	   	DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx);
+	   	return -EINVAL;
+	}
+   
+   	return 0;
+}
+
+static struct file_operations i810_buffer_fops = {
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 i810_mmap_buffers,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	    *priv	  = filp->private_data;
+	drm_device_t	    *dev;
+	drm_i810_private_t  *dev_priv;
+	drm_buf_t           *buf;
+	drm_i810_buf_priv_t *buf_priv;
+
+	lock_kernel();
+	dev	 = priv->dev;
+	dev_priv = dev->dev_private;
+	buf      = dev_priv->mmap_buffer;
+	buf_priv = buf->dev_private;
+   
+	vma->vm_flags |= (VM_IO | VM_DONTCOPY);
+	vma->vm_file = filp;
+   
+   	buf_priv->currently_mapped = I810_BUF_MAPPED;
+	unlock_kernel();
+
+	if (remap_page_range(vma->vm_start,
+			     VM_OFFSET(vma),
+			     vma->vm_end - vma->vm_start,
+			     vma->vm_page_prot)) return -EAGAIN;
+	return 0;
+}
+
+static int i810_map_buffer(drm_buf_t *buf, struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	struct file_operations *old_fops;
+	int retcode = 0;
+
+	if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL;
+
+	if(VM_DONTCOPY != 0) {
+		down_write(&current->mm->mmap_sem);
+		old_fops = filp->f_op;
+		filp->f_op = &i810_buffer_fops;
+		dev_priv->mmap_buffer = buf;
+		buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, 
+						    PROT_READ|PROT_WRITE,
+						    MAP_SHARED, 
+						    buf->bus_address);
+		dev_priv->mmap_buffer = NULL;
+   		filp->f_op = old_fops;
+		if ((unsigned long)buf_priv->virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed int)buf_priv->virtual;
+			buf_priv->virtual = 0;
+		}
+   		up_write(&current->mm->mmap_sem);
+	} else {
+		buf_priv->virtual = buf_priv->kernel_virtual;
+   		buf_priv->currently_mapped = I810_BUF_MAPPED;
+	}
+	return retcode;
+}
+
+static int i810_unmap_buffer(drm_buf_t *buf)
+{
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	int retcode = 0;
+
+	if(VM_DONTCOPY != 0) {
+		if(buf_priv->currently_mapped != I810_BUF_MAPPED) 
+			return -EINVAL;
+		down_write(&current->mm->mmap_sem);
+#if LINUX_VERSION_CODE < 0x020399
+        	retcode = do_munmap((unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#else
+        	retcode = do_munmap(current->mm, 
+				    (unsigned long)buf_priv->virtual, 
+				    (size_t) buf->total);
+#endif
+   		up_write(&current->mm->mmap_sem);
+	}
+   	buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+   	buf_priv->virtual = 0;
+
+	return retcode;
+}
+
+static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, 
+			       struct file *filp)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_buf_t	  *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	int retcode = 0;
+
+	buf = i810_freelist_get(dev);
+	if (!buf) {
+		retcode = -ENOMEM;
+	   	DRM_DEBUG("retcode=%d\n", retcode);
+		return retcode;
+	}
+   
+	retcode = i810_map_buffer(buf, filp);
+	if(retcode) {
+		i810_freelist_put(dev, buf);
+	   	DRM_DEBUG("mapbuf failed, retcode %d\n", retcode);
+		return retcode;
+	}
+	buf->pid     = priv->pid;
+	buf_priv = buf->dev_private;	
+	d->granted = 1;
+   	d->request_idx = buf->idx;
+   	d->request_size = buf->total;
+   	d->virtual = buf_priv->virtual;
+
+	return retcode;
+}
+
+static unsigned long i810_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+   
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) 
+		return 0;
+	
+	atomic_inc(&virt_to_page(address)->count);
+	set_bit(PG_locked, &virt_to_page(address)->flags);
+   
+	return address;
+}
+
+static void i810_free_page(drm_device_t *dev, unsigned long page)
+{
+	if(page == 0UL) 
+		return;
+	
+	atomic_dec(&virt_to_page(page)->count);
+	clear_bit(PG_locked, &virt_to_page(page)->flags);
+	wake_up(&virt_to_page(page)->wait);
+	free_page(page);
+	return;
+}
+
+static int i810_dma_cleanup(drm_device_t *dev)
+{
+	drm_device_dma_t *dma = dev->dma;
+
+	if(dev->dev_private) {
+		int i;
+	   	drm_i810_private_t *dev_priv = 
+	     		(drm_i810_private_t *) dev->dev_private;
+	   
+	   	if(dev_priv->ring.virtual_start) {
+		   	drm_ioremapfree((void *) dev_priv->ring.virtual_start,
+					dev_priv->ring.Size);
+		}
+	   	if(dev_priv->hw_status_page != 0UL) {
+		   	i810_free_page(dev, dev_priv->hw_status_page);
+		   	/* Need to rewrite hardware status page */
+		   	I810_WRITE(0x02080, 0x1ffff000);
+		}
+	   	drm_free(dev->dev_private, sizeof(drm_i810_private_t), 
+			 DRM_MEM_DRIVER);
+	   	dev->dev_private = NULL;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			drm_buf_t *buf = dma->buflist[ i ];
+			drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+			drm_ioremapfree(buf_priv->kernel_virtual, buf->total);
+		}
+	}
+   	return 0;
+}
+
+static int i810_wait_ring(drm_device_t *dev, int n)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+   	int iters = 0;
+   	unsigned long end;
+	unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+
+	end = jiffies + (HZ*3);
+   	while (ring->space < n) {
+	   	int i;
+	
+	   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+	   	ring->space = ring->head - (ring->tail+8);
+		if (ring->space < 0) ring->space += ring->Size;
+	   
+		if (ring->head != last_head)
+		   end = jiffies + (HZ*3);
+	  
+	   	iters++;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("space: %d wanted %d\n", ring->space, n);
+		   	DRM_ERROR("lockup\n");
+		   	goto out_wait_ring;
+		}
+
+	   	for (i = 0 ; i < 2000 ; i++) ;
+	}
+
+out_wait_ring:   
+   	return iters;
+}
+
+static void i810_kernel_lost_context(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	drm_i810_ring_buffer_t *ring = &(dev_priv->ring);
+      
+   	ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR;
+     	ring->tail = I810_READ(LP_RING + RING_TAIL);
+     	ring->space = ring->head - (ring->tail+8);
+     	if (ring->space < 0) ring->space += ring->Size;
+}
+
+static int i810_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	int my_idx = 24;
+   	u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx);
+   	int i;
+   
+   	if(dma->buf_count > 1019) {
+	   	/* Not enough space in the status page for the freelist */
+	   	return -EINVAL;
+	}
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+	   	buf_priv->in_use = hw_status++;
+	   	buf_priv->my_use_idx = my_idx;
+	   	my_idx += 4;
+
+	   	*buf_priv->in_use = I810_BUF_FREE;
+
+		buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, 
+						       buf->total);
+	}
+	return 0;
+}
+
+static int i810_dma_initialize(drm_device_t *dev, 
+			       drm_i810_private_t *dev_priv,
+			       drm_i810_init_t *init)
+{
+	drm_map_t *sarea_map;
+
+   	dev->dev_private = (void *) dev_priv;
+   	memset(dev_priv, 0, sizeof(drm_i810_private_t));
+
+   	if (init->ring_map_idx >= dev->map_count ||
+	    init->buffer_map_idx >= dev->map_count) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("ring_map or buffer_map are invalid\n");
+	   	return -EINVAL;
+	}
+   
+   	dev_priv->ring_map_idx = init->ring_map_idx;
+   	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_i810_sarea_t *) 
+		((u8 *)sarea_map->handle + 
+		 init->sarea_priv_offset);
+
+   	atomic_set(&dev_priv->flush_done, 0);
+	init_waitqueue_head(&dev_priv->flush_queue);
+   	
+   	dev_priv->ring.Start = init->ring_start;
+   	dev_priv->ring.End = init->ring_end;
+   	dev_priv->ring.Size = init->ring_size;
+
+   	dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + 
+						   init->ring_start, 
+						   init->ring_size);
+
+   	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;
+   
+   	if (dev_priv->ring.virtual_start == NULL) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("can not ioremap virtual address for"
+			  " ring buffer\n");
+	   	return -ENOMEM;
+	}
+
+	dev_priv->w = init->w;
+	dev_priv->h = init->h;
+	dev_priv->pitch = init->pitch;
+	dev_priv->back_offset = init->back_offset;
+	dev_priv->depth_offset = init->depth_offset;
+
+	dev_priv->front_di1 = init->front_offset | init->pitch_bits;
+	dev_priv->back_di1 = init->back_offset | init->pitch_bits;
+	dev_priv->zi1 = init->depth_offset | init->pitch_bits;
+	
+   
+   	/* Program Hardware Status Page */
+   	dev_priv->hw_status_page = i810_alloc_page(dev);
+   	memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE);
+   	if(dev_priv->hw_status_page == 0UL) {
+		i810_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate hardware status page\n");
+		return -ENOMEM;
+	}
+   	DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page);
+   
+   	I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page));
+   	DRM_DEBUG("Enabled hardware status page\n");
+   
+   	/* Now we need to init our freelist */
+   	if(i810_freelist_init(dev) != 0) {
+	   	i810_dma_cleanup(dev);
+	   	DRM_ERROR("Not enough space in the status page for"
+			  " the freelist\n");
+	   	return -ENOMEM;
+	}
+   	return 0;
+}
+
+int i810_dma_init(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t *priv = filp->private_data;
+   	drm_device_t *dev = priv->dev;
+   	drm_i810_private_t *dev_priv;
+   	drm_i810_init_t init;
+   	int retcode = 0;
+	
+  	if (copy_from_user(&init, (drm_i810_init_t *)arg, sizeof(init)))
+		return -EFAULT;
+	
+   	switch(init.func) {
+	 	case I810_INIT_DMA:
+	   		dev_priv = drm_alloc(sizeof(drm_i810_private_t), 
+					     DRM_MEM_DRIVER);
+	   		if(dev_priv == NULL) return -ENOMEM;
+	   		retcode = i810_dma_initialize(dev, dev_priv, &init);
+	   	break;
+	 	case I810_CLEANUP_DMA:
+	   		retcode = i810_dma_cleanup(dev);
+	   	break;
+	 	default:
+	   		retcode = -EINVAL;
+	   	break;
+	}
+   
+   	return retcode;
+}
+
+
+
+/* Most efficient way to verify state for the i810 is as it is
+ * emitted.  Non-conformant state is silently dropped.
+ *
+ * Use 'volatile' & local var tmp to force the emitted values to be
+ * identical to the verified ones.
+ */
+static void i810EmitContextVerified( drm_device_t *dev, 
+				     volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_CTX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_COLOR_FACTOR );
+	OUT_RING( code[I810_CTXREG_CF1] );
+
+	OUT_RING( GFX_OP_STIPPLE );
+	OUT_RING( code[I810_CTXREG_ST1] );
+
+	for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		} 
+	}
+
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+static void i810EmitTexVerified( drm_device_t *dev, 
+				 volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	int i, j = 0;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_TEX_SETUP_SIZE );
+
+	OUT_RING( GFX_OP_MAP_INFO );
+	OUT_RING( code[I810_TEXREG_MI1] );
+	OUT_RING( code[I810_TEXREG_MI2] );
+	OUT_RING( code[I810_TEXREG_MI3] );
+
+	for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) {
+		tmp = code[i];
+
+		if ((tmp & (7<<29)) == (3<<29) &&
+		    (tmp & (0x1f<<24)) < (0x1d<<24)) 
+		{
+			OUT_RING( tmp ); 
+			j++;
+		}
+	} 
+		
+	if (j & 1) 
+		OUT_RING( 0 ); 
+
+	ADVANCE_LP_RING();
+}
+
+
+/* Need to do some additional checking when setting the dest buffer.
+ */
+static void i810EmitDestVerified( drm_device_t *dev, 
+				  volatile unsigned int *code ) 
+{	
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	unsigned int tmp;
+	RING_LOCALS;
+
+	BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 );
+
+	tmp = code[I810_DESTREG_DI1];
+	if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) {
+		OUT_RING( CMD_OP_DESTBUFFER_INFO );
+		OUT_RING( tmp );
+	} else
+	   DRM_DEBUG("bad di1 %x (allow %x or %x)\n",
+		     tmp, dev_priv->front_di1, dev_priv->back_di1);
+
+	/* invarient:
+	 */
+	OUT_RING( CMD_OP_Z_BUFFER_INFO );
+	OUT_RING( dev_priv->zi1 );
+
+	OUT_RING( GFX_OP_DESTBUFFER_VARS );
+	OUT_RING( code[I810_DESTREG_DV1] );
+
+	OUT_RING( GFX_OP_DRAWRECT_INFO );
+	OUT_RING( code[I810_DESTREG_DR1] );
+	OUT_RING( code[I810_DESTREG_DR2] );
+	OUT_RING( code[I810_DESTREG_DR3] );
+	OUT_RING( code[I810_DESTREG_DR4] );
+	OUT_RING( 0 );
+
+	ADVANCE_LP_RING();
+}
+
+
+
+static void i810EmitState( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (dirty & I810_UPLOAD_BUFFERS) {
+		i810EmitDestVerified( dev, sarea_priv->BufferState );
+		sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS;
+	}
+
+	if (dirty & I810_UPLOAD_CTX) {
+		i810EmitContextVerified( dev, sarea_priv->ContextState );
+		sarea_priv->dirty &= ~I810_UPLOAD_CTX;
+	}
+
+	if (dirty & I810_UPLOAD_TEX0) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[0] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX0;
+	}
+
+	if (dirty & I810_UPLOAD_TEX1) {
+		i810EmitTexVerified( dev, sarea_priv->TexState[1] );
+		sarea_priv->dirty &= ~I810_UPLOAD_TEX1;
+	}
+}
+
+
+
+/* need to verify 
+ */
+static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, 
+				     unsigned int clear_color,
+				     unsigned int clear_zval )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int i;
+	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox ; i++, pbox++) {
+		unsigned int x = pbox->x1;
+		unsigned int y = pbox->y1;
+		unsigned int width = (pbox->x2 - x) * cpp;
+		unsigned int height = pbox->y2 - y;
+		unsigned int start = y * pitch + x * cpp;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+
+	   	if ( flags & I810_FRONT ) {	    
+		   	DRM_DEBUG("clear front\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_BACK ) {
+			DRM_DEBUG("clear back\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->back_offset + start );
+			OUT_RING( clear_color );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+
+		if ( flags & I810_DEPTH ) {
+			DRM_DEBUG("clear depth\n");
+			BEGIN_LP_RING( 6 );	    
+			OUT_RING( BR00_BITBLT_CLIENT | 
+				  BR00_OP_COLOR_BLT | 0x3 );
+			OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch );
+			OUT_RING( (height << 16) | width );
+			OUT_RING( dev_priv->depth_offset + start );
+			OUT_RING( clear_zval );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+		}
+	}
+}
+
+static void i810_dma_dispatch_swap( drm_device_t *dev )
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+      	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int pitch = dev_priv->pitch;
+	int cpp = 2;
+	int ofs = dev_priv->back_offset;
+	int i;
+	RING_LOCALS;
+
+	DRM_DEBUG("swapbuffers\n");
+
+  	i810_kernel_lost_context(dev);
+
+      	if (nbox > I810_NR_SAREA_CLIPRECTS)
+     		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	for (i = 0 ; i < nbox; i++, pbox++) 
+	{
+		unsigned int w = pbox->x2 - pbox->x1;
+		unsigned int h = pbox->y2 - pbox->y1;
+		unsigned int dst = pbox->x1*cpp + pbox->y1*pitch;
+		unsigned int start = ofs + dst;
+
+		if (pbox->x1 > pbox->x2 ||
+		    pbox->y1 > pbox->y2 ||
+		    pbox->x2 > dev_priv->w ||
+		    pbox->y2 > dev_priv->h)
+			continue;
+ 
+	   	DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n",
+			  pbox[i].x1, pbox[i].y1,
+			  pbox[i].x2, pbox[i].y2);
+
+		BEGIN_LP_RING( 6 );
+		OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 );
+		OUT_RING( pitch | (0xCC << 16));
+		OUT_RING( (h << 16) | (w * cpp));
+		OUT_RING( dst );
+		OUT_RING( pitch );	
+		OUT_RING( start );
+		ADVANCE_LP_RING();
+	}
+}
+
+
+static void i810_dma_dispatch_vertex(drm_device_t *dev, 
+				     drm_buf_t *buf,
+				     int discard,
+				     int used)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+   	drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	drm_clip_rect_t *box = sarea_priv->boxes;
+   	int nbox = sarea_priv->nbox;
+	unsigned long address = (unsigned long)buf->bus_address;
+	unsigned long start = address - dev->agp->base;     
+	int i = 0, u;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	if (nbox > I810_NR_SAREA_CLIPRECTS) 
+		nbox = I810_NR_SAREA_CLIPRECTS;
+
+	if (discard) {
+		u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+			    I810_BUF_HARDWARE);
+		if(u != I810_BUF_CLIENT) {
+			DRM_DEBUG("xxxx 2\n");
+		}
+	}
+
+	if (used > 4*1024) 
+		used = 0;
+
+	if (sarea_priv->dirty)
+	   i810EmitState( dev );
+
+  	DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", 
+		  address, used, nbox);
+
+   	dev_priv->counter++;
+   	DRM_DEBUG(  "dispatch counter : %ld\n", dev_priv->counter);
+   	DRM_DEBUG(  "i810_dma_dispatch\n");
+   	DRM_DEBUG(  "start : %lx\n", start);
+	DRM_DEBUG(  "used : %d\n", used);
+   	DRM_DEBUG(  "start + used - 4 : %ld\n", start + used - 4);
+
+	if (buf_priv->currently_mapped == I810_BUF_MAPPED) {
+		*(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE |
+					     sarea_priv->vertex_prim |
+					     ((used/4)-2));
+		
+		if (used & 4) {
+			*(u32 *)((u32)buf_priv->virtual + used) = 0;
+			used += 4;
+		}
+
+		i810_unmap_buffer(buf);
+	}
+		   
+	if (used) {
+		do {
+			if (i < nbox) {
+				BEGIN_LP_RING(4);
+				OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | 
+					  SC_ENABLE );
+				OUT_RING( GFX_OP_SCISSOR_INFO );
+				OUT_RING( box[i].x1 | (box[i].y1<<16) );
+				OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) );
+				ADVANCE_LP_RING();
+			}
+			
+			BEGIN_LP_RING(4);
+			OUT_RING( CMD_OP_BATCH_BUFFER );
+			OUT_RING( start | BB1_PROTECTED );
+			OUT_RING( start + used - 4 );
+			OUT_RING( 0 );
+			ADVANCE_LP_RING();
+			
+		} while (++i < nbox);
+	}
+
+	BEGIN_LP_RING(10);
+	OUT_RING( CMD_STORE_DWORD_IDX );
+	OUT_RING( 20 );
+	OUT_RING( dev_priv->counter );
+	OUT_RING( 0 );
+
+	if (discard) {
+		OUT_RING( CMD_STORE_DWORD_IDX );
+		OUT_RING( buf_priv->my_use_idx );
+		OUT_RING( I810_BUF_FREE );
+		OUT_RING( 0 );
+	}
+
+      	OUT_RING( CMD_REPORT_HEAD );
+	OUT_RING( 0 );
+   	ADVANCE_LP_RING();
+}
+
+
+/* Interrupts are only for flushing */
+static void i810_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+	drm_device_t	 *dev = (drm_device_t *)device;
+   	u16 temp;
+   
+	atomic_inc(&dev->total_irq);
+      	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+	else
+	   return;
+ 
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+}
+
+static void i810_dma_task_queue(void *device)
+{
+	drm_device_t *dev = (drm_device_t *) device;
+      	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+
+   	atomic_set(&dev_priv->flush_done, 1);
+   	wake_up_interruptible(&dev_priv->flush_queue);
+}
+
+int i810_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+	u16 temp;
+   
+	if (!irq)     return -EINVAL;
+	
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+	
+   	DRM_DEBUG(  "Interrupt Install : %d\n", irq);
+	DRM_DEBUG("%d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = i810_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+   	temp = I810_READ16(I810REG_HWSTAM);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_HWSTAM, temp);
+   	
+      	temp = I810_READ16(I810REG_INT_MASK_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+      	I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */
+
+				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   i810_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	temp = temp | 0x0003;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp); /* Enable bp & user interrupts */
+	return 0;
+}
+
+int i810_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+   	u16 temp;
+
+
+/*  	return 0; */
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+	
+	if (!irq) return -EINVAL;
+
+   	DRM_DEBUG(  "Interrupt UnInstall: %d\n", irq);	
+	DRM_DEBUG("%d\n", irq);
+   
+   	temp = I810_READ16(I810REG_INT_IDENTITY_R);
+   	temp = temp & ~(0x6000);
+   	if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, 
+				   temp); /* Clear all interrupts */
+   
+   	temp = I810_READ16(I810REG_INT_ENABLE_R);
+   	temp = temp & 0x6000;
+   	I810_WRITE16(I810REG_INT_ENABLE_R, 
+		     temp);                     /* Disable all interrupts */
+
+   	free_irq(irq, dev);
+
+	return 0;
+}
+
+int i810_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+	int		retcode;
+   
+   	DRM_DEBUG(  "i810_control\n");
+
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+	
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		if ((retcode = i810_irq_install(dev, ctl.irq)))
+			return retcode;
+		break;
+	case DRM_UNINST_HANDLER:
+		if ((retcode = i810_irq_uninstall(dev)))
+			return retcode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline void i810_dma_emit_flush(drm_device_t *dev)
+{
+   	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+   	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(2);
+      	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+      	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static inline void i810_dma_quiescent_emit(drm_device_t *dev)
+{
+      	drm_i810_private_t *dev_priv = dev->dev_private;
+   	RING_LOCALS;
+
+  	i810_kernel_lost_context(dev);
+
+   	BEGIN_LP_RING(4);
+   	OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE );
+   	OUT_RING( CMD_REPORT_HEAD );
+      	OUT_RING( 0 );
+      	OUT_RING( GFX_OP_USER_INTERRUPT );
+   	ADVANCE_LP_RING();
+
+/*  	i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */
+/*     	atomic_set(&dev_priv->flush_done, 1); */
+/*     	wake_up_interruptible(&dev_priv->flush_queue); */
+}
+
+static void i810_dma_quiescent(drm_device_t *dev)
+{
+      	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	unsigned long end;      
+
+   	if(dev_priv == NULL) {
+	   	return;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   
+   	for (;;) {
+		current->state = TASK_INTERRUPTIBLE;
+	      	i810_dma_quiescent_emit(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+   
+   	return;
+}
+
+static int i810_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	unsigned long end;
+   	int i, ret = 0;      
+
+   	if(dev_priv == NULL) {
+	   	return 0;
+	}
+      	atomic_set(&dev_priv->flush_done, 0);
+   	add_wait_queue(&dev_priv->flush_queue, &entry);
+   	end = jiffies + (HZ*3);
+   	for (;;) {
+		current->state = TASK_INTERRUPTIBLE;
+	      	i810_dma_emit_flush(dev);
+	   	if (atomic_read(&dev_priv->flush_done) == 1) break;
+		if((signed)(end - jiffies) <= 0) {
+		   	DRM_ERROR("lockup\n");
+		   	break;
+		}	   
+	      	schedule_timeout(HZ*3);
+	      	if (signal_pending(current)) {
+		   	ret = -EINTR; /* Can't restart */
+		   	break;
+		}
+	}
+   
+   	current->state = TASK_RUNNING;
+   	remove_wait_queue(&dev_priv->flush_queue, &entry);
+
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, 
+				   I810_BUF_FREE);
+
+		if (used == I810_BUF_HARDWARE)
+			DRM_DEBUG("reclaimed from HARDWARE\n");
+		if (used == I810_BUF_CLIENT)
+			DRM_DEBUG("still on client HARDWARE\n");
+	}
+
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void i810_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if (!dev->dev_private) return;
+	if (!dma->buflist) return;
+
+        i810_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_i810_buf_priv_t *buf_priv = buf->dev_private;
+	   
+		if (buf->pid == pid && buf_priv) {
+			int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, 
+					   I810_BUF_FREE);
+
+			if (used == I810_BUF_CLIENT)
+				DRM_DEBUG("reclaimed from client\n");
+		   	if(buf_priv->currently_mapped == I810_BUF_MAPPED)
+		     		buf_priv->currently_mapped = I810_BUF_UNMAPPED;
+		}
+	}
+}
+
+int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+   
+   	DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+		  lock.context, current->pid, dev->lock.hw_lock->lock,
+		  lock.flags);
+
+	if (lock.context < 0) {
+		return -EINVAL;
+	}
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+			
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+		   	DRM_DEBUG("Calling lock schedule\n");
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+	
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   DRM_DEBUG("fred\n");
+		   i810_dma_quiescent(dev);
+		}
+	}
+	DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+	return ret;
+}
+
+int i810_flush_ioctl(struct inode *inode, struct file *filp, 
+		     unsigned int cmd, unsigned long arg)
+{
+   	drm_file_t	  *priv	  = filp->private_data;
+   	drm_device_t	  *dev	  = priv->dev;
+   
+   	DRM_DEBUG("i810_flush_ioctl\n");
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_flush_ioctl called without lock held\n");
+		return -EINVAL;
+	}
+
+   	i810_flush_queue(dev);
+   	return 0;
+}
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_i810_vertex_t vertex;
+
+	if (copy_from_user(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex)))
+		return -EFAULT;
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n",
+		  vertex.idx, vertex.used, vertex.discard);
+
+	if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL;
+
+	i810_dma_dispatch_vertex( dev, 
+				  dma->buflist[ vertex.idx ], 
+				  vertex.discard, vertex.used );
+
+   	atomic_add(vertex.used, &dma->total_bytes);
+	atomic_inc(&dma->total_dmas);
+	sarea_priv->last_enqueue = dev_priv->counter-1;
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+   
+	return 0;
+}
+
+
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_i810_clear_t clear;
+
+   	if (copy_from_user(&clear, (drm_i810_clear_t *)arg, sizeof(clear)))
+		return -EFAULT;
+   
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_clear( dev, clear.flags, 
+				 clear.clear_color, 
+				 clear.clear_depth );
+   	return 0;
+}
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+   
+	DRM_DEBUG("i810_swap_bufs\n");
+
+   	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_swap_buf called without lock held\n");
+		return -EINVAL;
+	}
+
+	i810_dma_dispatch_swap( dev );
+   	return 0;
+}
+
+int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+   	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+      	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+      	sarea_priv->last_dispatch = (int) hw_status[5];
+	return 0;
+}
+
+int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	int		  retcode   = 0;
+	drm_i810_dma_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+
+	DRM_DEBUG("getbuf\n");
+   	if (copy_from_user(&d, (drm_i810_dma_t *)arg, sizeof(d)))
+		return -EFAULT;
+   
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+	
+	d.granted = 0;
+
+	retcode = i810_dma_get_buffer(dev, &d, filp);
+
+	DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n",
+		  current->pid, retcode, d.granted);
+
+	if (copy_to_user((drm_dma_t *)arg, &d, sizeof(d)))
+		return -EFAULT;
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return retcode;
+}
+
+int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	  *priv	    = filp->private_data;
+	drm_device_t	  *dev	    = priv->dev;
+	drm_i810_copy_t	  d;
+   	drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private;
+   	u32 *hw_status = (u32 *)dev_priv->hw_status_page;
+   	drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) 
+     					dev_priv->sarea_priv; 
+	drm_buf_t *buf;
+	drm_i810_buf_priv_t *buf_priv;
+	drm_device_dma_t *dma = dev->dma;
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("i810_dma called without lock held\n");
+		return -EINVAL;
+	}
+   
+   	if (copy_from_user(&d, (drm_i810_copy_t *)arg, sizeof(d)))
+		return -EFAULT;
+
+	if(d.idx < 0 || d.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[ d.idx ];
+   	buf_priv = buf->dev_private;
+	if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM;
+
+	/* Stopping end users copying their data to the entire kernel
+	   is good.. */
+	if (d.used < 0 || d.used > buf->total)
+		return -EINVAL;
+		
+   	if (copy_from_user(buf_priv->virtual, d.address, d.used))
+		return -EFAULT;
+
+   	sarea_priv->last_dispatch = (int) hw_status[5];
+
+	return 0;
+}
+
+int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	if(VM_DONTCOPY == 0) return 1;
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_drm.h linux/drivers/char/drm-4.0/i810_drm.h
--- linux.orig/drivers/char/drm-4.0/i810_drm.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_drm.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,194 @@
+#ifndef _I810_DRM_H_
+#define _I810_DRM_H_
+
+/* WARNING: These defines must be the same as what the Xserver uses.
+ * if you change them, you must change the defines in the Xserver.
+ */
+
+#ifndef _I810_DEFINES_
+#define _I810_DEFINES_
+
+#define I810_DMA_BUF_ORDER		12
+#define I810_DMA_BUF_SZ 		(1<<I810_DMA_BUF_ORDER)
+#define I810_DMA_BUF_NR 		256
+#define I810_NR_SAREA_CLIPRECTS 	8
+
+/* Each region is a minimum of 64k, and there are at most 64 of them.
+ */
+#define I810_NR_TEX_REGIONS 64
+#define I810_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+#define I810_UPLOAD_TEX0IMAGE  0x1 /* handled clientside */
+#define I810_UPLOAD_TEX1IMAGE  0x2 /* handled clientside */
+#define I810_UPLOAD_CTX        0x4
+#define I810_UPLOAD_BUFFERS    0x8
+#define I810_UPLOAD_TEX0       0x10
+#define I810_UPLOAD_TEX1       0x20
+#define I810_UPLOAD_CLIPRECTS  0x40
+
+
+/* Indices into buf.Setup where various bits of state are mirrored per
+ * context and per buffer.  These can be fired at the card as a unit,
+ * or in a piecewise fashion as required.
+ */
+
+/* Destbuffer state 
+ *    - backbuffer linear offset and pitch -- invarient in the current dri
+ *    - zbuffer linear offset and pitch -- also invarient
+ *    - drawing origin in back and depth buffers.
+ *
+ * Keep the depth/back buffer state here to acommodate private buffers
+ * in the future.
+ */
+#define I810_DESTREG_DI0  0	/* CMD_OP_DESTBUFFER_INFO (2 dwords) */
+#define I810_DESTREG_DI1  1
+#define I810_DESTREG_DV0  2	/* GFX_OP_DESTBUFFER_VARS (2 dwords) */
+#define I810_DESTREG_DV1  3
+#define I810_DESTREG_DR0  4	/* GFX_OP_DRAWRECT_INFO (4 dwords) */
+#define I810_DESTREG_DR1  5
+#define I810_DESTREG_DR2  6
+#define I810_DESTREG_DR3  7
+#define I810_DESTREG_DR4  8
+#define I810_DEST_SETUP_SIZE 10
+
+/* Context state
+ */
+#define I810_CTXREG_CF0   0	/* GFX_OP_COLOR_FACTOR */
+#define I810_CTXREG_CF1   1	
+#define I810_CTXREG_ST0   2     /* GFX_OP_STIPPLE */
+#define I810_CTXREG_ST1   3
+#define I810_CTXREG_VF    4	/* GFX_OP_VERTEX_FMT */
+#define I810_CTXREG_MT    5	/* GFX_OP_MAP_TEXELS */
+#define I810_CTXREG_MC0   6	/* GFX_OP_MAP_COLOR_STAGES - stage 0 */
+#define I810_CTXREG_MC1   7     /* GFX_OP_MAP_COLOR_STAGES - stage 1 */
+#define I810_CTXREG_MC2   8	/* GFX_OP_MAP_COLOR_STAGES - stage 2 */
+#define I810_CTXREG_MA0   9	/* GFX_OP_MAP_ALPHA_STAGES - stage 0 */
+#define I810_CTXREG_MA1   10	/* GFX_OP_MAP_ALPHA_STAGES - stage 1 */
+#define I810_CTXREG_MA2   11	/* GFX_OP_MAP_ALPHA_STAGES - stage 2 */
+#define I810_CTXREG_SDM   12	/* GFX_OP_SRC_DEST_MONO */
+#define I810_CTXREG_FOG   13	/* GFX_OP_FOG_COLOR */
+#define I810_CTXREG_B1    14	/* GFX_OP_BOOL_1 */
+#define I810_CTXREG_B2    15	/* GFX_OP_BOOL_2 */
+#define I810_CTXREG_LCS   16	/* GFX_OP_LINEWIDTH_CULL_SHADE_MODE */
+#define I810_CTXREG_PV    17	/* GFX_OP_PV_RULE -- Invarient! */
+#define I810_CTXREG_ZA    18	/* GFX_OP_ZBIAS_ALPHAFUNC */
+#define I810_CTXREG_AA    19	/* GFX_OP_ANTIALIAS */
+#define I810_CTX_SETUP_SIZE 20 
+
+/* Texture state (per tex unit)
+ */
+#define I810_TEXREG_MI0  0	/* GFX_OP_MAP_INFO (4 dwords) */
+#define I810_TEXREG_MI1  1	
+#define I810_TEXREG_MI2  2	
+#define I810_TEXREG_MI3  3	
+#define I810_TEXREG_MF   4	/* GFX_OP_MAP_FILTER */
+#define I810_TEXREG_MLC  5	/* GFX_OP_MAP_LOD_CTL */
+#define I810_TEXREG_MLL  6	/* GFX_OP_MAP_LOD_LIMITS */
+#define I810_TEXREG_MCS  7	/* GFX_OP_MAP_COORD_SETS ??? */
+#define I810_TEX_SETUP_SIZE 8
+
+#define I810_FRONT   0x1
+#define I810_BACK    0x2
+#define I810_DEPTH   0x4
+
+
+typedef struct _drm_i810_init {
+	enum {
+		I810_INIT_DMA = 0x01,
+		I810_CLEANUP_DMA = 0x02
+	} func;
+	int ring_map_idx;
+	int buffer_map_idx;
+	int sarea_priv_offset;
+	unsigned int ring_start;
+	unsigned int ring_end;
+	unsigned int ring_size;
+	unsigned int front_offset;
+	unsigned int back_offset;
+	unsigned int depth_offset;
+	unsigned int w;
+	unsigned int h;
+	unsigned int pitch;
+	unsigned int pitch_bits; 
+} drm_i810_init_t;
+
+/* Warning: If you change the SAREA structure you must change the Xserver
+ * structure as well */
+
+typedef struct _drm_i810_tex_region {
+	unsigned char next, prev; /* indices to form a circular LRU  */
+	unsigned char in_use;	/* owned by a client, or free? */
+	int age;		/* tracked by clients to update local LRU's */
+} drm_i810_tex_region_t;
+
+typedef struct _drm_i810_sarea {
+   	unsigned int ContextState[I810_CTX_SETUP_SIZE];
+   	unsigned int BufferState[I810_DEST_SETUP_SIZE];
+   	unsigned int TexState[2][I810_TEX_SETUP_SIZE];
+   	unsigned int dirty;
+
+	unsigned int nbox;
+	drm_clip_rect_t boxes[I810_NR_SAREA_CLIPRECTS];
+
+	/* Maintain an LRU of contiguous regions of texture space.  If
+	 * you think you own a region of texture memory, and it has an
+	 * age different to the one you set, then you are mistaken and
+	 * it has been stolen by another client.  If global texAge
+	 * hasn't changed, there is no need to walk the list.
+	 *
+	 * These regions can be used as a proxy for the fine-grained
+	 * texture information of other clients - by maintaining them
+	 * in the same lru which is used to age their own textures,
+	 * clients have an approximate lru for the whole of global
+	 * texture space, and can make informed decisions as to which
+	 * areas to kick out.  There is no need to choose whether to
+	 * kick out your own texture or someone else's - simply eject
+	 * them all in LRU order.  
+	 */
+   
+	drm_i810_tex_region_t texList[I810_NR_TEX_REGIONS+1]; 
+				/* Last elt is sentinal */
+        int texAge;		/* last time texture was uploaded */
+        int last_enqueue;	/* last time a buffer was enqueued */
+	int last_dispatch;	/* age of the most recently dispatched buffer */
+	int last_quiescent;     /*  */
+	int ctxOwner;		/* last context to upload state */
+
+	int vertex_prim;
+
+} drm_i810_sarea_t;
+
+typedef struct _drm_i810_clear {
+	int clear_color;
+	int clear_depth;
+	int flags;
+} drm_i810_clear_t;
+
+
+
+/* These may be placeholders if we have more cliprects than
+ * I810_NR_SAREA_CLIPRECTS.  In that case, the client sets discard to
+ * false, indicating that the buffer will be dispatched again with a
+ * new set of cliprects.
+ */
+typedef struct _drm_i810_vertex {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	int discard;		/* client is finished with the buffer? */
+} drm_i810_vertex_t;
+
+typedef struct _drm_i810_copy_t {
+   	int idx;		/* buffer index */
+	int used;		/* nr bytes in use */
+	void *address;		/* Address to copy from */
+} drm_i810_copy_t;
+
+typedef struct drm_i810_dma {
+	void *virtual;
+	int request_idx;
+	int request_size;
+	int granted;
+} drm_i810_dma_t;
+
+#endif /* _I810_DRM_H_ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_drv.c linux/drivers/char/drm-4.0/i810_drv.c
--- linux.orig/drivers/char/drm-4.0/i810_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,649 @@
+/* i810_drv.c -- I810 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "i810_drv.h"
+
+#define I810_NAME	 "i810"
+#define I810_DESC	 "Intel I810"
+#define I810_DATE	 "20000928"
+#define I810_MAJOR	 1
+#define I810_MINOR	 1
+#define I810_PATCHLEVEL	 0
+
+static drm_device_t	      i810_device;
+drm_ctx_t		      i810_res_ctx;
+
+static struct file_operations i810_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 i810_open,
+	flush:	 drm_flush,
+	release: i810_release,
+	ioctl:	 i810_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+      	poll:	 drm_poll,
+};
+
+static struct miscdevice      i810_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  I810_NAME,
+	fops:  &i810_fops,
+};
+
+static drm_ioctl_desc_t	      i810_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { i810_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { i810_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { i810_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { i810_markbufs,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { i810_infobufs,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { i810_freebufs,  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { i810_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { i810_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { i810_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { i810_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { i810_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { i810_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { i810_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { i810_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { i810_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)]   = { i810_dma_init,   1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)]  = { i810_clear_bufs, 1, 0 },
+      	[DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)]  = { i810_flush_ioctl,1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf,     1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)]   = { i810_swap_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)]   = { i810_copybuf,    1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy,     1, 0 },
+};
+
+#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls)
+
+#ifdef MODULE
+static char		      *i810 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Intel I810");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(i810, "s");
+
+#ifndef MODULE
+/* i810_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init i810_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("i810=", i810_options);
+#endif
+
+static int i810_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+#if DRM_DMA_HISTO
+	memset(&dev->histo, 0, sizeof(dev->histo));
+#endif
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int i810_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->irq) i810_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired) _drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* i810_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init i810_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(i810);
+#endif
+	DRM_DEBUG("doing misc_register\n");
+	if ((retcode = misc_register(&i810_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", I810_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, i810_misc.minor);
+	dev->name   = I810_NAME;
+
+   	DRM_DEBUG("doing mem init\n");
+	drm_mem_init();
+	DRM_DEBUG("doing proc init\n");
+	drm_proc_init(dev);
+	DRM_DEBUG("doing agp init\n");
+	dev->agp    = drm_agp_init();
+   	if(dev->agp == NULL) {
+	   	DRM_INFO("The i810 drm module requires the agpgart module"
+			 " to function correctly\nPlease load the agpgart"
+			 " module before you load the i810 module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&i810_misc);
+	   	i810_takedown(dev);
+	   	return -ENOMEM;
+	}
+	DRM_DEBUG("doing ctxbitmap init\n");
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&i810_misc);
+		i810_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 I810_NAME,
+		 I810_MAJOR,
+		 I810_MINOR,
+		 I810_PATCHLEVEL,
+		 I810_DATE,
+		 i810_misc.minor);
+
+	return 0;
+}
+
+/* i810_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit i810_cleanup(void)
+{
+	drm_device_t	      *dev = &i810_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&i810_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	i810_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(i810_init);
+module_exit(i810_cleanup);
+
+
+int i810_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))          \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = I810_MAJOR;
+	version.version_minor	   = I810_MINOR;
+	version.version_patchlevel = I810_PATCHLEVEL;
+
+	DRM_COPY(version.name, I810_NAME);
+	DRM_COPY(version.date, I810_DATE);
+	DRM_COPY(version.desc, I810_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int i810_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &i810_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return i810_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int i810_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	i810_reclaim_buffers(dev, priv->pid);
+		DRM_ERROR("Process %d dead, freeing lock for context %d\n",
+			  current->pid,
+			  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+
+				/* FIXME: may require heavy-handed reset of
+                                   hardware at this point, possibly
+                                   processed via a callback to the X
+                                   server. */
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	i810_reclaim_buffers(dev, priv->pid);
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+		return i810_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= I810_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &i810_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int i810_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+   	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+#if DRM_DMA_HISTOGRAM
+	atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles()
+						       - dev->lck_start)]);
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/i810_drv.h linux/drivers/char/drm-4.0/i810_drv.h
--- linux.orig/drivers/char/drm-4.0/i810_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/i810_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,225 @@
+/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _I810_DRV_H_
+#define _I810_DRV_H_
+
+typedef struct drm_i810_buf_priv {
+   	u32 *in_use;
+   	int my_use_idx;
+	int currently_mapped;
+	void *virtual;
+	void *kernel_virtual;
+	int map_count;
+   	struct vm_area_struct *vma;
+} drm_i810_buf_priv_t;
+
+typedef struct _drm_i810_ring_buffer{
+	int tail_mask;
+	unsigned long Start;
+	unsigned long End;
+	unsigned long Size;
+	u8 *virtual_start;
+	int head;
+	int tail;
+	int space;
+} drm_i810_ring_buffer_t;
+
+typedef struct drm_i810_private {
+   	int ring_map_idx;
+   	int buffer_map_idx;
+
+   	drm_i810_ring_buffer_t ring;
+	drm_i810_sarea_t *sarea_priv;
+
+      	unsigned long hw_status_page;
+   	unsigned long counter;
+
+   	atomic_t flush_done;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+	drm_buf_t *mmap_buffer;
+
+	
+	u32 front_di1, back_di1, zi1;
+	
+	int back_offset;
+	int depth_offset;
+	int w, h;
+	int pitch;
+} drm_i810_private_t;
+
+				/* i810_drv.c */
+extern int  i810_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_open(struct inode *inode, struct file *filp);
+extern int  i810_release(struct inode *inode, struct file *filp);
+extern int  i810_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* i810_dma.c */
+extern int  i810_dma_schedule(drm_device_t *dev, int locked);
+extern int  i810_getbuf(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  i810_irq_install(drm_device_t *dev, int irq);
+extern int  i810_irq_uninstall(drm_device_t *dev);
+extern int  i810_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_dma_init(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_flush_ioctl(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg);
+extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid);
+extern int  i810_getage(struct inode *inode, struct file *filp, unsigned int cmd,
+			unsigned long arg);
+extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma);
+extern int i810_copybuf(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int i810_docopy(struct inode *inode, struct file *filp, 
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_bufs.c */
+extern int  i810_addbufs(struct inode *inode, struct file *filp, 
+			unsigned int cmd, unsigned long arg);
+extern int  i810_infobufs(struct inode *inode, struct file *filp, 
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  i810_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+				/* i810_context.c */
+extern int  i810_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  i810_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  i810_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  i810_context_switch(drm_device_t *dev, int old, int new);
+extern int  i810_context_switch_complete(drm_device_t *dev, int new);
+
+#define I810_VERBOSE 0
+
+
+int i810_dma_vertex(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+int i810_swap_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg);
+
+int i810_clear_bufs(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg);
+
+#define GFX_OP_USER_INTERRUPT 		((0<<29)|(2<<23))
+#define GFX_OP_BREAKPOINT_INTERRUPT	((0<<29)|(1<<23))
+#define CMD_REPORT_HEAD			(7<<23)
+#define CMD_STORE_DWORD_IDX		((0x21<<23) | 0x1)
+#define CMD_OP_BATCH_BUFFER  ((0x0<<29)|(0x30<<23)|0x1)
+
+#define INST_PARSER_CLIENT   0x00000000
+#define INST_OP_FLUSH        0x02000000
+#define INST_FLUSH_MAP_CACHE 0x00000001
+
+
+#define BB1_START_ADDR_MASK   (~0x7)
+#define BB1_PROTECTED         (1<<0)
+#define BB1_UNPROTECTED       (0<<0)
+#define BB2_END_ADDR_MASK     (~0x7)
+
+#define I810REG_HWSTAM		0x02098
+#define I810REG_INT_IDENTITY_R	0x020a4
+#define I810REG_INT_MASK_R 	0x020a8
+#define I810REG_INT_ENABLE_R	0x020a0
+
+#define LP_RING     		0x2030
+#define HP_RING     		0x2040
+#define RING_TAIL      		0x00
+#define TAIL_ADDR		0x000FFFF8
+#define RING_HEAD      		0x04
+#define HEAD_WRAP_COUNT     	0xFFE00000
+#define HEAD_WRAP_ONE       	0x00200000
+#define HEAD_ADDR           	0x001FFFFC
+#define RING_START     		0x08
+#define START_ADDR          	0x00FFFFF8
+#define RING_LEN       		0x0C
+#define RING_NR_PAGES       	0x000FF000 
+#define RING_REPORT_MASK    	0x00000006
+#define RING_REPORT_64K     	0x00000002
+#define RING_REPORT_128K    	0x00000004
+#define RING_NO_REPORT      	0x00000000
+#define RING_VALID_MASK     	0x00000001
+#define RING_VALID          	0x00000001
+#define RING_INVALID        	0x00000000
+
+#define GFX_OP_SCISSOR         ((0x3<<29)|(0x1c<<24)|(0x10<<19))
+#define SC_UPDATE_SCISSOR       (0x1<<1)
+#define SC_ENABLE_MASK          (0x1<<0)
+#define SC_ENABLE               (0x1<<0)
+
+#define GFX_OP_SCISSOR_INFO    ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1))
+#define SCI_YMIN_MASK      (0xffff<<16)
+#define SCI_XMIN_MASK      (0xffff<<0)
+#define SCI_YMAX_MASK      (0xffff<<16)
+#define SCI_XMAX_MASK      (0xffff<<0)
+
+#define GFX_OP_COLOR_FACTOR      ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0)
+#define GFX_OP_STIPPLE           ((0x3<<29)|(0x1d<<24)|(0x83<<16))
+#define GFX_OP_MAP_INFO          ((0x3<<29)|(0x1d<<24)|0x2)
+#define GFX_OP_DESTBUFFER_VARS   ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
+#define GFX_OP_DRAWRECT_INFO     ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_PRIMITIVE         ((0x3<<29)|(0x1f<<24))
+
+#define CMD_OP_Z_BUFFER_INFO     ((0x0<<29)|(0x16<<23))
+#define CMD_OP_DESTBUFFER_INFO   ((0x0<<29)|(0x15<<23))
+
+#define BR00_BITBLT_CLIENT   0x40000000
+#define BR00_OP_COLOR_BLT    0x10000000
+#define BR00_OP_SRC_COPY_BLT 0x10C00000
+#define BR13_SOLID_PATTERN   0x80000000
+
+
+
+#endif
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/init.c linux/drivers/char/drm-4.0/init.c
--- linux.orig/drivers/char/drm-4.0/init.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/init.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,113 @@
+/* init.c -- Setup/Cleanup for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int			      drm_flags		= 0;
+
+/* drm_parse_option parses a single option.  See description for
+   drm_parse_options for details. */
+
+static void drm_parse_option(char *s)
+{
+	char *c, *r;
+	
+	DRM_DEBUG("\"%s\"\n", s);
+	if (!s || !*s) return;
+	for (c = s; *c && *c != ':'; c++); /* find : or \0 */
+	if (*c) r = c + 1; else r = NULL;  /* remember remainder */
+	*c = '\0';			   /* terminate */
+	if (!strcmp(s, "noctx")) {
+		drm_flags |= DRM_FLAG_NOCTX;
+		DRM_INFO("Server-mediated context switching OFF\n");
+		return;
+	}
+	if (!strcmp(s, "debug")) {
+		drm_flags |= DRM_FLAG_DEBUG;
+		DRM_INFO("Debug messages ON\n");
+		return;
+	}
+	DRM_ERROR("\"%s\" is not a valid option\n", s);
+	return;
+}
+
+/* drm_parse_options parse the insmod "drm=" options, or the command-line
+ * options passed to the kernel via LILO.  The grammar of the format is as
+ * follows:
+ *
+ * drm		::= 'drm=' option_list
+ * option_list	::= option [ ';' option_list ]
+ * option	::= 'device:' major
+ *		|   'debug' 
+ *		|   'noctx'
+ * major	::= INTEGER
+ *
+ * Note that 's' contains option_list without the 'drm=' part.
+ *
+ * device=major,minor specifies the device number used for /dev/drm
+ *	  if major == 0 then the misc device is used
+ *	  if major == 0 and minor == 0 then dynamic misc allocation is used
+ * debug=on specifies that debugging messages will be printk'd
+ * debug=trace specifies that each function call will be logged via printk
+ * debug=off turns off all debugging options
+ *
+ */
+
+void drm_parse_options(char *s)
+{
+	char *h, *t, *n;
+	
+	DRM_DEBUG("\"%s\"\n", s ?: "");
+	if (!s || !*s) return;
+
+	for (h = t = n = s; h && *h; h = n) {
+		for (; *t && *t != ';'; t++);	       /* find ; or \0 */
+		if (*t) n = t + 1; else n = NULL;      /* remember next */
+		*t = '\0';			       /* terminate */
+		drm_parse_option(h);		       /* parse */
+	}
+}
+
+/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0
+ * otherwise. */
+
+int drm_cpu_valid(void)
+{
+#if defined(__i386__)
+	if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */
+#endif
+#if defined(__sparc__) && !defined(__sparc_v9__)
+	if (1)
+		return 0; /* No cmpxchg before v9 sparc. */
+#endif
+	return 1;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/ioctl.c linux/drivers/char/drm-4.0/ioctl.c
--- linux.orig/drivers/char/drm-4.0/ioctl.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/ioctl.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,99 @@
+/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*-
+ * Created: Fri Jan  8 09:01:26 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_irq_busid_t p;
+	struct pci_dev	*dev;
+
+	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;
+	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)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u)))
+		return -EFAULT;
+	if (u.unique_len >= dev->unique_len) {
+		if (copy_to_user(u.unique, dev->unique, dev->unique_len))
+			return -EFAULT;
+	}
+	u.unique_len = dev->unique_len;
+	if (copy_to_user((drm_unique_t *)arg, &u, sizeof(u)))
+		return -EFAULT;
+	return 0;
+}
+
+int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_unique_t	 u;
+
+	if (dev->unique_len || dev->unique)
+		return -EBUSY;
+
+	if (copy_from_user(&u, (drm_unique_t *)arg, sizeof(u)))
+		return -EFAULT;
+
+	if (!u.unique_len || u.unique_len > 1024)
+		return -EINVAL;
+	
+	dev->unique_len = u.unique_len;
+	dev->unique	= drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER);
+	if (copy_from_user(dev->unique, u.unique, dev->unique_len))
+		return -EFAULT;
+	dev->unique[dev->unique_len] = '\0';
+
+	dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2,
+				 DRM_MEM_DRIVER);
+	sprintf(dev->devname, "%s@%s", dev->name, dev->unique);
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/lists.c linux/drivers/char/drm-4.0/lists.c
--- linux.orig/drivers/char/drm-4.0/lists.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/lists.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,218 @@
+/* lists.c -- Buffer list handling routines -*- linux-c -*-
+ * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_waitlist_create(drm_waitlist_t *bl, int count)
+{
+	if (bl->count) return -EINVAL;
+	
+	bl->count      = count;
+	bl->bufs       = drm_alloc((bl->count + 2) * sizeof(*bl->bufs),
+				   DRM_MEM_BUFLISTS);
+	bl->rp	       = bl->bufs;
+	bl->wp	       = bl->bufs;
+	bl->end	       = &bl->bufs[bl->count+1];
+	bl->write_lock = SPIN_LOCK_UNLOCKED;
+	bl->read_lock  = SPIN_LOCK_UNLOCKED;
+	return 0;
+}
+
+int drm_waitlist_destroy(drm_waitlist_t *bl)
+{
+	if (bl->rp != bl->wp) return -EINVAL;
+	if (bl->bufs) drm_free(bl->bufs,
+			       (bl->count + 2) * sizeof(*bl->bufs),
+			       DRM_MEM_BUFLISTS);
+	bl->count = 0;
+	bl->bufs  = NULL;
+	bl->rp	  = NULL;
+	bl->wp	  = NULL;
+	bl->end	  = NULL;
+	return 0;
+}
+
+int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf)
+{							
+	int	      left;
+	unsigned long flags;
+
+	left = DRM_LEFTCOUNT(bl);
+	if (!left) {
+		DRM_ERROR("Overflow while adding buffer %d from pid %d\n",
+			  buf->idx, buf->pid);
+		return -EINVAL;
+	}
+#if DRM_DMA_HISTOGRAM
+	buf->time_queued = get_cycles();
+#endif
+	buf->list	 = DRM_LIST_WAIT;
+	
+	spin_lock_irqsave(&bl->write_lock, flags);
+	*bl->wp = buf;
+	if (++bl->wp >= bl->end) bl->wp = bl->bufs;
+	spin_unlock_irqrestore(&bl->write_lock, flags);
+	
+	return 0;
+}
+
+drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl)
+{
+	drm_buf_t     *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bl->read_lock, flags);
+	buf = *bl->rp;
+	if (bl->rp == bl->wp) {
+		spin_unlock_irqrestore(&bl->read_lock, flags);
+		return NULL;
+	}				     
+	if (++bl->rp >= bl->end) bl->rp = bl->bufs;
+	spin_unlock_irqrestore(&bl->read_lock, flags);
+	
+	return buf;
+}
+
+int drm_freelist_create(drm_freelist_t *bl, int count)
+{
+	atomic_set(&bl->count, 0);
+	bl->next      = NULL;
+	init_waitqueue_head(&bl->waiting);
+	bl->low_mark  = 0;
+	bl->high_mark = 0;
+	atomic_set(&bl->wfh,   0);
+	bl->lock      = SPIN_LOCK_UNLOCKED;
+	++bl->initialized;
+	return 0;
+}
+
+int drm_freelist_destroy(drm_freelist_t *bl)
+{
+	atomic_set(&bl->count, 0);
+	bl->next = NULL;
+	return 0;
+}
+
+int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf)
+{
+	drm_device_dma_t *dma  = dev->dma;
+
+	if (!dma) {
+		DRM_ERROR("No DMA support\n");
+		return 1;
+	}
+
+	if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) {
+		DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	if (!bl) return 1;
+#if DRM_DMA_HISTOGRAM
+	buf->time_freed = get_cycles();
+	drm_histogram_compute(dev, buf);
+#endif
+	buf->list	= DRM_LIST_FREE;
+	
+	spin_lock(&bl->lock);
+	buf->next	= bl->next;
+	bl->next	= buf;
+	spin_unlock(&bl->lock);
+	
+	atomic_inc(&bl->count);
+	if (atomic_read(&bl->count) > dma->buf_count) {
+		DRM_ERROR("%d of %d buffers free after addition of %d\n",
+			  atomic_read(&bl->count), dma->buf_count, buf->idx);
+		return 1;
+	}
+				/* Check for high water mark */
+	if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) {
+		atomic_set(&bl->wfh, 0);
+		wake_up_interruptible(&bl->waiting);
+	}
+	return 0;
+}
+
+static drm_buf_t *drm_freelist_try(drm_freelist_t *bl)
+{
+	drm_buf_t	  *buf;
+
+	if (!bl) return NULL;
+	
+				/* Get buffer */
+	spin_lock(&bl->lock);
+	if (!bl->next) {
+		spin_unlock(&bl->lock);
+		return NULL;
+	}
+	buf	  = bl->next;
+	bl->next  = bl->next->next;
+	spin_unlock(&bl->lock);
+	
+	atomic_dec(&bl->count);
+	buf->next = NULL;
+	buf->list = DRM_LIST_NONE;
+	if (buf->waiting || buf->pending) {
+		DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n",
+			  buf->idx, buf->waiting, buf->pending, buf->list);
+	}
+	
+	return buf;
+}
+
+drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block)
+{
+	drm_buf_t	  *buf	= NULL;
+	DECLARE_WAITQUEUE(entry, current);
+
+	if (!bl || !bl->initialized) return NULL;
+	
+				/* Check for low water mark */
+	if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */
+		atomic_set(&bl->wfh, 1);
+	if (atomic_read(&bl->wfh)) {
+		if (block) {
+			add_wait_queue(&bl->waiting, &entry);
+			for (;;) {
+				current->state = TASK_INTERRUPTIBLE;
+				if (!atomic_read(&bl->wfh)
+				    && (buf = drm_freelist_try(bl))) break;
+				schedule();
+				if (signal_pending(current)) break;
+			}
+			current->state = TASK_RUNNING;
+			remove_wait_queue(&bl->waiting, &entry);
+		}
+		return buf;
+	}
+		
+	return drm_freelist_try(bl);
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/lock.c linux/drivers/char/drm-4.0/lock.c
--- linux.orig/drivers/char/drm-4.0/lock.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/lock.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,252 @@
+/* lock.c -- IOCTLs for locking -*- linux-c -*-
+ * Created: Tue Feb  2 08:37:54 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+int drm_block(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	DRM_DEBUG("\n");
+	return 0;
+}
+
+int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	do {
+		old = *lock;
+		if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT;
+		else			  new = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCKING_CONTEXT(old) == context) {
+		if (old & _DRM_LOCK_HELD) {
+			if (context != DRM_KERNEL_CONTEXT) {
+				DRM_ERROR("%d holds heavyweight lock\n",
+					  context);
+			}
+			return 0;
+		}
+	}
+	if (new == (context | _DRM_LOCK_HELD)) {
+				/* Have lock */
+		return 1;
+	}
+	return 0;
+}
+
+/* This takes a lock forcibly and hands it to context.	Should ONLY be used
+   inside *_unlock to give lock to kernel before calling *_dma_schedule. */
+int drm_lock_transfer(drm_device_t *dev,
+		      __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = context | _DRM_LOCK_HELD;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	return 1;
+}
+
+int drm_lock_free(drm_device_t *dev,
+		  __volatile__ unsigned int *lock, unsigned int context)
+{
+	unsigned int old, new, prev;
+	pid_t        pid = dev->lock.pid;
+
+	dev->lock.pid = 0;
+	do {
+		old  = *lock;
+		new  = 0;
+		prev = cmpxchg(lock, old, new);
+	} while (prev != old);
+	if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) {
+		DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n",
+			  context,
+			  _DRM_LOCKING_CONTEXT(old),
+			  pid);
+		return 1;
+	}
+	wake_up_interruptible(&dev->lock.lock_queue);
+	return 0;
+}
+
+static int drm_flush_queue(drm_device_t *dev, int context)
+{
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		atomic_inc(&q->block_write);
+		add_wait_queue(&q->flush_queue, &entry);
+		atomic_inc(&q->block_count);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!DRM_BUFCOUNT(&q->waitlist)) break;
+			schedule();
+			if (signal_pending(current)) {
+				ret = -EINTR; /* Can't restart */
+				break;
+			}
+		}
+		atomic_dec(&q->block_count);
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&q->flush_queue, &entry);
+	}
+	atomic_dec(&q->use_count);
+	atomic_inc(&q->total_flushed);
+		
+				/* NOTE: block_write is still incremented!
+				   Use drm_flush_unlock_queue to decrement. */
+	return ret;
+}
+
+static int drm_flush_unblock_queue(drm_device_t *dev, int context)
+{
+	drm_queue_t	  *q	= dev->queuelist[context];
+	
+	DRM_DEBUG("\n");
+	
+	atomic_inc(&q->use_count);
+	if (atomic_read(&q->use_count) > 1) {
+		if (atomic_read(&q->block_write)) {
+			atomic_dec(&q->block_write);
+			wake_up_interruptible(&q->write_queue);
+		}
+	}
+	atomic_dec(&q->use_count);
+	return 0;
+}
+
+int drm_flush_block_and_flush(drm_device_t *dev, int context,
+			      drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_queue(dev, i);
+		}
+	}
+	return ret;
+}
+
+int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags)
+{
+	int ret = 0;
+	int i;
+	
+	DRM_DEBUG("\n");
+	
+	if (flags & _DRM_LOCK_FLUSH) {
+		ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT);
+		if (!ret) ret = drm_flush_unblock_queue(dev, context);
+	}
+	if (flags & _DRM_LOCK_FLUSH_ALL) {
+		for (i = 0; !ret && i < dev->queue_count; i++) {
+			ret = drm_flush_unblock_queue(dev, i);
+		}
+	}
+		
+	return ret;
+}
+
+int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	int		  ret	  = 0;
+	drm_lock_t	  lock;
+
+	DRM_DEBUG("\n");
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+	ret = drm_flush_block_and_flush(dev, lock.context, lock.flags);
+	drm_flush_unblock(dev, lock.context, lock.flags);
+	return ret;
+}
+
+/* If we get here, it means that the process has called DRM_IOCTL_LOCK
+   without calling DRM_IOCTL_UNLOCK.
+   
+   If the lock is not held, then let the signal proceed as usual.
+   
+   If the lock is held, then set the contended flag and keep the signal
+   blocked.
+   
+
+   Return 1 if the signal should be delivered normally.
+   Return 0 if the signal should be blocked.  */
+
+int drm_notifier(void *priv)
+{
+	drm_sigdata_t *s = (drm_sigdata_t *)priv;
+	unsigned int  old, new, prev;
+
+
+				/* Allow signal delivery if lock isn't held */
+	if (!_DRM_LOCK_IS_HELD(s->lock->lock)
+	    || _DRM_LOCKING_CONTEXT(s->lock->lock) != s->context) return 1;
+	
+				/* Otherwise, set flag to force call to
+                                   drmUnlock */
+	do {
+		old  = s->lock->lock;
+		new  = old | _DRM_LOCK_CONT;
+		prev = cmpxchg(&s->lock->lock, old, new);
+	} while (prev != old);
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/memory.c linux/drivers/char/drm-4.0/memory.c
--- linux.orig/drivers/char/drm-4.0/memory.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/memory.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,448 @@
+/* memory.c -- Memory management wrappers for DRM -*- linux-c -*-
+ * Created: Thu Feb  4 14:00:34 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include <linux/wrapper.h>
+
+typedef struct drm_mem_stats {
+	const char	  *name;
+	int		  succeed_count;
+	int		  free_count;
+	int		  fail_count;
+	unsigned long	  bytes_allocated;
+	unsigned long	  bytes_freed;
+} drm_mem_stats_t;
+
+static spinlock_t	  drm_mem_lock	    = SPIN_LOCK_UNLOCKED;
+static unsigned long	  drm_ram_available = 0; /* In pages */
+static unsigned long	  drm_ram_used	    = 0;
+static drm_mem_stats_t	  drm_mem_stats[]   = {
+	[DRM_MEM_DMA]	    = { "dmabufs"  },
+	[DRM_MEM_SAREA]	    = { "sareas"   },
+	[DRM_MEM_DRIVER]    = { "driver"   },
+	[DRM_MEM_MAGIC]	    = { "magic"	   },
+	[DRM_MEM_IOCTLS]    = { "ioctltab" },
+	[DRM_MEM_MAPS]	    = { "maplist"  },
+	[DRM_MEM_VMAS]	    = { "vmalist"  },
+	[DRM_MEM_BUFS]	    = { "buflist"  },
+	[DRM_MEM_SEGS]	    = { "seglist"  },
+	[DRM_MEM_PAGES]	    = { "pagelist" },
+	[DRM_MEM_FILES]	    = { "files"	   },
+	[DRM_MEM_QUEUES]    = { "queues"   },
+	[DRM_MEM_CMDS]	    = { "commands" },
+	[DRM_MEM_MAPPINGS]  = { "mappings" },
+	[DRM_MEM_BUFLISTS]  = { "buflists" },
+	[DRM_MEM_AGPLISTS]  = { "agplist"  },
+	[DRM_MEM_TOTALAGP]  = { "totalagp" },
+	[DRM_MEM_BOUNDAGP]  = { "boundagp" },
+	[DRM_MEM_CTXBITMAP] = { "ctxbitmap"},
+	{ NULL, 0, }		/* Last entry must be null */
+};
+
+void drm_mem_init(void)
+{
+	drm_mem_stats_t *mem;
+	struct sysinfo	si;
+	
+	for (mem = drm_mem_stats; mem->name; ++mem) {
+		mem->succeed_count   = 0;
+		mem->free_count	     = 0;
+		mem->fail_count	     = 0;
+		mem->bytes_allocated = 0;
+		mem->bytes_freed     = 0;
+	}
+	
+	si_meminfo(&si);
+#if LINUX_VERSION_CODE < 0x020317
+				/* Changed to page count in 2.3.23 */
+	drm_ram_available = si.totalram >> PAGE_SHIFT;
+#else
+	drm_ram_available = si.totalram;
+#endif
+	drm_ram_used	  = 0;
+}
+
+/* drm_mem_info is called whenever a process reads /dev/drm/mem. */
+
+static int _drm_mem_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_mem_stats_t *pt;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("		  total counts			"
+		       " |    outstanding  \n");
+	DRM_PROC_PRINT("type	   alloc freed fail	bytes	   freed"
+		       " | allocs      bytes\n\n");
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "system", 0, 0, 0,
+		       drm_ram_available << (PAGE_SHIFT - 10));
+	DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB         |\n",
+		       "locked", 0, 0, 0, drm_ram_used >> 10);
+	DRM_PROC_PRINT("\n");
+	for (pt = drm_mem_stats; pt->name; pt++) {
+		DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n",
+			       pt->name,
+			       pt->succeed_count,
+			       pt->free_count,
+			       pt->fail_count,
+			       pt->bytes_allocated,
+			       pt->bytes_freed,
+			       pt->succeed_count - pt->free_count,
+			       (long)pt->bytes_allocated
+			       - (long)pt->bytes_freed);
+	}
+	
+	return len;
+}
+
+int drm_mem_info(char *buf, char **start, off_t offset, int len,
+		 int *eof, void *data)
+{
+	int ret;
+	
+	spin_lock(&drm_mem_lock);
+	ret = _drm_mem_info(buf, start, offset, len, eof, data);
+	spin_unlock(&drm_mem_lock);
+	return ret;
+}
+
+void *drm_alloc(size_t size, int area)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(area, "Allocating 0 bytes\n");
+		return NULL;
+	}
+	
+	if (!(pt = kmalloc(size, GFP_KERNEL))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
+{
+	void *pt;
+	
+	if (!(pt = drm_alloc(size, area))) return NULL;
+	if (oldpt && oldsize) {
+		memcpy(pt, oldpt, oldsize);
+		drm_free(oldpt, oldsize, area);
+	}
+	return pt;
+}
+
+char *drm_strdup(const char *s, int area)
+{
+	char *pt;
+	int	 length = s ? strlen(s) : 0;
+	
+	if (!(pt = drm_alloc(length+1, area))) return NULL;
+	strcpy(pt, s);
+	return pt;
+}
+
+void drm_strfree(const char *s, int area)
+{
+	unsigned int size;
+	
+	if (!s) return;
+	
+	size = 1 + (s ? strlen(s) : 0);
+	drm_free((void *)s, size, area);
+}
+
+void drm_free(void *pt, size_t size, int area)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n");
+	else	 kfree(pt);
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[area].bytes_freed += size;
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+unsigned long drm_alloc_pages(int order, int area)
+{
+	unsigned long address;
+	unsigned long bytes	  = PAGE_SIZE << order;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	spin_lock(&drm_mem_lock);
+	if ((drm_ram_used >> PAGE_SHIFT)
+	    > (DRM_RAM_PERCENT * drm_ram_available) / 100) {
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_unlock(&drm_mem_lock);
+	
+	address = __get_free_pages(GFP_KERNEL, order);
+	if (!address) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[area].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return 0;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_allocated += bytes;
+	drm_ram_used		            += bytes;
+	spin_unlock(&drm_mem_lock);
+	
+	
+				/* Zero outside the lock */
+	memset((void *)address, 0, bytes);
+	
+				/* Reserve */
+	for (addr = address, sz = bytes;
+	     sz > 0;
+	     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* Argument type changed in 2.4.0-test6/pre8 */
+		mem_map_reserve(virt_to_page(addr));
+#else
+		mem_map_reserve(MAP_NR(addr));
+#endif
+	}
+	
+	return address;
+}
+
+void drm_free_pages(unsigned long address, int order, int area)
+{
+	unsigned long bytes = PAGE_SIZE << order;
+	int		  alloc_count;
+	int		  free_count;
+	unsigned long addr;
+	unsigned int  sz;
+	
+	if (!address) {
+		DRM_MEM_ERROR(area, "Attempt to free address 0\n");
+	} else {
+				/* Unreserve */
+		for (addr = address, sz = bytes;
+		     sz > 0;
+		     addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* Argument type changed in 2.4.0-test6/pre8 */
+			mem_map_unreserve(virt_to_page(addr));
+#else
+			mem_map_unreserve(MAP_NR(addr));
+#endif
+		}
+		free_pages(address, order);
+	}
+	
+	spin_lock(&drm_mem_lock);
+	free_count  = ++drm_mem_stats[area].free_count;
+	alloc_count =	drm_mem_stats[area].succeed_count;
+	drm_mem_stats[area].bytes_freed += bytes;
+	drm_ram_used			-= bytes;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(area,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+void *drm_ioremap(unsigned long offset, unsigned long size)
+{
+	void *pt;
+	
+	if (!size) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Mapping 0 bytes at 0x%08lx\n", offset);
+		return NULL;
+	}
+	
+	if (!(pt = ioremap(offset, size))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count;
+		spin_unlock(&drm_mem_lock);
+		return NULL;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size;
+	spin_unlock(&drm_mem_lock);
+	return pt;
+}
+
+void drm_ioremapfree(void *pt, unsigned long size)
+{
+	int alloc_count;
+	int free_count;
+	
+	if (!pt)
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Attempt to free NULL pointer\n");
+	else
+		iounmap(pt);
+	
+	spin_lock(&drm_mem_lock);
+	drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size;
+	free_count  = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count;
+	alloc_count =	drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(DRM_MEM_MAPPINGS,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+agp_memory *drm_alloc_agp(int pages, u32 type)
+{
+	agp_memory *handle;
+
+	if (!pages) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n");
+		return NULL;
+	}
+	
+	if ((handle = drm_agp_allocate_memory(pages, type))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated
+			+= pages << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		return handle;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return NULL;
+}
+
+int drm_free_agp(agp_memory *handle, int pages)
+{
+	int           alloc_count;
+	int           free_count;
+	int           retval = -EINVAL;
+
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+			      "Attempt to free NULL AGP handle\n");
+		return retval;;
+	}
+	
+	if (drm_agp_free_memory(handle)) {
+		spin_lock(&drm_mem_lock);
+		free_count  = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count;
+		alloc_count =   drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed
+			+= pages << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		if (free_count > alloc_count) {
+			DRM_MEM_ERROR(DRM_MEM_TOTALAGP,
+				      "Excess frees: %d frees, %d allocs\n",
+				      free_count, alloc_count);
+		}
+		return 0;
+	}
+	return retval;
+}
+
+int drm_bind_agp(agp_memory *handle, unsigned int start)
+{
+	int retcode = -EINVAL;
+
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to bind NULL AGP handle\n");
+		return retcode;
+	}
+
+	if (!(retcode = drm_agp_bind_memory(handle, start))) {
+		spin_lock(&drm_mem_lock);
+		++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+		drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated
+			+= handle->page_count << PAGE_SHIFT;
+		spin_unlock(&drm_mem_lock);
+		return retcode;
+	}
+	spin_lock(&drm_mem_lock);
+	++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count;
+	spin_unlock(&drm_mem_lock);
+	return retcode;
+}
+
+int drm_unbind_agp(agp_memory *handle)
+{
+	int alloc_count;
+	int free_count;
+	int retcode = -EINVAL;
+	
+	if (!handle) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Attempt to unbind NULL AGP handle\n");
+		return retcode;
+	}
+
+	if ((retcode = drm_agp_unbind_memory(handle))) return retcode;
+	spin_lock(&drm_mem_lock);
+	free_count  = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count;
+	alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count;
+	drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed
+		+= handle->page_count << PAGE_SHIFT;
+	spin_unlock(&drm_mem_lock);
+	if (free_count > alloc_count) {
+		DRM_MEM_ERROR(DRM_MEM_BOUNDAGP,
+			      "Excess frees: %d frees, %d allocs\n",
+			      free_count, alloc_count);
+	}
+	return retcode;
+}
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_bufs.c linux/drivers/char/drm-4.0/mga_bufs.c
--- linux.orig/drivers/char/drm-4.0/mga_bufs.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_bufs.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,629 @@
+/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "linux/un.h"
+
+
+int mga_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_desc_t request;
+	drm_buf_entry_t *entry;
+	drm_buf_t *buf;
+	unsigned long offset;
+	unsigned long agp_offset;
+	int count;
+	int order;
+	int size;
+	int alignment;
+	int page_order;
+	int total;
+	int byte_count;
+	int i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count = request.count;
+	order = drm_order(request.size);
+	size	= 1 << order;
+	agp_offset = request.agp_start;
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total = PAGE_SIZE << page_order;
+	byte_count = 0;
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+   
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* This isnt neccessarily a good limit, but we have to stop a dumb
+	   32 bit overflow problem below */
+	   
+	if ( count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		   
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+   
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset = 0;
+
+   
+	while(entry->buf_count < count) {
+		buf = &entry->buflist[entry->buf_count];
+		buf->idx = dma->buf_count + entry->buf_count;
+		buf->total = alignment;
+		buf->order = order;
+		buf->used = 0;
+
+		buf->offset = offset; /* Hrm */
+		buf->bus_address = dev->agp->base + agp_offset + offset;
+		buf->address = (void *)(agp_offset + offset + dev->agp->base);
+		buf->next = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid = 0;
+
+		buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t),
+					     DRM_MEM_BUFS);
+		buf->dev_priv_size = sizeof(drm_mga_buf_priv_t);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed = 0;
+		buf->time_freed = 0;
+#endif
+		offset = offset + alignment;
+		entry->buf_count++;
+		byte_count += PAGE_SIZE << page_order;
+	}
+   
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+   
+	dma->buf_count  += entry->buf_count;
+
+	DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count);
+
+	dma->byte_count += byte_count;
+
+	DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count);
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+   
+	up(&dev->struct_sem);
+   
+	request.count = entry->buf_count;
+	request.size  = size;
+   
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+   
+	atomic_dec(&dev->buf_alloc);
+
+	DRM_DEBUG("count: %d\n", count);
+	DRM_DEBUG("order: %d\n", order);
+	DRM_DEBUG("size: %d\n", size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment: %d\n", alignment);
+	DRM_DEBUG("page_order: %d\n", page_order);
+	DRM_DEBUG("total: %d\n", total);
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	DRM_DEBUG("dma->flags : %x\n", dma->flags);
+
+	return 0;
+}
+
+int mga_addbufs_pci(struct inode *inode, struct file *filp, unsigned int cmd,
+		    unsigned long arg)
+{
+   	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 count;
+	int		 order;
+	int		 size;
+	int		 total;
+	int		 page_order;
+	drm_buf_entry_t	 *entry;
+	unsigned long	 page;
+	drm_buf_t	 *buf;
+	int		 alignment;
+	unsigned long	 offset;
+	int		 i;
+	int		 byte_count;
+	int		 page_count;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count	   = request.count;
+	order	   = drm_order(request.size);
+	size	   = 1 << order;
+	
+	DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
+		  request.count, request.size, size, order, dev->queue_count);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total	   = PAGE_SIZE << page_order;
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+	
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;	/* May only call once for each order */
+	}
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+	
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->seglist = drm_alloc(count * sizeof(*entry->seglist),
+				   DRM_MEM_SEGS);
+	if (!entry->seglist) {
+		drm_free(entry->buflist,
+			 count * sizeof(*entry->buflist),
+			 DRM_MEM_BUFS);
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->seglist, 0, count * sizeof(*entry->seglist));
+
+	dma->pagelist = drm_realloc(dma->pagelist,
+				    dma->page_count * sizeof(*dma->pagelist),
+				    (dma->page_count + (count << page_order))
+				    * sizeof(*dma->pagelist),
+				    DRM_MEM_PAGES);
+	DRM_DEBUG("pagelist: %d entries\n",
+		  dma->page_count + (count << page_order));
+
+
+	entry->buf_size	  = size;
+	entry->page_order = page_order;
+	byte_count	  = 0;
+	page_count	  = 0;
+	while (entry->buf_count < count) {
+		if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break;
+		entry->seglist[entry->seg_count++] = page;
+		for (i = 0; i < (1 << page_order); i++) {
+			DRM_DEBUG("page %d @ 0x%08lx\n",
+				  dma->page_count + page_count,
+				  page + PAGE_SIZE * i);
+			dma->pagelist[dma->page_count + page_count++]
+				= page + PAGE_SIZE * i;
+		}
+		for (offset = 0;
+		     offset + size <= total && entry->buf_count < count;
+		     offset += alignment, ++entry->buf_count) {
+			buf	     = &entry->buflist[entry->buf_count];
+			buf->idx     = dma->buf_count + entry->buf_count;
+			buf->total   = alignment;
+			buf->order   = order;
+			buf->used    = 0;
+			buf->offset  = (dma->byte_count + byte_count + offset);
+			buf->address = (void *)(page + offset);
+			buf->next    = NULL;
+			buf->waiting = 0;
+			buf->pending = 0;
+			init_waitqueue_head(&buf->dma_wait);
+			buf->pid     = 0;
+#if DRM_DMA_HISTOGRAM
+			buf->time_queued     = 0;
+			buf->time_dispatched = 0;
+			buf->time_completed  = 0;
+			buf->time_freed	     = 0;
+#endif
+			DRM_DEBUG("buffer %d @ %p\n",
+				  entry->buf_count, buf->address);
+		}
+		byte_count += PAGE_SIZE << page_order;
+	}
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count	+= entry->buf_count;
+	dma->seg_count	+= entry->seg_count;
+	dma->page_count += entry->seg_count << page_order;
+	dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order);
+	
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+	
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+
+int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_buf_desc_t	 request;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if(request.flags & _DRM_AGP_BUFFER)
+		return mga_addbufs_agp(inode, filp, cmd, arg);
+	else
+		return mga_addbufs_pci(inode, filp, cmd, arg);
+}
+
+int mga_infobufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_info_t	 request;
+	int		 i;
+	int		 count;
+
+	if (!dma) return -EINVAL;
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_info_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+		if (dma->bufs[i].buf_count) ++count;
+	}
+	
+	if (request.count >= count) {
+		for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) {
+			if (dma->bufs[i].buf_count) {
+				if (copy_to_user(&request.list[count].count,
+						 &dma->bufs[i].buf_count,
+						 sizeof(dma->bufs[0]
+							.buf_count)) ||
+				    copy_to_user(&request.list[count].size,
+						 &dma->bufs[i].buf_size,
+						 sizeof(dma->bufs[0].buf_size)) ||
+				    copy_to_user(&request.list[count].low_mark,
+						 &dma->bufs[i]
+						 .freelist.low_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.low_mark)) ||
+				    copy_to_user(&request.list[count]
+						 .high_mark,
+						 &dma->bufs[i]
+						 .freelist.high_mark,
+						 sizeof(dma->bufs[0]
+							.freelist.high_mark)))
+					return -EFAULT;
+				++count;
+			}
+		}
+	}
+	request.count = count;
+
+	if (copy_to_user((drm_buf_info_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+	
+	return 0;
+}
+
+int mga_markbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_desc_t	 request;
+	int		 order;
+	drm_buf_entry_t	 *entry;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	order = drm_order(request.size);
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	entry = &dma->bufs[order];
+
+	if (request.low_mark < 0 || request.low_mark > entry->buf_count)
+		return -EINVAL;
+	if (request.high_mark < 0 || request.high_mark > entry->buf_count)
+		return -EINVAL;
+
+	entry->freelist.low_mark  = request.low_mark;
+	entry->freelist.high_mark = request.high_mark;
+	
+	return 0;
+}
+
+int mga_freebufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	drm_buf_free_t	 request;
+	int		 i;
+	int		 idx;
+	drm_buf_t	 *buf;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_free_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	for (i = 0; i < request.count; i++) {
+		if (copy_from_user(&idx,
+				   &request.list[i],
+				   sizeof(idx)))
+			return -EFAULT;
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d freeing buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		drm_free_buffer(dev, buf);
+	}
+	
+	return 0;
+}
+
+int mga_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	int		 retcode = 0;
+	const int	 zero	 = 0;
+	unsigned long	 virtual;
+	unsigned long	 address;
+	drm_buf_map_t	 request;
+	int		 i;
+
+	if (!dma) return -EINVAL;
+	
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if(dma->flags & _DRM_DMA_USE_AGP) {
+			drm_mga_private_t *dev_priv = dev->dev_private;
+			drm_map_t *map = NULL;
+	 
+			map = dev->maplist[dev_priv->buffer_map_idx];
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			DRM_DEBUG("map->offset : %lx\n", map->offset);
+			DRM_DEBUG("map->size : %lx\n", map->size);
+			DRM_DEBUG("map->type : %d\n", map->type);
+			DRM_DEBUG("map->flags : %x\n", map->flags);
+			DRM_DEBUG("map->handle : %p\n", map->handle);
+			DRM_DEBUG("map->mtrr : %d\n", map->mtrr);
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size, 
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED, 
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			DRM_DEBUG("mmap error\n");
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+      
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+   
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	DRM_DEBUG("retcode : %d\n", retcode);
+
+	return retcode;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_context.c linux/drivers/char/drm-4.0/mga_context.c
--- linux.orig/drivers/char/drm-4.0/mga_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,209 @@
+/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	   Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+static int mga_alloc_queue(drm_device_t *dev)
+{
+   	return drm_ctxbitmap_next(dev);
+}
+
+int mga_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                mga_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int mga_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = mga_alloc_queue(dev);
+	}
+        if (ctx.handle == -1) {
+		return -ENOMEM;
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+   	/* This does nothing for the mga */
+	return 0;
+}
+
+int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return mga_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int mga_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	mga_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int mga_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	if(ctx.handle == DRM_KERNEL_CONTEXT+1) priv->remove_auth_on_close = 1;
+
+      	if(ctx.handle != DRM_KERNEL_CONTEXT) {
+	   	drm_ctxbitmap_free(dev, ctx.handle);
+	}
+	
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_dma.c linux/drivers/char/drm-4.0/mga_dma.c
--- linux.orig/drivers/char/drm-4.0/mga_dma.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_dma.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1051 @@
+/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+
+#define MGA_REG(reg)		2
+#define MGA_BASE(reg)		((unsigned long) \
+				((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle)
+#define MGA_ADDR(reg)		(MGA_BASE(reg) + reg)
+#define MGA_DEREF(reg)		*(__volatile__ int *)MGA_ADDR(reg)
+#define MGA_READ(reg)		MGA_DEREF(reg)
+#define MGA_WRITE(reg,val) 	do { MGA_DEREF(reg) = val; } while (0)
+
+#define PDEA_pagpxfer_enable 	     0x2
+
+static int mga_flush_queue(drm_device_t *dev);
+
+static unsigned long mga_alloc_page(drm_device_t *dev)
+{
+	unsigned long address;
+
+	address = __get_free_page(GFP_KERNEL);
+	if(address == 0UL) {
+		return 0;
+	}
+	atomic_inc(&virt_to_page(address)->count);
+	set_bit(PG_reserved, &virt_to_page(address)->flags);
+
+	return address;
+}
+
+static void mga_free_page(drm_device_t *dev, unsigned long page)
+{
+	if(!page) return;
+	atomic_dec(&virt_to_page(page)->count);
+	clear_bit(PG_reserved, &virt_to_page(page)->flags);
+	free_page(page);
+	return;
+}
+
+static void mga_delay(void)
+{
+	return;
+}
+
+/* These are two age tags that will never be sent to
+ * the hardware */
+#define MGA_BUF_USED 	0xffffffff
+#define MGA_BUF_FREE	0
+
+static int mga_freelist_init(drm_device_t *dev)
+{
+      	drm_device_dma_t *dma = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	int i;
+
+   	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	if(dev_priv->head == NULL) return -ENOMEM;
+   	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
+   	dev_priv->head->age = MGA_BUF_USED;
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		item = drm_alloc(sizeof(drm_mga_freelist_t),
+				 DRM_MEM_DRIVER);
+	   	if(item == NULL) return -ENOMEM;
+	   	memset(item, 0, sizeof(drm_mga_freelist_t));
+	  	item->age = MGA_BUF_FREE;
+	   	item->prev = dev_priv->head;
+	   	item->next = dev_priv->head->next;
+	   	if(dev_priv->head->next != NULL)
+			dev_priv->head->next->prev = item;
+	   	if(item->next == NULL) dev_priv->tail = item;
+	   	item->buf = buf;
+	   	buf_priv->my_freelist = item;
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+	   	dev_priv->head->next = item;
+	}
+
+   	return 0;
+}
+
+static void mga_freelist_cleanup(drm_device_t *dev)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	drm_mga_freelist_t *item;
+   	drm_mga_freelist_t *prev;
+
+   	item = dev_priv->head;
+   	while(item) {
+	   	prev = item;
+	   	item = item->next;
+	   	drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
+	}
+
+   	dev_priv->head = dev_priv->tail = NULL;
+}
+
+/* Frees dispatch lock */
+static inline void mga_dma_quiescent(drm_device_t *dev)
+{
+	drm_device_dma_t  *dma      = dev->dma;
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+   	unsigned long end;
+	int i;
+
+	DRM_DEBUG("dispatch_status = 0x%02lx\n", dev_priv->dispatch_status);
+	end = jiffies + (HZ*3);
+    	while(1) {
+		if(!test_and_set_bit(MGA_IN_DISPATCH,
+				     &dev_priv->dispatch_status)) {
+			break;
+		}
+	   	if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n",
+				  atomic_read(&dev->total_irq),
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup: dispatch_status = 0x%02lx,"
+				  " jiffies = %lu, end = %lu\n",
+				  dev_priv->dispatch_status, jiffies, end);
+			return;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();
+	}
+	end = jiffies + (HZ*3);
+    	DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS));
+    	while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+		if((signed)(end - jiffies) <= 0) {
+			DRM_ERROR("irqs: %d wanted %d\n",
+				  atomic_read(&dev->total_irq),
+				  atomic_read(&dma->total_lost));
+			DRM_ERROR("lockup\n");
+			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+			return;
+		}
+		for (i = 0 ; i < 2000 ; i++) mga_delay();
+	}
+    	sarea_priv->dirty |= MGA_DMA_FLUSH;
+
+    	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+	DRM_DEBUG("exit, dispatch_status = 0x%02lx\n",
+		  dev_priv->dispatch_status);
+}
+
+static void mga_reset_freelist(drm_device_t *dev)
+{
+   	drm_device_dma_t  *dma      = dev->dma;
+   	drm_buf_t *buf;
+   	drm_mga_buf_priv_t *buf_priv;
+	int i;
+
+   	for (i = 0; i < dma->buf_count; i++) {
+	   	buf = dma->buflist[ i ];
+	        buf_priv = buf->dev_private;
+		buf_priv->my_freelist->age = MGA_BUF_FREE;
+	}
+}
+
+/* Least recently used :
+ * These operations are not atomic b/c they are protected by the
+ * hardware lock */
+
+drm_buf_t *mga_freelist_get(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv =
+     		(drm_mga_private_t *) dev->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *next;
+	static int failed = 0;
+	int return_null = 0;
+
+	if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) {
+		DRM_DEBUG("Waiting on freelist,"
+			  " tail->age = %d, last_prim_age= %d\n",
+			  dev_priv->tail->age,
+			  dev_priv->last_prim_age);
+	   	add_wait_queue(&dev_priv->buf_queue, &entry);
+		set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+	   	for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+		   	mga_dma_schedule(dev, 0);
+			if(dev_priv->tail->age < dev_priv->last_prim_age)
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	schedule();
+		   	if (signal_pending(current)) {
+				++return_null;
+				break;
+			}
+		}
+		clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status);
+		current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->buf_queue, &entry);
+		if (return_null) return NULL;
+	}
+
+   	if(dev_priv->tail->age < dev_priv->last_prim_age) {
+		prev = dev_priv->tail->prev;
+	   	next = dev_priv->tail;
+	   	prev->next = NULL;
+	   	next->prev = next->next = NULL;
+	   	dev_priv->tail = prev;
+	   	next->age = MGA_BUF_USED;
+		failed = 0;
+	   	return next->buf;
+	}
+
+	failed++;
+   	return NULL;
+}
+
+int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf)
+{
+      	drm_mga_private_t *dev_priv =
+     		(drm_mga_private_t *) dev->dev_private;
+   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_freelist_t *prev;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *next;
+
+   	if(buf_priv->my_freelist->age == MGA_BUF_USED) {
+		/* Discarded buffer, put it on the tail */
+		next = buf_priv->my_freelist;
+		next->age = MGA_BUF_FREE;
+		prev = dev_priv->tail;
+		prev->next = next;
+		next->prev = prev;
+		next->next = NULL;
+		dev_priv->tail = next;
+	} else {
+		/* Normally aged buffer, put it on the head + 1,
+		 * as the real head is a sentinal element
+		 */
+		next = buf_priv->my_freelist;
+		head = dev_priv->head;
+		prev = head->next;
+		head->next = next;
+		prev->prev = next;
+		next->prev = head;
+		next->next = prev;
+	}
+
+   	return 0;
+}
+
+static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init)
+{
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_prim_buf_t *prim_buffer;
+   	int i, temp, size_of_buf;
+   	int offset = init->reserved_map_agpstart;
+
+   	dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) /
+				  PAGE_SIZE) * PAGE_SIZE;
+   	size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS;
+	dev_priv->warp_ucode_size = init->warp_ucode_size;
+   	dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) *
+					(MGA_NUM_PRIM_BUFS + 1),
+					DRM_MEM_DRIVER);
+   	if(dev_priv->prim_bufs == NULL) {
+		DRM_ERROR("Unable to allocate memory for prim_buf\n");
+		return -ENOMEM;
+	}
+   	memset(dev_priv->prim_bufs,
+	       0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1));
+
+   	temp = init->warp_ucode_size + dev_priv->primary_size;
+	temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
+
+	dev_priv->ioremap = drm_ioremap(dev->agp->base + offset,
+					temp);
+	if(dev_priv->ioremap == NULL) {
+		DRM_ERROR("Ioremap failed\n");
+		return -ENOMEM;
+	}
+   	init_waitqueue_head(&dev_priv->wait_queue);
+
+   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+	   	prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t),
+					DRM_MEM_DRIVER);
+	   	if(prim_buffer == NULL) return -ENOMEM;
+	   	memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t));
+	   	prim_buffer->phys_head = offset + dev->agp->base;
+	   	prim_buffer->current_dma_ptr =
+			prim_buffer->head =
+			(u32 *) (dev_priv->ioremap +
+				 offset -
+				 init->reserved_map_agpstart);
+	   	prim_buffer->num_dwords = 0;
+	   	prim_buffer->max_dwords = size_of_buf / sizeof(u32);
+	   	prim_buffer->max_dwords -= 5; /* Leave room for the softrap */
+	   	prim_buffer->sec_used = 0;
+	   	prim_buffer->idx = i;
+		prim_buffer->prim_age = i + 1;
+	   	offset = offset + size_of_buf;
+	   	dev_priv->prim_bufs[i] = prim_buffer;
+	}
+	dev_priv->current_prim_idx = 0;
+        dev_priv->next_prim =
+		dev_priv->last_prim =
+		dev_priv->current_prim =
+        	dev_priv->prim_bufs[0];
+	dev_priv->next_prim_age = 2;
+	dev_priv->last_prim_age = 1;
+   	set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status);
+   	return 0;
+}
+
+void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim)
+{
+       	drm_mga_private_t *dev_priv = dev->dev_private;
+      	drm_device_dma_t  *dma	    = dev->dma;
+       	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+ 	int use_agp = PDEA_pagpxfer_enable;
+	unsigned long end;
+   	int i;
+   	int next_idx;
+       	PRIMLOCALS;
+
+   	dev_priv->last_prim = prim;
+
+ 	/* We never check for overflow, b/c there is always room */
+    	PRIMPTR(prim);
+   	if(num_dwords <= 0) {
+		DRM_ERROR("num_dwords == 0 when dispatched\n");
+		goto out_prim_wait;
+	}
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+ 	PRIMOUTREG( MGAREG_DMAPAD, 0);
+       	PRIMOUTREG( MGAREG_DMAPAD, 0);
+   	PRIMOUTREG( MGAREG_SOFTRAP, 0);
+    	PRIMFINISH(prim);
+
+	end = jiffies + (HZ*3);
+    	if(sarea_priv->dirty & MGA_DMA_FLUSH) {
+		while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n",
+					  atomic_read(&dev->total_irq),
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup (flush)\n");
+				goto out_prim_wait;
+			}
+
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+		sarea_priv->dirty &= ~(MGA_DMA_FLUSH);
+	} else {
+		while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) {
+			if((signed)(end - jiffies) <= 0) {
+				DRM_ERROR("irqs: %d wanted %d\n",
+					  atomic_read(&dev->total_irq),
+					  atomic_read(&dma->total_lost));
+				DRM_ERROR("lockup (wait)\n");
+				goto out_prim_wait;
+			}
+
+			for (i = 0 ; i < 4096 ; i++) mga_delay();
+		}
+	}
+
+   	mga_flush_write_combine();
+    	atomic_inc(&dev_priv->pending_bufs);
+       	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+ 	MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp);
+   	prim->num_dwords = 0;
+	sarea_priv->last_enqueue = prim->prim_age;
+
+   	next_idx = prim->idx + 1;
+    	if(next_idx >= MGA_NUM_PRIM_BUFS)
+		next_idx = 0;
+
+    	dev_priv->next_prim = dev_priv->prim_bufs[next_idx];
+	return;
+
+ out_prim_wait:
+	prim->num_dwords = 0;
+	prim->sec_used = 0;
+	clear_bit(MGA_BUF_IN_USE, &prim->buffer_status);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status);
+	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+}
+
+int mga_advance_primary(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+   	drm_mga_private_t *dev_priv = dev->dev_private;
+   	drm_mga_prim_buf_t *prim_buffer;
+   	drm_device_dma_t  *dma      = dev->dma;
+   	int next_prim_idx;
+   	int ret = 0;
+
+   	/* This needs to reset the primary buffer if available,
+	 * we should collect stats on how many times it bites
+	 * it's tail */
+
+   	next_prim_idx = dev_priv->current_prim_idx + 1;
+   	if(next_prim_idx >= MGA_NUM_PRIM_BUFS)
+     		next_prim_idx = 0;
+   	prim_buffer = dev_priv->prim_bufs[next_prim_idx];
+	set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+      	/* In use is cleared in interrupt handler */
+
+   	if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) {
+	   	add_wait_queue(&dev_priv->wait_queue, &entry);
+	   	for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+		   	mga_dma_schedule(dev, 0);
+		   	if(!test_and_set_bit(MGA_BUF_IN_USE,
+					     &prim_buffer->buffer_status))
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+		   	atomic_inc(&dma->total_missed_sched);
+		   	schedule();
+		   	if (signal_pending(current)) {
+			   	ret = -ERESTARTSYS;
+			   	break;
+			}
+		}
+		current->state = TASK_RUNNING;
+	   	remove_wait_queue(&dev_priv->wait_queue, &entry);
+	   	if(ret) return ret;
+	}
+	clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status);
+
+   	/* This primary buffer is now free to use */
+   	prim_buffer->current_dma_ptr = prim_buffer->head;
+   	prim_buffer->num_dwords = 0;
+   	prim_buffer->sec_used = 0;
+	prim_buffer->prim_age = dev_priv->next_prim_age++;
+	if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) {
+		mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+		mga_reset_freelist(dev);
+		prim_buffer->prim_age = (dev_priv->next_prim_age += 2);
+	}
+
+	/* Reset all buffer status stuff */
+	clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status);
+	clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status);
+
+   	dev_priv->current_prim = prim_buffer;
+   	dev_priv->current_prim_idx = next_prim_idx;
+   	return 0;
+}
+
+/* More dynamic performance decisions */
+static inline int mga_decide_to_fire(drm_device_t *dev)
+{
+   	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+   	if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) {
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	return 1;
+	}
+
+	if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	    dev_priv->next_prim->num_dwords) {
+	   	return 1;
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) {
+		if(test_bit(MGA_BUF_SWAP_PENDING,
+			    &dev_priv->next_prim->buffer_status)) {
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) {
+			return 1;
+		}
+	}
+
+   	if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) {
+		if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) {
+			return 1;
+		}
+	}
+
+   	return 0;
+}
+
+int mga_dma_schedule(drm_device_t *dev, int locked)
+{
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+	int               retval    = 0;
+
+   	if (!dev_priv) return -EBUSY;
+	
+	if (test_and_set_bit(0, &dev->dma_flag)) {
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+
+   	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) ||
+	   test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) ||
+	   test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) {
+		locked = 1;
+	}
+
+   	if (!locked &&
+	    !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) {
+	   	clear_bit(0, &dev->dma_flag);
+		retval = -EBUSY;
+		goto sch_out_wakeup;
+	}
+
+   	if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) {
+	   	/* Fire dma buffer */
+	   	if(mga_decide_to_fire(dev)) {
+			clear_bit(MGA_BUF_FORCE_FIRE,
+				  &dev_priv->next_prim->buffer_status);
+		   	if(dev_priv->current_prim == dev_priv->next_prim) {
+				/* Schedule overflow for a later time */
+				set_bit(MGA_BUF_NEEDS_OVERFLOW,
+					&dev_priv->next_prim->buffer_status);
+			}
+		   	mga_fire_primary(dev, dev_priv->next_prim);
+		} else {
+			clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+		}
+	}
+
+	if (!locked) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+	clear_bit(0, &dev->dma_flag);
+
+sch_out_wakeup:
+      	if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) &&
+	   atomic_read(&dev_priv->pending_bufs) == 0) {
+		/* Everything has been processed by the hardware */
+		clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		wake_up_interruptible(&dev_priv->flush_queue);
+	}
+
+	if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)
+	   && dev_priv->tail->age < dev_priv->last_prim_age)
+		wake_up_interruptible(&dev_priv->buf_queue);
+
+	return retval;
+}
+
+static void mga_dma_service(int irq, void *device, struct pt_regs *regs)
+{
+    	drm_device_t	 *dev = (drm_device_t *)device;
+    	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+    	drm_mga_prim_buf_t *last_prim_buffer;
+
+    	atomic_inc(&dev->total_irq);
+	if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return;
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+   	last_prim_buffer = dev_priv->last_prim;
+    	last_prim_buffer->num_dwords = 0;
+    	last_prim_buffer->sec_used = 0;
+	dev_priv->sarea_priv->last_dispatch =
+		dev_priv->last_prim_age = last_prim_buffer->prim_age;
+      	clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status);
+      	clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status);
+      	clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status);
+      	atomic_dec(&dev_priv->pending_bufs);
+   	queue_task(&dev->tq, &tq_immediate);
+   	mark_bh(IMMEDIATE_BH);
+   	wake_up_interruptible(&dev_priv->wait_queue);
+}
+
+static void mga_dma_task_queue(void *device)
+{
+	mga_dma_schedule((drm_device_t *)device, 0);
+}
+
+int mga_dma_cleanup(drm_device_t *dev)
+{
+	if(dev->dev_private) {
+		drm_mga_private_t *dev_priv =
+			(drm_mga_private_t *) dev->dev_private;
+
+		if (dev->irq) mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+
+		if(dev_priv->ioremap) {
+			int temp = (dev_priv->warp_ucode_size +
+				    dev_priv->primary_size +
+				    PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE;
+
+			drm_ioremapfree((void *) dev_priv->ioremap, temp);
+		}
+	   	if(dev_priv->status_page != NULL) {
+		   	iounmap(dev_priv->status_page);
+		}
+	   	if(dev_priv->real_status_page != 0UL) {
+		   	mga_free_page(dev, dev_priv->real_status_page);
+		}
+	   	if(dev_priv->prim_bufs != NULL) {
+		   	int i;
+		   	for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) {
+			   	if(dev_priv->prim_bufs[i] != NULL) {
+			     		drm_free(dev_priv->prim_bufs[i],
+						 sizeof(drm_mga_prim_buf_t),
+						 DRM_MEM_DRIVER);
+				}
+			}
+		   	drm_free(dev_priv->prim_bufs, sizeof(void *) *
+				 (MGA_NUM_PRIM_BUFS + 1),
+				 DRM_MEM_DRIVER);
+		}
+		if(dev_priv->head != NULL) {
+		   	mga_freelist_cleanup(dev);
+		}
+
+
+		drm_free(dev->dev_private, sizeof(drm_mga_private_t),
+			 DRM_MEM_DRIVER);
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) {
+	drm_mga_private_t *dev_priv;
+	drm_map_t *sarea_map = NULL;
+
+	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
+	if(dev_priv == NULL) return -ENOMEM;
+	dev->dev_private = (void *) dev_priv;
+
+	memset(dev_priv, 0, sizeof(drm_mga_private_t));
+
+	if((init->reserved_map_idx >= dev->map_count) ||
+	   (init->buffer_map_idx >= dev->map_count)) {
+		mga_dma_cleanup(dev);
+		return -EINVAL;
+	}
+
+	dev_priv->reserved_map_idx = init->reserved_map_idx;
+	dev_priv->buffer_map_idx = init->buffer_map_idx;
+	sarea_map = dev->maplist[0];
+	dev_priv->sarea_priv = (drm_mga_sarea_t *)
+		((u8 *)sarea_map->handle +
+		 init->sarea_priv_offset);
+
+	/* Scale primary size to the next page */
+	dev_priv->chipset = init->chipset;
+	dev_priv->frontOffset = init->frontOffset;
+	dev_priv->backOffset = init->backOffset;
+	dev_priv->depthOffset = init->depthOffset;
+	dev_priv->textureOffset = init->textureOffset;
+	dev_priv->textureSize = init->textureSize;
+	dev_priv->cpp = init->cpp;
+	dev_priv->sgram = init->sgram;
+	dev_priv->stride = init->stride;
+
+	dev_priv->mAccess = init->mAccess;
+   	init_waitqueue_head(&dev_priv->flush_queue);
+	init_waitqueue_head(&dev_priv->buf_queue);
+	dev_priv->WarpPipe = 0xff000000;
+	dev_priv->vertexsize = 0;
+
+   	DRM_DEBUG("chipset=%d ucode_size=%d backOffset=%x depthOffset=%x\n",
+		  dev_priv->chipset, dev_priv->warp_ucode_size,
+		  dev_priv->backOffset, dev_priv->depthOffset);
+   	DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n",
+		  dev_priv->cpp, dev_priv->sgram, dev_priv->stride,
+		  dev_priv->mAccess);
+
+	memcpy(&dev_priv->WarpIndex, &init->WarpIndex,
+	       sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES);
+
+   	if(mga_init_primary_bufs(dev, init) != 0) {
+		DRM_ERROR("Can not initialize primary buffers\n");
+		mga_dma_cleanup(dev);
+		return -ENOMEM;
+	}
+   	dev_priv->real_status_page = mga_alloc_page(dev);
+      	if(dev_priv->real_status_page == 0UL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not allocate status page\n");
+		return -ENOMEM;
+	}
+
+   	dev_priv->status_page =
+		ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page),
+				PAGE_SIZE);
+
+   	if(dev_priv->status_page == NULL) {
+		mga_dma_cleanup(dev);
+		DRM_ERROR("Can not remap status page\n");
+		return -ENOMEM;
+	}
+
+   	/* Write status page when secend or softrap occurs */
+   	MGA_WRITE(MGAREG_PRIMPTR,
+		  virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003);
+
+
+	/* Private is now filled in, initialize the hardware */
+	{
+		PRIMLOCALS;
+		PRIMGETPTR( dev_priv );
+
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0x0100);
+		PRIMOUTREG(MGAREG_SOFTRAP, 0);
+		/* Poll for the first buffer to insure that
+		 * the status register will be correct
+		 */
+
+		mga_flush_write_combine();
+	   	MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL);
+
+		MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) |
+					   PDEA_pagpxfer_enable));
+
+	   	while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ;
+	}
+
+	if(mga_freelist_init(dev) != 0) {
+	   	DRM_ERROR("Could not initialize freelist\n");
+	   	mga_dma_cleanup(dev);
+	   	return -ENOMEM;
+	}
+	return 0;
+}
+
+int mga_dma_init(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_init_t init;
+
+	if (copy_from_user(&init, (drm_mga_init_t *)arg, sizeof(init)))
+		return -EFAULT;
+
+	switch(init.func) {
+	case MGA_INIT_DMA:
+		return mga_dma_initialize(dev, &init);
+	case MGA_CLEANUP_DMA:
+		return mga_dma_cleanup(dev);
+	}
+
+	return -EINVAL;
+}
+
+int mga_irq_install(drm_device_t *dev, int irq)
+{
+	int retcode;
+
+	if (!irq)     return -EINVAL;
+
+	down(&dev->struct_sem);
+	if (dev->irq) {
+		up(&dev->struct_sem);
+		return -EBUSY;
+	}
+	dev->irq = irq;
+	up(&dev->struct_sem);
+
+	DRM_DEBUG("install irq handler %d\n", irq);
+
+	dev->context_flag     = 0;
+	dev->interrupt_flag   = 0;
+	dev->dma_flag	      = 0;
+	dev->dma->next_buffer = NULL;
+	dev->dma->next_queue  = NULL;
+	dev->dma->this_buffer = NULL;
+	INIT_LIST_HEAD(&dev->tq.list);
+	dev->tq.sync	      = 0;
+	dev->tq.routine	      = mga_dma_task_queue;
+	dev->tq.data	      = dev;
+
+				/* Before installing handler */
+	MGA_WRITE(MGAREG_IEN, 0);
+   				/* Install handler */
+	if ((retcode = request_irq(dev->irq,
+				   mga_dma_service,
+				   SA_SHIRQ,
+				   dev->devname,
+				   dev))) {
+		down(&dev->struct_sem);
+		dev->irq = 0;
+		up(&dev->struct_sem);
+		return retcode;
+	}
+				/* After installing handler */
+   	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0x00000001);
+	return 0;
+}
+
+int mga_irq_uninstall(drm_device_t *dev)
+{
+	int irq;
+
+	down(&dev->struct_sem);
+	irq	 = dev->irq;
+	dev->irq = 0;
+	up(&dev->struct_sem);
+
+	if (!irq) return -EINVAL;
+   	DRM_DEBUG("remove irq handler %d\n", irq);
+      	MGA_WRITE(MGAREG_ICLEAR, 0x00000001);
+	MGA_WRITE(MGAREG_IEN, 0);
+	free_irq(irq, dev);
+	return 0;
+}
+
+int mga_control(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_control_t	ctl;
+
+	if (copy_from_user(&ctl, (drm_control_t *)arg, sizeof(ctl)))
+		return -EFAULT;
+
+	switch (ctl.func) {
+	case DRM_INST_HANDLER:
+		return mga_irq_install(dev, ctl.irq);
+	case DRM_UNINST_HANDLER:
+		return mga_irq_uninstall(dev);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mga_flush_queue(drm_device_t *dev)
+{
+   	DECLARE_WAITQUEUE(entry, current);
+  	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+   	int ret = 0;
+
+   	if(!dev_priv) return 0;
+
+   	if(dev_priv->next_prim->num_dwords != 0) {
+   		add_wait_queue(&dev_priv->flush_queue, &entry);
+		if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status)) 
+			DRM_ERROR("Incorrect mga_flush_queue logic\n");
+		set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status);
+		mga_dma_schedule(dev, 0);
+   		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+	   		if (!test_bit(MGA_IN_FLUSH,
+				      &dev_priv->dispatch_status))
+				break;
+		   	atomic_inc(&dev->total_sleeps);
+	      		schedule();
+	      		if (signal_pending(current)) {
+		   		ret = -EINTR; /* Can't restart */
+				clear_bit(MGA_IN_FLUSH,
+					  &dev_priv->dispatch_status);
+		   		break;
+			}
+		}
+		current->state = TASK_RUNNING;
+   		remove_wait_queue(&dev_priv->flush_queue, &entry);
+	}
+   	return ret;
+}
+
+/* Must be called with the lock held */
+void mga_reclaim_buffers(drm_device_t *dev, pid_t pid)
+{
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma) return;
+      	if(dev->dev_private == NULL) return;
+	if(dma->buflist == NULL) return;
+
+	DRM_DEBUG("buf_count=%d\n", dma->buf_count);
+	
+        mga_flush_queue(dev);
+
+	for (i = 0; i < dma->buf_count; i++) {
+	   	drm_buf_t *buf = dma->buflist[ i ];
+	   	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+
+		/* Only buffers that need to get reclaimed ever
+		 * get set to free
+		 */
+		if (buf->pid == pid  && buf_priv) {
+			if(buf_priv->my_freelist->age == MGA_BUF_USED)
+		     		buf_priv->my_freelist->age = MGA_BUF_FREE;
+		}
+	}
+}
+
+int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	DECLARE_WAITQUEUE(entry, current);
+	int		  ret	= 0;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	if (lock.context < 0) return -EINVAL;
+
+	/* Only one queue:
+	 */
+
+	if (!ret) {
+		add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				ret = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  lock.context)) {
+				dev->lock.pid	    = current->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				ret = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	}
+
+	if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+		if (lock.flags & _DRM_LOCK_QUIESCENT) {
+		   DRM_DEBUG("_DRM_LOCK_QUIESCENT\n");
+		   mga_flush_queue(dev);
+		   mga_dma_quiescent(dev);
+		}
+	}
+
+	if (ret) DRM_DEBUG("%d %s\n", lock.context,
+			   ret ? "interrupted" : "has lock");
+	return ret;
+}
+
+int mga_flush_ioctl(struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg)
+{
+       	drm_file_t	  *priv	  = filp->private_data;
+    	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+      	drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("lock not held\n");
+		return -EINVAL;
+	}
+
+   	if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) {
+		drm_mga_prim_buf_t *temp_buf;
+
+		temp_buf = dev_priv->current_prim;
+
+		if(temp_buf && temp_buf->num_dwords) {
+			set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status);
+			mga_advance_primary(dev);
+ 		}
+		mga_dma_schedule(dev, 1);
+	}
+   	if(lock.flags & _DRM_LOCK_QUIESCENT) {
+		mga_flush_queue(dev);
+		mga_dma_quiescent(dev);
+	}
+
+    	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_drm.h linux/drivers/char/drm-4.0/mga_drm.h
--- linux.orig/drivers/char/drm-4.0/mga_drm.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_drm.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,274 @@
+/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ *          Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRM_H_
+#define _MGA_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmMga.h)
+ */
+#ifndef _MGA_DEFINES_
+#define _MGA_DEFINES_
+
+#define MGA_F  0x1		/* fog */
+#define MGA_A  0x2		/* alpha */
+#define MGA_S  0x4		/* specular */
+#define MGA_T2 0x8		/* multitexture */
+
+#define MGA_WARP_TGZ            0
+#define MGA_WARP_TGZF           (MGA_F)
+#define MGA_WARP_TGZA           (MGA_A)
+#define MGA_WARP_TGZAF          (MGA_F|MGA_A)
+#define MGA_WARP_TGZS           (MGA_S)
+#define MGA_WARP_TGZSF          (MGA_S|MGA_F)
+#define MGA_WARP_TGZSA          (MGA_S|MGA_A)
+#define MGA_WARP_TGZSAF         (MGA_S|MGA_F|MGA_A)
+#define MGA_WARP_T2GZ           (MGA_T2)
+#define MGA_WARP_T2GZF          (MGA_T2|MGA_F)
+#define MGA_WARP_T2GZA          (MGA_T2|MGA_A)
+#define MGA_WARP_T2GZAF         (MGA_T2|MGA_A|MGA_F)
+#define MGA_WARP_T2GZS          (MGA_T2|MGA_S)
+#define MGA_WARP_T2GZSF         (MGA_T2|MGA_S|MGA_F)
+#define MGA_WARP_T2GZSA         (MGA_T2|MGA_S|MGA_A)
+#define MGA_WARP_T2GZSAF        (MGA_T2|MGA_S|MGA_F|MGA_A)
+
+#define MGA_MAX_G400_PIPES 16
+#define MGA_MAX_G200_PIPES  8	/* no multitex */
+#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES
+
+#define MGA_CARD_TYPE_G200 1
+#define MGA_CARD_TYPE_G400 2
+
+#define MGA_FRONT   0x1
+#define MGA_BACK    0x2
+#define MGA_DEPTH   0x4
+
+/* 3d state excluding texture units:
+ */
+#define MGA_CTXREG_DSTORG     0	/* validated */
+#define MGA_CTXREG_MACCESS    1	
+#define MGA_CTXREG_PLNWT      2 	
+#define MGA_CTXREG_DWGCTL     3	
+#define MGA_CTXREG_ALPHACTRL  4
+#define MGA_CTXREG_FOGCOLOR   5
+#define MGA_CTXREG_WFLAG      6
+#define MGA_CTXREG_TDUAL0     7
+#define MGA_CTXREG_TDUAL1     8
+#define MGA_CTXREG_FCOL       9
+#define MGA_CTXREG_STENCIL    10
+#define MGA_CTXREG_STENCILCTL 11
+#define MGA_CTX_SETUP_SIZE    12
+
+/* 2d state
+ */
+#define MGA_2DREG_PITCH 	0
+#define MGA_2D_SETUP_SIZE 	1
+
+/* Each texture unit has a state:
+ */
+#define MGA_TEXREG_CTL        0
+#define MGA_TEXREG_CTL2       1
+#define MGA_TEXREG_FILTER     2
+#define MGA_TEXREG_BORDERCOL  3
+#define MGA_TEXREG_ORG        4 /* validated */
+#define MGA_TEXREG_ORG1       5
+#define MGA_TEXREG_ORG2       6
+#define MGA_TEXREG_ORG3       7
+#define MGA_TEXREG_ORG4       8
+#define MGA_TEXREG_WIDTH      9
+#define MGA_TEXREG_HEIGHT     10
+#define MGA_TEX_SETUP_SIZE    11
+
+/* What needs to be changed for the current vertex dma buffer?
+ */
+#define MGA_UPLOAD_CTX        0x1
+#define MGA_UPLOAD_TEX0       0x2
+#define MGA_UPLOAD_TEX1       0x4
+#define MGA_UPLOAD_PIPE       0x8
+#define MGA_UPLOAD_TEX0IMAGE  0x10 /* handled client-side */
+#define MGA_UPLOAD_TEX1IMAGE  0x20 /* handled client-side */
+#define MGA_UPLOAD_2D 	      0x40
+#define MGA_WAIT_AGE          0x80 /* handled client-side */
+#define MGA_UPLOAD_CLIPRECTS  0x100 /* handled client-side */
+#define MGA_DMA_FLUSH	      0x200 /* set when someone gets the lock
+                                       quiescent */
+
+/* 32 buffers of 64k each, total 2 meg.
+ */
+#define MGA_DMA_BUF_ORDER     16
+#define MGA_DMA_BUF_SZ        (1<<MGA_DMA_BUF_ORDER)
+#define MGA_DMA_BUF_NR        31
+
+/* Keep these small for testing.
+ */
+#define MGA_NR_SAREA_CLIPRECTS 8
+
+/* 2 heaps (1 for card, 1 for agp), each divided into upto 128
+ * regions, subject to a minimum region size of (1<<16) == 64k. 
+ *
+ * Clients may subdivide regions internally, but when sharing between
+ * clients, the region size is the minimum granularity. 
+ */
+
+#define MGA_CARD_HEAP 0
+#define MGA_AGP_HEAP  1
+#define MGA_NR_TEX_HEAPS 2
+#define MGA_NR_TEX_REGIONS 16
+#define MGA_LOG_MIN_TEX_REGION_SIZE 16
+#endif
+
+typedef struct _drm_mga_warp_index {
+   	int installed;
+   	unsigned long phys_addr;
+   	int size;
+} drm_mga_warp_index_t;
+
+typedef struct drm_mga_init {
+   	enum { 
+	   	MGA_INIT_DMA = 0x01,
+	       	MGA_CLEANUP_DMA = 0x02
+	} func;
+   	int reserved_map_agpstart;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	int sarea_priv_offset;
+   	int primary_size;
+   	int warp_ucode_size;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+        unsigned int agpTextureOffset;
+        unsigned int agpTextureSize;
+   	unsigned int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int chipset;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_WARP_PIPES];
+	unsigned int mAccess;
+} drm_mga_init_t;
+
+/* Warning: if you change the sarea structure, you must change the Xserver
+ * structures as well */
+
+typedef struct _drm_mga_tex_region {
+	unsigned char next, prev;	
+	unsigned char in_use;	
+	unsigned int age;			
+} drm_mga_tex_region_t;
+
+typedef struct _drm_mga_sarea {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex dma buffer.
+	 */
+   	unsigned int ContextState[MGA_CTX_SETUP_SIZE];
+   	unsigned int ServerState[MGA_2D_SETUP_SIZE];
+   	unsigned int TexState[2][MGA_TEX_SETUP_SIZE];
+   	unsigned int WarpPipe;
+   	unsigned int dirty;
+
+   	unsigned int nbox;
+   	drm_clip_rect_t boxes[MGA_NR_SAREA_CLIPRECTS];
+
+
+	/* Information about the most recently used 3d drawable.  The
+	 * client fills in the req_* fields, the server fills in the 
+	 * exported_ fields and puts the cliprects into boxes, above.
+	 *
+	 * The client clears the exported_drawable field before
+	 * clobbering the boxes data.
+	 */
+        unsigned int req_drawable;	 /* the X drawable id */
+	unsigned int req_draw_buffer;	 /* MGA_FRONT or MGA_BACK */
+
+        unsigned int exported_drawable;	 
+	unsigned int exported_index; 
+        unsigned int exported_stamp;	
+        unsigned int exported_buffers;	 
+        unsigned int exported_nfront;
+        unsigned int exported_nback;
+	int exported_back_x, exported_front_x, exported_w;	
+	int exported_back_y, exported_front_y, exported_h;
+   	drm_clip_rect_t exported_boxes[MGA_NR_SAREA_CLIPRECTS];
+   
+	/* Counters for aging textures and for client-side throttling.
+	 */
+        unsigned int last_enqueue;	/* last time a buffer was enqueued */
+	unsigned int last_dispatch;	/* age of the most recently dispatched buffer */
+	unsigned int last_quiescent;     /*  */
+
+
+	/* LRU lists for texture memory in agp space and on the card
+	 */
+	drm_mga_tex_region_t texList[MGA_NR_TEX_HEAPS][MGA_NR_TEX_REGIONS+1];
+	unsigned int texAge[MGA_NR_TEX_HEAPS];
+	
+	/* Mechanism to validate card state.
+	 */
+   	int ctxOwner;
+   	int vertexsize;
+} drm_mga_sarea_t;	
+
+/* Device specific ioctls:
+ */
+typedef struct _drm_mga_clear {
+	unsigned int clear_color;
+	unsigned int clear_depth;
+	unsigned int flags;
+	unsigned int clear_depth_mask;
+	unsigned int clear_color_mask;
+} drm_mga_clear_t;
+
+typedef struct _drm_mga_swap {
+   	int dummy;
+} drm_mga_swap_t;
+
+typedef struct _drm_mga_iload {
+	int idx;
+	int length;
+	unsigned int destOrg;
+} drm_mga_iload_t;
+
+typedef struct _drm_mga_vertex {
+   	int idx;		/* buffer to queue */
+	int used;		/* bytes in use */
+	int discard;		/* client finished with buffer?  */
+} drm_mga_vertex_t;
+
+typedef struct _drm_mga_indices {
+   	int idx;		/* buffer to queue */
+	unsigned int start;		
+	unsigned int end;		
+	int discard;		/* client finished with buffer?  */
+} drm_mga_indices_t;
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_drv.c linux/drivers/char/drm-4.0/mga_drv.c
--- linux.orig/drivers/char/drm-4.0/mga_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,666 @@
+/* mga_drv.c -- Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "mga_drv.h"
+
+#define MGA_NAME	 "mga"
+#define MGA_DESC	 "Matrox G200/G400"
+#define MGA_DATE	 "20000928"
+#define MGA_MAJOR	 2
+#define MGA_MINOR	 0
+#define MGA_PATCHLEVEL	 1
+
+static drm_device_t	      mga_device;
+drm_ctx_t		      mga_res_ctx;
+
+static struct file_operations mga_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 mga_open,
+	flush:	 drm_flush,
+	release: mga_release,
+	ioctl:	 mga_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+   	poll:	 drm_poll,
+};
+
+static struct miscdevice      mga_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  MGA_NAME,
+	fops:  &mga_fops,
+};
+
+static drm_ioctl_desc_t	      mga_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]     = { mga_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]  = { drm_getunique,  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]   = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]   = { drm_irq_busid,  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]  = { drm_setunique,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	      = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]     = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_CONTROL)]     = { mga_control,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]  = { drm_authmagic,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]     = { drm_addmap,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]    = { mga_addbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]   = { mga_markbufs,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]   = { mga_infobufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]    = { mga_mapbufs,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]   = { mga_freebufs,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]     = { mga_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]      = { mga_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]     = { mga_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]     = { mga_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]  = { mga_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]     = { mga_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]     = { mga_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]    = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]     = { drm_rmdraw,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	      = { mga_dma,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	      = { mga_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]      = { mga_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]      = { drm_finish,	  1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]  = { drm_agp_enable,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]    = { drm_agp_info,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]   = { drm_agp_alloc,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]    = { drm_agp_free,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]    = { drm_agp_bind,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]  = { drm_agp_unbind,  1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)]    = { mga_dma_init,    1, 1 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)]    = { mga_swap_bufs,   1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)]   = { mga_clear_bufs,  1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)]   = { mga_iload,       1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)]  = { mga_vertex,      1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)]   = { mga_flush_ioctl, 1, 0 },
+   	[DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices,     1, 0 },
+};
+
+#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls)
+
+#ifdef MODULE
+static char		      *mga = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("Matrox G200/G400");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(mga, "s");
+
+#ifndef MODULE
+/* mga_options is called by the kernel to parse command-line options passed
+ * via the boot-loader (e.g., LILO).  It calls the insmod option routine,
+ * drm_parse_drm.
+ */
+
+static int __init mga_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("mga=", mga_options);
+#endif
+
+static int mga_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int mga_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	if (dev->dev_private) mga_dma_cleanup(dev);
+	if (dev->irq) mga_irq_uninstall(dev);
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+   				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->queuelist) {
+		for (i = 0; i < dev->queue_count; i++) {
+			drm_waitlist_destroy(&dev->queuelist[i]->waitlist);
+			if (dev->queuelist[i]) {
+				drm_free(dev->queuelist[i],
+					 sizeof(*dev->queuelist[0]),
+					 DRM_MEM_QUEUES);
+				dev->queuelist[i] = NULL;
+			}
+		}
+		drm_free(dev->queuelist,
+			 dev->queue_slots * sizeof(*dev->queuelist),
+			 DRM_MEM_QUEUES);
+		dev->queuelist	 = NULL;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* mga_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init mga_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(mga);
+#endif
+	if ((retcode = misc_register(&mga_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, mga_misc.minor);
+	dev->name   = MGA_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+	dev->agp    = drm_agp_init();
+      	if(dev->agp == NULL) {
+	   	DRM_INFO("The mga drm module requires the agpgart module"
+		         " to function correctly\nPlease load the agpgart"
+		         " module before you load the mga module\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&mga_misc);
+	   	mga_takedown(dev);
+	   	return -ENOMEM;
+	}
+#ifdef CONFIG_MTRR
+   	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size * 1024 * 1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&mga_misc);
+		mga_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 MGA_NAME,
+		 MGA_MAJOR,
+		 MGA_MINOR,
+		 MGA_PATCHLEVEL,
+		 MGA_DATE,
+		 mga_misc.minor);
+
+	return 0;
+}
+
+/* mga_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit mga_cleanup(void)
+{
+	drm_device_t	      *dev = &mga_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&mga_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+#ifdef CONFIG_MTRR
+   	if(dev->agp && dev->agp->agp_mtrr) {
+	   	int retval;
+	   	retval = mtrr_del(dev->agp->agp_mtrr,
+				  dev->agp->agp_info.aper_base,
+				  dev->agp->agp_info.aper_size * 1024*1024);
+	   	DRM_DEBUG("mtrr_del = %d\n", retval);
+	}
+#endif
+
+	mga_takedown(dev);
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+}
+
+module_init(mga_init);
+module_exit(mga_cleanup);
+
+
+int mga_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = MGA_MAJOR;
+	version.version_minor	   = MGA_MINOR;
+	version.version_patchlevel = MGA_PATCHLEVEL;
+
+	DRM_COPY(version.name, MGA_NAME);
+	DRM_COPY(version.date, MGA_DATE);
+	DRM_COPY(version.desc, MGA_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int mga_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &mga_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return mga_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int mga_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev    = priv->dev;
+	DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n",
+		  current->pid, dev->device, dev->open_count);
+
+	if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)
+	    && dev->lock.pid == current->pid) {
+	      	mga_reclaim_buffers(dev, priv->pid);
+		DRM_INFO("Process %d dead (ctx %d, d_s = 0x%02lx)\n",
+			 current->pid,
+			 _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock),
+			 dev->dev_private ?
+			 ((drm_mga_private_t *)dev->dev_private)
+			 ->dispatch_status
+			 : 0);
+
+		if (dev->dev_private)
+			((drm_mga_private_t *)dev->dev_private)
+				->dispatch_status &= MGA_IN_DISPATCH;
+		
+		drm_lock_free(dev,
+			      &dev->lock.hw_lock->lock,
+			      _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	} else if (dev->lock.hw_lock) {
+	   	/* The lock is required to reclaim buffers */
+	   	DECLARE_WAITQUEUE(entry, current);
+	   	add_wait_queue(&dev->lock.lock_queue, &entry);
+		for (;;) {
+			current->state = TASK_INTERRUPTIBLE;
+			if (!dev->lock.hw_lock) {
+				/* Device has been unregistered */
+				retcode = -EINTR;
+				break;
+			}
+			if (drm_lock_take(&dev->lock.hw_lock->lock,
+					  DRM_KERNEL_CONTEXT)) {
+				dev->lock.pid	    = priv->pid;
+				dev->lock.lock_time = jiffies;
+				atomic_inc(&dev->total_locks);
+				break;	/* Got lock */
+			}
+				/* Contention */
+			atomic_inc(&dev->total_sleeps);
+			schedule();
+			if (signal_pending(current)) {
+				retcode = -ERESTARTSYS;
+				break;
+			}
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&dev->lock.lock_queue, &entry);
+	   	if(!retcode) {
+		   	mga_reclaim_buffers(dev, priv->pid);
+			if (dev->dev_private)
+				((drm_mga_private_t *)dev->dev_private)
+					->dispatch_status &= MGA_IN_DISPATCH;
+		   	drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				      DRM_KERNEL_CONTEXT);
+		}
+	}
+	drm_fasync(-1, filp, 0);
+
+	down(&dev->struct_sem);
+	if (priv->remove_auth_on_close == 1) {
+		drm_file_t *temp = dev->file_first;
+		while(temp) {
+			temp->authenticated = 0;
+			temp = temp->next;
+		}
+	}
+	if (priv->prev) priv->prev->next = priv->next;
+	else		dev->file_first	 = priv->next;
+	if (priv->next) priv->next->prev = priv->prev;
+	else		dev->file_last	 = priv->prev;
+	up(&dev->struct_sem);
+
+	drm_free(priv, sizeof(*priv), DRM_MEM_FILES);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+   	atomic_inc(&dev->total_close);
+   	spin_lock(&dev->count_lock);
+   	if (!--dev->open_count) {
+	   	if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+		   	DRM_ERROR("Device busy: %d %d\n",
+				  atomic_read(&dev->ioctl_count),
+				  dev->blocked);
+		   	spin_unlock(&dev->count_lock);
+			unlock_kernel();
+		   	return -EBUSY;
+		}
+	   	spin_unlock(&dev->count_lock);
+		unlock_kernel();
+	   	return mga_takedown(dev);
+	}
+	spin_unlock(&dev->count_lock);
+	unlock_kernel();
+	return retcode;
+}
+
+
+/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	if (nr >= MGA_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &mga_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function: pid = %d, cmd = 0x%02x,"
+				  " nr = 0x%02x, dev 0x%x, auth = %d\n",
+				  current->pid, cmd, nr, dev->device,
+				  priv->authenticated);
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int mga_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+	mga_dma_schedule(dev, 1);
+
+	if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+			  DRM_KERNEL_CONTEXT)) DRM_ERROR("\n");
+
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_drv.h linux/drivers/char/drm-4.0/mga_drv.h
--- linux.orig/drivers/char/drm-4.0/mga_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,520 @@
+/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Rickard E. (Rik) Faith <faith@valinux.com>
+ * 	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#ifndef _MGA_DRV_H_
+#define _MGA_DRV_H_
+
+#define MGA_BUF_IN_USE         0
+#define MGA_BUF_SWAP_PENDING   1
+#define MGA_BUF_FORCE_FIRE     2
+#define MGA_BUF_NEEDS_OVERFLOW 3
+
+typedef struct {
+	long buffer_status; /* long req'd for set_bit() --RR */
+   	int num_dwords;
+   	int max_dwords;
+   	u32 *current_dma_ptr;
+   	u32 *head;
+   	u32 phys_head;
+	unsigned int prim_age;
+   	int sec_used;
+   	int idx;
+} drm_mga_prim_buf_t;
+
+typedef struct _drm_mga_freelist {
+   	__volatile__ unsigned int age;
+   	drm_buf_t *buf;
+   	struct _drm_mga_freelist *next;
+   	struct _drm_mga_freelist *prev;
+} drm_mga_freelist_t;
+
+#define MGA_IN_DISPATCH   0
+#define MGA_IN_FLUSH      1
+#define MGA_IN_WAIT       2
+#define MGA_IN_GETBUF	  3
+
+typedef struct _drm_mga_private {
+   	long dispatch_status;  /* long req'd for set_bit() --RR */
+	unsigned int next_prim_age;
+	__volatile__ unsigned int last_prim_age;
+   	int reserved_map_idx;
+   	int buffer_map_idx;
+   	drm_mga_sarea_t *sarea_priv;
+   	int primary_size;
+   	int warp_ucode_size;
+   	int chipset;
+   	unsigned int frontOffset;
+   	unsigned int backOffset;
+   	unsigned int depthOffset;
+   	unsigned int textureOffset;
+   	unsigned int textureSize;
+   	int cpp;
+   	unsigned int stride;
+   	int sgram;
+	int use_agp;
+   	drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES];
+	unsigned int WarpPipe;
+	unsigned int vertexsize;
+   	atomic_t pending_bufs;
+   	void *status_page;
+   	unsigned long real_status_page;
+   	u8 *ioremap;
+   	drm_mga_prim_buf_t **prim_bufs;
+   	drm_mga_prim_buf_t *next_prim;
+   	drm_mga_prim_buf_t *last_prim;
+   	drm_mga_prim_buf_t *current_prim;
+   	int current_prim_idx;
+   	drm_mga_freelist_t *head;
+   	drm_mga_freelist_t *tail;
+   	wait_queue_head_t flush_queue;	/* Processes waiting until flush    */
+      	wait_queue_head_t wait_queue;	/* Processes waiting until interrupt */
+	wait_queue_head_t buf_queue;    /* Processes waiting for a free buf */
+	/* Some validated register values:
+	 */
+	u32 mAccess;
+} drm_mga_private_t;
+
+				/* mga_drv.c */
+extern int  mga_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_open(struct inode *inode, struct file *filp);
+extern int  mga_release(struct inode *inode, struct file *filp);
+extern int  mga_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* mga_dma.c */
+extern int  mga_dma_schedule(drm_device_t *dev, int locked);
+extern int  mga_dma(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_irq_install(drm_device_t *dev, int irq);
+extern int  mga_irq_uninstall(drm_device_t *dev);
+extern int  mga_control(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+/* mga_dma_init does init and release */
+extern int mga_dma_init(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int mga_dma_cleanup(drm_device_t *dev);
+extern int mga_flush_ioctl(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern unsigned int mga_create_sync_tag(drm_device_t *dev);
+extern drm_buf_t *mga_freelist_get(drm_device_t *dev);
+extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf);
+extern int mga_advance_primary(drm_device_t *dev);
+extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid);
+
+
+				/* mga_bufs.c */
+extern int  mga_addbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_infobufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_markbufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_freebufs(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+extern int  mga_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  mga_addmap(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+				/* mga_state.c */
+extern int  mga_clear_bufs(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  mga_swap_bufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_iload(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_vertex(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+extern int  mga_indices(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+				/* mga_context.c */
+extern int  mga_resctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_addctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_modctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_getctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_switchctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  mga_newctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  mga_rmctx(struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+extern int  mga_context_switch(drm_device_t *dev, int old, int new);
+extern int  mga_context_switch_complete(drm_device_t *dev, int new);
+
+#define mga_flush_write_combine()	mb()
+
+typedef enum {
+	TT_GENERAL,
+	TT_BLIT,
+	TT_VECTOR,
+	TT_VERTEX
+} transferType_t;
+
+typedef struct {
+   	drm_mga_freelist_t *my_freelist;
+	int discard;
+	int dispatched;
+} drm_mga_buf_priv_t;
+
+#define DWGREG0 	0x1c00
+#define DWGREG0_END 	0x1dff
+#define DWGREG1		0x2c00
+#define DWGREG1_END	0x2dff
+
+#define ISREG0(r)	(r >= DWGREG0 && r <= DWGREG0_END)
+#define ADRINDEX0(r)	(u8)((r - DWGREG0) >> 2)
+#define ADRINDEX1(r)	(u8)(((r - DWGREG1) >> 2) | 0x80)
+#define ADRINDEX(r)	(ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r))
+
+#define MGA_VERBOSE 0
+#define MGA_NUM_PRIM_BUFS 	8
+
+#define PRIMLOCALS	u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \
+			int outcount, num_dwords
+
+#define PRIM_OVERFLOW(dev, dev_priv, length) do {			   \
+	drm_mga_prim_buf_t *tmp_buf =					   \
+ 		dev_priv->prim_bufs[dev_priv->current_prim_idx];	   \
+	if( test_bit(MGA_BUF_NEEDS_OVERFLOW, &tmp_buf->buffer_status)) {   \
+ 		mga_advance_primary(dev);				   \
+ 		mga_dma_schedule(dev, 1);				   \
+		tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+ 	} else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length ||   \
+ 	           tmp_buf->sec_used > MGA_DMA_BUF_NR/2) {		   \
+		set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status);	   \
+ 		mga_advance_primary(dev);				   \
+ 		mga_dma_schedule(dev, 1);				   \
+		tmp_buf = dev_priv->prim_bufs[dev_priv->current_prim_idx]; \
+	}								   \
+	if(MGA_VERBOSE)							   \
+		DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__);		   \
+	dma_ptr = tmp_buf->current_dma_ptr;				   \
+	num_dwords = tmp_buf->num_dwords;				   \
+	phys_head = tmp_buf->phys_head;					   \
+	outcount = 0;							   \
+} while(0)
+
+#define PRIMGETPTR(dev_priv) do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if(MGA_VERBOSE)							\
+		DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__);		\
+	dma_ptr = tmp_buf->current_dma_ptr;				\
+	num_dwords = tmp_buf->num_dwords;				\
+	phys_head = tmp_buf->phys_head;					\
+	outcount = 0;							\
+} while(0)
+
+#define PRIMPTR(prim_buf) do {					\
+	if(MGA_VERBOSE)						\
+		DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__);	\
+	dma_ptr = prim_buf->current_dma_ptr;			\
+	num_dwords = prim_buf->num_dwords;			\
+	phys_head = prim_buf->phys_head;			\
+	outcount = 0;						\
+} while(0)
+
+#define PRIMFINISH(prim_buf) do {				\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	        \
+        }							\
+	prim_buf->num_dwords = num_dwords;			\
+	prim_buf->current_dma_ptr = dma_ptr;			\
+} while(0)
+
+#define PRIMADVANCE(dev_priv)	do {				\
+drm_mga_prim_buf_t *tmp_buf = 					\
+	dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	if (MGA_VERBOSE) {					\
+		DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__);	\
+                if (outcount & 3)				\
+                      DRM_DEBUG(" --- truncation\n");	\
+        }							\
+	tmp_buf->num_dwords = num_dwords;      			\
+	tmp_buf->current_dma_ptr = dma_ptr;    			\
+} while (0)
+
+#define PRIMUPDATE(dev_priv)	do {					\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	tmp_buf->sec_used++;						\
+} while (0)
+
+#define AGEBUF(dev_priv, buf_priv)	do {				\
+	drm_mga_prim_buf_t *tmp_buf =					\
+		dev_priv->prim_bufs[dev_priv->current_prim_idx];	\
+	buf_priv->my_freelist->age = tmp_buf->prim_age;			\
+} while (0)
+
+
+#define PRIMOUTREG(reg, val) do {					\
+	tempIndex[outcount]=ADRINDEX(reg);				\
+	dma_ptr[1+outcount] = val;					\
+	if (MGA_VERBOSE)						\
+		DRM_DEBUG("   PRIMOUT %d: 0x%x -- 0x%x\n",		\
+		       num_dwords + 1 + outcount, ADRINDEX(reg), val);	\
+	if( ++outcount == 4) {						\
+		outcount = 0;						\
+		dma_ptr[0] = *(unsigned long *)tempIndex;		\
+		dma_ptr+=5;						\
+		num_dwords += 5;					\
+	}								\
+}while (0)
+
+/* A reduced set of the mga registers.
+ */
+
+#define MGAREG_MGA_EXEC 			0x0100
+#define MGAREG_ALPHACTRL 			0x2c7c
+#define MGAREG_AR0 				0x1c60
+#define MGAREG_AR1 				0x1c64
+#define MGAREG_AR2 				0x1c68
+#define MGAREG_AR3 				0x1c6c
+#define MGAREG_AR4 				0x1c70
+#define MGAREG_AR5 				0x1c74
+#define MGAREG_AR6 				0x1c78
+#define MGAREG_CXBNDRY				0x1c80
+#define MGAREG_CXLEFT 				0x1ca0
+#define MGAREG_CXRIGHT				0x1ca4
+#define MGAREG_DMAPAD 				0x1c54
+#define MGAREG_DSTORG 				0x2cb8
+#define MGAREG_DWGCTL 				0x1c00
+#define MGAREG_DWGSYNC				0x2c4c
+#define MGAREG_FCOL 				0x1c24
+#define MGAREG_FIFOSTATUS 			0x1e10
+#define MGAREG_FOGCOL 				0x1cf4
+#define MGAREG_FXBNDRY				0x1c84
+#define MGAREG_FXLEFT 				0x1ca8
+#define MGAREG_FXRIGHT				0x1cac
+#define MGAREG_ICLEAR 				0x1e18
+#define MGAREG_IEN 				0x1e1c
+#define MGAREG_LEN 				0x1c5c
+#define MGAREG_MACCESS				0x1c04
+#define MGAREG_PITCH 				0x1c8c
+#define MGAREG_PLNWT 				0x1c1c
+#define MGAREG_PRIMADDRESS 			0x1e58
+#define MGAREG_PRIMEND				0x1e5c
+#define MGAREG_PRIMPTR				0x1e50
+#define MGAREG_SECADDRESS 			0x2c40
+#define MGAREG_SECEND 				0x2c44
+#define MGAREG_SETUPADDRESS 			0x2cd0
+#define MGAREG_SETUPEND 			0x2cd4
+#define MGAREG_SOFTRAP				0x2c48
+#define MGAREG_SRCORG 				0x2cb4
+#define MGAREG_STATUS 				0x1e14
+#define MGAREG_STENCIL				0x2cc8
+#define MGAREG_STENCILCTL 			0x2ccc
+#define MGAREG_TDUALSTAGE0 			0x2cf8
+#define MGAREG_TDUALSTAGE1 			0x2cfc
+#define MGAREG_TEXBORDERCOL 			0x2c5c
+#define MGAREG_TEXCTL 				0x2c30
+#define MGAREG_TEXCTL2				0x2c3c
+#define MGAREG_TEXFILTER 			0x2c58
+#define MGAREG_TEXHEIGHT 			0x2c2c
+#define MGAREG_TEXORG 				0x2c24
+#define MGAREG_TEXORG1				0x2ca4
+#define MGAREG_TEXORG2				0x2ca8
+#define MGAREG_TEXORG3				0x2cac
+#define MGAREG_TEXORG4				0x2cb0
+#define MGAREG_TEXTRANS 			0x2c34
+#define MGAREG_TEXTRANSHIGH 			0x2c38
+#define MGAREG_TEXWIDTH 			0x2c28
+#define MGAREG_WACCEPTSEQ 			0x1dd4
+#define MGAREG_WCODEADDR 			0x1e6c
+#define MGAREG_WFLAG 				0x1dc4
+#define MGAREG_WFLAG1 				0x1de0
+#define MGAREG_WFLAGNB				0x1e64
+#define MGAREG_WFLAGNB1 			0x1e08
+#define MGAREG_WGETMSB				0x1dc8
+#define MGAREG_WIADDR 				0x1dc0
+#define MGAREG_WIADDR2				0x1dd8
+#define MGAREG_WMISC 				0x1e70
+#define MGAREG_WVRTXSZ				0x1dcc
+#define MGAREG_YBOT 				0x1c9c
+#define MGAREG_YDST 				0x1c90
+#define MGAREG_YDSTLEN				0x1c88
+#define MGAREG_YDSTORG				0x1c94
+#define MGAREG_YTOP 				0x1c98
+#define MGAREG_ZORG 				0x1c0c
+
+/* Warp registers */
+#define MGAREG_WR0                              0x2d00
+#define MGAREG_WR1                              0x2d04
+#define MGAREG_WR2                              0x2d08
+#define MGAREG_WR3                              0x2d0c
+#define MGAREG_WR4                              0x2d10
+#define MGAREG_WR5                              0x2d14
+#define MGAREG_WR6                              0x2d18
+#define MGAREG_WR7                              0x2d1c
+#define MGAREG_WR8                              0x2d20
+#define MGAREG_WR9                              0x2d24
+#define MGAREG_WR10                             0x2d28
+#define MGAREG_WR11                             0x2d2c
+#define MGAREG_WR12                             0x2d30
+#define MGAREG_WR13                             0x2d34
+#define MGAREG_WR14                             0x2d38
+#define MGAREG_WR15                             0x2d3c
+#define MGAREG_WR16                             0x2d40
+#define MGAREG_WR17                             0x2d44
+#define MGAREG_WR18                             0x2d48
+#define MGAREG_WR19                             0x2d4c
+#define MGAREG_WR20                             0x2d50
+#define MGAREG_WR21                             0x2d54
+#define MGAREG_WR22                             0x2d58
+#define MGAREG_WR23                             0x2d5c
+#define MGAREG_WR24                             0x2d60
+#define MGAREG_WR25                             0x2d64
+#define MGAREG_WR26                             0x2d68
+#define MGAREG_WR27                             0x2d6c
+#define MGAREG_WR28                             0x2d70
+#define MGAREG_WR29                             0x2d74
+#define MGAREG_WR30                             0x2d78
+#define MGAREG_WR31                             0x2d7c
+#define MGAREG_WR32                             0x2d80
+#define MGAREG_WR33                             0x2d84
+#define MGAREG_WR34                             0x2d88
+#define MGAREG_WR35                             0x2d8c
+#define MGAREG_WR36                             0x2d90
+#define MGAREG_WR37                             0x2d94
+#define MGAREG_WR38                             0x2d98
+#define MGAREG_WR39                             0x2d9c
+#define MGAREG_WR40                             0x2da0
+#define MGAREG_WR41                             0x2da4
+#define MGAREG_WR42                             0x2da8
+#define MGAREG_WR43                             0x2dac
+#define MGAREG_WR44                             0x2db0
+#define MGAREG_WR45                             0x2db4
+#define MGAREG_WR46                             0x2db8
+#define MGAREG_WR47                             0x2dbc
+#define MGAREG_WR48                             0x2dc0
+#define MGAREG_WR49                             0x2dc4
+#define MGAREG_WR50                             0x2dc8
+#define MGAREG_WR51                             0x2dcc
+#define MGAREG_WR52                             0x2dd0
+#define MGAREG_WR53                             0x2dd4
+#define MGAREG_WR54                             0x2dd8
+#define MGAREG_WR55                             0x2ddc
+#define MGAREG_WR56                             0x2de0
+#define MGAREG_WR57                             0x2de4
+#define MGAREG_WR58                             0x2de8
+#define MGAREG_WR59                             0x2dec
+#define MGAREG_WR60                             0x2df0
+#define MGAREG_WR61                             0x2df4
+#define MGAREG_WR62                             0x2df8
+#define MGAREG_WR63                             0x2dfc
+
+#define PDEA_pagpxfer_enable			0x2
+
+#define WIA_wmode_suspend			0x0
+#define WIA_wmode_start 			0x3
+#define WIA_wagp_agp				0x4
+
+#define DC_opcod_line_open 			0x0
+#define DC_opcod_autoline_open 			0x1
+#define DC_opcod_line_close 			0x2
+#define DC_opcod_autoline_close 		0x3
+#define DC_opcod_trap 				0x4
+#define DC_opcod_texture_trap 			0x6
+#define DC_opcod_bitblt 			0x8
+#define DC_opcod_iload 				0x9
+#define DC_atype_rpl 				0x0
+#define DC_atype_rstr 				0x10
+#define DC_atype_zi 				0x30
+#define DC_atype_blk 				0x40
+#define DC_atype_i 				0x70
+#define DC_linear_xy 				0x0
+#define DC_linear_linear 			0x80
+#define DC_zmode_nozcmp 			0x0
+#define DC_zmode_ze 				0x200
+#define DC_zmode_zne 				0x300
+#define DC_zmode_zlt 				0x400
+#define DC_zmode_zlte 				0x500
+#define DC_zmode_zgt 				0x600
+#define DC_zmode_zgte 				0x700
+#define DC_solid_disable 			0x0
+#define DC_solid_enable 			0x800
+#define DC_arzero_disable 			0x0
+#define DC_arzero_enable 			0x1000
+#define DC_sgnzero_disable 			0x0
+#define DC_sgnzero_enable 			0x2000
+#define DC_shftzero_disable 			0x0
+#define DC_shftzero_enable 			0x4000
+#define DC_bop_SHIFT 				16
+#define DC_trans_SHIFT 				20
+#define DC_bltmod_bmonolef 			0x0
+#define DC_bltmod_bmonowf 			0x8000000
+#define DC_bltmod_bplan 			0x2000000
+#define DC_bltmod_bfcol 			0x4000000
+#define DC_bltmod_bu32bgr 			0x6000000
+#define DC_bltmod_bu32rgb 			0xe000000
+#define DC_bltmod_bu24bgr 			0x16000000
+#define DC_bltmod_bu24rgb 			0x1e000000
+#define DC_pattern_disable 			0x0
+#define DC_pattern_enable 			0x20000000
+#define DC_transc_disable 			0x0
+#define DC_transc_enable 			0x40000000
+#define DC_clipdis_disable 			0x0
+#define DC_clipdis_enable 			0x80000000
+
+
+#define SETADD_mode_vertlist                   	0x0
+
+
+#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | 		\
+		       DC_sgnzero_enable | DC_shftzero_enable | 	\
+		       (0xC << DC_bop_SHIFT) | DC_clipdis_enable | 	\
+		       DC_solid_enable | DC_transc_enable)
+
+
+#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy |	\
+		      DC_solid_disable | DC_arzero_disable | 		\
+		      DC_sgnzero_enable | DC_shftzero_enable | 		\
+		      (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | 	\
+		      DC_pattern_disable | DC_transc_disable | 		\
+		      DC_clipdis_enable)				\
+
+#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\
+		       DC_arzero_enable | DC_sgnzero_enable |		\
+		       DC_atype_i)
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/mga_state.c linux/drivers/char/drm-4.0/mga_state.c
--- linux.orig/drivers/char/drm-4.0/mga_state.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/mga_state.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1017 @@
+/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Jeff Hartmann <jhartmann@valinux.com>
+ * 	    Keith Whitwell <keithw@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "mga_drv.h"
+#include "drm.h"
+
+/* If you change the functions to set state, PLEASE
+ * change these values
+ */
+
+#define MGAEMITCLIP_SIZE	10
+#define MGAEMITCTX_SIZE		20
+#define MGAG200EMITTEX_SIZE 	20
+#define MGAG400EMITTEX0_SIZE	30
+#define MGAG400EMITTEX1_SIZE	25
+#define MGAG400EMITPIPE_SIZE	50
+#define MGAG200EMITPIPE_SIZE	15
+
+#define MAX_STATE_SIZE ((MGAEMITCLIP_SIZE * MGA_NR_SAREA_CLIPRECTS) + \
+			MGAEMITCTX_SIZE + MGAG400EMITTEX0_SIZE + \
+			MGAG400EMITTEX1_SIZE + MGAG400EMITPIPE_SIZE)
+
+static void mgaEmitClipRect(drm_mga_private_t * dev_priv,
+			    drm_clip_rect_t * box)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+
+	/* This takes 10 dwords */
+	PRIMGETPTR(dev_priv);
+
+	/* Force reset of dwgctl on G400 (eliminates clip disable bit) */
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+#if 0
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0);
+		PRIMOUTREG(MGAREG_DWGSYNC, 0);
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+#else
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+		PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+		PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+		PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000);
+#endif
+	}
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1));
+	PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / dev_priv->cpp);
+	PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / dev_priv->cpp);
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaEmitContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	PRIMLOCALS;
+
+	/* This takes a max of 20 dwords */
+	PRIMGETPTR(dev_priv);
+
+	PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]);
+	PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]);
+	PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]);
+	PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]);
+	PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]);
+	PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset);	/* invarient */
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]);
+		PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]);
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+
+		PRIMOUTREG(MGAREG_STENCIL, regs[MGA_CTXREG_STENCIL]);
+		PRIMOUTREG(MGAREG_STENCILCTL, regs[MGA_CTXREG_STENCILCTL]);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	} else {
+		PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+	}
+
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitTex(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 20 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR24, regs[MGA_TEXREG_WIDTH]);
+
+	PRIMOUTREG(MGAREG_WR34, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define TMC_dualtex_enable 		0x80
+
+static void mgaG400EmitTex0(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[0];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 30 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR49, 0);
+
+	PRIMOUTREG(MGAREG_WR57, 0);
+	PRIMOUTREG(MGAREG_WR53, 0);
+	PRIMOUTREG(MGAREG_WR61, 0);
+	PRIMOUTREG(MGAREG_WR52, 0x40);
+
+	PRIMOUTREG(MGAREG_WR60, 0x40);
+	PRIMOUTREG(MGAREG_WR54, regs[MGA_TEXREG_WIDTH] | 0x40);
+	PRIMOUTREG(MGAREG_WR62, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define TMC_map1_enable 		0x80000000
+
+static void mgaG400EmitTex1(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->TexState[1];
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 25 dwords */
+
+	PRIMOUTREG(MGAREG_TEXCTL2,
+		   regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000);
+	PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]);
+	PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]);
+	PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]);
+
+	PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]);
+	PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]);
+	PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]);
+	PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]);
+
+	PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]);
+	PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]);
+	PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]);
+	PRIMOUTREG(MGAREG_WR49, 0);
+
+	PRIMOUTREG(MGAREG_WR57, 0);
+	PRIMOUTREG(MGAREG_WR53, 0);
+	PRIMOUTREG(MGAREG_WR61, 0);
+	PRIMOUTREG(MGAREG_WR52, regs[MGA_TEXREG_WIDTH] | 0x40);
+
+	PRIMOUTREG(MGAREG_WR60, regs[MGA_TEXREG_HEIGHT] | 0x40);
+	PRIMOUTREG(MGAREG_TEXTRANS, 0xffff);
+	PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff);
+	PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000);
+
+	PRIMADVANCE(dev_priv);
+}
+
+#define MAGIC_FPARAM_HEX_VALUE 0x46480000
+/* This is the hex value of 12800.0f which is a magic value we must
+ * set in wr56.
+ */
+
+static void mgaG400EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 50 dwords */
+
+	/* Establish vertex size.  
+	 */
+	PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	if (pipe & MGA_T2) {
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000);
+	} else {
+		if (dev_priv->WarpPipe & MGA_T2) {
+			/* Flush the WARP pipe */
+			PRIMOUTREG(MGAREG_YDST, 0);
+			PRIMOUTREG(MGAREG_FXLEFT, 0);
+			PRIMOUTREG(MGAREG_FXRIGHT, 1);
+			PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD);
+
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1);
+			PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000);
+			PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0);
+			PRIMOUTREG(MGAREG_TEXCTL2, 0x00008000);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+		}
+
+		PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+		PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0);
+		PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000);
+	}
+
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(MGAREG_WFLAG1, 0);
+	PRIMOUTREG(MGAREG_WR56, MAGIC_FPARAM_HEX_VALUE);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+
+	PRIMOUTREG(MGAREG_WR49, 0);	/* Tex stage 0 */
+	PRIMOUTREG(MGAREG_WR57, 0);	/* Tex stage 0 */
+	PRIMOUTREG(MGAREG_WR53, 0);	/* Tex stage 1 */
+	PRIMOUTREG(MGAREG_WR61, 0);	/* Tex stage 1 */
+
+	PRIMOUTREG(MGAREG_WR54, 0x40);	/* Tex stage 0 : w */
+	PRIMOUTREG(MGAREG_WR62, 0x40);	/* Tex stage 0 : h */
+	PRIMOUTREG(MGAREG_WR52, 0x40);	/* Tex stage 1 : w */
+	PRIMOUTREG(MGAREG_WR60, 0x40);	/* Tex stage 1 : h */
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR2,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+	PRIMADVANCE(dev_priv);
+}
+
+static void mgaG200EmitPipe(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->WarpPipe;
+	PRIMLOCALS;
+
+	PRIMGETPTR(dev_priv);
+
+	/* This takes 15 dwords */
+
+	PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend);
+	PRIMOUTREG(MGAREG_WVRTXSZ, 7);
+	PRIMOUTREG(MGAREG_WFLAG, 0);
+	PRIMOUTREG(MGAREG_WR24, 0);	/* tex w/h */
+
+	PRIMOUTREG(MGAREG_WR25, 0x100);
+	PRIMOUTREG(MGAREG_WR34, 0);	/* tex w/h */
+	PRIMOUTREG(MGAREG_WR42, 0xFFFF);
+	PRIMOUTREG(MGAREG_WR60, 0xFFFF);
+
+	/* Dma pading required due to hw bug */
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff);
+	PRIMOUTREG(MGAREG_WIADDR,
+		   (u32) (dev_priv->WarpIndex[pipe].
+			  phys_addr | WIA_wmode_start | WIA_wagp_agp));
+
+	PRIMADVANCE( dev_priv );
+}
+
+static void mgaEmitState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		int multitex = sarea_priv->WarpPipe & MGA_T2;
+
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG400EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG400EmitTex0(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+
+		if ((dirty & MGA_UPLOAD_TEX1) && multitex) {
+			mgaG400EmitTex1(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX1;
+		}
+	} else {
+		if (sarea_priv->WarpPipe != dev_priv->WarpPipe) {
+			mgaG200EmitPipe(dev_priv);
+			dev_priv->WarpPipe = sarea_priv->WarpPipe;
+		}
+
+		if (dirty & MGA_UPLOAD_CTX) {
+			mgaEmitContext(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_CTX;
+		}
+
+		if (dirty & MGA_UPLOAD_TEX0) {
+			mgaG200EmitTex(dev_priv);
+			sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+		}
+	}
+}
+
+/* Disallow all write destinations except the front and backbuffer.
+ */
+static int mgaVerifyContext(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+
+	if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset &&
+	    regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) {
+		DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n",
+			  regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset,
+			  dev_priv->backOffset);
+		regs[MGA_CTXREG_DSTORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Disallow texture reads from PCI space.
+ */
+static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) {
+		DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n",
+			  sarea_priv->TexState[unit][MGA_TEXREG_ORG],
+			  unit);
+		sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mgaVerifyState(drm_mga_private_t * dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	int rv = 0;
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	if (dirty & MGA_UPLOAD_CTX)
+		rv |= mgaVerifyContext(dev_priv);
+
+	if (dirty & MGA_UPLOAD_TEX0)
+		rv |= mgaVerifyTex(dev_priv, 0);
+
+	if (dev_priv->chipset == MGA_CARD_TYPE_G400) {
+		if (dirty & MGA_UPLOAD_TEX1)
+			rv |= mgaVerifyTex(dev_priv, 1);
+
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES);
+	} else {
+		if (dirty & MGA_UPLOAD_PIPE)
+			rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES);
+	}
+
+	return rv == 0;
+}
+
+static int mgaVerifyIload(drm_mga_private_t * dev_priv,
+			  unsigned long bus_address,
+			  unsigned int dstOrg, int length)
+{
+	if (dstOrg < dev_priv->textureOffset ||
+	    dstOrg + length >
+	    (dev_priv->textureOffset + dev_priv->textureSize)) {
+		return -EINVAL;
+	}
+	if (length % 64) {
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* This copies a 64 byte aligned agp region to the frambuffer
+ * with a standard blit, the ioctl needs to do checking */
+
+static void mga_dma_dispatch_tex_blit(drm_device_t * dev,
+				      unsigned long bus_address,
+				      int length, unsigned int destOrg)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	int use_agp = PDEA_pagpxfer_enable | 0x00000001;
+	u16 y2;
+	PRIMLOCALS;
+
+	y2 = length / 64;
+
+	PRIM_OVERFLOW(dev, dev_priv, 30);
+
+	PRIMOUTREG(MGAREG_DSTORG, destOrg);
+	PRIMOUTREG(MGAREG_MACCESS, 0x00000000);
+	PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp);
+	PRIMOUTREG(MGAREG_AR5, 64);
+
+	PRIMOUTREG(MGAREG_PITCH, 64);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	PRIMOUTREG(MGAREG_AR0, 63);
+	PRIMOUTREG(MGAREG_AR3, 0);
+	PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16));
+	PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned long address = (unsigned long) buf->bus_address;
+	int length = buf->used;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	PRIMLOCALS;
+
+	if (buf->used) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).
+		 */
+		buf_priv->dispatched = 1;
+		PRIM_OVERFLOW(dev, dev_priv,
+			      (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS)));
+		mgaEmitState(dev_priv);
+
+#if 0
+		length = dev_priv->vertexsize * 3 * 4;
+#endif
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SECADDRESS,
+				   ((u32) address) | TT_VERTEX);
+			PRIMOUTREG(MGAREG_SECEND,
+				   (((u32) (address + length)) | use_agp));
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+
+
+}
+
+
+static void mga_dma_dispatch_indices(drm_device_t * dev,
+				     drm_buf_t * buf,
+				     unsigned int start, unsigned int end)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int address = (unsigned int) buf->bus_address;
+	int use_agp = PDEA_pagpxfer_enable;
+	int i = 0;
+	PRIMLOCALS;
+
+	if (start != end) {
+		/* WARNING: if you change any of the state functions verify
+		 * these numbers (Overestimating this doesn't hurt).
+		 */
+		buf_priv->dispatched = 1;
+		PRIM_OVERFLOW(dev, dev_priv,
+			      (MAX_STATE_SIZE + (5 * MGA_NR_SAREA_CLIPRECTS)));
+		mgaEmitState(dev_priv);
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mgaEmitClipRect(dev_priv,
+						&sarea_priv->boxes[i]);
+			}
+
+			PRIMGETPTR(dev_priv);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_SETUPADDRESS,
+				   ((address + start) |
+				    SETADD_mode_vertlist));
+			PRIMOUTREG(MGAREG_SETUPEND,
+				   ((address + end) | use_agp));
+/*  				   ((address + start + 12) | use_agp)); */
+			PRIMADVANCE(dev_priv);
+		} while (++i < sarea_priv->nbox);
+	}
+	if (buf_priv->discard) {
+		if (buf_priv->dispatched == 1)
+			AGEBUF(dev_priv, buf_priv);
+		buf_priv->dispatched = 0;
+		mga_freelist_put(dev, buf);
+	}
+}
+
+
+static void mga_dma_dispatch_clear(drm_device_t * dev, int flags,
+				   unsigned int clear_color,
+				   unsigned int clear_zval,
+				   unsigned int clear_colormask,
+				   unsigned int clear_depthmask)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	unsigned int cmd;
+	int i;
+	PRIMLOCALS;
+
+	if (dev_priv->sgram)
+		cmd = MGA_CLEAR_CMD | DC_atype_blk;
+	else
+		cmd = MGA_CLEAR_CMD | DC_atype_rstr;
+
+	PRIM_OVERFLOW(dev, dev_priv, 35 * MGA_NR_SAREA_CLIPRECTS);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int height = pbox[i].y2 - pbox[i].y1;
+
+		if (flags & MGA_FRONT) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_colormask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_BACK) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_colormask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_color);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+
+		if (flags & MGA_DEPTH) {
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_PLNWT, clear_depthmask);
+			PRIMOUTREG(MGAREG_YDSTLEN,
+				   (pbox[i].y1 << 16) | height);
+			PRIMOUTREG(MGAREG_FXBNDRY,
+				   (pbox[i].x2 << 16) | pbox[i].x1);
+
+			PRIMOUTREG(MGAREG_DMAPAD, 0);
+			PRIMOUTREG(MGAREG_FCOL, clear_zval);
+			PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset);
+			PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd);
+		}
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+	PRIMADVANCE(dev_priv);
+}
+
+static void mga_dma_dispatch_swap(drm_device_t * dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int *regs = sarea_priv->ContextState;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int i;
+	int pixel_stride = dev_priv->stride / dev_priv->cpp;
+
+	PRIMLOCALS;
+
+	PRIM_OVERFLOW(dev, dev_priv, (MGA_NR_SAREA_CLIPRECTS * 5) + 20);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7100);
+	PRIMOUTREG(MGAREG_DWGSYNC, 0x7000);
+
+	PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset);
+	PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess);
+	PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset);
+	PRIMOUTREG(MGAREG_AR5, pixel_stride);
+
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD);
+
+	for (i = 0; i < nbox; i++) {
+		unsigned int h = pbox[i].y2 - pbox[i].y1;
+		unsigned int start = pbox[i].y1 * pixel_stride;
+
+		PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1);
+		PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1);
+		PRIMOUTREG(MGAREG_FXBNDRY,
+			   pbox[i].x1 | ((pbox[i].x2 - 1) << 16));
+		PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC,
+			   (pbox[i].y1 << 16) | h);
+	}
+
+	/* Force reset of DWGCTL */
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_DMAPAD, 0);
+	PRIMOUTREG(MGAREG_SRCORG, 0);
+	PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]);
+
+	PRIMADVANCE(dev_priv);
+}
+
+int mga_clear_bufs(struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_clear_t clear;
+
+	if (copy_from_user(&clear, (drm_mga_clear_t *) arg, sizeof(clear)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_clear_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_clear(dev, clear.flags,
+			       clear.clear_color,
+			       clear.clear_depth,
+			       clear.clear_color_mask,
+			       clear.clear_depth_mask);
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_swap_bufs(struct inode *inode, struct file *filp,
+		  unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_swap_bufs called without lock held\n");
+		return -EINVAL;
+	}
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX;
+	mga_dma_dispatch_swap(dev);
+	PRIMUPDATE(dev_priv);
+	set_bit(MGA_BUF_SWAP_PENDING,
+		&dev_priv->current_prim->buffer_status);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_iload(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_iload_t iload;
+	unsigned long bus_address;
+
+	if (copy_from_user(&iload, (drm_mga_iload_t *) arg, sizeof(iload)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_iload called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(iload.idx < 0 || iload.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[iload.idx];
+	buf_priv = buf->dev_private;
+	bus_address = buf->bus_address;
+
+	if (mgaVerifyIload(dev_priv,
+			   bus_address, iload.destOrg, iload.length)) {
+		mga_freelist_put(dev, buf);
+		return -EINVAL;
+	}
+
+	sarea_priv->dirty |= MGA_UPLOAD_CTX;
+
+	mga_dma_dispatch_tex_blit(dev, bus_address, iload.length,
+				  iload.destOrg);
+	AGEBUF(dev_priv, buf_priv);
+	buf_priv->discard = 1;
+	mga_freelist_put(dev, buf);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+int mga_vertex(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_vertex_t vertex;
+
+	if (copy_from_user(&vertex, (drm_mga_vertex_t *) arg, sizeof(vertex)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_vertex called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(vertex.idx < 0 || vertex.idx > dma->buf_count) return -EINVAL;
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	buf->used = vertex.used;
+	buf_priv->discard = vertex.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (vertex.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_vertex(dev, buf);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+int mga_indices(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_mga_private_t *dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_indices_t indices;
+
+	if (copy_from_user(&indices,
+			   (drm_mga_indices_t *)arg, sizeof(indices)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_indices called without lock held\n");
+		return -EINVAL;
+	}
+
+	if(indices.idx < 0 || indices.idx > dma->buf_count) return -EINVAL;
+	buf = dma->buflist[indices.idx];
+	buf_priv = buf->dev_private;
+
+	buf_priv->discard = indices.discard;
+
+	if (!mgaVerifyState(dev_priv)) {
+		if (indices.discard) {
+			if (buf_priv->dispatched == 1)
+				AGEBUF(dev_priv, buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	mga_dma_dispatch_indices(dev, buf, indices.start, indices.end);
+
+	PRIMUPDATE(dev_priv);
+	mga_flush_write_combine();
+	mga_dma_schedule(dev, 1);
+	return 0;
+}
+
+
+
+static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d)
+{
+	int i;
+	drm_buf_t *buf;
+
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = mga_freelist_get(dev);
+		if (!buf)
+			break;
+		buf->pid = current->pid;
+		if (copy_to_user(&d->request_indices[i],
+				 &buf->idx, sizeof(buf->idx)))
+			return -EFAULT;
+		if (copy_to_user(&d->request_sizes[i],
+				 &buf->total, sizeof(buf->total)))
+			return -EFAULT;
+		++d->granted_count;
+	}
+	return 0;
+}
+
+int mga_dma(struct inode *inode, struct file *filp, unsigned int cmd,
+	    unsigned long arg)
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int retcode = 0;
+	drm_dma_t d;
+
+	if (copy_from_user(&d, (drm_dma_t *) arg, sizeof(d)))
+		return -EFAULT;
+
+	if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+		DRM_ERROR("mga_dma called without lock held\n");
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if (d.send_count != 0) {
+		DRM_ERROR
+		    ("Process %d trying to send %d buffers via drmDMA\n",
+		     current->pid, d.send_count);
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if (d.request_count < 0 || d.request_count > dma->buf_count) {
+		DRM_ERROR
+		    ("Process %d trying to get %d buffers (of %d max)\n",
+		     current->pid, d.request_count, dma->buf_count);
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if (d.request_count) {
+		retcode = mga_dma_get_buffers(dev, &d);
+	}
+
+	if (copy_to_user((drm_dma_t *) arg, &d, sizeof(d)))
+		return -EFAULT;
+	return retcode;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/proc.c linux/drivers/char/drm-4.0/proc.c
--- linux.orig/drivers/char/drm-4.0/proc.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/proc.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,578 @@
+/* proc.c -- /proc support for DRM -*- linux-c -*-
+ * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+static struct proc_dir_entry *drm_root	   = NULL;
+static struct proc_dir_entry *drm_dev_root = NULL;
+static char		     drm_slot_name[64];
+
+static int	   drm_name_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+static int	   drm_vm_info(char *buf, char **start, off_t offset,
+			       int len, int *eof, void *data);
+static int	   drm_clients_info(char *buf, char **start, off_t offset,
+				    int len, int *eof, void *data);
+static int	   drm_queues_info(char *buf, char **start, off_t offset,
+				   int len, int *eof, void *data);
+static int	   drm_bufs_info(char *buf, char **start, off_t offset,
+				 int len, int *eof, void *data);
+#if DRM_DEBUG_CODE
+static int	   drm_vma_info(char *buf, char **start, off_t offset,
+				int len, int *eof, void *data);
+#endif
+#if DRM_DMA_HISTOGRAM
+static int	   drm_histo_info(char *buf, char **start, off_t offset,
+				  int len, int *eof, void *data);
+#endif
+
+struct drm_proc_list {
+	const char *name;
+	int	   (*f)(char *, char **, off_t, int, int *, void *);
+} drm_proc_list[] = {
+	{ "name",    drm_name_info    },
+	{ "mem",     drm_mem_info     },
+	{ "vm",	     drm_vm_info      },
+	{ "clients", drm_clients_info },
+	{ "queues",  drm_queues_info  },
+	{ "bufs",    drm_bufs_info    },
+#if DRM_DEBUG_CODE
+	{ "vma",     drm_vma_info     },
+#endif
+#if DRM_DMA_HISTOGRAM
+	{ "histo",   drm_histo_info   },
+#endif
+};
+#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0]))
+
+int drm_proc_init(drm_device_t *dev)
+{
+	struct proc_dir_entry *ent;
+	int		      i, j;
+
+	drm_root = create_proc_entry("dri", S_IFDIR, NULL);
+	if (!drm_root) {
+		DRM_ERROR("Cannot create /proc/dri\n");
+		return -1;
+	}
+
+				/* Instead of doing this search, we should
+				   add some global support for /proc/dri. */
+	for (i = 0; i < 8; i++) {
+		sprintf(drm_slot_name, "dri/%d", i);
+		drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL);
+		if (!drm_dev_root) {
+			DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name);
+			remove_proc_entry("dri", NULL);
+			break;
+		}
+		if (drm_dev_root->nlink == 2) break;
+		drm_dev_root = NULL;
+	}
+	if (!drm_dev_root) {
+		DRM_ERROR("Cannot find slot in /proc/dri\n");
+		return -1;
+	}
+
+	for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+		ent = create_proc_entry(drm_proc_list[i].name,
+					S_IFREG|S_IRUGO, drm_dev_root);
+		if (!ent) {
+			DRM_ERROR("Cannot create /proc/%s/%s\n",
+				  drm_slot_name, drm_proc_list[i].name);
+			for (j = 0; j < i; j++)
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			remove_proc_entry(drm_slot_name, NULL);
+			remove_proc_entry("dri", NULL);
+			return -1;
+		}
+		ent->read_proc = drm_proc_list[i].f;
+		ent->data      = dev;
+	}
+
+	return 0;
+}
+
+
+int drm_proc_cleanup(void)
+{
+	int i;
+	
+	if (drm_root) {
+		if (drm_dev_root) {
+			for (i = 0; i < DRM_PROC_ENTRIES; i++) {
+				remove_proc_entry(drm_proc_list[i].name,
+						  drm_dev_root);
+			}
+			remove_proc_entry(drm_slot_name, NULL);
+		}
+		remove_proc_entry("dri", NULL);
+		remove_proc_entry(DRM_NAME, NULL);
+	}
+	drm_root = drm_dev_root = NULL;
+	return 0;
+}
+
+static int drm_name_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	if (dev->unique) {
+		DRM_PROC_PRINT("%s 0x%x %s\n",
+			       dev->name, dev->device, dev->unique);
+	} else {
+		DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device);
+	}
+	return len;
+}
+
+static int _drm_vm_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_map_t    *map;
+				/* Hardcoded from _DRM_FRAME_BUFFER,
+                                   _DRM_REGISTERS, _DRM_SHM, and
+                                   _DRM_AGP. */
+	const char   *types[] = { "FB", "REG", "SHM", "AGP" };
+	const char   *type;
+	int	     i;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("slot	 offset	      size type flags	 "
+		       "address mtrr\n\n");
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->type < 0 || map->type > 3) type = "??";
+		else				    type = types[map->type];
+		DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s  0x%02x 0x%08lx ",
+			       i,
+			       map->offset,
+			       map->size,
+			       type,
+			       map->flags,
+			       (unsigned long)map->handle);
+		if (map->mtrr < 0) {
+			DRM_PROC_PRINT("none\n");
+		} else {
+			DRM_PROC_PRINT("%4d\n", map->mtrr);
+		}
+	}
+
+	return len;
+}
+
+static int drm_vm_info(char *buf, char **start, off_t offset, int len,
+		       int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vm_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_queues_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     i;
+	drm_queue_t  *q;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("  ctx/flags   use   fin"
+		       "   blk/rw/rwf  wait    flushed	   queued"
+		       "      locks\n\n");
+	for (i = 0; i < dev->queue_count; i++) {
+		q = dev->queuelist[i];
+		atomic_inc(&q->use_count);
+		DRM_PROC_PRINT_RET(atomic_dec(&q->use_count),
+				   "%5d/0x%03x %5d %5d"
+				   " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n",
+				   i,
+				   q->flags,
+				   atomic_read(&q->use_count),
+				   atomic_read(&q->finalization),
+				   atomic_read(&q->block_count),
+				   atomic_read(&q->block_read) ? 'r' : '-',
+				   atomic_read(&q->block_write) ? 'w' : '-',
+				   waitqueue_active(&q->read_queue) ? 'r':'-',
+				   waitqueue_active(&q->write_queue) ? 'w':'-',
+				   waitqueue_active(&q->flush_queue) ? 'f':'-',
+				   DRM_BUFCOUNT(&q->waitlist),
+				   atomic_read(&q->total_flushed),
+				   atomic_read(&q->total_queued),
+				   atomic_read(&q->total_locks));
+		atomic_dec(&q->use_count);
+	}
+	
+	return len;
+}
+
+static int drm_queues_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_queues_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+/* drm_bufs_info is called whenever a process reads
+   /dev/drm/<dev>/bufs. */
+
+static int _drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+
+	if (!dma)	return 0;
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT(" o     size count  free	 segs pages    kB\n\n");
+	for (i = 0; i <= DRM_MAX_ORDER; i++) {
+		if (dma->bufs[i].buf_count)
+			DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n",
+				       i,
+				       dma->bufs[i].buf_size,
+				       dma->bufs[i].buf_count,
+				       atomic_read(&dma->bufs[i]
+						   .freelist.count),
+				       dma->bufs[i].seg_count,
+				       dma->bufs[i].seg_count
+				       *(1 << dma->bufs[i].page_order),
+				       (dma->bufs[i].seg_count
+					* (1 << dma->bufs[i].page_order))
+				       * PAGE_SIZE / 1024);
+	}
+	DRM_PROC_PRINT("\n");
+	for (i = 0; i < dma->buf_count; i++) {
+		if (i && !(i%32)) DRM_PROC_PRINT("\n");
+		DRM_PROC_PRINT(" %d", dma->buflist[i]->list);
+	}
+	DRM_PROC_PRINT("\n");
+
+	return len;
+}
+
+static int drm_bufs_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_bufs_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+
+static int _drm_clients_info(char *buf, char **start, off_t offset, int len,
+			     int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	drm_file_t   *priv;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("a dev	pid    uid	magic	  ioctls\n\n");
+	for (priv = dev->file_first; priv; priv = priv->next) {
+		DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n",
+			       priv->authenticated ? 'y' : 'n',
+			       priv->minor,
+			       priv->pid,
+			       priv->uid,
+			       priv->magic,
+			       priv->ioctl_count);
+	}
+
+	return len;
+}
+
+static int drm_clients_info(char *buf, char **start, off_t offset, int len,
+			    int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_clients_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+
+#if DRM_DEBUG_CODE
+
+#define DRM_VMA_VERBOSE 0
+
+static int _drm_vma_info(char *buf, char **start, off_t offset, int len,
+			 int *eof, void *data)
+{
+	drm_device_t	      *dev = (drm_device_t *)data;
+	drm_vma_entry_t	      *pt;
+	struct vm_area_struct *vma;
+#if DRM_VMA_VERBOSE
+	unsigned long	      i;
+	unsigned long	      address;
+	pgd_t		      *pgd;
+	pmd_t		      *pmd;
+	pte_t		      *pte;
+#endif
+#if defined(__i386__)
+	unsigned int	      pgprot;
+#endif
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+	DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n",
+		       atomic_read(&dev->vma_count),
+		       high_memory, virt_to_phys(high_memory));
+	for (pt = dev->vmalist; pt; pt = pt->next) {
+		if (!(vma = pt->vma)) continue;
+		DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx",
+			       pt->pid,
+			       vma->vm_start,
+			       vma->vm_end,
+			       vma->vm_flags & VM_READ	   ? 'r' : '-',
+			       vma->vm_flags & VM_WRITE	   ? 'w' : '-',
+			       vma->vm_flags & VM_EXEC	   ? 'x' : '-',
+			       vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
+			       vma->vm_flags & VM_LOCKED   ? 'l' : '-',
+			       vma->vm_flags & VM_IO	   ? 'i' : '-',
+			       VM_OFFSET(vma));
+		
+#if defined(__i386__)
+		pgprot = pgprot_val(vma->vm_page_prot);
+		DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c",
+			       pgprot & _PAGE_PRESENT  ? 'p' : '-',
+			       pgprot & _PAGE_RW       ? 'w' : 'r',
+			       pgprot & _PAGE_USER     ? 'u' : 's',
+			       pgprot & _PAGE_PWT      ? 't' : 'b',
+			       pgprot & _PAGE_PCD      ? 'u' : 'c',
+			       pgprot & _PAGE_ACCESSED ? 'a' : '-',
+			       pgprot & _PAGE_DIRTY    ? 'd' : '-',
+			       pgprot & _PAGE_PSE      ? 'm' : 'k',
+			       pgprot & _PAGE_GLOBAL   ? 'g' : 'l' );
+#endif		
+		DRM_PROC_PRINT("\n");
+#if 0
+		for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) {
+			pgd = pgd_offset(vma->vm_mm, i);
+			pmd = pmd_offset(pgd, i);
+			pte = pte_offset(pmd, i);
+			if (pte_present(*pte)) {
+				address = __pa(pte_page(*pte))
+					+ (i & (PAGE_SIZE-1));
+				DRM_PROC_PRINT("      0x%08lx -> 0x%08lx"
+					       " %c%c%c%c%c\n",
+					       i,
+					       address,
+					       pte_read(*pte)  ? 'r' : '-',
+					       pte_write(*pte) ? 'w' : '-',
+					       pte_exec(*pte)  ? 'x' : '-',
+					       pte_dirty(*pte) ? 'd' : '-',
+					       pte_young(*pte) ? 'a' : '-' );
+			} else {
+				DRM_PROC_PRINT("      0x%08lx\n", i);
+			}
+		}
+#endif
+	}
+	
+	return len;
+}
+
+static int drm_vma_info(char *buf, char **start, off_t offset, int len,
+			int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_vma_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
+
+
+#if DRM_DMA_HISTOGRAM
+static int _drm_histo_info(char *buf, char **start, off_t offset, int len,
+			   int *eof, void *data)
+{
+	drm_device_t	 *dev = (drm_device_t *)data;
+	drm_device_dma_t *dma = dev->dma;
+	int		 i;
+	unsigned long	 slot_value = DRM_DMA_HISTOGRAM_INITIAL;
+	unsigned long	 prev_value = 0;
+	drm_buf_t	 *buffer;
+
+	if (offset > 0) return 0; /* no partial requests */
+	len  = 0;
+	*eof = 1;
+
+	DRM_PROC_PRINT("general statistics:\n");
+	DRM_PROC_PRINT("total	 %10u\n", atomic_read(&dev->histo.total));
+	DRM_PROC_PRINT("open	 %10u\n", atomic_read(&dev->total_open));
+	DRM_PROC_PRINT("close	 %10u\n", atomic_read(&dev->total_close));
+	DRM_PROC_PRINT("ioctl	 %10u\n", atomic_read(&dev->total_ioctl));
+	DRM_PROC_PRINT("irq	 %10u\n", atomic_read(&dev->total_irq));
+	DRM_PROC_PRINT("ctx	 %10u\n", atomic_read(&dev->total_ctx));
+	
+	DRM_PROC_PRINT("\nlock statistics:\n");
+	DRM_PROC_PRINT("locks	 %10u\n", atomic_read(&dev->total_locks));
+	DRM_PROC_PRINT("unlocks	 %10u\n", atomic_read(&dev->total_unlocks));
+	DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends));
+	DRM_PROC_PRINT("sleeps	 %10u\n", atomic_read(&dev->total_sleeps));
+
+
+	if (dma) {
+		DRM_PROC_PRINT("\ndma statistics:\n");
+		DRM_PROC_PRINT("prio	 %10u\n",
+			       atomic_read(&dma->total_prio));
+		DRM_PROC_PRINT("bytes	 %10u\n",
+			       atomic_read(&dma->total_bytes));
+		DRM_PROC_PRINT("dmas	 %10u\n",
+			       atomic_read(&dma->total_dmas));
+		DRM_PROC_PRINT("missed:\n");
+		DRM_PROC_PRINT("  dma	 %10u\n",
+			       atomic_read(&dma->total_missed_dma));
+		DRM_PROC_PRINT("  lock	 %10u\n",
+			       atomic_read(&dma->total_missed_lock));
+		DRM_PROC_PRINT("  free	 %10u\n",
+			       atomic_read(&dma->total_missed_free));
+		DRM_PROC_PRINT("  sched	 %10u\n",
+			       atomic_read(&dma->total_missed_sched));
+		DRM_PROC_PRINT("tried	 %10u\n",
+			       atomic_read(&dma->total_tried));
+		DRM_PROC_PRINT("hit	 %10u\n",
+			       atomic_read(&dma->total_hit));
+		DRM_PROC_PRINT("lost	 %10u\n",
+			       atomic_read(&dma->total_lost));
+		
+		buffer = dma->next_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("next_buffer    none\n");
+		}
+		buffer = dma->this_buffer;
+		if (buffer) {
+			DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx);
+		} else {
+			DRM_PROC_PRINT("this_buffer    none\n");
+		}
+	}
+	
+
+	DRM_PROC_PRINT("\nvalues:\n");
+	if (dev->lock.hw_lock) {
+		DRM_PROC_PRINT("lock	       0x%08x\n",
+			       dev->lock.hw_lock->lock);
+	} else {
+		DRM_PROC_PRINT("lock		     none\n");
+	}
+	DRM_PROC_PRINT("context_flag   0x%08lx\n", dev->context_flag);
+	DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag);
+	DRM_PROC_PRINT("dma_flag       0x%08lx\n", dev->dma_flag);
+
+	DRM_PROC_PRINT("queue_count    %10d\n",	 dev->queue_count);
+	DRM_PROC_PRINT("last_context   %10d\n",	 dev->last_context);
+	DRM_PROC_PRINT("last_switch    %10lu\n", dev->last_switch);
+	DRM_PROC_PRINT("last_checked   %10d\n",	 dev->last_checked);
+		
+	
+	DRM_PROC_PRINT("\n		       q2d	  d2c	     c2f"
+		       "	q2c	   q2f	      dma	 sch"
+		       "	ctx	  lacq	     lhld\n\n");
+	for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) {
+		DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u"
+			       " %10u %10u %10u %10u %10u\n",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ",
+			       i == DRM_DMA_HISTOGRAM_SLOTS - 1
+			       ? prev_value : slot_value ,
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_dispatched[i]),
+			       atomic_read(&dev->histo
+					   .dispatched_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .completed_to_freed[i]),
+			       
+			       atomic_read(&dev->histo
+					   .queued_to_completed[i]),
+			       atomic_read(&dev->histo
+					   .queued_to_freed[i]),
+			       atomic_read(&dev->histo.dma[i]),
+			       atomic_read(&dev->histo.schedule[i]),
+			       atomic_read(&dev->histo.ctx[i]),
+			       atomic_read(&dev->histo.lacq[i]),
+			       atomic_read(&dev->histo.lhld[i]));
+		prev_value = slot_value;
+		slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value);
+	}
+	return len;
+}
+
+static int drm_histo_info(char *buf, char **start, off_t offset, int len,
+			  int *eof, void *data)
+{
+	drm_device_t *dev = (drm_device_t *)data;
+	int	     ret;
+
+	down(&dev->struct_sem);
+	ret = _drm_histo_info(buf, start, offset, len, eof, data);
+	up(&dev->struct_sem);
+	return ret;
+}
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_bufs.c linux/drivers/char/drm-4.0/r128_bufs.c
--- linux.orig/drivers/char/drm-4.0/r128_bufs.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_bufs.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,319 @@
+/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+#include "linux/un.h"
+
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+int r128_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd,
+		     unsigned long arg)
+{
+	drm_file_t       *priv = filp->private_data;
+	drm_device_t     *dev  = priv->dev;
+	drm_device_dma_t *dma  = dev->dma;
+	drm_buf_desc_t    request;
+	drm_buf_entry_t  *entry;
+	drm_buf_t        *buf;
+	unsigned long     offset;
+	unsigned long     agp_offset;
+	int               count;
+	int               order;
+	int               size;
+	int               alignment;
+	int               page_order;
+	int               total;
+	int               byte_count;
+	int               i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	count      = request.count;
+	order      = drm_order(request.size);
+	size       = 1 << order;
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total      = PAGE_SIZE << page_order;
+
+	byte_count = 0;
+	agp_offset = dev->agp->base + request.agp_start;
+
+	DRM_DEBUG("count:      %d\n",  count);
+	DRM_DEBUG("order:      %d\n",  order);
+	DRM_DEBUG("size:       %d\n",  size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment:  %d\n",  alignment);
+	DRM_DEBUG("page_order: %d\n",  page_order);
+	DRM_DEBUG("total:      %d\n",  total);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* Might be a poor limit, but take that up with XFree86 
+	   if its a problem */
+	   
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset            = 0;
+
+	for (offset = 0;
+	     entry->buf_count < count;
+	     offset += alignment, ++entry->buf_count) {
+		buf          = &entry->buflist[entry->buf_count];
+		buf->idx     = dma->buf_count + entry->buf_count;
+		buf->total   = alignment;
+		buf->order   = order;
+		buf->used    = 0;
+		buf->offset  = (dma->byte_count + offset);
+		buf->address = (void *)(agp_offset + offset);
+		buf->next    = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid     = 0;
+
+		buf->dev_priv_size = sizeof(drm_r128_buf_priv_t);
+		buf->dev_private   = drm_alloc(sizeof(drm_r128_buf_priv_t),
+					       DRM_MEM_BUFS);
+		memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed  = 0;
+		buf->time_freed      = 0;
+#endif
+
+		byte_count += PAGE_SIZE << page_order;
+
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+#endif
+
+int r128_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_buf_desc_t		request;
+
+	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	if (copy_from_user(&request,
+			   (drm_buf_desc_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (request.flags & _DRM_AGP_BUFFER)
+		return r128_addbufs_agp(inode, filp, cmd, arg);
+	else
+#endif
+		return -EINVAL;
+}
+
+int r128_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_r128_private_t	*dev_priv	= dev->dev_private;
+	drm_device_dma_t	*dma		= dev->dma;
+	int			 retcode	= 0;
+	const int		 zero		= 0;
+	unsigned long		 virtual;
+	unsigned long		 address;
+	drm_buf_map_t		 request;
+	int			 i;
+
+	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request,
+			   (drm_buf_map_t *)arg,
+			   sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if (dma->flags & _DRM_DMA_USE_AGP) {
+			drm_map_t *map;
+
+			map = dev_priv->buffers;
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size,
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED,
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg,
+			 &request,
+			 sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_cce.c linux/drivers/char/drm-4.0/r128_cce.c
--- linux.orig/drivers/char/drm-4.0/r128_cce.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_cce.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1253 @@
+/* r128_cce.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+#include <linux/delay.h>
+
+
+/* FIXME: Temporary CCE packet buffer */
+u32 r128_cce_buffer[(1 << 14)] __attribute__ ((aligned (32)));
+
+/* CCE microcode (from ATI) */
+static u32 r128_cce_microcode[] = {
+	0, 276838400, 0, 268449792, 2, 142, 2, 145, 0, 1076765731, 0,
+	1617039951, 0, 774592877, 0, 1987540286, 0, 2307490946U, 0,
+	599558925, 0, 589505315, 0, 596487092, 0, 589505315, 1,
+	11544576, 1, 206848, 1, 311296, 1, 198656, 2, 912273422, 11,
+	262144, 0, 0, 1, 33559837, 1, 7438, 1, 14809, 1, 6615, 12, 28,
+	1, 6614, 12, 28, 2, 23, 11, 18874368, 0, 16790922, 1, 409600, 9,
+	30, 1, 147854772, 16, 420483072, 3, 8192, 0, 10240, 1, 198656,
+	1, 15630, 1, 51200, 10, 34858, 9, 42, 1, 33559823, 2, 10276, 1,
+	15717, 1, 15718, 2, 43, 1, 15936948, 1, 570480831, 1, 14715071,
+	12, 322123831, 1, 33953125, 12, 55, 1, 33559908, 1, 15718, 2,
+	46, 4, 2099258, 1, 526336, 1, 442623, 4, 4194365, 1, 509952, 1,
+	459007, 3, 0, 12, 92, 2, 46, 12, 176, 1, 15734, 1, 206848, 1,
+	18432, 1, 133120, 1, 100670734, 1, 149504, 1, 165888, 1,
+	15975928, 1, 1048576, 6, 3145806, 1, 15715, 16, 2150645232U, 2,
+	268449859, 2, 10307, 12, 176, 1, 15734, 1, 15735, 1, 15630, 1,
+	15631, 1, 5253120, 6, 3145810, 16, 2150645232U, 1, 15864, 2, 82,
+	1, 343310, 1, 1064207, 2, 3145813, 1, 15728, 1, 7817, 1, 15729,
+	3, 15730, 12, 92, 2, 98, 1, 16168, 1, 16167, 1, 16002, 1, 16008,
+	1, 15974, 1, 15975, 1, 15990, 1, 15976, 1, 15977, 1, 15980, 0,
+	15981, 1, 10240, 1, 5253120, 1, 15720, 1, 198656, 6, 110, 1,
+	180224, 1, 103824738, 2, 112, 2, 3145839, 0, 536885440, 1,
+	114880, 14, 125, 12, 206975, 1, 33559995, 12, 198784, 0,
+	33570236, 1, 15803, 0, 15804, 3, 294912, 1, 294912, 3, 442370,
+	1, 11544576, 0, 811612160, 1, 12593152, 1, 11536384, 1,
+	14024704, 7, 310382726, 0, 10240, 1, 14796, 1, 14797, 1, 14793,
+	1, 14794, 0, 14795, 1, 268679168, 1, 9437184, 1, 268449792, 1,
+	198656, 1, 9452827, 1, 1075854602, 1, 1075854603, 1, 557056, 1,
+	114880, 14, 159, 12, 198784, 1, 1109409213, 12, 198783, 1,
+	1107312059, 12, 198784, 1, 1109409212, 2, 162, 1, 1075854781, 1,
+	1073757627, 1, 1075854780, 1, 540672, 1, 10485760, 6, 3145894,
+	16, 274741248, 9, 168, 3, 4194304, 3, 4209949, 0, 0, 0, 256, 14,
+	174, 1, 114857, 1, 33560007, 12, 176, 0, 10240, 1, 114858, 1,
+	33560018, 1, 114857, 3, 33560007, 1, 16008, 1, 114874, 1,
+	33560360, 1, 114875, 1, 33560154, 0, 15963, 0, 256, 0, 4096, 1,
+	409611, 9, 188, 0, 10240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_REMAPFREE(_m)                                                    \
+	do {                                                                \
+		if ((_m)->handle && (_m)->size)                             \
+			drm_ioremapfree((_m)->handle, (_m)->size);          \
+	} while (0)
+
+#define DO_FIND_MAP(_m, _o)                                                 \
+	do {                                                                \
+		int _i;                                                     \
+		for (_i = 0; _i < dev->map_count; _i++) {                   \
+			if (dev->maplist[_i]->offset == _o) {               \
+				_m = dev->maplist[_i];                      \
+				break;                                      \
+			}                                                   \
+		}                                                           \
+	} while (0)
+
+
+int R128_READ_PLL(drm_device_t *dev, int addr)
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f);
+	return R128_READ(R128_CLOCK_CNTL_DATA);
+}
+
+#if 0
+static void r128_status( drm_r128_private_t *dev_priv )
+{
+	printk( "GUI_STAT           = 0x%08x\n",
+		(unsigned int)R128_READ( R128_GUI_STAT ) );
+	printk( "PM4_STAT           = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_STAT ) );
+	printk( "PM4_BUFFER_DL_WPTR = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_DL_WPTR ) );
+	printk( "PM4_BUFFER_DL_RPTR = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_DL_RPTR ) );
+	printk( "PM4_MICRO_CNTL     = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_MICRO_CNTL ) );
+	printk( "PM4_BUFFER_CNTL    = 0x%08x\n",
+		(unsigned int)R128_READ( R128_PM4_BUFFER_CNTL ) );
+}
+#endif
+
+
+/* ================================================================
+ * Engine, FIFO control
+ */
+
+static int r128_do_pixcache_flush( drm_r128_private_t *dev_priv )
+{
+	u32 tmp;
+	int i;
+
+	tmp = R128_READ( R128_PC_NGUI_CTLSTAT ) | R128_PC_FLUSH_ALL;
+	R128_WRITE( R128_PC_NGUI_CTLSTAT, tmp );
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(R128_READ( R128_PC_NGUI_CTLSTAT ) & R128_PC_BUSY) ) {
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_fifo( drm_r128_private_t *dev_priv, int entries )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		int slots = R128_READ( R128_GUI_STAT ) & R128_GUI_FIFOCNT_MASK;
+		if ( slots >= entries ) return 0;
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+static int r128_do_wait_for_idle( drm_r128_private_t *dev_priv )
+{
+	int i, ret;
+
+	ret = r128_do_wait_for_fifo( dev_priv, 64 );
+	if ( !ret ) return ret;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(R128_READ( R128_GUI_STAT ) & R128_GUI_ACTIVE) ) {
+			r128_do_pixcache_flush( dev_priv );
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "%s failed!\n", __FUNCTION__ );
+	return -EBUSY;
+}
+
+
+/* ================================================================
+ * CCE control, initialization
+ */
+
+/* Load the microcode for the CCE */
+static void r128_cce_load_microcode( drm_r128_private_t *dev_priv )
+{
+	int i;
+
+	r128_do_wait_for_idle( dev_priv );
+
+	R128_WRITE( R128_PM4_MICROCODE_ADDR, 0 );
+	for ( i = 0 ; i < 256 ; i++ ) {
+		R128_WRITE( R128_PM4_MICROCODE_DATAH,
+			    r128_cce_microcode[i * 2] );
+		R128_WRITE( R128_PM4_MICROCODE_DATAL,
+			    r128_cce_microcode[i * 2 + 1] );
+	}
+}
+
+/* Flush any pending commands to the CCE.  This should only be used just
+ * prior to a wait for idle, as it informs the engine that the command
+ * stream is ending.
+ */
+static void r128_do_cce_flush( drm_r128_private_t *dev_priv )
+{
+	u32 tmp;
+
+	tmp = R128_READ( R128_PM4_BUFFER_DL_WPTR ) | R128_PM4_BUFFER_DL_DONE;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, tmp );
+}
+
+/* Wait for the CCE to go idle.
+ */
+static int r128_do_cce_idle( drm_r128_private_t *dev_priv )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( *dev_priv->ring.head == dev_priv->ring.tail ) {
+			int pm4stat = R128_READ( R128_PM4_STAT );
+			if ( ( (pm4stat & R128_PM4_FIFOCNT_MASK) >=
+			       dev_priv->cce_fifo_size ) &&
+			     !(pm4stat & (R128_PM4_BUSY |
+					  R128_PM4_GUI_ACTIVE)) ) {
+				return r128_do_pixcache_flush( dev_priv );
+			}
+		}
+		udelay( 1 );
+	}
+
+#if 0
+	DRM_ERROR( "failed!\n" );
+	r128_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+/* Start the Concurrent Command Engine.
+ */
+static void r128_do_cce_start( drm_r128_private_t *dev_priv )
+{
+	r128_do_wait_for_idle( dev_priv );
+
+	R128_WRITE( R128_PM4_BUFFER_CNTL,
+		    dev_priv->cce_mode | dev_priv->ring.size_l2qw );
+	R128_READ( R128_PM4_BUFFER_ADDR ); /* as per the sample code */
+	R128_WRITE( R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN );
+
+	dev_priv->cce_running = 1;
+}
+
+/* Reset the Concurrent Command Engine.  This will not flush any pending
+ * commangs, so you must wait for the CCE command stream to complete
+ * before calling this routine.
+ */
+static void r128_do_cce_reset( drm_r128_private_t *dev_priv )
+{
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 );
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 );
+	*dev_priv->ring.head = 0;
+	dev_priv->ring.tail = 0;
+}
+
+/* Stop the Concurrent Command Engine.  This will not flush any pending
+ * commangs, so you must flush the command stream and wait for the CCE
+ * to go idle before calling this routine.
+ */
+static void r128_do_cce_stop( drm_r128_private_t *dev_priv )
+{
+	R128_WRITE( R128_PM4_MICRO_CNTL, 0 );
+	R128_WRITE( R128_PM4_BUFFER_CNTL, R128_PM4_NONPM4 );
+
+	dev_priv->cce_running = 0;
+}
+
+/* Reset the engine.  This will stop the CCE if it is running.
+ */
+static int r128_do_engine_reset( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32 clock_cntl_index, mclk_cntl, gen_reset_cntl;
+
+	r128_do_pixcache_flush( dev_priv );
+
+	clock_cntl_index = R128_READ( R128_CLOCK_CNTL_INDEX );
+	mclk_cntl = R128_READ_PLL( dev, R128_MCLK_CNTL );
+
+	R128_WRITE_PLL( R128_MCLK_CNTL,
+			mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP );
+
+	gen_reset_cntl = R128_READ( R128_GEN_RESET_CNTL );
+
+	/* Taken from the sample code - do not change */
+	R128_WRITE( R128_GEN_RESET_CNTL,
+		    gen_reset_cntl | R128_SOFT_RESET_GUI );
+	R128_READ( R128_GEN_RESET_CNTL );
+	R128_WRITE( R128_GEN_RESET_CNTL,
+		    gen_reset_cntl & ~R128_SOFT_RESET_GUI );
+	R128_READ( R128_GEN_RESET_CNTL );
+
+	R128_WRITE_PLL( R128_MCLK_CNTL, mclk_cntl );
+	R128_WRITE( R128_CLOCK_CNTL_INDEX, clock_cntl_index );
+	R128_WRITE( R128_GEN_RESET_CNTL, gen_reset_cntl );
+
+	/* Reset the CCE ring */
+	r128_do_cce_reset( dev_priv );
+
+	/* The CCE is no longer running after an engine reset */
+	dev_priv->cce_running = 0;
+
+	/* Reset any pending vertex, indirect buffers */
+	r128_freelist_reset( dev );
+
+	return 0;
+}
+
+static void r128_cce_init_ring_buffer( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	u32 ring_start;
+	u32 tmp;
+
+	/* The manual (p. 2) says this address is in "VM space".  This
+	 * means it's an offset from the start of AGP space.
+	 */
+	ring_start = dev_priv->cce_ring->offset - dev->agp->base;
+	R128_WRITE( R128_PM4_BUFFER_OFFSET, ring_start | R128_AGP_OFFSET );
+
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, 0 );
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR, 0 );
+
+	/* DL_RPTR_ADDR is a physical address in AGP space. */
+	*dev_priv->ring.head = 0;
+	R128_WRITE( R128_PM4_BUFFER_DL_RPTR_ADDR,
+		    dev_priv->ring_rptr->offset );
+
+	/* Set watermark control */
+	R128_WRITE( R128_PM4_BUFFER_WM_CNTL,
+		    ((R128_WATERMARK_L/4) << R128_WMA_SHIFT)
+		    | ((R128_WATERMARK_M/4) << R128_WMB_SHIFT)
+		    | ((R128_WATERMARK_N/4) << R128_WMC_SHIFT)
+		    | ((R128_WATERMARK_K/64) << R128_WB_WM_SHIFT) );
+
+	/* Force read.  Why?  Because it's in the examples... */
+	R128_READ( R128_PM4_BUFFER_ADDR );
+
+	/* Turn on bus mastering */
+	tmp = R128_READ( R128_BUS_CNTL ) & ~R128_BUS_MASTER_DIS;
+	R128_WRITE( R128_BUS_CNTL, tmp );
+}
+
+static int r128_do_init_cce( drm_device_t *dev, drm_r128_init_t *init )
+{
+	drm_r128_private_t *dev_priv;
+        int i;
+
+	dev_priv = drm_alloc( sizeof(drm_r128_private_t), DRM_MEM_DRIVER );
+	if ( dev_priv == NULL )
+		return -ENOMEM;
+	dev->dev_private = (void *)dev_priv;
+
+	memset( dev_priv, 0, sizeof(drm_r128_private_t) );
+
+	dev_priv->is_pci = init->is_pci;
+
+	/* GH: We don't support PCI cards until PCI GART is implemented.
+	 * Fail here so we can remove all checks for PCI cards around
+	 * the CCE ring code.
+	 */
+	if ( dev_priv->is_pci ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->usec_timeout = init->usec_timeout;
+	if ( dev_priv->usec_timeout < 1 ||
+	     dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->cce_mode = init->cce_mode;
+	dev_priv->cce_secure = init->cce_secure;
+
+	/* GH: Simple idle check.
+	 */
+	atomic_set( &dev_priv->idle_count, 0 );
+
+	/* We don't support anything other than bus-mastering ring mode,
+	 * but the ring can be in either AGP or PCI space for the ring
+	 * read pointer.
+	 */
+	if ( ( init->cce_mode != R128_PM4_192BM ) &&
+	     ( init->cce_mode != R128_PM4_128BM_64INDBM ) &&
+	     ( init->cce_mode != R128_PM4_64BM_128INDBM ) &&
+	     ( init->cce_mode != R128_PM4_64BM_64VCBM_64INDBM ) ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	switch ( init->cce_mode ) {
+	case R128_PM4_NONPM4:
+		dev_priv->cce_fifo_size = 0;
+		break;
+	case R128_PM4_192PIO:
+	case R128_PM4_192BM:
+		dev_priv->cce_fifo_size = 192;
+		break;
+	case R128_PM4_128PIO_64INDBM:
+	case R128_PM4_128BM_64INDBM:
+		dev_priv->cce_fifo_size = 128;
+		break;
+	case R128_PM4_64PIO_128INDBM:
+	case R128_PM4_64BM_128INDBM:
+	case R128_PM4_64PIO_64VCBM_64INDBM:
+	case R128_PM4_64BM_64VCBM_64INDBM:
+	case R128_PM4_64PIO_64VCPIO_64INDPIO:
+		dev_priv->cce_fifo_size = 64;
+		break;
+	}
+
+	dev_priv->fb_bpp	= init->fb_bpp;
+	dev_priv->front_offset	= init->front_offset;
+	dev_priv->front_pitch	= init->front_pitch;
+	dev_priv->back_offset	= init->back_offset;
+	dev_priv->back_pitch	= init->back_pitch;
+
+	dev_priv->depth_bpp	= init->depth_bpp;
+	dev_priv->depth_offset	= init->depth_offset;
+	dev_priv->depth_pitch	= init->depth_pitch;
+	dev_priv->span_offset	= init->span_offset;
+
+	dev_priv->front_pitch_offset_c = (((dev_priv->front_pitch/8) << 21) |
+					  (dev_priv->front_offset >> 5));
+	dev_priv->back_pitch_offset_c = (((dev_priv->back_pitch/8) << 21) |
+					 (dev_priv->back_offset >> 5));
+	dev_priv->depth_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) |
+					  (dev_priv->depth_offset >> 5) |
+					  R128_DST_TILE);
+	dev_priv->span_pitch_offset_c = (((dev_priv->depth_pitch/8) << 21) |
+					 (dev_priv->span_offset >> 5));
+
+	/* FIXME: We want multiple shared areas, including one shared
+	 * only by the X Server and kernel module.
+	 */
+	for ( i = 0 ; i < dev->map_count ; i++ ) {
+		if ( dev->maplist[i]->type == _DRM_SHM ) {
+			dev_priv->sarea = dev->maplist[i];
+			break;
+		}
+	}
+
+	DO_FIND_MAP( dev_priv->fb, init->fb_offset );
+	DO_FIND_MAP( dev_priv->mmio, init->mmio_offset );
+	DO_FIND_MAP( dev_priv->cce_ring, init->ring_offset );
+	DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset );
+	DO_FIND_MAP( dev_priv->buffers, init->buffers_offset );
+
+	if ( !dev_priv->is_pci ) {
+		DO_FIND_MAP( dev_priv->agp_textures,
+			     init->agp_textures_offset );
+	}
+
+	dev_priv->sarea_priv =
+		(drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle +
+				     init->sarea_priv_offset);
+
+	DO_REMAP( dev_priv->cce_ring );
+	DO_REMAP( dev_priv->ring_rptr );
+	DO_REMAP( dev_priv->buffers );
+#if 0
+	if ( !dev_priv->is_pci ) {
+		DO_REMAP( dev_priv->agp_textures );
+	}
+#endif
+
+	dev_priv->ring.head = ((__volatile__ u32 *)
+			       dev_priv->ring_rptr->handle);
+
+	dev_priv->ring.start = (u32 *)dev_priv->cce_ring->handle;
+	dev_priv->ring.end = ((u32 *)dev_priv->cce_ring->handle
+			      + init->ring_size / sizeof(u32));
+	dev_priv->ring.size = init->ring_size;
+	dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 );
+
+	dev_priv->ring.tail_mask =
+		(dev_priv->ring.size / sizeof(u32)) - 1;
+
+	dev_priv->sarea_priv->last_frame = 0;
+	R128_WRITE( R128_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame );
+
+	dev_priv->sarea_priv->last_dispatch = 0;
+	R128_WRITE( R128_LAST_DISPATCH_REG,
+		    dev_priv->sarea_priv->last_dispatch );
+
+	r128_cce_init_ring_buffer( dev );
+	r128_cce_load_microcode( dev_priv );
+	r128_do_engine_reset( dev );
+
+	return 0;
+}
+
+static int r128_do_cleanup_cce( drm_device_t *dev )
+{
+	if ( dev->dev_private ) {
+		drm_r128_private_t *dev_priv = dev->dev_private;
+
+		DO_REMAPFREE( dev_priv->cce_ring );
+		DO_REMAPFREE( dev_priv->ring_rptr );
+		DO_REMAPFREE( dev_priv->buffers );
+#if 0
+		if ( !dev_priv->is_pci ) {
+			DO_REMAPFREE( dev_priv->agp_textures );
+		}
+#endif
+
+		drm_free( dev->dev_private, sizeof(drm_r128_private_t),
+			  DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+int r128_cce_init( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_init_t init;
+
+	if ( copy_from_user( &init, (drm_r128_init_t *)arg, sizeof(init) ) )
+		return -EFAULT;
+
+	switch ( init.func ) {
+	case R128_INIT_CCE:
+		return r128_do_init_cce( dev, &init );
+	case R128_CLEANUP_CCE:
+		return r128_do_cleanup_cce( dev );
+	}
+
+	return -EINVAL;
+}
+
+int r128_cce_start( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( dev_priv->cce_running || dev_priv->cce_mode == R128_PM4_NONPM4 ) {
+		DRM_DEBUG( "%s while CCE running\n", __FUNCTION__ );
+		return 0;
+	}
+
+	r128_do_cce_start( dev_priv );
+
+	return 0;
+}
+
+/* Stop the CCE.  The engine must have been idled before calling this
+ * routine.
+ */
+int r128_cce_stop( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_cce_stop_t stop;
+	int ret;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stop, (drm_r128_init_t *)arg, sizeof(stop) ) )
+		return -EFAULT;
+
+	/* Flush any pending CCE commands.  This ensures any outstanding
+	 * commands are exectuted by the engine before we turn it off.
+	 */
+	if ( stop.flush ) {
+		r128_do_cce_flush( dev_priv );
+	}
+
+	/* If we fail to make the engine go idle, we return an error
+	 * code so that the DRM ioctl wrapper can try again.
+	 */
+	if ( stop.idle ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+	}
+
+	/* Finally, we can turn off the CCE.  If the engine isn't idle,
+	 * we will get some dropped triangles as they won't be fully
+	 * rendered before the CCE is shut down.
+	 */
+	r128_do_cce_stop( dev_priv );
+
+	/* Reset the engine */
+	r128_do_engine_reset( dev );
+
+	return 0;
+}
+
+/* Just reset the CCE ring.  Called as part of an X Server engine reset.
+ */
+int r128_cce_reset( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv ) {
+		DRM_DEBUG( "%s called before init done\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	r128_do_cce_reset( dev_priv );
+
+	/* The CCE is no longer running after an engine reset */
+	dev_priv->cce_running = 0;
+
+	return 0;
+}
+
+int r128_cce_idle( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( dev_priv->cce_running ) {
+		r128_do_cce_flush( dev_priv );
+	}
+
+	return r128_do_cce_idle( dev_priv );
+}
+
+int r128_engine_reset( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return r128_do_engine_reset( dev );
+}
+
+
+/* ================================================================
+ * Freelist management
+ */
+#define R128_BUFFER_USED	0xffffffff
+#define R128_BUFFER_FREE	0
+
+#if 0
+static int r128_freelist_init( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_freelist_t *entry;
+	int i;
+
+	dev_priv->head = drm_alloc( sizeof(drm_r128_freelist_t),
+				    DRM_MEM_DRIVER );
+	if ( dev_priv->head == NULL )
+		return -ENOMEM;
+
+	memset( dev_priv->head, 0, sizeof(drm_r128_freelist_t) );
+	dev_priv->head->age = R128_BUFFER_USED;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+
+		entry = drm_alloc( sizeof(drm_r128_freelist_t),
+				   DRM_MEM_DRIVER );
+		if ( !entry ) return -ENOMEM;
+
+		entry->age = R128_BUFFER_FREE;
+		entry->buf = buf;
+		entry->prev = dev_priv->head;
+		entry->next = dev_priv->head->next;
+		if ( !entry->next )
+			dev_priv->tail = entry;
+
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+		buf_priv->list_entry = entry;
+
+		dev_priv->head->next = entry;
+
+		if ( dev_priv->head->next )
+			dev_priv->head->next->prev = entry;
+	}
+
+	return 0;
+
+}
+#endif
+
+drm_buf_t *r128_freelist_get( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t *buf;
+	int i, t;
+
+	/* FIXME: Optimize -- use freelist code */
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		if ( buf->pid == 0 )
+			return buf;
+	}
+
+	for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) {
+		u32 done_age = R128_READ( R128_LAST_DISPATCH_REG );
+
+		for ( i = 0 ; i < dma->buf_count ; i++ ) {
+			buf = dma->buflist[i];
+			buf_priv = buf->dev_private;
+			if ( buf->pending && buf_priv->age <= done_age ) {
+				/* The buffer has been processed, so it
+				 * can now be used.
+				 */
+				buf->pending = 0;
+				return buf;
+			}
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "returning NULL!\n" );
+	return NULL;
+}
+
+void r128_freelist_reset( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	int i;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		drm_buf_t *buf = dma->buflist[i];
+		drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+		buf_priv->age = 0;
+	}
+}
+
+
+/* ================================================================
+ * CCE packet submission
+ */
+
+int r128_wait_ring( drm_r128_private_t *dev_priv, int n )
+{
+	drm_r128_ring_buffer_t *ring = &dev_priv->ring;
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		ring->space = *ring->head - ring->tail;
+		if ( ring->space <= 0 )
+			ring->space += ring->size;
+
+		if ( ring->space >= n )
+			return 0;
+
+		udelay( 1 );
+	}
+
+	return -EBUSY;
+}
+
+void r128_update_ring_snapshot( drm_r128_private_t *dev_priv )
+{
+	drm_r128_ring_buffer_t *ring = &dev_priv->ring;
+
+	ring->space = *ring->head - ring->tail;
+#if R128_PERFORMANCE_BOXES
+	if ( ring->space == 0 )
+		atomic_inc( &dev_priv->idle_count );
+#endif
+	if ( ring->space <= 0 )
+		ring->space += ring->size;
+}
+
+#if 0
+static int r128_verify_command( drm_r128_private_t *dev_priv,
+				u32 cmd, int *size )
+{
+	int writing = 1;
+
+	*size = 0;
+
+	switch ( cmd & R128_CCE_PACKET_MASK ) {
+	case R128_CCE_PACKET0:
+		if ( (cmd & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2) &&
+		     (cmd & R128_CCE_PACKET0_REG_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP >> 2) ) {
+			writing = 0;
+		}
+		*size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+		break;
+
+	case R128_CCE_PACKET1:
+		if ( (cmd & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2) &&
+		     (cmd & R128_CCE_PACKET1_REG0_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP >> 2) ) {
+			writing = 0;
+		}
+		if ( (cmd & R128_CCE_PACKET1_REG1_MASK) <= (0x1004 << 9) &&
+		     (cmd & R128_CCE_PACKET1_REG1_MASK) !=
+		     (R128_PM4_VC_FPU_SETUP << 9) ) {
+			writing = 0;
+		}
+		*size = 3;
+		break;
+
+	case R128_CCE_PACKET2:
+		break;
+
+	case R128_CCE_PACKET3:
+		*size = ((cmd & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2;
+		break;
+
+	}
+
+	return writing;
+}
+
+static int r128_submit_packet_ring_secure( drm_r128_private_t *dev_priv,
+					   u32 *commands, int *count )
+{
+#if 0
+	int write = dev_priv->sarea_priv->ring_write;
+	int *write_ptr = dev_priv->ring_start + write;
+	int c = *count;
+	u32 tmp = 0;
+	int psize = 0;
+	int writing = 1;
+	int timeout;
+
+	while ( c > 0 ) {
+		tmp = *commands++;
+		if ( !psize ) {
+			writing = r128_verify_command( dev_priv, tmp, &psize );
+		}
+		psize--;
+
+		if ( writing ) {
+			write++;
+			*write_ptr++ = tmp;
+		}
+		if ( write >= dev_priv->ring_entries ) {
+			write = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+		timeout = 0;
+		while ( write == *dev_priv->ring_read_ptr ) {
+			R128_READ( R128_PM4_BUFFER_DL_RPTR );
+			if ( timeout++ >= dev_priv->usec_timeout )
+				return -EBUSY;
+			udelay( 1 );
+		}
+		c--;
+	}
+
+	if ( write < 32 ) {
+		memcpy( dev_priv->ring_end,
+			dev_priv->ring_start,
+			write * sizeof(u32) );
+	}
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );
+
+	*count = 0;
+#endif
+	return 0;
+}
+
+static int r128_submit_packet_ring_insecure( drm_r128_private_t *dev_priv,
+					     u32 *commands, int *count )
+{
+#if 0
+	int write = dev_priv->sarea_priv->ring_write;
+	int *write_ptr = dev_priv->ring_start + write;
+	int c = *count;
+	int timeout;
+
+	while ( c > 0 ) {
+		write++;
+		*write_ptr++ = *commands++;
+		if ( write >= dev_priv->ring_entries ) {
+			write = 0;
+			write_ptr = dev_priv->ring_start;
+		}
+
+		timeout = 0;
+		while ( write == *dev_priv->ring_read_ptr ) {
+			R128_READ( R128_PM4_BUFFER_DL_RPTR );
+			if ( timeout++ >= dev_priv->usec_timeout )
+				return -EBUSY;
+			udelay( 1 );
+		}
+		c--;
+	}
+
+	if ( write < 32 ) {
+		memcpy( dev_priv->ring_end,
+			dev_priv->ring_start,
+			write * sizeof(u32) );
+	}
+
+	/* Make sure WC cache has been flushed */
+	r128_flush_write_combine();
+
+	dev_priv->sarea_priv->ring_write = write;
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );
+
+	*count = 0;
+#endif
+	return 0;
+}
+#endif
+
+/* Internal packet submission routine.  This uses the insecure versions
+ * of the packet submission functions, and thus should only be used for
+ * packets generated inside the kernel module.
+ */
+int r128_do_submit_packet( drm_r128_private_t *dev_priv,
+			   u32 *buffer, int count )
+{
+	int c = count;
+	int ret = 0;
+
+#if 0
+	int left = 0;
+
+	if ( c >= dev_priv->ring_entries ) {
+		c = dev_priv->ring_entries - 1;
+		left = count - c;
+	}
+
+	/* Since this is only used by the kernel we can use the
+	 * insecure ring buffer submit packet routine.
+	 */
+	ret = r128_submit_packet_ring_insecure( dev_priv, buffer, &c );
+	c += left;
+#endif
+
+	return ( ret < 0 ) ? ret : c;
+}
+
+/* External packet submission routine.  This uses the secure versions
+ * by default, and can thus submit packets received from user space.
+ */
+int r128_cce_packet( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_packet_t packet;
+	u32 *buffer;
+	int c;
+	int size;
+	int ret = 0;
+
+#if 0
+	/* GH: Disable packet submission for now.
+	 */
+	return -EINVAL;
+#endif
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_submit_packet called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &packet, (drm_r128_packet_t *)arg,
+			     sizeof(packet) ) )
+		return -EFAULT;
+
+#if 0
+	c = packet.count;
+	size = c * sizeof(*buffer);
+
+	{
+		int left = 0;
+
+		if ( c >= dev_priv->ring_entries ) {
+			c = dev_priv->ring_entries - 1;
+			size = c * sizeof(*buffer);
+			left = packet.count - c;
+		}
+
+		buffer = kmalloc( size, 0 );
+		if ( buffer == NULL)
+			return -ENOMEM;
+		if ( copy_from_user( buffer, packet.buffer, size ) )
+			return -EFAULT;
+
+		if ( dev_priv->cce_secure ) {
+			ret = r128_submit_packet_ring_secure( dev_priv,
+							      buffer, &c );
+		} else {
+			ret = r128_submit_packet_ring_insecure( dev_priv,
+								buffer, &c );
+		}
+		c += left;
+	}
+
+	kfree( buffer );
+#else
+	c = 0;
+#endif
+
+	packet.count = c;
+	if ( copy_to_user( (drm_r128_packet_t *)arg, &packet,
+			   sizeof(packet) ) )
+		return -EFAULT;
+
+	if ( ret ) {
+		return ret;
+	} else if ( c > 0 ) {
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+#if 0
+static int r128_send_vertbufs( drm_device_t *dev, drm_r128_vertex_t *v )
+{
+	drm_device_dma_t    *dma      = dev->dma;
+	drm_r128_private_t  *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_buf_t           *buf;
+	int                  i, ret;
+	RING_LOCALS;
+
+	/* Make sure we have valid data */
+	for (i = 0; i < v->send_count; i++) {
+		int idx = v->send_indices[i];
+
+		if (idx < 0 || idx >= dma->buf_count) {
+			DRM_ERROR("Index %d (of %d max)\n",
+				  idx, dma->buf_count - 1);
+			return -EINVAL;
+		}
+		buf = dma->buflist[idx];
+		if (buf->pid != current->pid) {
+			DRM_ERROR("Process %d using buffer owned by %d\n",
+				  current->pid, buf->pid);
+			return -EINVAL;
+		}
+		if (buf->pending) {
+			DRM_ERROR("Sending pending buffer:"
+				  " buffer %d, offset %d\n",
+				  v->send_indices[i], i);
+			return -EINVAL;
+		}
+	}
+
+	/* Wait for idle, if we've wrapped to make sure that all pending
+           buffers have been processed */
+	if (dev_priv->submit_age == R128_MAX_VBUF_AGE) {
+		if ((ret = r128_do_cce_idle(dev)) < 0) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset(dev);
+	}
+
+	/* Make sure WC cache has been flushed (if in PIO mode) */
+	if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine();
+
+	/* FIXME: Add support for sending vertex buffer to the CCE here
+	   instead of in client code.  The v->prim holds the primitive
+	   type that should be drawn.  Loop over the list buffers in
+	   send_indices[] and submit a packet for each VB.
+
+	   This will require us to loop over the clip rects here as
+	   well, which implies that we extend the kernel driver to allow
+	   cliprects to be stored here.  Note that the cliprects could
+	   possibly come from the X server instead of the client, but
+	   this will require additional changes to the DRI to allow for
+	   this optimization. */
+
+	/* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */
+#if 0
+	cce_buffer[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0);
+	cce_buffer[1] = dev_priv->submit_age;
+
+	if ((ret = r128_do_submit_packet(dev, cce_buffer, 2)) < 0) {
+		/* Until we add support for sending VBs to the CCE in
+		   this routine, we can recover from this error.  After
+		   we add that support, we won't be able to easily
+		   recover, so we will probably have to implement
+		   another mechanism for handling timeouts from packets
+		   submitted directly by the kernel. */
+		return ret;
+	}
+#else
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_VB_AGE_REG, 0 ) );
+	OUT_RING( dev_priv->submit_age );
+
+	ADVANCE_RING();
+#endif
+	/* Now that the submit packet request has succeeded, we can mark
+           the buffers as pending */
+	for (i = 0; i < v->send_count; i++) {
+		buf = dma->buflist[v->send_indices[i]];
+		buf->pending = 1;
+
+		buf_priv      = buf->dev_private;
+		buf_priv->age = dev_priv->submit_age;
+	}
+
+	dev_priv->submit_age++;
+
+	return 0;
+}
+#endif
+
+
+
+
+static int r128_cce_get_buffers( drm_device_t *dev, drm_dma_t *d )
+{
+	int i;
+	drm_buf_t *buf;
+
+	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
+		buf = r128_freelist_get( dev );
+		if ( !buf ) return -EAGAIN;
+
+		buf->pid = current->pid;
+
+		if ( copy_to_user( &d->request_indices[i], &buf->idx,
+				   sizeof(buf->idx) ) )
+			return -EFAULT;
+		if ( copy_to_user( &d->request_sizes[i], &buf->total,
+				   sizeof(buf->total) ) )
+			return -EFAULT;
+
+		d->granted_count++;
+	}
+	return 0;
+}
+
+int r128_cce_buffers( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int ret = 0;
+	drm_dma_t d;
+
+	if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) )
+		return -EFAULT;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if ( d.send_count != 0 ) {
+		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
+			   current->pid, d.send_count );
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
+		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
+			   current->pid, d.request_count, dma->buf_count );
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if ( d.request_count ) {
+		ret = r128_cce_get_buffers( dev, &d );
+	}
+
+	if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) )
+		return -EFAULT;
+
+	return ret;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_context.c linux/drivers/char/drm-4.0/r128_context.c
--- linux.orig/drivers/char/drm-4.0/r128_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,217 @@
+/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+
+extern drm_ctx_t r128_res_ctx;
+
+static int r128_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int r128_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                r128_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+
+        return 0;
+}
+
+int r128_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+
+        return 0;
+}
+
+
+int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int r128_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = r128_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		r128_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return r128_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int r128_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	r128_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int r128_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_drm.h linux/drivers/char/drm-4.0/r128_drm.h
--- linux.orig/drivers/char/drm-4.0/r128_drm.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_drm.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,272 @@
+/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*-
+ * Created: Wed Apr  5 19:24:19 2000 by kevin@precisioninsight.com
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef _R128_DRM_H_
+#define _R128_DRM_H_
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the X server file (r128_sarea.h)
+ */
+#ifndef __R128_SAREA_DEFINES__
+#define __R128_SAREA_DEFINES__
+
+/* What needs to be changed for the current vertex buffer?
+ */
+#define R128_UPLOAD_CONTEXT		0x001
+#define R128_UPLOAD_SETUP		0x002
+#define R128_UPLOAD_TEX0		0x004
+#define R128_UPLOAD_TEX1		0x008
+#define R128_UPLOAD_TEX0IMAGES		0x010
+#define R128_UPLOAD_TEX1IMAGES		0x020
+#define R128_UPLOAD_CORE		0x040
+#define R128_UPLOAD_MASKS		0x080
+#define R128_UPLOAD_WINDOW		0x100
+#define R128_UPLOAD_CLIPRECTS		0x200	/* handled client-side */
+#define R128_REQUIRE_QUIESCENCE		0x400
+#define R128_UPLOAD_ALL			0x7ff
+
+#define R128_FRONT			0x1
+#define R128_BACK			0x2
+#define R128_DEPTH			0x4
+
+/* Primitive types
+ */
+#define R128_POINTS			0x1
+#define R128_LINES			0x2
+#define R128_LINE_STRIP			0x3
+#define R128_TRIANGLES			0x4
+#define R128_TRIANGLE_FAN		0x5
+#define R128_TRIANGLE_STRIP		0x6
+
+/* Vertex/indirect buffer size
+ */
+#if 1
+#define R128_BUFFER_SIZE		16384
+#else
+#define R128_BUFFER_SIZE		(128 * 1024)
+#endif
+
+/* Byte offsets for indirect buffer data
+ */
+#define R128_INDEX_PRIM_OFFSET		20
+#define R128_HOSTDATA_BLIT_OFFSET	32
+
+/* 2048x2048 @ 32bpp texture requires this many indirect buffers
+ */
+#define R128_MAX_BLIT_BUFFERS		((2048 * 2048 * 4) / R128_BUFFER_SIZE)
+
+/* Keep these small for testing.
+ */
+#define R128_NR_SAREA_CLIPRECTS		12
+
+/* There are 2 heaps (local/AGP).  Each region within a heap is a
+ *  minimum of 64k, and there are at most 64 of them per heap.
+ */
+#define R128_LOCAL_TEX_HEAP		0
+#define R128_AGP_TEX_HEAP		1
+#define R128_NR_TEX_HEAPS		2
+#define R128_NR_TEX_REGIONS		64
+#define R128_LOG_TEX_GRANULARITY	16
+
+#define R128_NR_CONTEXT_REGS		12
+#define R128_TEX_MAXLEVELS		11
+
+#endif /* __R128_SAREA_DEFINES__ */
+
+typedef struct {
+	/* Context state - can be written in one large chunk */
+	unsigned int dst_pitch_offset_c;
+	unsigned int dp_gui_master_cntl_c;
+	unsigned int sc_top_left_c;
+	unsigned int sc_bottom_right_c;
+	unsigned int z_offset_c;
+	unsigned int z_pitch_c;
+	unsigned int z_sten_cntl_c;
+	unsigned int tex_cntl_c;
+	unsigned int misc_3d_state_cntl_reg;
+	unsigned int texture_clr_cmp_clr_c;
+	unsigned int texture_clr_cmp_msk_c;
+	unsigned int fog_color_c;
+
+	/* Texture state */
+	unsigned int tex_size_pitch_c;
+	unsigned int constant_color_c;
+
+	/* Setup state */
+	unsigned int pm4_vc_fpu_setup;
+	unsigned int setup_cntl;
+
+	/* Mask state */
+	unsigned int dp_write_mask;
+	unsigned int sten_ref_mask_c;
+	unsigned int plane_3d_mask_c;
+
+	/* Window state */
+	unsigned int window_xy_offset;
+
+	/* Core state */
+	unsigned int scale_3d_cntl;
+} drm_r128_context_regs_t;
+
+/* Setup registers for each texture unit */
+typedef struct {
+	unsigned int tex_cntl;
+	unsigned int tex_combine_cntl;
+	unsigned int tex_size_pitch;
+	unsigned int tex_offset[R128_TEX_MAXLEVELS];
+	unsigned int tex_border_color;
+} drm_r128_texture_regs_t;
+
+
+typedef struct drm_tex_region {
+	unsigned char next, prev;
+	unsigned char in_use;
+	int age;
+} drm_tex_region_t;
+
+typedef struct drm_r128_sarea {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex buffer.
+	 */
+	drm_r128_context_regs_t context_state;
+	drm_r128_texture_regs_t tex_state[R128_NR_TEX_HEAPS];
+	unsigned int dirty;
+	unsigned int vertsize;
+	unsigned int vc_format;
+
+	/* The current cliprects, or a subset thereof.
+	 */
+	drm_clip_rect_t boxes[R128_NR_SAREA_CLIPRECTS];
+	unsigned int nbox;
+
+	/* Counters for client-side throttling of rendering clients.
+	 */
+	unsigned int last_frame;
+	unsigned int last_dispatch;
+
+	drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1];
+	int tex_age[R128_NR_TEX_HEAPS];
+	int ctx_owner;
+} drm_r128_sarea_t;
+
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmR128.h)
+ */
+typedef struct drm_r128_init {
+	enum {
+		R128_INIT_CCE    = 0x01,
+		R128_CLEANUP_CCE = 0x02
+	} func;
+	int sarea_priv_offset;
+	int is_pci;
+	int cce_mode;
+	int cce_secure;
+	int ring_size;
+	int usec_timeout;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset, front_pitch;
+	unsigned int back_offset, back_pitch;
+	unsigned int depth_bpp;
+	unsigned int depth_offset, depth_pitch;
+	unsigned int span_offset;
+
+	unsigned int fb_offset;
+	unsigned int mmio_offset;
+	unsigned int ring_offset;
+	unsigned int ring_rptr_offset;
+	unsigned int buffers_offset;
+	unsigned int agp_textures_offset;
+} drm_r128_init_t;
+
+typedef struct drm_r128_cce_stop {
+	int flush;
+	int idle;
+} drm_r128_cce_stop_t;
+
+typedef struct drm_r128_clear {
+	unsigned int flags;
+	int x, y, w, h;
+	unsigned int clear_color;
+	unsigned int clear_depth;
+} drm_r128_clear_t;
+
+typedef struct drm_r128_vertex {
+	int prim;
+	int idx;			/* Index of vertex buffer */
+	int count;			/* Number of vertices in buffer */
+	int discard;			/* Client finished with buffer? */
+} drm_r128_vertex_t;
+
+typedef struct drm_r128_indices {
+	int prim;
+	int idx;
+	int start;
+	int end;
+	int discard;			/* Client finished with buffer? */
+} drm_r128_indices_t;
+
+typedef struct drm_r128_blit {
+	int idx;
+	int pitch;
+	int offset;
+	int format;
+	unsigned short x, y;
+	unsigned short width, height;
+} drm_r128_blit_t;
+
+typedef struct drm_r128_depth {
+	enum {
+		R128_WRITE_SPAN		= 0x01,
+		R128_WRITE_PIXELS	= 0x02,
+		R128_READ_SPAN		= 0x03,
+		R128_READ_PIXELS	= 0x04
+	} func;
+	int n;
+	int *x;
+	int *y;
+	unsigned int *buffer;
+	unsigned char *mask;
+} drm_r128_depth_t;
+
+typedef struct drm_r128_stipple {
+	unsigned int *mask;
+} drm_r128_stipple_t;
+
+typedef struct drm_r128_packet {
+	unsigned int *buffer;
+	int count;
+	int flags;
+} drm_r128_packet_t;
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_drv.c linux/drivers/char/drm-4.0/r128_drv.c
--- linux.orig/drivers/char/drm-4.0/r128_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,700 @@
+/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "r128_drv.h"
+
+#define R128_NAME		"r128"
+#define R128_DESC		"ATI Rage 128"
+#define R128_DATE		"20001215"
+#define R128_MAJOR		2
+#define R128_MINOR		1
+#define R128_PATCHLEVEL		2
+
+static drm_device_t	r128_device;
+drm_ctx_t		r128_res_ctx;
+
+static struct file_operations r128_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 r128_open,
+	flush:	 drm_flush,
+	release: r128_release,
+	ioctl:	 r128_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      r128_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  R128_NAME,
+	fops:  &r128_fops,
+};
+
+static drm_ioctl_desc_t	      r128_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]      = { r128_version,      0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]   = { drm_getunique,     0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]    = { drm_getmagic,      0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]    = { drm_irq_busid,     0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]   = { drm_setunique,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	       = { drm_block,         1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]      = { drm_unblock,       1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]   = { drm_authmagic,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]      = { drm_addmap,        1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]     = { r128_addbufs,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]    = { drm_markbufs,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]    = { drm_infobufs,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]     = { r128_mapbufs,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]    = { drm_freebufs,      1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]      = { r128_addctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]       = { r128_rmctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]      = { r128_modctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]      = { r128_getctx,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]   = { r128_switchctx,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]      = { r128_newctx,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]      = { r128_resctx,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]     = { drm_adddraw,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]      = { drm_rmdraw,	      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	       = { r128_cce_buffers,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	       = { r128_lock,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]       = { r128_unlock,	      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]       = { drm_finish,	      1, 0 },
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]  = { drm_agp_acquire,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]  = { drm_agp_release,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]   = { drm_agp_enable,    1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]     = { drm_agp_info,      1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]    = { drm_agp_alloc,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]     = { drm_agp_free,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]     = { drm_agp_bind,      1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]   = { drm_agp_unbind,    1, 1 },
+#endif
+
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)]    = { r128_cce_init,     1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_START)] = { r128_cce_start,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_STOP)]  = { r128_cce_stop,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_RESET)] = { r128_cce_reset,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CCE_IDLE)]  = { r128_cce_idle,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)]   = { r128_engine_reset, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_SWAP)]    = { r128_cce_swap,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_CLEAR)]   = { r128_cce_clear,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)]  = { r128_cce_vertex,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_INDICES)] = { r128_cce_indices,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_BLIT)]    = { r128_cce_blit,     1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_DEPTH)]   = { r128_cce_depth,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_STIPPLE)] = { r128_cce_stipple,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)]  = { r128_cce_packet,   1, 0 },
+};
+#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls)
+
+#ifdef MODULE
+static char		      *r128 = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("r128");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(r128, "s");
+
+#ifndef MODULE
+/* r128_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init r128_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("r128=", r128_options);
+#endif
+
+static int r128_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	r128_res_ctx.handle=-1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int r128_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until r128_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+#endif
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* r128_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init r128_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(r128);
+#endif
+
+	if ((retcode = misc_register(&r128_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", R128_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, r128_misc.minor);
+	dev->name   = R128_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+	if (dev->agp == NULL) {
+		DRM_ERROR("Cannot initialize agpgart module.\n");
+		drm_proc_cleanup();
+		misc_deregister(&r128_misc);
+		r128_takedown(dev);
+		return -ENOMEM;
+	}
+
+#ifdef CONFIG_MTRR
+	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size*1024*1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+#endif
+
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&r128_misc);
+		r128_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 R128_NAME,
+		 R128_MAJOR,
+		 R128_MINOR,
+		 R128_PATCHLEVEL,
+		 R128_DATE,
+		 r128_misc.minor);
+
+	return 0;
+}
+
+/* r128_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit r128_cleanup(void)
+{
+	drm_device_t	      *dev = &r128_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&r128_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	r128_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(r128_init);
+module_exit(r128_cleanup);
+
+
+int r128_version(struct inode *inode, struct file *filp,
+		 unsigned int cmd, unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)					\
+	len = strlen(value);					\
+	if (len > name##_len) len = name##_len;			\
+	name##_len = strlen(value);				\
+	if (len && name) {					\
+		if (copy_to_user(name, value, len))		\
+			return -EFAULT;				\
+	}
+
+	version.version_major	   = R128_MAJOR;
+	version.version_minor	   = R128_MINOR;
+	version.version_patchlevel = R128_PATCHLEVEL;
+
+	DRM_COPY(version.name, R128_NAME);
+	DRM_COPY(version.date, R128_DATE);
+	DRM_COPY(version.desc, R128_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int r128_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &r128_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return r128_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return retcode;
+}
+
+int r128_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return r128_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+int r128_ioctl(struct inode *inode, struct file *filp,
+	       unsigned int cmd, unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= R128_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &r128_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			   || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+#if 0
+	if ( retcode ) {
+		DRM_INFO( "%s 0x%x ret = %d\n", __FUNCTION__, nr, retcode );
+	}
+#endif
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int r128_lock(struct inode *inode, struct file *filp,
+	      unsigned int cmd, unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+        if (lock.context < 0)
+                return -EINVAL;
+
+        if (!ret) {
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+			DRM_DEBUG( "not quiescent!\n" );
+#if 0
+                        r128_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int r128_unlock(struct inode *inode, struct file *filp,
+		unsigned int cmd, unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != r128_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_drv.h linux/drivers/char/drm-4.0/r128_drv.h
--- linux.orig/drivers/char/drm-4.0/r128_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,469 @@
+/* r128_drv.h -- Private header for r128 driver -*- linux-c -*-
+ * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@valinux.com>
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __R128_DRV_H__
+#define __R128_DRV_H__
+
+typedef struct drm_r128_freelist {
+   	unsigned int age;
+   	drm_buf_t *buf;
+   	struct drm_r128_freelist *next;
+   	struct drm_r128_freelist *prev;
+} drm_r128_freelist_t;
+
+typedef struct drm_r128_ring_buffer {
+	u32 *start;
+	u32 *end;
+	int size;
+	int size_l2qw;
+
+	volatile u32 *head;
+	u32 tail;
+	u32 tail_mask;
+	int space;
+} drm_r128_ring_buffer_t;
+
+typedef struct drm_r128_private {
+	drm_r128_ring_buffer_t ring;
+	drm_r128_sarea_t *sarea_priv;
+
+	int cce_mode;
+	int cce_fifo_size;
+	int cce_secure;
+	int cce_running;
+
+   	drm_r128_freelist_t *head;
+   	drm_r128_freelist_t *tail;
+
+	int usec_timeout;
+	int is_pci;
+
+	atomic_t idle_count;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset;
+	unsigned int front_pitch;
+	unsigned int back_offset;
+	unsigned int back_pitch;
+
+	unsigned int depth_bpp;
+	unsigned int depth_offset;
+	unsigned int depth_pitch;
+	unsigned int span_offset;
+
+	u32 front_pitch_offset_c;
+	u32 back_pitch_offset_c;
+	u32 depth_pitch_offset_c;
+	u32 span_pitch_offset_c;
+
+	drm_map_t *sarea;
+	drm_map_t *fb;
+	drm_map_t *mmio;
+	drm_map_t *cce_ring;
+	drm_map_t *ring_rptr;
+	drm_map_t *buffers;
+	drm_map_t *agp_textures;
+} drm_r128_private_t;
+
+typedef struct drm_r128_buf_priv {
+	u32 age;
+	int prim;
+	int discard;
+	int dispatched;
+   	drm_r128_freelist_t *list_entry;
+} drm_r128_buf_priv_t;
+
+				/* r128_drv.c */
+extern int  r128_version( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int  r128_open( struct inode *inode, struct file *filp );
+extern int  r128_release( struct inode *inode, struct file *filp );
+extern int  r128_ioctl( struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg );
+extern int  r128_lock( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg );
+extern int  r128_unlock( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg );
+
+				/* r128_cce.c */
+extern int r128_cce_init( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_start( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_stop( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_reset( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_idle( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_engine_reset( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int r128_cce_packet( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int r128_cce_buffers( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+
+extern void r128_freelist_reset( drm_device_t *dev );
+extern drm_buf_t *r128_freelist_get( drm_device_t *dev );
+
+extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n );
+extern void r128_update_ring_snapshot( drm_r128_private_t *dev_priv );
+
+				/* r128_state.c */
+extern int r128_cce_clear( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_swap( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_vertex( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int r128_cce_indices( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+extern int r128_cce_blit( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int r128_cce_depth( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int r128_cce_stipple( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+
+				/* r128_bufs.c */
+extern int r128_addbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int r128_mapbufs(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+
+				/* r128_context.c */
+extern int  r128_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  r128_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  r128_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  r128_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  r128_context_switch(drm_device_t *dev, int old, int new);
+extern int  r128_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Rage 128 kernel driver.
+ */
+
+#define R128_AUX_SC_CNTL		0x1660
+#	define R128_AUX1_SC_EN			(1 << 0)
+#	define R128_AUX1_SC_MODE_OR		(0 << 1)
+#	define R128_AUX1_SC_MODE_NAND		(1 << 1)
+#	define R128_AUX2_SC_EN			(1 << 2)
+#	define R128_AUX2_SC_MODE_OR		(0 << 3)
+#	define R128_AUX2_SC_MODE_NAND		(1 << 3)
+#	define R128_AUX3_SC_EN			(1 << 4)
+#	define R128_AUX3_SC_MODE_OR		(0 << 5)
+#	define R128_AUX3_SC_MODE_NAND		(1 << 5)
+#define R128_AUX1_SC_LEFT		0x1664
+#define R128_AUX1_SC_RIGHT		0x1668
+#define R128_AUX1_SC_TOP		0x166c
+#define R128_AUX1_SC_BOTTOM		0x1670
+#define R128_AUX2_SC_LEFT		0x1674
+#define R128_AUX2_SC_RIGHT		0x1678
+#define R128_AUX2_SC_TOP		0x167c
+#define R128_AUX2_SC_BOTTOM		0x1680
+#define R128_AUX3_SC_LEFT		0x1684
+#define R128_AUX3_SC_RIGHT		0x1688
+#define R128_AUX3_SC_TOP		0x168c
+#define R128_AUX3_SC_BOTTOM		0x1690
+
+#define R128_BRUSH_DATA0		0x1480
+#define R128_BUS_CNTL			0x0030
+#	define R128_BUS_MASTER_DIS		(1 << 6)
+
+#define R128_CLOCK_CNTL_INDEX		0x0008
+#define R128_CLOCK_CNTL_DATA		0x000c
+#	define R128_PLL_WR_EN			(1 << 7)
+
+#define R128_CONSTANT_COLOR_C		0x1d34
+
+#define R128_DP_GUI_MASTER_CNTL		0x146c
+#       define R128_GMC_SRC_PITCH_OFFSET_CNTL	(1    <<  0)
+#       define R128_GMC_DST_PITCH_OFFSET_CNTL	(1    <<  1)
+#	define R128_GMC_BRUSH_SOLID_COLOR	(13   <<  4)
+#	define R128_GMC_BRUSH_NONE		(15   <<  4)
+#	define R128_GMC_DST_16BPP		(4    <<  8)
+#	define R128_GMC_DST_24BPP		(5    <<  8)
+#	define R128_GMC_DST_32BPP		(6    <<  8)
+#       define R128_GMC_DST_DATATYPE_SHIFT	8
+#	define R128_GMC_SRC_DATATYPE_COLOR	(3    << 12)
+#	define R128_DP_SRC_SOURCE_MEMORY	(2    << 24)
+#	define R128_DP_SRC_SOURCE_HOST_DATA	(3    << 24)
+#	define R128_GMC_CLR_CMP_CNTL_DIS	(1    << 28)
+#	define R128_GMC_AUX_CLIP_DIS		(1    << 29)
+#	define R128_GMC_WR_MSK_DIS		(1    << 30)
+#	define R128_ROP3_S			0x00cc0000
+#	define R128_ROP3_P			0x00f00000
+#define R128_DP_WRITE_MASK		0x16cc
+#define R128_DST_PITCH_OFFSET_C		0x1c80
+#	define R128_DST_TILE			(1 << 31)
+
+#define R128_GEN_RESET_CNTL		0x00f0
+#	define R128_SOFT_RESET_GUI		(1 <<  0)
+
+#define R128_GUI_SCRATCH_REG0		0x15e0
+#define R128_GUI_SCRATCH_REG1		0x15e4
+#define R128_GUI_SCRATCH_REG2		0x15e8
+#define R128_GUI_SCRATCH_REG3		0x15ec
+#define R128_GUI_SCRATCH_REG4		0x15f0
+#define R128_GUI_SCRATCH_REG5		0x15f4
+
+#define R128_GUI_STAT			0x1740
+#	define R128_GUI_FIFOCNT_MASK		0x0fff
+#	define R128_GUI_ACTIVE			(1 << 31)
+
+#define R128_MCLK_CNTL			0x000f
+#	define R128_FORCE_GCP			(1 << 16)
+#	define R128_FORCE_PIPE3D_CP		(1 << 17)
+#	define R128_FORCE_RCP			(1 << 18)
+
+#define R128_PC_GUI_CTLSTAT		0x1748
+#define R128_PC_NGUI_CTLSTAT		0x0184
+#	define R128_PC_FLUSH_GUI		(3 << 0)
+#	define R128_PC_RI_GUI			(1 << 2)
+#	define R128_PC_FLUSH_ALL		0x00ff
+#	define R128_PC_BUSY			(1 << 31)
+
+#define R128_PRIM_TEX_CNTL_C		0x1cb0
+
+#define R128_SCALE_3D_CNTL		0x1a00
+#define R128_SEC_TEX_CNTL_C		0x1d00
+#define R128_SEC_TEXTURE_BORDER_COLOR_C	0x1d3c
+#define R128_SETUP_CNTL			0x1bc4
+#define R128_STEN_REF_MASK_C		0x1d40
+
+#define R128_TEX_CNTL_C			0x1c9c
+#	define R128_TEX_CACHE_FLUSH		(1 << 23)
+
+#define R128_WINDOW_XY_OFFSET		0x1bcc
+
+
+/* CCE registers
+ */
+#define R128_PM4_BUFFER_OFFSET		0x0700
+#define R128_PM4_BUFFER_CNTL		0x0704
+#	define R128_PM4_MASK			(15 << 28)
+#	define R128_PM4_NONPM4			(0  << 28)
+#	define R128_PM4_192PIO			(1  << 28)
+#	define R128_PM4_192BM			(2  << 28)
+#	define R128_PM4_128PIO_64INDBM		(3  << 28)
+#	define R128_PM4_128BM_64INDBM		(4  << 28)
+#	define R128_PM4_64PIO_128INDBM		(5  << 28)
+#	define R128_PM4_64BM_128INDBM		(6  << 28)
+#	define R128_PM4_64PIO_64VCBM_64INDBM	(7  << 28)
+#	define R128_PM4_64BM_64VCBM_64INDBM	(8  << 28)
+#	define R128_PM4_64PIO_64VCPIO_64INDPIO	(15 << 28)
+
+#define R128_PM4_BUFFER_WM_CNTL		0x0708
+#	define R128_WMA_SHIFT			0
+#	define R128_WMB_SHIFT			8
+#	define R128_WMC_SHIFT			16
+#	define R128_WB_WM_SHIFT			24
+
+#define R128_PM4_BUFFER_DL_RPTR_ADDR	0x070c
+#define R128_PM4_BUFFER_DL_RPTR		0x0710
+#define R128_PM4_BUFFER_DL_WPTR		0x0714
+#	define R128_PM4_BUFFER_DL_DONE		(1 << 31)
+
+#define R128_PM4_VC_FPU_SETUP		0x071c
+
+#define R128_PM4_IW_INDOFF		0x0738
+#define R128_PM4_IW_INDSIZE		0x073c
+
+#define R128_PM4_STAT			0x07b8
+#	define R128_PM4_FIFOCNT_MASK		0x0fff
+#	define R128_PM4_BUSY			(1 << 16)
+#	define R128_PM4_GUI_ACTIVE		(1 << 31)
+
+#define R128_PM4_MICROCODE_ADDR		0x07d4
+#define R128_PM4_MICROCODE_RADDR	0x07d8
+#define R128_PM4_MICROCODE_DATAH	0x07dc
+#define R128_PM4_MICROCODE_DATAL	0x07e0
+
+#define R128_PM4_BUFFER_ADDR		0x07f0
+#define R128_PM4_MICRO_CNTL		0x07fc
+#	define R128_PM4_MICRO_FREERUN		(1 << 30)
+
+#define R128_PM4_FIFO_DATA_EVEN		0x1000
+#define R128_PM4_FIFO_DATA_ODD		0x1004
+
+
+/* CCE command packets
+ */
+#define R128_CCE_PACKET0		0x00000000
+#define R128_CCE_PACKET1		0x40000000
+#define R128_CCE_PACKET2		0x80000000
+#define R128_CCE_PACKET3		0xC0000000
+#	define R128_CNTL_HOSTDATA_BLT		0x00009400
+#	define R128_CNTL_PAINT_MULTI		0x00009A00
+#	define R128_CNTL_BITBLT_MULTI		0x00009B00
+#	define R128_3D_RNDR_GEN_INDX_PRIM	0x00002300
+
+#define R128_CCE_PACKET_MASK		0xC0000000
+#define R128_CCE_PACKET_COUNT_MASK	0x3fff0000
+#define R128_CCE_PACKET0_REG_MASK	0x000007ff
+#define R128_CCE_PACKET1_REG0_MASK	0x000007ff
+#define R128_CCE_PACKET1_REG1_MASK	0x003ff800
+
+#define R128_CCE_VC_CNTL_PRIM_TYPE_NONE		0x00000000
+#define R128_CCE_VC_CNTL_PRIM_TYPE_POINT	0x00000001
+#define R128_CCE_VC_CNTL_PRIM_TYPE_LINE		0x00000002
+#define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE	0x00000003
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST	0x00000004
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN	0x00000005
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP	0x00000006
+#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2	0x00000007
+#define R128_CCE_VC_CNTL_PRIM_WALK_IND		0x00000010
+#define R128_CCE_VC_CNTL_PRIM_WALK_LIST		0x00000020
+#define R128_CCE_VC_CNTL_PRIM_WALK_RING		0x00000030
+#define R128_CCE_VC_CNTL_NUM_SHIFT		16
+
+#define R128_DATATYPE_CI8		2
+#define R128_DATATYPE_ARGB1555		3
+#define R128_DATATYPE_RGB565		4
+#define R128_DATATYPE_RGB888		5
+#define R128_DATATYPE_ARGB8888		6
+#define R128_DATATYPE_RGB332		7
+#define R128_DATATYPE_RGB8		9
+#define R128_DATATYPE_ARGB4444		15
+
+/* Constants */
+#define R128_AGP_OFFSET			0x02000000
+
+#define R128_WATERMARK_L		16
+#define R128_WATERMARK_M		8
+#define R128_WATERMARK_N		8
+#define R128_WATERMARK_K		128
+
+#define R128_MAX_USEC_TIMEOUT	100000	/* 100 ms */
+
+#define R128_LAST_FRAME_REG		R128_GUI_SCRATCH_REG0
+#define R128_LAST_DISPATCH_REG		R128_GUI_SCRATCH_REG1
+#define R128_MAX_VB_AGE			0xffffffff
+
+#define R128_MAX_VB_VERTS		(0xffff)
+
+
+#define R128_BASE(reg)		((unsigned long)(dev_priv->mmio->handle))
+#define R128_ADDR(reg)		(R128_BASE(reg) + reg)
+
+#define R128_READ(reg)		readl(R128_ADDR(reg))
+#define R128_WRITE(reg,val)	writel(val,R128_ADDR(reg))
+
+#define R128_READ8(reg)		readb(R128_ADDR(reg))
+#define R128_WRITE8(reg,val)	writeb(val,R128_ADDR(reg))
+
+#define R128_WRITE_PLL(addr,val)                                              \
+do {                                                                          \
+	R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \
+	R128_WRITE(R128_CLOCK_CNTL_DATA, (val));                              \
+} while (0)
+
+extern int R128_READ_PLL(drm_device_t *dev, int addr);
+
+#define R128CCE0(p,r,n)   ((p) | ((n) << 16) | ((r) >> 2))
+#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2))
+#define R128CCE2(p)       ((p))
+#define R128CCE3(p,n)     ((p) | ((n) << 16))
+
+
+
+
+#define CCE_PACKET0( reg, n )		(R128_CCE_PACKET0 |		\
+					 ((n) << 16) | ((reg) >> 2))
+#define CCE_PACKET1( reg0, reg1 )	(R128_CCE_PACKET1 |		\
+					 (((reg1) >> 2) << 11) | ((reg0) >> 2))
+#define CCE_PACKET2()			(R128_CCE_PACKET2)
+#define CCE_PACKET3( pkt, n )		(R128_CCE_PACKET3 |		\
+					 (pkt) | ((n) << 16))
+
+
+#define r128_flush_write_combine()		mb()
+
+
+#define R128_VERBOSE	0
+
+#define RING_LOCALS	int write; unsigned int tail_mask; volatile u32 *ring;
+
+#define BEGIN_RING( n ) do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "BEGIN_RING( %d ) in %s\n",			\
+			   n, __FUNCTION__ );				\
+	}								\
+	if ( dev_priv->ring.space < n * sizeof(u32) ) {			\
+		r128_wait_ring( dev_priv, n * sizeof(u32) );		\
+	}								\
+	dev_priv->ring.space -= n * sizeof(u32);			\
+	ring = dev_priv->ring.start;					\
+	write = dev_priv->ring.tail;					\
+	tail_mask = dev_priv->ring.tail_mask;				\
+} while (0)
+
+#define ADVANCE_RING() do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n",	\
+			  write, dev_priv->ring.tail );			\
+	}								\
+	if ( write < 32 ) {						\
+		memcpy( dev_priv->ring.end,				\
+			dev_priv->ring.start,				\
+			write * sizeof(u32) );				\
+	}								\
+	r128_flush_write_combine();					\
+	dev_priv->ring.tail = write;					\
+	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );			\
+} while (0)
+
+#define OUT_RING( x ) do {						\
+	if ( R128_VERBOSE ) {						\
+		DRM_INFO( "   OUT_RING( 0x%08x ) at 0x%x\n",		\
+			   (unsigned int)(x), write );			\
+	}								\
+	ring[write++] = x;						\
+	write &= tail_mask;						\
+} while (0)
+
+#define R128_PERFORMANCE_BOXES	0
+
+#endif /* __R128_DRV_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/r128_state.c linux/drivers/char/drm-4.0/r128_state.c
--- linux.orig/drivers/char/drm-4.0/r128_state.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/r128_state.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1605 @@
+/* r128_state.c -- State support for r128 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 by gareth@valinux.com
+ *
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "r128_drv.h"
+#include "drm.h"
+
+
+/* ================================================================
+ * CCE hardware state programming functions
+ */
+
+static void r128_emit_clip_rects( drm_r128_private_t *dev_priv,
+				  drm_clip_rect_t *boxes, int count )
+{
+	u32 aux_sc_cntl = 0x00000000;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 17 );
+
+	if ( count >= 1 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX1_SC_LEFT, 3 ) );
+		OUT_RING( boxes[0].x1 );
+		OUT_RING( boxes[0].x2 - 1 );
+		OUT_RING( boxes[0].y1 );
+		OUT_RING( boxes[0].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX1_SC_EN | R128_AUX1_SC_MODE_OR);
+	}
+	if ( count >= 2 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX2_SC_LEFT, 3 ) );
+		OUT_RING( boxes[1].x1 );
+		OUT_RING( boxes[1].x2 - 1 );
+		OUT_RING( boxes[1].y1 );
+		OUT_RING( boxes[1].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX2_SC_EN | R128_AUX2_SC_MODE_OR);
+	}
+	if ( count >= 3 ) {
+		OUT_RING( CCE_PACKET0( R128_AUX3_SC_LEFT, 3 ) );
+		OUT_RING( boxes[2].x1 );
+		OUT_RING( boxes[2].x2 - 1 );
+		OUT_RING( boxes[2].y1 );
+		OUT_RING( boxes[2].y2 - 1 );
+
+		aux_sc_cntl |= (R128_AUX3_SC_EN | R128_AUX3_SC_MODE_OR);
+	}
+
+	OUT_RING( CCE_PACKET0( R128_AUX_SC_CNTL, 0 ) );
+	OUT_RING( aux_sc_cntl );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_core( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_SCALE_3D_CNTL, 0 ) );
+	OUT_RING( ctx->scale_3d_cntl );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_context( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 13 );
+
+	OUT_RING( CCE_PACKET0( R128_DST_PITCH_OFFSET_C, 11 ) );
+	OUT_RING( ctx->dst_pitch_offset_c );
+	OUT_RING( ctx->dp_gui_master_cntl_c );
+	OUT_RING( ctx->sc_top_left_c );
+	OUT_RING( ctx->sc_bottom_right_c );
+	OUT_RING( ctx->z_offset_c );
+	OUT_RING( ctx->z_pitch_c );
+	OUT_RING( ctx->z_sten_cntl_c );
+	OUT_RING( ctx->tex_cntl_c );
+	OUT_RING( ctx->misc_3d_state_cntl_reg );
+	OUT_RING( ctx->texture_clr_cmp_clr_c );
+	OUT_RING( ctx->texture_clr_cmp_msk_c );
+	OUT_RING( ctx->fog_color_c );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_setup( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 3 );
+
+	OUT_RING( CCE_PACKET1( R128_SETUP_CNTL, R128_PM4_VC_FPU_SETUP ) );
+	OUT_RING( ctx->setup_cntl );
+	OUT_RING( ctx->pm4_vc_fpu_setup );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_masks( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) );
+	OUT_RING( ctx->dp_write_mask );
+
+	OUT_RING( CCE_PACKET0( R128_STEN_REF_MASK_C, 1 ) );
+	OUT_RING( ctx->sten_ref_mask_c );
+	OUT_RING( ctx->plane_3d_mask_c );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_window( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_WINDOW_XY_OFFSET, 0 ) );
+	OUT_RING( ctx->window_xy_offset );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_tex0( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_context_regs_t *ctx = &sarea_priv->context_state;
+	drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 7 + R128_TEX_MAXLEVELS );
+
+	OUT_RING( CCE_PACKET0( R128_PRIM_TEX_CNTL_C,
+			       2 + R128_TEX_MAXLEVELS ) );
+	OUT_RING( tex->tex_cntl );
+	OUT_RING( tex->tex_combine_cntl );
+	OUT_RING( ctx->tex_size_pitch_c );
+	for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) {
+		OUT_RING( tex->tex_offset[i] );
+	}
+
+	OUT_RING( CCE_PACKET0( R128_CONSTANT_COLOR_C, 1 ) );
+	OUT_RING( ctx->constant_color_c );
+	OUT_RING( tex->tex_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_tex1( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_texture_regs_t *tex = &sarea_priv->tex_state[1];
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 + R128_TEX_MAXLEVELS );
+
+	OUT_RING( CCE_PACKET0( R128_SEC_TEX_CNTL_C,
+			       1 + R128_TEX_MAXLEVELS ) );
+	OUT_RING( tex->tex_cntl );
+	OUT_RING( tex->tex_combine_cntl );
+	for ( i = 0 ; i < R128_TEX_MAXLEVELS ; i++ ) {
+		OUT_RING( tex->tex_offset[i] );
+	}
+
+	OUT_RING( CCE_PACKET0( R128_SEC_TEXTURE_BORDER_COLOR_C, 0 ) );
+	OUT_RING( tex->tex_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void r128_emit_state( drm_r128_private_t *dev_priv )
+{
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty );
+
+	if ( dirty & R128_UPLOAD_CORE ) {
+		r128_emit_core( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_CORE;
+	}
+
+	if ( dirty & R128_UPLOAD_CONTEXT ) {
+		r128_emit_context( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_CONTEXT;
+	}
+
+	if ( dirty & R128_UPLOAD_SETUP ) {
+		r128_emit_setup( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_SETUP;
+	}
+
+	if ( dirty & R128_UPLOAD_MASKS ) {
+		r128_emit_masks( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_MASKS;
+	}
+
+	if ( dirty & R128_UPLOAD_WINDOW ) {
+		r128_emit_window( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_WINDOW;
+	}
+
+	if ( dirty & R128_UPLOAD_TEX0 ) {
+		r128_emit_tex0( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_TEX0;
+	}
+
+	if ( dirty & R128_UPLOAD_TEX1 ) {
+		r128_emit_tex1( dev_priv );
+		sarea_priv->dirty &= ~R128_UPLOAD_TEX1;
+	}
+
+	/* Turn off the texture cache flushing */
+	sarea_priv->context_state.tex_cntl_c &= ~R128_TEX_CACHE_FLUSH;
+
+	sarea_priv->dirty &= ~R128_REQUIRE_QUIESCENCE;
+}
+
+
+#if R128_PERFORMANCE_BOXES
+/* ================================================================
+ * Performance monitoring functions
+ */
+
+static void r128_clear_box( drm_r128_private_t *dev_priv,
+			    int x, int y, int w, int h,
+			    int r, int g, int b )
+{
+	u32 pitch, offset;
+	u32 fb_bpp, color;
+	RING_LOCALS;
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		color = (((r & 0xf8) << 8) |
+			 ((g & 0xfc) << 3) |
+			 ((b & 0xf8) >> 3));
+		break;
+	case 24:
+		fb_bpp = R128_GMC_DST_24BPP;
+		color = ((r << 16) | (g << 8) | b);
+		break;
+	case 32:
+		fb_bpp = R128_GMC_DST_32BPP;
+		color = (((0xff) << 24) | (r << 16) | (g <<  8) | b);
+		break;
+	default:
+		return;
+	}
+
+	offset = dev_priv->back_offset;
+	pitch = dev_priv->back_pitch >> 3;
+
+	BEGIN_RING( 6 );
+
+	OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+	OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+		  | R128_GMC_BRUSH_SOLID_COLOR
+		  | fb_bpp
+		  | R128_GMC_SRC_DATATYPE_COLOR
+		  | R128_ROP3_P
+		  | R128_GMC_CLR_CMP_CNTL_DIS
+		  | R128_GMC_AUX_CLIP_DIS );
+
+	OUT_RING( (pitch << 21) | (offset >> 5) );
+	OUT_RING( color );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (w << 16) | h );
+
+	ADVANCE_RING();
+}
+
+static void r128_cce_performance_boxes( drm_r128_private_t *dev_priv )
+{
+	if ( atomic_read( &dev_priv->idle_count ) == 0 ) {
+		r128_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 );
+	} else {
+		atomic_set( &dev_priv->idle_count, 0 );
+	}
+}
+
+#endif
+
+
+/* ================================================================
+ * CCE command dispatch functions
+ */
+
+static void r128_print_dirty( const char *msg, unsigned int flags )
+{
+	DRM_INFO( "%s: (0x%x) %s%s%s%s%s%s%s%s%s\n",
+		  msg,
+		  flags,
+		  (flags & R128_UPLOAD_CORE)        ? "core, " : "",
+		  (flags & R128_UPLOAD_CONTEXT)     ? "context, " : "",
+		  (flags & R128_UPLOAD_SETUP)       ? "setup, " : "",
+		  (flags & R128_UPLOAD_TEX0)        ? "tex0, " : "",
+		  (flags & R128_UPLOAD_TEX1)        ? "tex1, " : "",
+		  (flags & R128_UPLOAD_MASKS)       ? "masks, " : "",
+		  (flags & R128_UPLOAD_WINDOW)      ? "window, " : "",
+		  (flags & R128_UPLOAD_CLIPRECTS)   ? "cliprects, " : "",
+		  (flags & R128_REQUIRE_QUIESCENCE) ? "quiescence, " : "" );
+}
+
+static void r128_cce_dispatch_clear( drm_device_t *dev,
+				     unsigned int flags,
+				     int cx, int cy, int cw, int ch,
+				     unsigned int clear_color,
+				     unsigned int clear_depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	u32 fb_bpp, depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 32:
+		fb_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return;
+	}
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n",
+			   pbox[i].x1, pbox[i].y1, pbox[i].x2,
+			   pbox[i].y2, flags );
+
+		if ( flags & (R128_FRONT | R128_BACK) ) {
+			BEGIN_RING( 2 );
+
+			OUT_RING( CCE_PACKET0( R128_DP_WRITE_MASK, 0 ) );
+			OUT_RING( sarea_priv->context_state.plane_3d_mask_c );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_FRONT ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | fb_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS );
+
+			OUT_RING( dev_priv->front_pitch_offset_c );
+			OUT_RING( clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_BACK ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | fb_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS );
+
+			OUT_RING( dev_priv->back_pitch_offset_c );
+			OUT_RING( clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & R128_DEPTH ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_AUX_CLIP_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( clear_depth );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+	}
+}
+
+static void r128_cce_dispatch_swap( drm_device_t *dev )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	u32 fb_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+#if R128_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	r128_cce_performance_boxes( dev_priv );
+#endif
+
+	switch ( dev_priv->fb_bpp ) {
+	case 16:
+		fb_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 32:
+	default:
+		fb_bpp = R128_GMC_DST_32BPP;
+		break;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		BEGIN_RING( 7 );
+
+		OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+			  | R128_GMC_DST_PITCH_OFFSET_CNTL
+			  | R128_GMC_BRUSH_NONE
+			  | fb_bpp
+			  | R128_GMC_SRC_DATATYPE_COLOR
+			  | R128_ROP3_S
+			  | R128_DP_SRC_SOURCE_MEMORY
+			  | R128_GMC_CLR_CMP_CNTL_DIS
+			  | R128_GMC_AUX_CLIP_DIS
+			  | R128_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->back_pitch_offset_c );
+		OUT_RING( dev_priv->front_pitch_offset_c );
+
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (w << 16) | h );
+
+		ADVANCE_RING();
+	}
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_LAST_FRAME_REG, 0 ) );
+	OUT_RING( dev_priv->sarea_priv->last_frame );
+
+	ADVANCE_RING();
+}
+
+static void r128_cce_dispatch_vertex( drm_device_t *dev,
+				      drm_buf_t *buf )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->buffers->offset + buf->offset - dev->agp->base;
+	int size = buf->used;
+	int prim = buf_priv->prim;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: buf=%d nbox=%d\n",
+		   __FUNCTION__, buf->idx, sarea_priv->nbox );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		r128_print_dirty( "dispatch_vertex", sarea_priv->dirty );
+
+	if ( buf->used ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) {
+			r128_emit_state( dev_priv );
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				r128_emit_clip_rects( dev_priv,
+						      &sarea_priv->boxes[i],
+						      sarea_priv->nbox - i );
+			}
+
+			/* Emit the vertex buffer rendering commands */
+			BEGIN_RING( 5 );
+
+			OUT_RING( CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, 3 ) );
+			OUT_RING( offset );
+			OUT_RING( size );
+			OUT_RING( format );
+			OUT_RING( prim | R128_CCE_VC_CNTL_PRIM_WALK_LIST |
+				  (size << R128_CCE_VC_CNTL_NUM_SHIFT) );
+
+			ADVANCE_RING();
+
+			i += 3;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+
+	sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+
+
+
+static void r128_cce_dispatch_indirect( drm_device_t *dev,
+					drm_buf_t *buf,
+					int start, int end )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n",
+		   buf->idx, start, end );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( start != end ) {
+		int offset = (dev_priv->buffers->offset - dev->agp->base
+			      + buf->offset + start);
+		int dwords = (end - start + 3) / sizeof(u32);
+
+		/* Indirect buffer data must be an even number of
+		 * dwords, so if we've been given an odd number we must
+		 * pad the data with a Type-2 CCE packet.
+		 */
+		if ( dwords & 1 ) {
+			u32 *data = (u32 *)
+				((char *)dev_priv->buffers->handle
+				 + buf->offset + start);
+			data[dwords++] = R128_CCE_PACKET2;
+		}
+
+		buf_priv->dispatched = 1;
+
+		/* Fire off the indirect buffer */
+		BEGIN_RING( 3 );
+
+		OUT_RING( CCE_PACKET0( R128_PM4_IW_INDOFF, 1 ) );
+		OUT_RING( offset );
+		OUT_RING( dwords );
+
+		ADVANCE_RING();
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the indirect buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+}
+
+static void r128_cce_dispatch_indices( drm_device_t *dev,
+				       drm_buf_t *buf,
+				       int start, int end,
+				       int count )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_buf_priv_t *buf_priv = buf->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->buffers->offset - dev->agp->base;
+	int prim = buf_priv->prim;
+	u32 *data;
+	int dwords;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		r128_print_dirty( "dispatch_indices", sarea_priv->dirty );
+
+	if ( start != end ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~R128_UPLOAD_CLIPRECTS ) {
+			r128_emit_state( dev_priv );
+		}
+
+		dwords = (end - start + 3) / sizeof(u32);
+
+		data = (u32 *)((char *)dev_priv->buffers->handle
+			       + buf->offset + start);
+
+		data[0] = CCE_PACKET3( R128_3D_RNDR_GEN_INDX_PRIM, dwords-2 );
+
+		data[1] = offset;
+		data[2] = R128_MAX_VB_VERTS;
+		data[3] = format;
+		data[4] = (prim | R128_CCE_VC_CNTL_PRIM_WALK_IND |
+			   (count << 16));
+
+		if ( count & 0x1 ) {
+			data[dwords-1] &= 0x0000ffff;
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				r128_emit_clip_rects( dev_priv,
+						      &sarea_priv->boxes[i],
+						      sarea_priv->nbox - i );
+			}
+
+			r128_cce_dispatch_indirect( dev, buf, start, end );
+
+			i += 3;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+
+		OUT_RING( CCE_PACKET0( R128_LAST_DISPATCH_REG, 0 ) );
+		OUT_RING( buf_priv->age );
+
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+#if 0
+	if ( dev_priv->submit_age == R128_MAX_VB_AGE ) {
+		ret = r128_do_cce_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+		dev_priv->submit_age = 0;
+		r128_freelist_reset( dev );
+	}
+#endif
+
+	sarea_priv->dirty &= ~R128_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+static int r128_cce_dispatch_blit( drm_device_t *dev,
+				   drm_r128_blit_t *blit )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	u32 *data;
+	int dword_shift, dwords;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	/* The compiler won't optimize away a division by a variable,
+	 * even if the only legal values are powers of two.  Thus, we'll
+	 * use a shift instead.
+	 */
+	switch ( blit->format ) {
+	case R128_DATATYPE_ARGB1555:
+	case R128_DATATYPE_RGB565:
+	case R128_DATATYPE_ARGB4444:
+		dword_shift = 1;
+		break;
+	case R128_DATATYPE_ARGB8888:
+		dword_shift = 0;
+		break;
+	default:
+		DRM_ERROR( "invalid blit format %d\n", blit->format );
+		return -EINVAL;
+	}
+
+	/* Flush the pixel cache, and mark the contents as Read Invalid.
+	 * This ensures no pixel data gets mixed up with the texture
+	 * data from the host data blit, otherwise part of the texture
+	 * image may be corrupted.
+	 */
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) );
+	OUT_RING( R128_PC_RI_GUI | R128_PC_FLUSH_GUI );
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer.
+	 */
+	buf = dma->buflist[blit->idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", blit->idx );
+		return -EINVAL;
+	}
+
+	buf_priv->discard = 1;
+
+	dwords = (blit->width * blit->height) >> dword_shift;
+
+	data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset);
+
+	data[0] = CCE_PACKET3( R128_CNTL_HOSTDATA_BLT, dwords + 6 );
+	data[1] = ( R128_GMC_DST_PITCH_OFFSET_CNTL
+		    | R128_GMC_BRUSH_NONE
+		    | (blit->format << 8)
+		    | R128_GMC_SRC_DATATYPE_COLOR
+		    | R128_ROP3_S
+		    | R128_DP_SRC_SOURCE_HOST_DATA
+		    | R128_GMC_CLR_CMP_CNTL_DIS
+		    | R128_GMC_AUX_CLIP_DIS
+		    | R128_GMC_WR_MSK_DIS );
+
+	data[2] = (blit->pitch << 21) | (blit->offset >> 5);
+	data[3] = 0xffffffff;
+	data[4] = 0xffffffff;
+	data[5] = (blit->y << 16) | blit->x;
+	data[6] = (blit->height << 16) | blit->width;
+	data[7] = dwords;
+
+	buf->used = (dwords + 8) * sizeof(u32);
+
+	r128_cce_dispatch_indirect( dev, buf, 0, buf->used );
+
+	/* Flush the pixel cache after the blit completes.  This ensures
+	 * the texture data is written out to memory before rendering
+	 * continues.
+	 */
+	BEGIN_RING( 2 );
+
+	OUT_RING( CCE_PACKET0( R128_PC_GUI_CTLSTAT, 0 ) );
+	OUT_RING( R128_PC_FLUSH_GUI );
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+
+/* ================================================================
+ * Tiled depth buffer management
+ *
+ * FIXME: These should all set the destination write mask for when we
+ * have hardware stencil support.
+ */
+
+static int r128_cce_dispatch_write_span( drm_device_t *dev,
+					 drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, x, y;
+	u32 *buffer;
+	u8 *mask;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( copy_from_user( &x, depth->x, sizeof(x) ) ) {
+		return -EFAULT;
+	}
+	if ( copy_from_user( &y, depth->y, sizeof(y) ) ) {
+		return -EFAULT;
+	}
+
+	buffer = kmalloc( depth->n * sizeof(u32), 0 );
+	if ( buffer == NULL )
+		return -ENOMEM;
+	if ( copy_from_user( buffer, depth->buffer,
+			     depth->n * sizeof(u32) ) ) {
+		kfree( buffer );
+		return -EFAULT;
+	}
+
+	if ( depth->mask ) {
+		mask = kmalloc( depth->n * sizeof(u8), 0 );
+		if ( mask == NULL ) {
+			kfree( buffer );
+			return -ENOMEM;
+		}
+		if ( copy_from_user( mask, depth->mask,
+				     depth->n * sizeof(u8) ) ) {
+			kfree( buffer );
+			kfree( mask );
+			return -EFAULT;
+		}
+
+		for ( i = 0 ; i < count ; i++, x++ ) {
+			if ( mask[i] ) {
+				BEGIN_RING( 6 );
+
+				OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI,
+						       4 ) );
+				OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+					  | R128_GMC_BRUSH_SOLID_COLOR
+					  | depth_bpp
+					  | R128_GMC_SRC_DATATYPE_COLOR
+					  | R128_ROP3_P
+					  | R128_GMC_CLR_CMP_CNTL_DIS
+					  | R128_GMC_WR_MSK_DIS );
+
+				OUT_RING( dev_priv->depth_pitch_offset_c );
+				OUT_RING( buffer[i] );
+
+				OUT_RING( (x << 16) | y );
+				OUT_RING( (1 << 16) | 1 );
+
+				ADVANCE_RING();
+			}
+		}
+
+		kfree( mask );
+	} else {
+		for ( i = 0 ; i < count ; i++, x++ ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( buffer[i] );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (1 << 16) | 1 );
+
+			ADVANCE_RING();
+		}
+	}
+
+	kfree( buffer );
+
+	return 0;
+}
+
+static int r128_cce_dispatch_write_pixels( drm_device_t *dev,
+					   drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, *x, *y;
+	u32 *buffer;
+	u8 *mask;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+
+	x = kmalloc( count * sizeof(*x), 0 );
+	if ( x == NULL ) {
+		return -ENOMEM;
+	}
+	y = kmalloc( count * sizeof(*y), 0 );
+	if ( y == NULL ) {
+		kfree( x );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+	if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+
+	buffer = kmalloc( depth->n * sizeof(u32), 0 );
+	if ( buffer == NULL ) {
+		kfree( x );
+		kfree( y );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( buffer, depth->buffer,
+			     depth->n * sizeof(u32) ) ) {
+		kfree( x );
+		kfree( y );
+		kfree( buffer );
+		return -EFAULT;
+	}
+
+	if ( depth->mask ) {
+		mask = kmalloc( depth->n * sizeof(u8), 0 );
+		if ( mask == NULL ) {
+			kfree( x );
+			kfree( y );
+			kfree( buffer );
+			return -ENOMEM;
+		}
+		if ( copy_from_user( mask, depth->mask,
+				     depth->n * sizeof(u8) ) ) {
+			kfree( x );
+			kfree( y );
+			kfree( buffer );
+			kfree( mask );
+			return -EFAULT;
+		}
+
+		for ( i = 0 ; i < count ; i++ ) {
+			if ( mask[i] ) {
+				BEGIN_RING( 6 );
+
+				OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI,
+						       4 ) );
+				OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+					  | R128_GMC_BRUSH_SOLID_COLOR
+					  | depth_bpp
+					  | R128_GMC_SRC_DATATYPE_COLOR
+					  | R128_ROP3_P
+					  | R128_GMC_CLR_CMP_CNTL_DIS
+					  | R128_GMC_WR_MSK_DIS );
+
+				OUT_RING( dev_priv->depth_pitch_offset_c );
+				OUT_RING( buffer[i] );
+
+				OUT_RING( (x[i] << 16) | y[i] );
+				OUT_RING( (1 << 16) | 1 );
+
+				ADVANCE_RING();
+			}
+		}
+
+		kfree( mask );
+	} else {
+		for ( i = 0 ; i < count ; i++ ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CCE_PACKET3( R128_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( R128_GMC_DST_PITCH_OFFSET_CNTL
+				  | R128_GMC_BRUSH_SOLID_COLOR
+				  | depth_bpp
+				  | R128_GMC_SRC_DATATYPE_COLOR
+				  | R128_ROP3_P
+				  | R128_GMC_CLR_CMP_CNTL_DIS
+				  | R128_GMC_WR_MSK_DIS );
+
+			OUT_RING( dev_priv->depth_pitch_offset_c );
+			OUT_RING( buffer[i] );
+
+			OUT_RING( (x[i] << 16) | y[i] );
+			OUT_RING( (1 << 16) | 1 );
+
+			ADVANCE_RING();
+		}
+	}
+
+	kfree( x );
+	kfree( y );
+	kfree( buffer );
+
+	return 0;
+}
+
+static int r128_cce_dispatch_read_span( drm_device_t *dev,
+					drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, x, y;
+	u32 depth_bpp;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( copy_from_user( &x, depth->x, sizeof(x) ) ) {
+		return -EFAULT;
+	}
+	if ( copy_from_user( &y, depth->y, sizeof(y) ) ) {
+		return -EFAULT;
+	}
+
+	BEGIN_RING( 7 );
+
+	OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+	OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+		  | R128_GMC_DST_PITCH_OFFSET_CNTL
+		  | R128_GMC_BRUSH_NONE
+		  | depth_bpp
+		  | R128_GMC_SRC_DATATYPE_COLOR
+		  | R128_ROP3_S
+		  | R128_DP_SRC_SOURCE_MEMORY
+		  | R128_GMC_CLR_CMP_CNTL_DIS
+		  | R128_GMC_WR_MSK_DIS );
+
+	OUT_RING( dev_priv->depth_pitch_offset_c );
+	OUT_RING( dev_priv->span_pitch_offset_c );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (0 << 16) | 0 );
+	OUT_RING( (count << 16) | 1 );
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+static int r128_cce_dispatch_read_pixels( drm_device_t *dev,
+					  drm_r128_depth_t *depth )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int count, *x, *y;
+	u32 depth_bpp;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	switch ( dev_priv->depth_bpp ) {
+	case 16:
+		depth_bpp = R128_GMC_DST_16BPP;
+		break;
+	case 24:
+	case 32:
+		depth_bpp = R128_GMC_DST_32BPP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	count = depth->n;
+	if ( count > dev_priv->depth_pitch ) {
+		count = dev_priv->depth_pitch;
+	}
+
+	x = kmalloc( count * sizeof(*x), 0 );
+	if ( x == NULL ) {
+		return -ENOMEM;
+	}
+	y = kmalloc( count * sizeof(*y), 0 );
+	if ( y == NULL ) {
+		kfree( x );
+		return -ENOMEM;
+	}
+	if ( copy_from_user( x, depth->x, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+	if ( copy_from_user( y, depth->y, count * sizeof(int) ) ) {
+		kfree( x );
+		kfree( y );
+		return -EFAULT;
+	}
+
+	for ( i = 0 ; i < count ; i++ ) {
+		BEGIN_RING( 7 );
+
+		OUT_RING( CCE_PACKET3( R128_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( R128_GMC_SRC_PITCH_OFFSET_CNTL
+			  | R128_GMC_DST_PITCH_OFFSET_CNTL
+			  | R128_GMC_BRUSH_NONE
+			  | depth_bpp
+			  | R128_GMC_SRC_DATATYPE_COLOR
+			  | R128_ROP3_S
+			  | R128_DP_SRC_SOURCE_MEMORY
+			  | R128_GMC_CLR_CMP_CNTL_DIS
+			  | R128_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->depth_pitch_offset_c );
+		OUT_RING( dev_priv->span_pitch_offset_c );
+
+		OUT_RING( (x[i] << 16) | y[i] );
+		OUT_RING( (i << 16) | 0 );
+		OUT_RING( (1 << 16) | 1 );
+
+		ADVANCE_RING();
+	}
+
+	kfree( x );
+	kfree( y );
+
+	return 0;
+}
+
+
+/* ================================================================
+ * Polygon stipple
+ */
+
+static void r128_cce_dispatch_stipple( drm_device_t *dev, u32 *stipple )
+{
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	r128_update_ring_snapshot( dev_priv );
+
+	BEGIN_RING( 33 );
+
+	OUT_RING(  CCE_PACKET0( R128_BRUSH_DATA0, 31 ) );
+	for ( i = 0 ; i < 32 ; i++ ) {
+		OUT_RING( stipple[i] );
+	}
+
+	ADVANCE_RING();
+}
+
+
+/* ================================================================
+ * IOCTL functions
+ */
+
+int r128_cce_clear( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_r128_clear_t clear;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_cce_clear called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &clear, (drm_r128_clear_t *) arg,
+			     sizeof(clear) ) )
+		return -EFAULT;
+
+	if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS;
+
+	r128_cce_dispatch_clear( dev, clear.flags,
+				 clear.x, clear.y, clear.w, clear.h,
+				 clear.clear_color, clear.clear_depth );
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS;
+
+	return 0;
+}
+
+int r128_cce_swap( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_r128_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "r128_cce_swap called without lock held\n" );
+		return -EINVAL;
+	}
+
+	if ( sarea_priv->nbox > R128_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = R128_NR_SAREA_CLIPRECTS;
+
+	r128_cce_dispatch_swap( dev );
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= R128_UPLOAD_CONTEXT | R128_UPLOAD_MASKS;
+
+	return 0;
+}
+
+int r128_cce_vertex( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_vertex_t vertex;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &vertex, (drm_r128_vertex_t *)arg,
+			     sizeof(vertex) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   vertex.idx, vertex.count, vertex.discard );
+
+	if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   vertex.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( vertex.prim < 0 ||
+	     vertex.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) {
+		DRM_ERROR( "buffer prim %d\n", vertex.prim );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", vertex.idx );
+		return -EINVAL;
+	}
+
+	buf->used = vertex.count;
+	buf_priv->prim = vertex.prim;
+	buf_priv->discard = vertex.discard;
+
+	r128_cce_dispatch_vertex( dev, buf );
+
+	return 0;
+}
+
+int r128_cce_indices( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_r128_buf_priv_t *buf_priv;
+	drm_r128_indices_t elts;
+	int count;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &elts, (drm_r128_indices_t *)arg,
+			     sizeof(elts) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d buf=%d s=%d e=%d d=%d\n",
+		   __FUNCTION__, current->pid,
+		   elts.idx, elts.start, elts.end, elts.discard );
+
+	if ( elts.idx < 0 || elts.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   elts.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( elts.prim < 0 ||
+	     elts.prim > R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2 ) {
+		DRM_ERROR( "buffer prim %d\n", elts.prim );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[elts.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", elts.idx );
+		return -EINVAL;
+	}
+
+	count = (elts.end - elts.start) / sizeof(u16);
+	elts.start -= R128_INDEX_PRIM_OFFSET;
+
+	if ( elts.start & 0x7 ) {
+		DRM_ERROR( "misaligned buffer 0x%x\n", elts.start );
+		return -EINVAL;
+	}
+	if ( elts.start < buf->used ) {
+		DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used );
+		return -EINVAL;
+	}
+
+	buf->used = elts.end;
+	buf_priv->prim = elts.prim;
+	buf_priv->discard = elts.discard;
+
+	r128_cce_dispatch_indices( dev, buf, elts.start, elts.end, count );
+
+	return 0;
+}
+
+int r128_cce_blit( struct inode *inode, struct file *filp,
+		   unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	drm_r128_blit_t blit;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &blit, (drm_r128_blit_t *)arg,
+			     sizeof(blit) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d\n",
+		   __FUNCTION__, current->pid, blit.idx );
+
+	if ( blit.idx < 0 || blit.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   blit.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+
+	return r128_cce_dispatch_blit( dev, &blit );
+}
+
+int r128_cce_depth( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_depth_t depth;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &depth, (drm_r128_depth_t *)arg,
+			     sizeof(depth) ) )
+		return -EFAULT;
+
+	switch ( depth.func ) {
+	case R128_WRITE_SPAN:
+		return r128_cce_dispatch_write_span( dev, &depth );
+	case R128_WRITE_PIXELS:
+		return r128_cce_dispatch_write_pixels( dev, &depth );
+	case R128_READ_SPAN:
+		return r128_cce_dispatch_read_span( dev, &depth );
+	case R128_READ_PIXELS:
+		return r128_cce_dispatch_read_pixels( dev, &depth );
+	}
+
+	return -EINVAL;
+}
+
+int r128_cce_stipple( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_r128_stipple_t stipple;
+	u32 mask[32];
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stipple, (drm_r128_stipple_t *)arg,
+			     sizeof(stipple) ) )
+		return -EFAULT;
+
+	if ( copy_from_user( &mask, stipple.mask,
+			     32 * sizeof(u32) ) )
+		return -EFAULT;
+
+	r128_cce_dispatch_stipple( dev, mask );
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_bufs.c linux/drivers/char/drm-4.0/radeon_bufs.c
--- linux.orig/drivers/char/drm-4.0/radeon_bufs.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_bufs.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,312 @@
+/* radeon_bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *	    Jeff Hartmann <jhartmann@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include "drmP.h"
+#include "radeon_drv.h"
+#include "linux/un.h"
+
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+int radeon_addbufs_agp(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg)
+{
+	drm_file_t       *priv = filp->private_data;
+	drm_device_t     *dev  = priv->dev;
+	drm_device_dma_t *dma  = dev->dma;
+	drm_buf_desc_t    request;
+	drm_buf_entry_t  *entry;
+	drm_buf_t        *buf;
+	unsigned long     offset;
+	unsigned long     agp_offset;
+	int               count;
+	int               order;
+	int               size;
+	int               alignment;
+	int               page_order;
+	int               total;
+	int               byte_count;
+	int               i;
+
+	if (!dma) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	count      = request.count;
+	order      = drm_order(request.size);
+	size       = 1 << order;
+
+	alignment  = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size;
+	page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0;
+	total      = PAGE_SIZE << page_order;
+
+	byte_count = 0;
+	agp_offset = dev->agp->base + request.agp_start;
+
+	DRM_DEBUG("count:      %d\n",  count);
+	DRM_DEBUG("order:      %d\n",  order);
+	DRM_DEBUG("size:       %d\n",  size);
+	DRM_DEBUG("agp_offset: %ld\n", agp_offset);
+	DRM_DEBUG("alignment:  %d\n",  alignment);
+	DRM_DEBUG("page_order: %d\n",  page_order);
+	DRM_DEBUG("total:      %d\n",  total);
+
+	if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL;
+	if (dev->queue_count) return -EBUSY; /* Not while in use */
+
+	spin_lock(&dev->count_lock);
+	if (dev->buf_use) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	atomic_inc(&dev->buf_alloc);
+	spin_unlock(&dev->count_lock);
+
+	down(&dev->struct_sem);
+	entry = &dma->bufs[order];
+	if (entry->buf_count) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM; /* May only call once for each order */
+	}
+
+	/* Might be too low a limit. XFree folks need to fix this properly */
+	
+	if(count < 0 || count > 4096)
+	{
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -EINVAL;
+	}
+		
+	entry->buflist = drm_alloc(count * sizeof(*entry->buflist),
+				   DRM_MEM_BUFS);
+	if (!entry->buflist) {
+		up(&dev->struct_sem);
+		atomic_dec(&dev->buf_alloc);
+		return -ENOMEM;
+	}
+	memset(entry->buflist, 0, count * sizeof(*entry->buflist));
+
+	entry->buf_size   = size;
+	entry->page_order = page_order;
+	offset            = 0;
+
+	for (offset = 0;
+	     entry->buf_count < count;
+	     offset += alignment, ++entry->buf_count) {
+		buf          = &entry->buflist[entry->buf_count];
+		buf->idx     = dma->buf_count + entry->buf_count;
+		buf->total   = alignment;
+		buf->order   = order;
+		buf->used    = 0;
+		buf->offset  = (dma->byte_count + offset);
+		buf->address = (void *)(agp_offset + offset);
+		buf->next    = NULL;
+		buf->waiting = 0;
+		buf->pending = 0;
+		init_waitqueue_head(&buf->dma_wait);
+		buf->pid     = 0;
+
+		buf->dev_priv_size = sizeof(drm_radeon_buf_priv_t);
+		buf->dev_private   = drm_alloc(sizeof(drm_radeon_buf_priv_t),
+					       DRM_MEM_BUFS);
+                if (!buf->dev_private) {
+                        up(&dev->struct_sem);
+                        atomic_dec(&dev->buf_alloc);
+                        return -ENOMEM;
+                }
+		memset(buf->dev_private, 0, buf->dev_priv_size);
+
+#if DRM_DMA_HISTOGRAM
+		buf->time_queued     = 0;
+		buf->time_dispatched = 0;
+		buf->time_completed  = 0;
+		buf->time_freed      = 0;
+#endif
+
+		byte_count += PAGE_SIZE << page_order;
+
+		DRM_DEBUG("buffer %d @ %p\n",
+			  entry->buf_count, buf->address);
+	}
+
+	DRM_DEBUG("byte_count: %d\n", byte_count);
+
+	dma->buflist = drm_realloc(dma->buflist,
+				   dma->buf_count * sizeof(*dma->buflist),
+				   (dma->buf_count + entry->buf_count)
+				   * sizeof(*dma->buflist),
+				   DRM_MEM_BUFS);
+	for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++)
+		dma->buflist[i] = &entry->buflist[i - dma->buf_count];
+
+	dma->buf_count  += entry->buf_count;
+	dma->byte_count += byte_count;
+
+	drm_freelist_create(&entry->freelist, entry->buf_count);
+	for (i = 0; i < entry->buf_count; i++) {
+		drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]);
+	}
+
+	up(&dev->struct_sem);
+
+	request.count = entry->buf_count;
+	request.size  = size;
+
+	if (copy_to_user((drm_buf_desc_t *)arg, &request, sizeof(request)))
+		return -EFAULT;
+
+	dma->flags = _DRM_DMA_USE_AGP;
+
+	atomic_dec(&dev->buf_alloc);
+	return 0;
+}
+#endif
+
+int radeon_addbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_radeon_private_t	*dev_priv	= dev->dev_private;
+	drm_buf_desc_t		request;
+
+	if (!dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	if (copy_from_user(&request, (drm_buf_desc_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (request.flags & _DRM_AGP_BUFFER)
+		return radeon_addbufs_agp(inode, filp, cmd, arg);
+	else
+#endif
+		return -EINVAL;
+}
+
+int radeon_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t		*priv		= filp->private_data;
+	drm_device_t		*dev		= priv->dev;
+	drm_radeon_private_t	*dev_priv	= dev->dev_private;
+	drm_device_dma_t	*dma		= dev->dma;
+	int			 retcode	= 0;
+	const int		 zero		= 0;
+	unsigned long		 virtual;
+	unsigned long		 address;
+	drm_buf_map_t		 request;
+	int			 i;
+
+	if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL;
+
+	DRM_DEBUG("\n");
+
+	spin_lock(&dev->count_lock);
+	if (atomic_read(&dev->buf_alloc)) {
+		spin_unlock(&dev->count_lock);
+		return -EBUSY;
+	}
+	++dev->buf_use;		/* Can't allocate more after this call */
+	spin_unlock(&dev->count_lock);
+
+	if (copy_from_user(&request, (drm_buf_map_t *)arg, sizeof(request)))
+		return -EFAULT;
+
+	if (request.count >= dma->buf_count) {
+		if (dma->flags & _DRM_DMA_USE_AGP) {
+			drm_map_t *map;
+
+			map = dev_priv->buffers;
+			if (!map) {
+				retcode = -EINVAL;
+				goto done;
+			}
+
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, map->size,
+					  PROT_READ|PROT_WRITE,
+					  MAP_SHARED,
+					  (unsigned long)map->offset);
+			up_write(&current->mm->mmap_sem);
+		} else {
+			down_write(&current->mm->mmap_sem);
+			virtual = do_mmap(filp, 0, dma->byte_count,
+					  PROT_READ|PROT_WRITE, MAP_SHARED, 0);
+			up_write(&current->mm->mmap_sem);
+		}
+		if (virtual > -1024UL) {
+			/* Real error */
+			retcode = (signed long)virtual;
+			goto done;
+		}
+		request.virtual = (void *)virtual;
+
+		for (i = 0; i < dma->buf_count; i++) {
+			if (copy_to_user(&request.list[i].idx,
+					 &dma->buflist[i]->idx,
+					 sizeof(request.list[0].idx))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].total,
+					 &dma->buflist[i]->total,
+					 sizeof(request.list[0].total))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			if (copy_to_user(&request.list[i].used,
+					 &zero,
+					 sizeof(zero))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+			address = virtual + dma->buflist[i]->offset;
+			if (copy_to_user(&request.list[i].address,
+					 &address,
+					 sizeof(address))) {
+				retcode = -EFAULT;
+				goto done;
+			}
+		}
+	}
+ done:
+	request.count = dma->buf_count;
+	DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode);
+
+	if (copy_to_user((drm_buf_map_t *)arg, &request, sizeof(request)))
+		return -EFAULT;
+
+	return retcode;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_context.c linux/drivers/char/drm-4.0/radeon_context.c
--- linux.orig/drivers/char/drm-4.0/radeon_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,215 @@
+/* radeon_context.c -- IOCTLs for Radeon contexts -*- linux-c -*-
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Kevin E. Martin <martin@valinux.com>
+ *         Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+
+extern drm_ctx_t radeon_res_ctx;
+
+static int radeon_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int radeon_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                radeon_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+
+        return 0;
+}
+
+int radeon_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+
+        return 0;
+}
+
+
+int radeon_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i], &i, sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int radeon_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = radeon_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = radeon_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		radeon_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int radeon_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't hanlde any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		     unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return radeon_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int radeon_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	radeon_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int radeon_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_cp.c linux/drivers/char/drm-4.0/radeon_cp.c
--- linux.orig/drivers/char/drm-4.0/radeon_cp.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_cp.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1314 @@
+/* radeon_cp.c -- CP support for Radeon -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+
+#include <linux/interrupt.h>	/* For task queue support */
+#include <linux/delay.h>
+
+#define RADEON_FIFO_DEBUG	0
+
+
+/* CP microcode (from ATI) */
+static u32 radeon_cp_microcode[][2] = {
+	{ 0x21007000, 0000000000 },
+	{ 0x20007000, 0000000000 },
+	{ 0x000000b4, 0x00000004 },
+	{ 0x000000b8, 0x00000004 },
+	{ 0x6f5b4d4c, 0000000000 },
+	{ 0x4c4c427f, 0000000000 },
+	{ 0x5b568a92, 0000000000 },
+	{ 0x4ca09c6d, 0000000000 },
+	{ 0xad4c4c4c, 0000000000 },
+	{ 0x4ce1af3d, 0000000000 },
+	{ 0xd8afafaf, 0000000000 },
+	{ 0xd64c4cdc, 0000000000 },
+	{ 0x4cd10d10, 0000000000 },
+	{ 0x000f0000, 0x00000016 },
+	{ 0x362f242d, 0000000000 },
+	{ 0x00000012, 0x00000004 },
+	{ 0x000f0000, 0x00000016 },
+	{ 0x362f282d, 0000000000 },
+	{ 0x000380e7, 0x00000002 },
+	{ 0x04002c97, 0x00000002 },
+	{ 0x000f0001, 0x00000016 },
+	{ 0x333a3730, 0000000000 },
+	{ 0x000077ef, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000021, 0x0000001a },
+	{ 0x00004000, 0x0000001e },
+	{ 0x00000017, 0x00000004 },
+	{ 0x0003802b, 0x00000002 },
+	{ 0x040067e0, 0x00000002 },
+	{ 0x00000017, 0x00000004 },
+	{ 0x000077e0, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x000037e1, 0x00000002 },
+	{ 0x040067e1, 0x00000006 },
+	{ 0x000077e0, 0x00000002 },
+	{ 0x000077e1, 0x00000002 },
+	{ 0x000077e1, 0x00000006 },
+	{ 0xffffffff, 0000000000 },
+	{ 0x10000000, 0000000000 },
+	{ 0x0003802b, 0x00000002 },
+	{ 0x040067e0, 0x00000006 },
+	{ 0x00007675, 0x00000002 },
+	{ 0x00007676, 0x00000002 },
+	{ 0x00007677, 0x00000002 },
+	{ 0x00007678, 0x00000006 },
+	{ 0x0003802c, 0x00000002 },
+	{ 0x04002676, 0x00000002 },
+	{ 0x00007677, 0x00000002 },
+	{ 0x00007678, 0x00000006 },
+	{ 0x0000002f, 0x00000018 },
+	{ 0x0000002f, 0x00000018 },
+	{ 0000000000, 0x00000006 },
+	{ 0x00000030, 0x00000018 },
+	{ 0x00000030, 0x00000018 },
+	{ 0000000000, 0x00000006 },
+	{ 0x01605000, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x00098000, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x64c0603e, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00080000, 0x00000016 },
+	{ 0000000000, 0000000000 },
+	{ 0x0400251d, 0x00000002 },
+	{ 0x00007580, 0x00000002 },
+	{ 0x00067581, 0x00000002 },
+	{ 0x04002580, 0x00000002 },
+	{ 0x00067581, 0x00000002 },
+	{ 0x00000049, 0x00000004 },
+	{ 0x00005000, 0000000000 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x0000750e, 0x00000002 },
+	{ 0x00019000, 0x00000002 },
+	{ 0x00011055, 0x00000014 },
+	{ 0x00000055, 0x00000012 },
+	{ 0x0400250f, 0x00000002 },
+	{ 0x0000504f, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007565, 0x00000002 },
+	{ 0x00007566, 0x00000002 },
+	{ 0x00000058, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x01e655b4, 0x00000002 },
+	{ 0x4401b0e4, 0x00000002 },
+	{ 0x01c110e4, 0x00000002 },
+	{ 0x26667066, 0x00000018 },
+	{ 0x040c2565, 0x00000002 },
+	{ 0x00000066, 0x00000018 },
+	{ 0x04002564, 0x00000002 },
+	{ 0x00007566, 0x00000002 },
+	{ 0x0000005d, 0x00000004 },
+	{ 0x00401069, 0x00000008 },
+	{ 0x00101000, 0x00000002 },
+	{ 0x000d80ff, 0x00000002 },
+	{ 0x0080006c, 0x00000008 },
+	{ 0x000f9000, 0x00000002 },
+	{ 0x000e00ff, 0x00000002 },
+	{ 0000000000, 0x00000006 },
+	{ 0x0000008f, 0x00000018 },
+	{ 0x0000005b, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007576, 0x00000002 },
+	{ 0x00065000, 0x00000002 },
+	{ 0x00009000, 0x00000002 },
+	{ 0x00041000, 0x00000002 },
+	{ 0x0c00350e, 0x00000002 },
+	{ 0x00049000, 0x00000002 },
+	{ 0x00051000, 0x00000002 },
+	{ 0x01e785f8, 0x00000002 },
+	{ 0x00200000, 0x00000002 },
+	{ 0x0060007e, 0x0000000c },
+	{ 0x00007563, 0x00000002 },
+	{ 0x006075f0, 0x00000021 },
+	{ 0x20007073, 0x00000004 },
+	{ 0x00005073, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00007576, 0x00000002 },
+	{ 0x00007577, 0x00000002 },
+	{ 0x0000750e, 0x00000002 },
+	{ 0x0000750f, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00600083, 0x0000000c },
+	{ 0x006075f0, 0x00000021 },
+	{ 0x000075f8, 0x00000002 },
+	{ 0x00000083, 0x00000004 },
+	{ 0x000a750e, 0x00000002 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x0020750f, 0x00000002 },
+	{ 0x00600086, 0x00000004 },
+	{ 0x00007570, 0x00000002 },
+	{ 0x00007571, 0x00000002 },
+	{ 0x00007572, 0x00000006 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00007568, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x00000095, 0x0000000c },
+	{ 0x00058000, 0x00000002 },
+	{ 0x0c607562, 0x00000002 },
+	{ 0x00000097, 0x00000004 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x00600096, 0x00000004 },
+	{ 0x400070e5, 0000000000 },
+	{ 0x000380e6, 0x00000002 },
+	{ 0x040025c5, 0x00000002 },
+	{ 0x000380e5, 0x00000002 },
+	{ 0x000000a8, 0x0000001c },
+	{ 0x000650aa, 0x00000018 },
+	{ 0x040025bb, 0x00000002 },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x040075bc, 0000000000 },
+	{ 0x000075bb, 0x00000002 },
+	{ 0x000075bc, 0000000000 },
+	{ 0x00090000, 0x00000006 },
+	{ 0x00090000, 0x00000002 },
+	{ 0x000d8002, 0x00000006 },
+	{ 0x00007832, 0x00000002 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x000380e7, 0x00000002 },
+	{ 0x04002c97, 0x00000002 },
+	{ 0x00007820, 0x00000002 },
+	{ 0x00007821, 0x00000002 },
+	{ 0x00007800, 0000000000 },
+	{ 0x01200000, 0x00000002 },
+	{ 0x20077000, 0x00000002 },
+	{ 0x01200000, 0x00000002 },
+	{ 0x20007000, 0x00000002 },
+	{ 0x00061000, 0x00000002 },
+	{ 0x0120751b, 0x00000002 },
+	{ 0x8040750a, 0x00000002 },
+	{ 0x8040750b, 0x00000002 },
+	{ 0x00110000, 0x00000002 },
+	{ 0x000380e5, 0x00000002 },
+	{ 0x000000c6, 0x0000001c },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x844075bd, 0x00000002 },
+	{ 0x000610aa, 0x00000018 },
+	{ 0x840075bb, 0x00000002 },
+	{ 0x000610ab, 0x00000018 },
+	{ 0x844075bc, 0x00000002 },
+	{ 0x000000c9, 0x00000004 },
+	{ 0x804075bd, 0x00000002 },
+	{ 0x800075bb, 0x00000002 },
+	{ 0x804075bc, 0x00000002 },
+	{ 0x00108000, 0x00000002 },
+	{ 0x01400000, 0x00000002 },
+	{ 0x006000cd, 0x0000000c },
+	{ 0x20c07000, 0x00000020 },
+	{ 0x000000cf, 0x00000012 },
+	{ 0x00800000, 0x00000006 },
+	{ 0x0080751d, 0x00000006 },
+	{ 0000000000, 0000000000 },
+	{ 0x0000775c, 0x00000002 },
+	{ 0x00a05000, 0x00000002 },
+	{ 0x00661000, 0x00000002 },
+	{ 0x0460275d, 0x00000020 },
+	{ 0x00004000, 0000000000 },
+	{ 0x01e00830, 0x00000002 },
+	{ 0x21007000, 0000000000 },
+	{ 0x6464614d, 0000000000 },
+	{ 0x69687420, 0000000000 },
+	{ 0x00000073, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0x00005000, 0x00000002 },
+	{ 0x000380d0, 0x00000002 },
+	{ 0x040025e0, 0x00000002 },
+	{ 0x000075e1, 0000000000 },
+	{ 0x00000001, 0000000000 },
+	{ 0x000380e0, 0x00000002 },
+	{ 0x04002394, 0x00000002 },
+	{ 0x00005000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0x00000008, 0000000000 },
+	{ 0x00000004, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+	{ 0000000000, 0000000000 },
+};
+
+
+#define DO_IOREMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size)
+
+#define DO_IOREMAPFREE(_m)						\
+	do {								\
+		if ((_m)->handle && (_m)->size)				\
+			drm_ioremapfree((_m)->handle, (_m)->size);	\
+	} while (0)
+
+#define DO_FIND_MAP(_m, _o)						\
+	do {								\
+		int _i;							\
+		for (_i = 0; _i < dev->map_count; _i++) {		\
+			if (dev->maplist[_i]->offset == _o) {		\
+				_m = dev->maplist[_i];			\
+				break;					\
+			}						\
+		}							\
+	} while (0)
+
+
+int RADEON_READ_PLL(drm_device_t *dev, int addr)
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+
+	RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX, addr & 0x1f);
+	return RADEON_READ(RADEON_CLOCK_CNTL_DATA);
+}
+
+#if RADEON_FIFO_DEBUG
+static void radeon_status( drm_radeon_private_t *dev_priv )
+{
+	printk( "%s:\n", __FUNCTION__ );
+	printk( "RBBM_STATUS = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_RBBM_STATUS ) );
+	printk( "CP_RB_RTPR = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_CP_RB_RPTR ) );
+	printk( "CP_RB_WTPR = 0x%08x\n",
+		(unsigned int)RADEON_READ( RADEON_CP_RB_WPTR ) );
+}
+#endif
+
+
+/* ================================================================
+ * Engine, FIFO control
+ */
+
+static int radeon_do_pixcache_flush( drm_radeon_private_t *dev_priv )
+{
+	u32 tmp;
+	int i;
+
+	tmp  = RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT );
+	tmp |= RADEON_RB2D_DC_FLUSH_ALL;
+	RADEON_WRITE( RADEON_RB2D_DSTCACHE_CTLSTAT, tmp );
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(RADEON_READ( RADEON_RB2D_DSTCACHE_CTLSTAT )
+		       & RADEON_RB2D_DC_BUSY) ) {
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+static int radeon_do_wait_for_fifo( drm_radeon_private_t *dev_priv,
+				    int entries )
+{
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		int slots = ( RADEON_READ( RADEON_RBBM_STATUS )
+			      & RADEON_RBBM_FIFOCNT_MASK );
+		if ( slots >= entries ) return 0;
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+static int radeon_do_wait_for_idle( drm_radeon_private_t *dev_priv )
+{
+	int i, ret;
+
+	ret = radeon_do_wait_for_fifo( dev_priv, 64 );
+	if ( ret < 0 ) return ret;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		if ( !(RADEON_READ( RADEON_RBBM_STATUS )
+		       & RADEON_RBBM_ACTIVE) ) {
+			radeon_do_pixcache_flush( dev_priv );
+			return 0;
+		}
+		udelay( 1 );
+	}
+
+#if RADEON_FIFO_DEBUG
+	DRM_ERROR( "failed!\n" );
+	radeon_status( dev_priv );
+#endif
+	return -EBUSY;
+}
+
+
+/* ================================================================
+ * CP control, initialization
+ */
+
+/* Load the microcode for the CP */
+static void radeon_cp_load_microcode( drm_radeon_private_t *dev_priv )
+{
+	int i;
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	RADEON_WRITE( RADEON_CP_ME_RAM_ADDR, 0 );
+	for ( i = 0 ; i < 256 ; i++ ) {
+		RADEON_WRITE( RADEON_CP_ME_RAM_DATAH,
+			      radeon_cp_microcode[i][1] );
+		RADEON_WRITE( RADEON_CP_ME_RAM_DATAL,
+			      radeon_cp_microcode[i][0] );
+	}
+}
+
+/* Flush any pending commands to the CP.  This should only be used just
+ * prior to a wait for idle, as it informs the engine that the command
+ * stream is ending.
+ */
+static void radeon_do_cp_flush( drm_radeon_private_t *dev_priv )
+{
+#if 0
+	u32 tmp;
+
+	tmp = RADEON_READ( RADEON_CP_RB_WPTR ) | (1 << 31);
+	RADEON_WRITE( RADEON_CP_RB_WPTR, tmp );
+#endif
+}
+
+/* Wait for the CP to go idle.
+ */
+int radeon_do_cp_idle( drm_radeon_private_t *dev_priv )
+{
+	RING_LOCALS;
+
+	BEGIN_RING( 6 );
+
+	RADEON_PURGE_CACHE();
+	RADEON_PURGE_ZCACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+
+	return radeon_do_wait_for_idle( dev_priv );
+}
+
+/* Start the Command Processor.
+ */
+static void radeon_do_cp_start( drm_radeon_private_t *dev_priv )
+{
+	RING_LOCALS;
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	RADEON_WRITE( RADEON_CP_CSQ_CNTL, dev_priv->cp_mode );
+
+	dev_priv->cp_running = 1;
+
+	BEGIN_RING( 6 );
+
+	RADEON_PURGE_CACHE();
+	RADEON_PURGE_ZCACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+}
+
+/* Reset the Command Processor.  This will not flush any pending
+ * commands, so you must wait for the CP command stream to complete
+ * before calling this routine.
+ */
+static void radeon_do_cp_reset( drm_radeon_private_t *dev_priv )
+{
+	u32 cur_read_ptr;
+
+	cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR );
+	RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr );
+	*dev_priv->ring.head = cur_read_ptr;
+	dev_priv->ring.tail = cur_read_ptr;
+}
+
+/* Stop the Command Processor.  This will not flush any pending
+ * commands, so you must flush the command stream and wait for the CP
+ * to go idle before calling this routine.
+ */
+static void radeon_do_cp_stop( drm_radeon_private_t *dev_priv )
+{
+	RADEON_WRITE( RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIDIS_INDDIS );
+
+	dev_priv->cp_running = 0;
+}
+
+/* Reset the engine.  This will stop the CP if it is running.
+ */
+static int radeon_do_engine_reset( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_do_pixcache_flush( dev_priv );
+
+	clock_cntl_index = RADEON_READ( RADEON_CLOCK_CNTL_INDEX );
+	mclk_cntl = RADEON_READ_PLL( dev, RADEON_MCLK_CNTL );
+
+	/* FIXME: remove magic number here and in radeon ddx driver!!! */
+	RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl | 0x003f00000 );
+
+	rbbm_soft_reset = RADEON_READ( RADEON_RBBM_SOFT_RESET );
+
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset |
+						RADEON_SOFT_RESET_CP |
+						RADEON_SOFT_RESET_HI |
+						RADEON_SOFT_RESET_SE |
+						RADEON_SOFT_RESET_RE |
+						RADEON_SOFT_RESET_PP |
+						RADEON_SOFT_RESET_E2 |
+						RADEON_SOFT_RESET_RB |
+						RADEON_SOFT_RESET_HDP ) );
+	RADEON_READ( RADEON_RBBM_SOFT_RESET );
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET, ( rbbm_soft_reset &
+						~( RADEON_SOFT_RESET_CP |
+						   RADEON_SOFT_RESET_HI |
+						   RADEON_SOFT_RESET_SE |
+						   RADEON_SOFT_RESET_RE |
+						   RADEON_SOFT_RESET_PP |
+						   RADEON_SOFT_RESET_E2 |
+						   RADEON_SOFT_RESET_RB |
+						   RADEON_SOFT_RESET_HDP ) ) );
+	RADEON_READ( RADEON_RBBM_SOFT_RESET );
+
+
+	RADEON_WRITE_PLL( RADEON_MCLK_CNTL, mclk_cntl );
+	RADEON_WRITE( RADEON_CLOCK_CNTL_INDEX, clock_cntl_index );
+	RADEON_WRITE( RADEON_RBBM_SOFT_RESET,  rbbm_soft_reset );
+
+	/* Reset the CP ring */
+	radeon_do_cp_reset( dev_priv );
+
+	/* The CP is no longer running after an engine reset */
+	dev_priv->cp_running = 0;
+
+	/* Reset any pending vertex, indirect buffers */
+	radeon_freelist_reset( dev );
+
+	return 0;
+}
+
+static void radeon_cp_init_ring_buffer( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	u32 ring_start, cur_read_ptr;
+	u32 tmp;
+
+	/* Initialize the memory controller */
+	RADEON_WRITE( RADEON_MC_FB_LOCATION,
+		      (dev_priv->agp_vm_start - 1) & 0xffff0000 );
+	RADEON_WRITE( RADEON_MC_AGP_LOCATION,
+		      (((dev_priv->agp_vm_start - 1 +
+			 dev_priv->agp_size) & 0xffff0000) |
+		       (dev_priv->agp_vm_start >> 16)) );
+
+	ring_start = (dev_priv->cp_ring->offset
+		      - dev->agp->base
+		      + dev_priv->agp_vm_start);
+
+	RADEON_WRITE( RADEON_CP_RB_BASE, ring_start );
+
+	/* Set the write pointer delay */
+	RADEON_WRITE( RADEON_CP_RB_WPTR_DELAY, 0 );
+
+	/* Initialize the ring buffer's read and write pointers */
+	cur_read_ptr = RADEON_READ( RADEON_CP_RB_RPTR );
+	RADEON_WRITE( RADEON_CP_RB_WPTR, cur_read_ptr );
+	*dev_priv->ring.head = cur_read_ptr;
+	dev_priv->ring.tail = cur_read_ptr;
+
+	RADEON_WRITE( RADEON_CP_RB_RPTR_ADDR, dev_priv->ring_rptr->offset );
+
+	/* Set ring buffer size */
+	RADEON_WRITE( RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw );
+
+	radeon_do_wait_for_idle( dev_priv );
+
+	/* Turn off PCI GART */
+	tmp = RADEON_READ( RADEON_AIC_CNTL ) & ~RADEON_PCIGART_TRANSLATE_EN;
+	RADEON_WRITE( RADEON_AIC_CNTL, tmp );
+
+	/* Turn on bus mastering */
+	tmp = RADEON_READ( RADEON_BUS_CNTL ) & ~RADEON_BUS_MASTER_DIS;
+	RADEON_WRITE( RADEON_BUS_CNTL, tmp );
+
+	/* Sync everything up */
+	RADEON_WRITE( RADEON_ISYNC_CNTL,
+		      (RADEON_ISYNC_ANY2D_IDLE3D |
+		       RADEON_ISYNC_ANY3D_IDLE2D |
+		       RADEON_ISYNC_WAIT_IDLEGUI |
+		       RADEON_ISYNC_CPSCRATCH_IDLEGUI) );
+}
+
+static int radeon_do_init_cp( drm_device_t *dev, drm_radeon_init_t *init )
+{
+	drm_radeon_private_t *dev_priv;
+        int i;
+
+	dev_priv = drm_alloc( sizeof(drm_radeon_private_t), DRM_MEM_DRIVER );
+	if ( dev_priv == NULL )
+		return -ENOMEM;
+	dev->dev_private = (void *)dev_priv;
+
+	memset( dev_priv, 0, sizeof(drm_radeon_private_t) );
+
+	dev_priv->is_pci = init->is_pci;
+
+	/* We don't support PCI cards until PCI GART is implemented.
+	 * Fail here so we can remove all checks for PCI cards around
+	 * the CP ring code.
+	 */
+	if ( dev_priv->is_pci ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->usec_timeout = init->usec_timeout;
+	if ( dev_priv->usec_timeout < 1 ||
+	     dev_priv->usec_timeout > RADEON_MAX_USEC_TIMEOUT ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	dev_priv->cp_mode = init->cp_mode;
+
+	/* Simple idle check.
+	 */
+	atomic_set( &dev_priv->idle_count, 0 );
+
+	/* We don't support anything other than bus-mastering ring mode,
+	 * but the ring can be in either AGP or PCI space for the ring
+	 * read pointer.
+	 */
+	if ( ( init->cp_mode != RADEON_CSQ_PRIBM_INDDIS ) &&
+	     ( init->cp_mode != RADEON_CSQ_PRIBM_INDBM ) ) {
+		drm_free( dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+		return -EINVAL;
+	}
+
+	switch ( init->fb_bpp ) {
+	case 16:
+		dev_priv->color_fmt = RADEON_COLOR_FORMAT_RGB565;
+		break;
+	case 32:
+	default:
+		dev_priv->color_fmt = RADEON_COLOR_FORMAT_ARGB8888;
+		break;
+	}
+	dev_priv->front_offset	= init->front_offset;
+	dev_priv->front_pitch	= init->front_pitch;
+	dev_priv->back_offset	= init->back_offset;
+	dev_priv->back_pitch	= init->back_pitch;
+
+	switch ( init->depth_bpp ) {
+	case 16:
+		dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_16BIT_INT_Z;
+		break;
+	case 32:
+	default:
+		dev_priv->depth_fmt = RADEON_DEPTH_FORMAT_24BIT_INT_Z;
+		break;
+	}
+	dev_priv->depth_offset	= init->depth_offset;
+	dev_priv->depth_pitch	= init->depth_pitch;
+
+	dev_priv->front_pitch_offset = (((dev_priv->front_pitch/64) << 22) |
+					(dev_priv->front_offset >> 10));
+	dev_priv->back_pitch_offset = (((dev_priv->back_pitch/64) << 22) |
+				       (dev_priv->back_offset >> 10));
+	dev_priv->depth_pitch_offset = (((dev_priv->depth_pitch/64) << 22) |
+					(dev_priv->depth_offset >> 10));
+
+	/* Hardware state for depth clears.  Remove this if/when we no
+	 * longer clear the depth buffer with a 3D rectangle.  Hard-code
+	 * all values to prevent unwanted 3D state from slipping through
+	 * and screwing with the clear operation.
+	 */
+	dev_priv->depth_clear.rb3d_cntl = (RADEON_PLANE_MASK_ENABLE |
+					   RADEON_Z_ENABLE |
+					   (dev_priv->color_fmt << 10) |
+					   RADEON_ZBLOCK16);
+
+	dev_priv->depth_clear.rb3d_zstencilcntl = (dev_priv->depth_fmt |
+						   RADEON_Z_TEST_ALWAYS |
+						   RADEON_STENCIL_TEST_ALWAYS |
+						   RADEON_STENCIL_S_FAIL_KEEP |
+						   RADEON_STENCIL_ZPASS_KEEP |
+						   RADEON_STENCIL_ZFAIL_KEEP |
+						   RADEON_Z_WRITE_ENABLE);
+
+	dev_priv->depth_clear.se_cntl = (RADEON_FFACE_CULL_CW |
+					 RADEON_BFACE_SOLID |
+					 RADEON_FFACE_SOLID |
+					 RADEON_FLAT_SHADE_VTX_LAST |
+
+					 RADEON_DIFFUSE_SHADE_FLAT |
+					 RADEON_ALPHA_SHADE_FLAT |
+					 RADEON_SPECULAR_SHADE_FLAT |
+					 RADEON_FOG_SHADE_FLAT |
+
+					 RADEON_VTX_PIX_CENTER_OGL |
+					 RADEON_ROUND_MODE_TRUNC |
+					 RADEON_ROUND_PREC_8TH_PIX);
+
+	/* FIXME: We want multiple shared areas, including one shared
+	 * only by the X Server and kernel module.
+	 */
+	for ( i = 0 ; i < dev->map_count ; i++ ) {
+		if ( dev->maplist[i]->type == _DRM_SHM ) {
+			dev_priv->sarea = dev->maplist[i];
+			break;
+		}
+	}
+
+	DO_FIND_MAP( dev_priv->fb, init->fb_offset );
+	DO_FIND_MAP( dev_priv->mmio, init->mmio_offset );
+	DO_FIND_MAP( dev_priv->cp_ring, init->ring_offset );
+	DO_FIND_MAP( dev_priv->ring_rptr, init->ring_rptr_offset );
+	DO_FIND_MAP( dev_priv->buffers, init->buffers_offset );
+
+	if ( !dev_priv->is_pci ) {
+		DO_FIND_MAP( dev_priv->agp_textures,
+			     init->agp_textures_offset );
+	}
+
+	dev_priv->sarea_priv =
+		(drm_radeon_sarea_t *)((u8 *)dev_priv->sarea->handle +
+				       init->sarea_priv_offset);
+
+	DO_IOREMAP( dev_priv->cp_ring );
+	DO_IOREMAP( dev_priv->ring_rptr );
+	DO_IOREMAP( dev_priv->buffers );
+#if 0
+	if ( !dev_priv->is_pci ) {
+		DO_IOREMAP( dev_priv->agp_textures );
+	}
+#endif
+
+	dev_priv->agp_size = init->agp_size;
+	dev_priv->agp_vm_start = RADEON_READ( RADEON_CONFIG_APER_SIZE );
+	dev_priv->agp_buffers_offset = (dev_priv->buffers->offset
+					- dev->agp->base
+					+ dev_priv->agp_vm_start);
+
+	dev_priv->ring.head = ((__volatile__ u32 *)
+			       dev_priv->ring_rptr->handle);
+
+	dev_priv->ring.start = (u32 *)dev_priv->cp_ring->handle;
+	dev_priv->ring.end = ((u32 *)dev_priv->cp_ring->handle
+			      + init->ring_size / sizeof(u32));
+	dev_priv->ring.size = init->ring_size;
+	dev_priv->ring.size_l2qw = drm_order( init->ring_size / 8 );
+
+	dev_priv->ring.tail_mask =
+		(dev_priv->ring.size / sizeof(u32)) - 1;
+
+#if 0
+	/* Initialize the scratch register pointer.  This will cause
+	 * the scratch register values to be written out to memory
+	 * whenever they are updated.
+	 * FIXME: This doesn't quite work yet, so we're disabling it
+	 * for the release.
+	 */
+	RADEON_WRITE( RADEON_SCRATCH_ADDR, (dev_priv->ring_rptr->offset +
+					    RADEON_SCRATCH_REG_OFFSET) );
+	RADEON_WRITE( RADEON_SCRATCH_UMSK, 0x7 );
+#endif
+
+	dev_priv->scratch = ((__volatile__ u32 *)
+			     dev_priv->ring_rptr->handle +
+			     (RADEON_SCRATCH_REG_OFFSET / sizeof(u32)));
+
+	dev_priv->sarea_priv->last_frame = 0;
+	RADEON_WRITE( RADEON_LAST_FRAME_REG,
+		      dev_priv->sarea_priv->last_frame );
+
+	dev_priv->sarea_priv->last_dispatch = 0;
+	RADEON_WRITE( RADEON_LAST_DISPATCH_REG,
+		      dev_priv->sarea_priv->last_dispatch );
+
+	dev_priv->sarea_priv->last_clear = 0;
+	RADEON_WRITE( RADEON_LAST_CLEAR_REG,
+		      dev_priv->sarea_priv->last_clear );
+
+	radeon_cp_load_microcode( dev_priv );
+	radeon_cp_init_ring_buffer( dev );
+	radeon_do_engine_reset( dev );
+
+#if ROTATE_BUFS
+	dev_priv->last_buf = 0;
+#endif
+
+	return 0;
+}
+
+static int radeon_do_cleanup_cp( drm_device_t *dev )
+{
+	if ( dev->dev_private ) {
+		drm_radeon_private_t *dev_priv = dev->dev_private;
+
+		DO_IOREMAPFREE( dev_priv->cp_ring );
+		DO_IOREMAPFREE( dev_priv->ring_rptr );
+		DO_IOREMAPFREE( dev_priv->buffers );
+#if 0
+		if ( !dev_priv->is_pci ) {
+			DO_IOREMAPFREE( dev_priv->agp_textures );
+		}
+#endif
+
+		drm_free( dev->dev_private, sizeof(drm_radeon_private_t),
+			  DRM_MEM_DRIVER );
+		dev->dev_private = NULL;
+	}
+
+	return 0;
+}
+
+int radeon_cp_init( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_init_t init;
+
+	if ( copy_from_user( &init, (drm_radeon_init_t *)arg, sizeof(init) ) )
+		return -EFAULT;
+
+	switch ( init.func ) {
+	case RADEON_INIT_CP:
+		return radeon_do_init_cp( dev, &init );
+	case RADEON_CLEANUP_CP:
+		return radeon_do_cleanup_cp( dev );
+	}
+
+	return -EINVAL;
+}
+
+int radeon_cp_start( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( dev_priv->cp_running ) {
+		DRM_DEBUG( "%s while CP running\n", __FUNCTION__ );
+		return 0;
+	}
+	if ( dev_priv->cp_mode == RADEON_CSQ_PRIDIS_INDDIS ) {
+		DRM_DEBUG( "%s called with bogus CP mode (%d)\n",
+			   __FUNCTION__, dev_priv->cp_mode );
+		return 0;
+	}
+
+	radeon_do_cp_start( dev_priv );
+
+	return 0;
+}
+
+/* Stop the CP.  The engine must have been idled before calling this
+ * routine.
+ */
+int radeon_cp_stop( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_cp_stop_t stop;
+	int ret;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stop, (drm_radeon_init_t *)arg, sizeof(stop) ) )
+		return -EFAULT;
+
+	/* Flush any pending CP commands.  This ensures any outstanding
+	 * commands are exectuted by the engine before we turn it off.
+	 */
+	if ( stop.flush ) {
+		radeon_do_cp_flush( dev_priv );
+	}
+
+	/* If we fail to make the engine go idle, we return an error
+	 * code so that the DRM ioctl wrapper can try again.
+	 */
+	if ( stop.idle ) {
+		ret = radeon_do_cp_idle( dev_priv );
+		if ( ret < 0 ) return ret;
+	}
+
+	/* Finally, we can turn off the CP.  If the engine isn't idle,
+	 * we will get some dropped triangles as they won't be fully
+	 * rendered before the CP is shut down.
+	 */
+	radeon_do_cp_stop( dev_priv );
+
+	/* Reset the engine */
+	radeon_do_engine_reset( dev );
+
+	return 0;
+}
+
+/* Just reset the CP ring.  Called as part of an X Server engine reset.
+ */
+int radeon_cp_reset( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv ) {
+		DRM_DEBUG( "%s called before init done\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	radeon_do_cp_reset( dev_priv );
+
+	/* The CP is no longer running after an engine reset */
+	dev_priv->cp_running = 0;
+
+	return 0;
+}
+
+int radeon_cp_idle( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return radeon_do_cp_idle( dev_priv );
+}
+
+int radeon_engine_reset( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	return radeon_do_engine_reset( dev );
+}
+
+
+/* ================================================================
+ * Fullscreen mode
+ */
+
+static int radeon_do_init_pageflip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	dev_priv->crtc_offset =      RADEON_READ( RADEON_CRTC_OFFSET );
+	dev_priv->crtc_offset_cntl = RADEON_READ( RADEON_CRTC_OFFSET_CNTL );
+
+	RADEON_WRITE( RADEON_CRTC_OFFSET, dev_priv->front_offset );
+	RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL,
+		      dev_priv->crtc_offset_cntl |
+		      RADEON_CRTC_OFFSET_FLIP_CNTL );
+
+	dev_priv->page_flipping = 1;
+	dev_priv->current_page = 0;
+
+	return 0;
+}
+
+int radeon_do_cleanup_pageflip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	RADEON_WRITE( RADEON_CRTC_OFFSET,      dev_priv->crtc_offset );
+	RADEON_WRITE( RADEON_CRTC_OFFSET_CNTL, dev_priv->crtc_offset_cntl );
+
+	dev_priv->page_flipping = 0;
+	dev_priv->current_page = 0;
+
+	return 0;
+}
+
+int radeon_fullscreen( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+        drm_file_t *priv = filp->private_data;
+        drm_device_t *dev = priv->dev;
+	drm_radeon_fullscreen_t fs;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &fs, (drm_radeon_fullscreen_t *)arg,
+			     sizeof(fs) ) )
+		return -EFAULT;
+
+	switch ( fs.func ) {
+	case RADEON_INIT_FULLSCREEN:
+		return radeon_do_init_pageflip( dev );
+	case RADEON_CLEANUP_FULLSCREEN:
+		return radeon_do_cleanup_pageflip( dev );
+	}
+
+	return -EINVAL;
+}
+
+
+/* ================================================================
+ * Freelist management
+ */
+#define RADEON_BUFFER_USED	0xffffffff
+#define RADEON_BUFFER_FREE	0
+
+#if 0
+static int radeon_freelist_init( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_freelist_t *entry;
+	int i;
+
+	dev_priv->head = drm_alloc( sizeof(drm_radeon_freelist_t),
+				    DRM_MEM_DRIVER );
+	if ( dev_priv->head == NULL )
+		return -ENOMEM;
+
+	memset( dev_priv->head, 0, sizeof(drm_radeon_freelist_t) );
+	dev_priv->head->age = RADEON_BUFFER_USED;
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+
+		entry = drm_alloc( sizeof(drm_radeon_freelist_t),
+				   DRM_MEM_DRIVER );
+		if ( !entry ) return -ENOMEM;
+
+		entry->age = RADEON_BUFFER_FREE;
+		entry->buf = buf;
+		entry->prev = dev_priv->head;
+		entry->next = dev_priv->head->next;
+		if ( !entry->next )
+			dev_priv->tail = entry;
+
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+		buf_priv->list_entry = entry;
+
+		dev_priv->head->next = entry;
+
+		if ( dev_priv->head->next )
+			dev_priv->head->next->prev = entry;
+	}
+
+	return 0;
+
+}
+#endif
+
+drm_buf_t *radeon_freelist_get( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_buf_t *buf;
+	int i, t;
+#if ROTATE_BUFS
+	int start;
+#endif
+
+	/* FIXME: Optimize -- use freelist code */
+
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		if ( buf->pid == 0 ) {
+			DRM_DEBUG( "  ret buf=%d last=%d pid=0\n",
+				   buf->idx, dev_priv->last_buf );
+			return buf;
+		}
+		DRM_DEBUG( "    skipping buf=%d pid=%d\n",
+			   buf->idx, buf->pid );
+	}
+
+#if ROTATE_BUFS
+	if ( ++dev_priv->last_buf >= dma->buf_count )
+		dev_priv->last_buf = 0;
+	start = dev_priv->last_buf;
+#endif
+	for ( t = 0 ; t < dev_priv->usec_timeout ; t++ ) {
+#if 0
+		/* FIXME: Disable this for now */
+		u32 done_age = dev_priv->scratch[RADEON_LAST_DISPATCH];
+#else
+		u32 done_age = RADEON_READ( RADEON_LAST_DISPATCH_REG );
+#endif
+#if ROTATE_BUFS
+		for ( i = start ; i < dma->buf_count ; i++ ) {
+#else
+		for ( i = 0 ; i < dma->buf_count ; i++ ) {
+#endif
+			buf = dma->buflist[i];
+			buf_priv = buf->dev_private;
+			if ( buf->pending && buf_priv->age <= done_age ) {
+				/* The buffer has been processed, so it
+				 * can now be used.
+				 */
+				buf->pending = 0;
+				DRM_DEBUG( "  ret buf=%d last=%d age=%d done=%d\n", buf->idx, dev_priv->last_buf, buf_priv->age, done_age );
+				return buf;
+			}
+			DRM_DEBUG( "    skipping buf=%d age=%d done=%d\n",
+				   buf->idx, buf_priv->age,
+				   done_age );
+#if ROTATE_BUFS
+			start = 0;
+#endif
+		}
+		udelay( 1 );
+	}
+
+	DRM_ERROR( "returning NULL!\n" );
+	return NULL;
+}
+
+void radeon_freelist_reset( drm_device_t *dev )
+{
+	drm_device_dma_t *dma = dev->dma;
+#if ROTATE_BUFS
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+#endif
+	int i;
+
+#if ROTATE_BUFS
+	dev_priv->last_buf = 0;
+#endif
+	for ( i = 0 ; i < dma->buf_count ; i++ ) {
+		drm_buf_t *buf = dma->buflist[i];
+		drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+		buf_priv->age = 0;
+	}
+}
+
+
+/* ================================================================
+ * CP command submission
+ */
+
+int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n )
+{
+	drm_radeon_ring_buffer_t *ring = &dev_priv->ring;
+	int i;
+
+	for ( i = 0 ; i < dev_priv->usec_timeout ; i++ ) {
+		ring->space = *ring->head - ring->tail;
+		if ( ring->space <= 0 )
+			ring->space += ring->size;
+
+		if ( ring->space >= n )
+			return 0;
+
+		udelay( 1 );
+	}
+
+	/* FIXME: This return value is ignored in the BEGIN_RING macro! */
+	DRM_ERROR( "failed!\n" );
+	return -EBUSY;
+}
+
+void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_ring_buffer_t *ring = &dev_priv->ring;
+
+	ring->space = *ring->head - ring->tail;
+	if ( ring->space == 0 )
+		atomic_inc( &dev_priv->idle_count );
+	if ( ring->space <= 0 )
+		ring->space += ring->size;
+}
+
+static int radeon_cp_get_buffers( drm_device_t *dev, drm_dma_t *d )
+{
+	int i;
+	drm_buf_t *buf;
+
+	for ( i = d->granted_count ; i < d->request_count ; i++ ) {
+		buf = radeon_freelist_get( dev );
+		if ( !buf ) return -EAGAIN;
+
+		buf->pid = current->pid;
+
+		if ( copy_to_user( &d->request_indices[i], &buf->idx,
+				   sizeof(buf->idx) ) )
+			return -EFAULT;
+		if ( copy_to_user( &d->request_sizes[i], &buf->total,
+				   sizeof(buf->total) ) )
+			return -EFAULT;
+
+		d->granted_count++;
+	}
+	return 0;
+}
+
+int radeon_cp_buffers( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_device_dma_t *dma = dev->dma;
+	int ret = 0;
+	drm_dma_t d;
+
+	if ( copy_from_user( &d, (drm_dma_t *) arg, sizeof(d) ) )
+		return -EFAULT;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	/* Please don't send us buffers.
+	 */
+	if ( d.send_count != 0 ) {
+		DRM_ERROR( "Process %d trying to send %d buffers via drmDMA\n",
+			   current->pid, d.send_count );
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if ( d.request_count < 0 || d.request_count > dma->buf_count ) {
+		DRM_ERROR( "Process %d trying to get %d buffers (of %d max)\n",
+			   current->pid, d.request_count, dma->buf_count );
+		return -EINVAL;
+	}
+
+	d.granted_count = 0;
+
+	if ( d.request_count ) {
+		ret = radeon_cp_get_buffers( dev, &d );
+	}
+
+	if ( copy_to_user( (drm_dma_t *) arg, &d, sizeof(d) ) )
+		return -EFAULT;
+
+	return ret;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_drm.h linux/drivers/char/drm-4.0/radeon_drm.h
--- linux.orig/drivers/char/drm-4.0/radeon_drm.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_drm.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,325 @@
+/* radeon_drm.h -- Public header for the radeon driver -*- linux-c -*-
+ *
+ * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __RADEON_DRM_H__
+#define __RADEON_DRM_H__
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the X server file (radeon_sarea.h)
+ */
+#ifndef __RADEON_SAREA_DEFINES__
+#define __RADEON_SAREA_DEFINES__
+
+/* What needs to be changed for the current vertex buffer?
+ */
+#define RADEON_UPLOAD_CONTEXT		0x00000001
+#define RADEON_UPLOAD_VERTFMT		0x00000002
+#define RADEON_UPLOAD_LINE		0x00000004
+#define RADEON_UPLOAD_BUMPMAP		0x00000008
+#define RADEON_UPLOAD_MASKS		0x00000010
+#define RADEON_UPLOAD_VIEWPORT		0x00000020
+#define RADEON_UPLOAD_SETUP		0x00000040
+#define RADEON_UPLOAD_TCL		0x00000080
+#define RADEON_UPLOAD_MISC		0x00000100
+#define RADEON_UPLOAD_TEX0		0x00000200
+#define RADEON_UPLOAD_TEX1		0x00000400
+#define RADEON_UPLOAD_TEX2		0x00000800
+#define RADEON_UPLOAD_TEX0IMAGES	0x00001000
+#define RADEON_UPLOAD_TEX1IMAGES	0x00002000
+#define RADEON_UPLOAD_TEX2IMAGES	0x00004000
+#define RADEON_UPLOAD_CLIPRECTS		0x00008000 /* handled client-side */
+#define RADEON_REQUIRE_QUIESCENCE	0x00010000
+#define RADEON_UPLOAD_ALL		0x0001ffff
+
+#define RADEON_FRONT			0x1
+#define RADEON_BACK			0x2
+#define RADEON_DEPTH			0x4
+
+/* Primitive types
+ */
+#define RADEON_POINTS			0x1
+#define RADEON_LINES			0x2
+#define RADEON_LINE_STRIP		0x3
+#define RADEON_TRIANGLES		0x4
+#define RADEON_TRIANGLE_FAN		0x5
+#define RADEON_TRIANGLE_STRIP		0x6
+
+/* Vertex/indirect buffer size
+ */
+#define RADEON_BUFFER_SIZE		16384
+
+/* Byte offsets for indirect buffer data
+ */
+#define RADEON_INDEX_PRIM_OFFSET	20
+#define RADEON_HOSTDATA_BLIT_OFFSET	32
+
+#define RADEON_SCRATCH_REG_OFFSET	32
+
+/* Keep these small for testing
+ */
+#define RADEON_NR_SAREA_CLIPRECTS	12
+
+/* There are 2 heaps (local/AGP).  Each region within a heap is a
+ * minimum of 64k, and there are at most 64 of them per heap.
+ */
+#define RADEON_LOCAL_TEX_HEAP		0
+#define RADEON_AGP_TEX_HEAP		1
+#define RADEON_NR_TEX_HEAPS		2
+#define RADEON_NR_TEX_REGIONS		64
+#define RADEON_LOG_TEX_GRANULARITY	16
+
+#define RADEON_MAX_TEXTURE_LEVELS	11
+#define RADEON_MAX_TEXTURE_UNITS	3
+
+#endif /* __RADEON_SAREA_DEFINES__ */
+
+typedef struct {
+	unsigned int red;
+	unsigned int green;
+	unsigned int blue;
+	unsigned int alpha;
+} radeon_color_regs_t;
+
+typedef struct {
+	/* Context state */
+	unsigned int pp_misc;				/* 0x1c14 */
+	unsigned int pp_fog_color;
+	unsigned int re_solid_color;
+	unsigned int rb3d_blendcntl;
+	unsigned int rb3d_depthoffset;
+	unsigned int rb3d_depthpitch;
+	unsigned int rb3d_zstencilcntl;
+
+	unsigned int pp_cntl;				/* 0x1c38 */
+	unsigned int rb3d_cntl;
+	unsigned int rb3d_coloroffset;
+	unsigned int re_width_height;
+	unsigned int rb3d_colorpitch;
+	unsigned int se_cntl;
+
+	/* Vertex format state */
+	unsigned int se_coord_fmt;			/* 0x1c50 */
+
+	/* Line state */
+	unsigned int re_line_pattern;			/* 0x1cd0 */
+	unsigned int re_line_state;
+
+	unsigned int se_line_width;			/* 0x1db8 */
+
+	/* Bumpmap state */
+	unsigned int pp_lum_matrix;			/* 0x1d00 */
+
+	unsigned int pp_rot_matrix_0;			/* 0x1d58 */
+	unsigned int pp_rot_matrix_1;
+
+	/* Mask state */
+	unsigned int rb3d_stencilrefmask;		/* 0x1d7c */
+	unsigned int rb3d_ropcntl;
+	unsigned int rb3d_planemask;
+
+	/* Viewport state */
+	unsigned int se_vport_xscale;			/* 0x1d98 */
+	unsigned int se_vport_xoffset;
+	unsigned int se_vport_yscale;
+	unsigned int se_vport_yoffset;
+	unsigned int se_vport_zscale;
+	unsigned int se_vport_zoffset;
+
+	/* Setup state */
+	unsigned int se_cntl_status;			/* 0x2140 */
+
+#ifdef TCL_ENABLE
+	/* TCL state */
+	radeon_color_regs_t se_tcl_material_emmissive;	/* 0x2210 */
+	radeon_color_regs_t se_tcl_material_ambient;
+	radeon_color_regs_t se_tcl_material_diffuse;
+	radeon_color_regs_t se_tcl_material_specular;
+	unsigned int se_tcl_shininess;
+	unsigned int se_tcl_output_vtx_fmt;
+	unsigned int se_tcl_output_vtx_sel;
+	unsigned int se_tcl_matrix_select_0;
+	unsigned int se_tcl_matrix_select_1;
+	unsigned int se_tcl_ucp_vert_blend_ctl;
+	unsigned int se_tcl_texture_proc_ctl;
+	unsigned int se_tcl_light_model_ctl;
+	unsigned int se_tcl_per_light_ctl[4];
+#endif
+
+	/* Misc state */
+	unsigned int re_top_left;			/* 0x26c0 */
+	unsigned int re_misc;
+} drm_radeon_context_regs_t;
+
+/* Setup registers for each texture unit
+ */
+typedef struct {
+	unsigned int pp_txfilter;
+	unsigned int pp_txformat;
+	unsigned int pp_txoffset;
+	unsigned int pp_txcblend;
+	unsigned int pp_txablend;
+	unsigned int pp_tfactor;
+
+	unsigned int pp_border_color;
+
+#ifdef CUBIC_ENABLE
+	unsigned int pp_cubic_faces;
+	unsigned int pp_cubic_offset[5];
+#endif
+} drm_radeon_texture_regs_t;
+
+typedef struct {
+	unsigned char next, prev;
+	unsigned char in_use;
+	int age;
+} drm_radeon_tex_region_t;
+
+typedef struct {
+	/* The channel for communication of state information to the kernel
+	 * on firing a vertex buffer.
+	 */
+	drm_radeon_context_regs_t context_state;
+	drm_radeon_texture_regs_t tex_state[RADEON_MAX_TEXTURE_UNITS];
+	unsigned int dirty;
+	unsigned int vertsize;
+	unsigned int vc_format;
+
+	/* The current cliprects, or a subset thereof.
+	 */
+	drm_clip_rect_t boxes[RADEON_NR_SAREA_CLIPRECTS];
+	unsigned int nbox;
+
+	/* Counters for client-side throttling of rendering clients.
+	 */
+	unsigned int last_frame;
+	unsigned int last_dispatch;
+	unsigned int last_clear;
+
+	drm_radeon_tex_region_t tex_list[RADEON_NR_TEX_HEAPS][RADEON_NR_TEX_REGIONS+1];
+	int tex_age[RADEON_NR_TEX_HEAPS];
+	int ctx_owner;
+} drm_radeon_sarea_t;
+
+
+/* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (xf86drmRadeon.h)
+ */
+typedef struct drm_radeon_init {
+	enum {
+		RADEON_INIT_CP    = 0x01,
+		RADEON_CLEANUP_CP = 0x02
+	} func;
+	int sarea_priv_offset;
+	int is_pci;
+	int cp_mode;
+	int agp_size;
+	int ring_size;
+	int usec_timeout;
+
+	unsigned int fb_bpp;
+	unsigned int front_offset, front_pitch;
+	unsigned int back_offset, back_pitch;
+	unsigned int depth_bpp;
+	unsigned int depth_offset, depth_pitch;
+
+	unsigned int fb_offset;
+	unsigned int mmio_offset;
+	unsigned int ring_offset;
+	unsigned int ring_rptr_offset;
+	unsigned int buffers_offset;
+	unsigned int agp_textures_offset;
+} drm_radeon_init_t;
+
+typedef struct drm_radeon_cp_stop {
+	int flush;
+	int idle;
+} drm_radeon_cp_stop_t;
+
+typedef struct drm_radeon_fullscreen {
+	enum {
+		RADEON_INIT_FULLSCREEN    = 0x01,
+		RADEON_CLEANUP_FULLSCREEN = 0x02
+	} func;
+} drm_radeon_fullscreen_t;
+
+#define CLEAR_X1	0
+#define CLEAR_Y1	1
+#define CLEAR_X2	2
+#define CLEAR_Y2	3
+#define CLEAR_DEPTH	4
+
+typedef struct drm_radeon_clear {
+	unsigned int flags;
+	int x, y, w, h;
+	unsigned int clear_color;
+	unsigned int clear_depth;
+	union {
+		float f[5];
+		unsigned int ui[5];
+	} rect;
+} drm_radeon_clear_t;
+
+typedef struct drm_radeon_vertex {
+	int prim;
+	int idx;			/* Index of vertex buffer */
+	int count;			/* Number of vertices in buffer */
+	int discard;			/* Client finished with buffer? */
+} drm_radeon_vertex_t;
+
+typedef struct drm_radeon_indices {
+	int prim;
+	int idx;
+	int start;
+	int end;
+	int discard;			/* Client finished with buffer? */
+} drm_radeon_indices_t;
+
+typedef struct drm_radeon_blit {
+	int idx;
+	int pitch;
+	int offset;
+	int format;
+	unsigned short x, y;
+	unsigned short width, height;
+} drm_radeon_blit_t;
+
+typedef struct drm_radeon_stipple {
+	unsigned int *mask;
+} drm_radeon_stipple_t;
+
+typedef struct drm_radeon_indirect {
+	int idx;
+	int start;
+	int end;
+	int discard;
+} drm_radeon_indirect_t;
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_drv.c linux/drivers/char/drm-4.0/radeon_drv.c
--- linux.orig/drivers/char/drm-4.0/radeon_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,703 @@
+/* radeon_drv.c -- ATI Radeon driver -*- linux-c -*-
+ *
+ * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Kevin E. Martin <martin@valinux.com>
+ *          Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "radeon_drv.h"
+
+#define RADEON_NAME		"radeon"
+#define RADEON_DESC		"ATI Radeon"
+#define RADEON_DATE		"20010105"
+#define RADEON_MAJOR		1
+#define RADEON_MINOR		0
+#define RADEON_PATCHLEVEL	0
+
+static drm_device_t	      radeon_device;
+drm_ctx_t	              radeon_res_ctx;
+
+static struct file_operations radeon_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 radeon_open,
+	flush:	 drm_flush,
+	release: radeon_release,
+	ioctl:	 radeon_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      radeon_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  RADEON_NAME,
+	fops:  &radeon_fops,
+};
+
+static drm_ioctl_desc_t	      radeon_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]       = { radeon_version,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)]    = { drm_getunique,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]     = { drm_getmagic,	0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]     = { drm_irq_busid,	0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)]    = { drm_setunique,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	        = { drm_block,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]       = { drm_unblock,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)]    = { drm_authmagic,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]       = { drm_addmap,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)]      = { radeon_addbufs,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)]     = { drm_markbufs,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)]     = { drm_infobufs,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)]      = { radeon_mapbufs,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)]     = { drm_freebufs,	1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]       = { radeon_addctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]        = { radeon_rmctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]       = { radeon_modctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]       = { radeon_getctx,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)]    = { radeon_switchctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]       = { radeon_newctx,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]       = { radeon_resctx,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]      = { drm_adddraw,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]       = { drm_rmdraw,		1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_DMA)]	        = { radeon_cp_buffers,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	        = { radeon_lock,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]        = { radeon_unlock,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]        = { drm_finish,		1, 0 },
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = { drm_agp_acquire,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = { drm_agp_release,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = { drm_agp_enable,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = { drm_agp_info,	1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = { drm_agp_alloc,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = { drm_agp_free,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = { drm_agp_bind,	1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = { drm_agp_unbind,	1, 1 },
+#endif
+
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_INIT)]  = { radeon_cp_init,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_START)] = { radeon_cp_start,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_STOP)]  = { radeon_cp_stop,   1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_RESET)] = { radeon_cp_reset,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CP_IDLE)]  = { radeon_cp_idle,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_RESET)] = { radeon_engine_reset, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_FULLSCREEN)] = { radeon_fullscreen, 1, 0 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_SWAP)]    = { radeon_cp_swap,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_CLEAR)]   = { radeon_cp_clear,   1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_VERTEX)]  = { radeon_cp_vertex,  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDICES)] = { radeon_cp_indices, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_BLIT)]    = { radeon_cp_blit,    1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_STIPPLE)] = { radeon_cp_stipple, 1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RADEON_INDIRECT)]= { radeon_cp_indirect,1, 1 },
+};
+#define RADEON_IOCTL_COUNT DRM_ARRAY_SIZE(radeon_ioctls)
+
+#ifdef MODULE
+static char		      *radeon = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_DESCRIPTION("radeon");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_PARM(radeon, "s");
+
+#ifndef MODULE
+/* radeon_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init radeon_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("radeon=", radeon_options);
+#endif
+
+static int radeon_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	drm_dma_setup(dev);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	    = dev->buf;
+	dev->buf_wp	    = dev->buf;
+	dev->buf_end	    = dev->buf + DRM_BSZ;
+	dev->buf_async	    = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	radeon_res_ctx.handle = -1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int radeon_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *entry;
+		drm_agp_mem_t *nexte;
+
+				/* Remove AGP resources, but leave dev->agp
+                                   intact until radeon_cleanup is called. */
+		for (entry = dev->agp->memory; entry; entry = nexte) {
+			nexte = entry->next;
+			if (entry->bound) drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS);
+		}
+		dev->agp->memory = NULL;
+
+		if (dev->agp->acquired)	_drm_agp_release();
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled  = 0;
+	}
+#endif
+
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	drm_dma_takedown(dev);
+
+	dev->queue_count     = 0;
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* radeon_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init radeon_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &radeon_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(radeon);
+#endif
+
+	if ((retcode = misc_register(&radeon_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", RADEON_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, radeon_misc.minor);
+	dev->name   = RADEON_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+      	if (dev->agp == NULL) {
+	   	DRM_ERROR("Cannot initialize agpgart module.\n");
+	   	drm_proc_cleanup();
+	   	misc_deregister(&radeon_misc);
+	   	radeon_takedown(dev);
+	   	return -ENOMEM;
+	}
+
+#ifdef CONFIG_MTRR
+	dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base,
+				      dev->agp->agp_info.aper_size*1024*1024,
+				      MTRR_TYPE_WRCOMB,
+				      1);
+#endif
+#endif
+
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&radeon_misc);
+		radeon_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 RADEON_NAME,
+		 RADEON_MAJOR,
+		 RADEON_MINOR,
+		 RADEON_PATCHLEVEL,
+		 RADEON_DATE,
+		 radeon_misc.minor);
+
+	return 0;
+}
+
+/* radeon_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit radeon_cleanup(void)
+{
+	drm_device_t	      *dev = &radeon_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&radeon_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	radeon_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(radeon_init);
+module_exit(radeon_cleanup);
+
+
+int radeon_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = RADEON_MAJOR;
+	version.version_minor	   = RADEON_MINOR;
+	version.version_patchlevel = RADEON_PATCHLEVEL;
+
+	DRM_COPY(version.name, RADEON_NAME);
+	DRM_COPY(version.date, RADEON_DATE);
+	DRM_COPY(version.desc, RADEON_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int radeon_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &radeon_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return radeon_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	return retcode;
+}
+
+int radeon_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+
+	/* Force the cleanup of page flipping when required */
+	if ( dev->dev_private ) {
+		drm_radeon_private_t *dev_priv = dev->dev_private;
+		if ( dev_priv->page_flipping ) {
+			radeon_do_cleanup_pageflip( dev );
+		}
+	}
+
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return radeon_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* radeon_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int radeon_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= RADEON_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &radeon_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int radeon_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+        if (lock.context < 0 /* || lock.context >= dev->queue_count */)
+                return -EINVAL;
+
+        if (!ret) {
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+			DRM_DEBUG("not quiescent!\n");
+#if 0
+                        radeon_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != radeon_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int radeon_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != radeon_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_drv.h linux/drivers/char/drm-4.0/radeon_drv.h
--- linux.orig/drivers/char/drm-4.0/radeon_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,709 @@
+/* radeon_drv.h -- Private header for radeon driver -*- linux-c -*-
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *   Rickard E. (Rik) Faith <faith@valinux.com>
+ *   Kevin E. Martin <martin@valinux.com>
+ *   Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#ifndef __RADEON_DRV_H__
+#define __RADEON_DRV_H__
+
+typedef struct drm_radeon_freelist {
+   	unsigned int age;
+   	drm_buf_t *buf;
+   	struct drm_radeon_freelist *next;
+   	struct drm_radeon_freelist *prev;
+} drm_radeon_freelist_t;
+
+typedef struct drm_radeon_ring_buffer {
+	u32 *start;
+	u32 *end;
+	int size;
+	int size_l2qw;
+
+	volatile u32 *head;
+	u32 tail;
+	u32 tail_mask;
+	int space;
+} drm_radeon_ring_buffer_t;
+
+typedef struct drm_radeon_depth_clear_t {
+	u32 rb3d_cntl;
+	u32 rb3d_zstencilcntl;
+	u32 se_cntl;
+} drm_radeon_depth_clear_t;
+
+typedef struct drm_radeon_private {
+	drm_radeon_ring_buffer_t ring;
+	drm_radeon_sarea_t *sarea_priv;
+
+	int agp_size;
+	u32 agp_vm_start;
+	u32 agp_buffers_offset;
+
+	int cp_mode;
+	int cp_running;
+
+   	drm_radeon_freelist_t *head;
+   	drm_radeon_freelist_t *tail;
+/* FIXME: ROTATE_BUFS is a hask to cycle through bufs until freelist
+   code is used.  Note this hides a problem with the scratch register
+   (used to keep track of last buffer completed) being written to before
+   the last buffer has actually completed rendering. */
+#define ROTATE_BUFS 1
+#if ROTATE_BUFS
+	int last_buf;
+#endif
+	volatile u32 *scratch;
+
+	int usec_timeout;
+	int is_pci;
+
+	atomic_t idle_count;
+
+	int page_flipping;
+	int current_page;
+	u32 crtc_offset;
+	u32 crtc_offset_cntl;
+
+	unsigned int color_fmt;
+	unsigned int front_offset;
+	unsigned int front_pitch;
+	unsigned int back_offset;
+	unsigned int back_pitch;
+
+	unsigned int depth_fmt;
+	unsigned int depth_offset;
+	unsigned int depth_pitch;
+
+	u32 front_pitch_offset;
+	u32 back_pitch_offset;
+	u32 depth_pitch_offset;
+
+	drm_radeon_depth_clear_t depth_clear;
+
+	drm_map_t *sarea;
+	drm_map_t *fb;
+	drm_map_t *mmio;
+	drm_map_t *cp_ring;
+	drm_map_t *ring_rptr;
+	drm_map_t *buffers;
+	drm_map_t *agp_textures;
+} drm_radeon_private_t;
+
+typedef struct drm_radeon_buf_priv {
+	u32 age;
+	int prim;
+	int discard;
+	int dispatched;
+   	drm_radeon_freelist_t *list_entry;
+} drm_radeon_buf_priv_t;
+
+				/* radeon_drv.c */
+extern int  radeon_version( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int  radeon_open( struct inode *inode, struct file *filp );
+extern int  radeon_release( struct inode *inode, struct file *filp );
+extern int  radeon_ioctl( struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg );
+extern int  radeon_lock( struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg );
+extern int  radeon_unlock( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+
+				/* radeon_cp.c */
+extern int radeon_cp_init( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_start( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_stop( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_reset( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_idle( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_engine_reset( struct inode *inode, struct file *filp,
+				unsigned int cmd, unsigned long arg );
+extern int radeon_fullscreen( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_buffers( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+
+extern void radeon_freelist_reset( drm_device_t *dev );
+extern drm_buf_t *radeon_freelist_get( drm_device_t *dev );
+
+extern int radeon_wait_ring( drm_radeon_private_t *dev_priv, int n );
+extern void radeon_update_ring_snapshot( drm_radeon_private_t *dev_priv );
+
+extern int radeon_do_cp_idle( drm_radeon_private_t *dev_priv );
+extern int radeon_do_cleanup_pageflip( drm_device_t *dev );
+
+				/* radeon_state.c */
+extern int radeon_cp_clear( struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg );
+extern int radeon_cp_swap( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_vertex( struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg );
+extern int radeon_cp_indices( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_blit( struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg );
+extern int radeon_cp_stipple( struct inode *inode, struct file *filp,
+			      unsigned int cmd, unsigned long arg );
+extern int radeon_cp_indirect( struct inode *inode, struct file *filp,
+			       unsigned int cmd, unsigned long arg );
+
+				/* radeon_bufs.c */
+extern int radeon_addbufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int radeon_mapbufs(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+
+				/* radeon_context.c */
+extern int  radeon_resctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_addctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_modctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_getctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_switchctx(struct inode *inode, struct file *filp,
+			     unsigned int cmd, unsigned long arg);
+extern int  radeon_newctx(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  radeon_rmctx(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+extern int  radeon_context_switch(drm_device_t *dev, int old, int new);
+extern int  radeon_context_switch_complete(drm_device_t *dev, int new);
+
+
+/* Register definitions, register access macros and drmAddMap constants
+ * for Radeon kernel driver.
+ */
+
+#define RADEON_AUX_SCISSOR_CNTL		0x26f0
+#	define RADEON_EXCLUSIVE_SCISSOR_0	(1 << 24)
+#	define RADEON_EXCLUSIVE_SCISSOR_1	(1 << 25)
+#	define RADEON_EXCLUSIVE_SCISSOR_2	(1 << 26)
+#	define RADEON_SCISSOR_0_ENABLE		(1 << 28)
+#	define RADEON_SCISSOR_1_ENABLE		(1 << 29)
+#	define RADEON_SCISSOR_2_ENABLE		(1 << 30)
+
+#define RADEON_BUS_CNTL			0x0030
+#	define RADEON_BUS_MASTER_DIS		(1 << 6)
+
+#define RADEON_CLOCK_CNTL_DATA		0x000c
+#	define RADEON_PLL_WR_EN			(1 << 7)
+#define RADEON_CLOCK_CNTL_INDEX		0x0008
+#define RADEON_CONFIG_APER_SIZE		0x0108
+#define RADEON_CRTC_OFFSET		0x0224
+#define RADEON_CRTC_OFFSET_CNTL		0x0228
+#	define RADEON_CRTC_TILE_EN		(1 << 15)
+#	define RADEON_CRTC_OFFSET_FLIP_CNTL	(1 << 16)
+
+#define RADEON_RB3D_COLORPITCH		0x1c48
+#define RADEON_RB3D_DEPTHCLEARVALUE	0x1c30
+#define RADEON_RB3D_DEPTHXY_OFFSET	0x1c60
+
+#define RADEON_DP_GUI_MASTER_CNTL	0x146c
+#	define RADEON_GMC_SRC_PITCH_OFFSET_CNTL	(1 << 0)
+#	define RADEON_GMC_DST_PITCH_OFFSET_CNTL	(1 << 1)
+#	define RADEON_GMC_BRUSH_SOLID_COLOR	(13 << 4)
+#	define RADEON_GMC_BRUSH_NONE		(15 << 4)
+#	define RADEON_GMC_DST_16BPP		(4 << 8)
+#	define RADEON_GMC_DST_24BPP		(5 << 8)
+#	define RADEON_GMC_DST_32BPP		(6 << 8)
+#	define RADEON_GMC_DST_DATATYPE_SHIFT	8
+#	define RADEON_GMC_SRC_DATATYPE_COLOR	(3 << 12)
+#	define RADEON_DP_SRC_SOURCE_MEMORY	(2 << 24)
+#	define RADEON_DP_SRC_SOURCE_HOST_DATA	(3 << 24)
+#	define RADEON_GMC_CLR_CMP_CNTL_DIS	(1 << 28)
+#	define RADEON_GMC_WR_MSK_DIS		(1 << 30)
+#	define RADEON_ROP3_S			0x00cc0000
+#	define RADEON_ROP3_P			0x00f00000
+#define RADEON_DP_WRITE_MASK		0x16cc
+#define RADEON_DST_PITCH_OFFSET		0x142c
+#define RADEON_DST_PITCH_OFFSET_C	0x1c80
+#	define RADEON_DST_TILE_LINEAR		(0 << 30)
+#	define RADEON_DST_TILE_MACRO		(1 << 30)
+#	define RADEON_DST_TILE_MICRO		(2 << 30)
+#	define RADEON_DST_TILE_BOTH		(3 << 30)
+
+#define RADEON_SCRATCH_REG0		0x15e0
+#define RADEON_SCRATCH_REG1		0x15e4
+#define RADEON_SCRATCH_REG2		0x15e8
+#define RADEON_SCRATCH_REG3		0x15ec
+#define RADEON_SCRATCH_REG4		0x15f0
+#define RADEON_SCRATCH_REG5		0x15f4
+#define RADEON_SCRATCH_UMSK		0x0770
+#define RADEON_SCRATCH_ADDR		0x0774
+
+#define RADEON_HOST_PATH_CNTL		0x0130
+#	define RADEON_HDP_SOFT_RESET		(1 << 26)
+#	define RADEON_HDP_WC_TIMEOUT_MASK	(7 << 28)
+#	define RADEON_HDP_WC_TIMEOUT_28BCLK	(7 << 28)
+
+#define RADEON_ISYNC_CNTL		0x1724
+#	define RADEON_ISYNC_ANY2D_IDLE3D	(1 << 0)
+#	define RADEON_ISYNC_ANY3D_IDLE2D	(1 << 1)
+#	define RADEON_ISYNC_TRIG2D_IDLE3D	(1 << 2)
+#	define RADEON_ISYNC_TRIG3D_IDLE2D	(1 << 3)
+#	define RADEON_ISYNC_WAIT_IDLEGUI	(1 << 4)
+#	define RADEON_ISYNC_CPSCRATCH_IDLEGUI	(1 << 5)
+
+#define RADEON_MC_AGP_LOCATION		0x014c
+#define RADEON_MC_FB_LOCATION		0x0148
+#define RADEON_MCLK_CNTL		0x0012
+
+#define RADEON_PP_BORDER_COLOR_0	0x1d40
+#define RADEON_PP_BORDER_COLOR_1	0x1d44
+#define RADEON_PP_BORDER_COLOR_2	0x1d48
+#define RADEON_PP_CNTL			0x1c38
+#	define RADEON_SCISSOR_ENABLE		(1 <<  1)
+#define RADEON_PP_LUM_MATRIX		0x1d00
+#define RADEON_PP_MISC			0x1c14
+#define RADEON_PP_ROT_MATRIX_0		0x1d58
+#define RADEON_PP_TXFILTER_0		0x1c54
+#define RADEON_PP_TXFILTER_1		0x1c6c
+#define RADEON_PP_TXFILTER_2		0x1c84
+
+#define RADEON_RB2D_DSTCACHE_CTLSTAT	0x342c
+#	define RADEON_RB2D_DC_FLUSH		(3 << 0)
+#	define RADEON_RB2D_DC_FREE		(3 << 2)
+#	define RADEON_RB2D_DC_FLUSH_ALL		0xf
+#	define RADEON_RB2D_DC_BUSY		(1 << 31)
+#define RADEON_RB3D_CNTL		0x1c3c
+#	define RADEON_ALPHA_BLEND_ENABLE	(1 << 0)
+#	define RADEON_PLANE_MASK_ENABLE		(1 << 1)
+#	define RADEON_DITHER_ENABLE		(1 << 2)
+#	define RADEON_ROUND_ENABLE		(1 << 3)
+#	define RADEON_SCALE_DITHER_ENABLE	(1 << 4)
+#	define RADEON_DITHER_INIT		(1 << 5)
+#	define RADEON_ROP_ENABLE		(1 << 6)
+#	define RADEON_STENCIL_ENABLE		(1 << 7)
+#	define RADEON_Z_ENABLE			(1 << 8)
+#	define RADEON_DEPTH_XZ_OFFEST_ENABLE	(1 << 9)
+#	define RADEON_ZBLOCK8			(0 << 15)
+#	define RADEON_ZBLOCK16			(1 << 15)
+#define RADEON_RB3D_DEPTHOFFSET		0x1c24
+#define RADEON_RB3D_PLANEMASK		0x1d84
+#define RADEON_RB3D_STENCILREFMASK	0x1d7c
+#define RADEON_RB3D_ZCACHE_MODE		0x3250
+#define RADEON_RB3D_ZCACHE_CTLSTAT	0x3254
+#	define RADEON_RB3D_ZC_FLUSH		(1 << 0)
+#	define RADEON_RB3D_ZC_FREE		(1 << 2)
+#	define RADEON_RB3D_ZC_FLUSH_ALL		0x5
+#	define RADEON_RB3D_ZC_BUSY		(1 << 31)
+#define RADEON_RB3D_ZSTENCILCNTL	0x1c2c
+#	define RADEON_Z_TEST_MASK		(7 << 4)
+#	define RADEON_Z_TEST_ALWAYS		(7 << 4)
+#	define RADEON_STENCIL_TEST_ALWAYS	(7 << 12)
+#	define RADEON_STENCIL_S_FAIL_KEEP	(0 << 16)
+#	define RADEON_STENCIL_ZPASS_KEEP	(0 << 20)
+#	define RADEON_STENCIL_ZFAIL_KEEP	(0 << 20)
+#	define RADEON_Z_WRITE_ENABLE		(1 << 30)
+#define RADEON_RBBM_SOFT_RESET		0x00f0
+#	define RADEON_SOFT_RESET_CP		(1 <<  0)
+#	define RADEON_SOFT_RESET_HI		(1 <<  1)
+#	define RADEON_SOFT_RESET_SE		(1 <<  2)
+#	define RADEON_SOFT_RESET_RE		(1 <<  3)
+#	define RADEON_SOFT_RESET_PP		(1 <<  4)
+#	define RADEON_SOFT_RESET_E2		(1 <<  5)
+#	define RADEON_SOFT_RESET_RB		(1 <<  6)
+#	define RADEON_SOFT_RESET_HDP		(1 <<  7)
+#define RADEON_RBBM_STATUS		0x0e40
+#	define RADEON_RBBM_FIFOCNT_MASK		0x007f
+#	define RADEON_RBBM_ACTIVE		(1 << 31)
+#define RADEON_RE_LINE_PATTERN		0x1cd0
+#define RADEON_RE_MISC			0x26c4
+#define RADEON_RE_TOP_LEFT		0x26c0
+#define RADEON_RE_WIDTH_HEIGHT		0x1c44
+#define RADEON_RE_STIPPLE_ADDR		0x1cc8
+#define RADEON_RE_STIPPLE_DATA		0x1ccc
+
+#define RADEON_SCISSOR_TL_0		0x1cd8
+#define RADEON_SCISSOR_BR_0		0x1cdc
+#define RADEON_SCISSOR_TL_1		0x1ce0
+#define RADEON_SCISSOR_BR_1		0x1ce4
+#define RADEON_SCISSOR_TL_2		0x1ce8
+#define RADEON_SCISSOR_BR_2		0x1cec
+#define RADEON_SE_COORD_FMT		0x1c50
+#define RADEON_SE_CNTL			0x1c4c
+#	define RADEON_FFACE_CULL_CW		(0 << 0)
+#	define RADEON_BFACE_SOLID		(3 << 1)
+#	define RADEON_FFACE_SOLID		(3 << 3)
+#	define RADEON_FLAT_SHADE_VTX_LAST	(3 << 6)
+#	define RADEON_DIFFUSE_SHADE_FLAT	(1 << 8)
+#	define RADEON_DIFFUSE_SHADE_GOURAUD	(2 << 8)
+#	define RADEON_ALPHA_SHADE_FLAT		(1 << 10)
+#	define RADEON_ALPHA_SHADE_GOURAUD	(2 << 10)
+#	define RADEON_SPECULAR_SHADE_FLAT	(1 << 12)
+#	define RADEON_SPECULAR_SHADE_GOURAUD	(2 << 12)
+#	define RADEON_FOG_SHADE_FLAT		(1 << 14)
+#	define RADEON_FOG_SHADE_GOURAUD		(2 << 14)
+#	define RADEON_VPORT_XY_XFORM_ENABLE	(1 << 24)
+#	define RADEON_VPORT_Z_XFORM_ENABLE	(1 << 25)
+#	define RADEON_VTX_PIX_CENTER_OGL	(1 << 27)
+#	define RADEON_ROUND_MODE_TRUNC		(0 << 28)
+#	define RADEON_ROUND_PREC_8TH_PIX	(1 << 30)
+#define RADEON_SE_CNTL_STATUS		0x2140
+#define RADEON_SE_LINE_WIDTH		0x1db8
+#define RADEON_SE_VPORT_XSCALE		0x1d98
+#define RADEON_SURFACE_ACCESS_FLAGS	0x0bf8
+#define RADEON_SURFACE_ACCESS_CLR	0x0bfc
+#define RADEON_SURFACE_CNTL		0x0b00
+#	define RADEON_SURF_TRANSLATION_DIS	(1 << 8)
+#	define RADEON_NONSURF_AP0_SWP_MASK	(3 << 20)
+#	define RADEON_NONSURF_AP0_SWP_LITTLE	(0 << 20)
+#	define RADEON_NONSURF_AP0_SWP_BIG16	(1 << 20)
+#	define RADEON_NONSURF_AP0_SWP_BIG32	(2 << 20)
+#	define RADEON_NONSURF_AP1_SWP_MASK	(3 << 22)
+#	define RADEON_NONSURF_AP1_SWP_LITTLE	(0 << 22)
+#	define RADEON_NONSURF_AP1_SWP_BIG16	(1 << 22)
+#	define RADEON_NONSURF_AP1_SWP_BIG32	(2 << 22)
+#define RADEON_SURFACE0_INFO		0x0b0c
+#	define RADEON_SURF_PITCHSEL_MASK	(0x1ff << 0)
+#	define RADEON_SURF_TILE_MODE_MASK	(3 << 16)
+#	define RADEON_SURF_TILE_MODE_MACRO	(0 << 16)
+#	define RADEON_SURF_TILE_MODE_MICRO	(1 << 16)
+#	define RADEON_SURF_TILE_MODE_32BIT_Z	(2 << 16)
+#	define RADEON_SURF_TILE_MODE_16BIT_Z	(3 << 16)
+#define RADEON_SURFACE0_LOWER_BOUND	0x0b04
+#define RADEON_SURFACE0_UPPER_BOUND	0x0b08
+#define RADEON_SURFACE1_INFO		0x0b1c
+#define RADEON_SURFACE1_LOWER_BOUND	0x0b14
+#define RADEON_SURFACE1_UPPER_BOUND	0x0b18
+#define RADEON_SURFACE2_INFO		0x0b2c
+#define RADEON_SURFACE2_LOWER_BOUND	0x0b24
+#define RADEON_SURFACE2_UPPER_BOUND	0x0b28
+#define RADEON_SURFACE3_INFO		0x0b3c
+#define RADEON_SURFACE3_LOWER_BOUND	0x0b34
+#define RADEON_SURFACE3_UPPER_BOUND	0x0b38
+#define RADEON_SURFACE4_INFO		0x0b4c
+#define RADEON_SURFACE4_LOWER_BOUND	0x0b44
+#define RADEON_SURFACE4_UPPER_BOUND	0x0b48
+#define RADEON_SURFACE5_INFO		0x0b5c
+#define RADEON_SURFACE5_LOWER_BOUND	0x0b54
+#define RADEON_SURFACE5_UPPER_BOUND	0x0b58
+#define RADEON_SURFACE6_INFO		0x0b6c
+#define RADEON_SURFACE6_LOWER_BOUND	0x0b64
+#define RADEON_SURFACE6_UPPER_BOUND	0x0b68
+#define RADEON_SURFACE7_INFO		0x0b7c
+#define RADEON_SURFACE7_LOWER_BOUND	0x0b74
+#define RADEON_SURFACE7_UPPER_BOUND	0x0b78
+#define RADEON_SW_SEMAPHORE		0x013c
+
+#define RADEON_WAIT_UNTIL		0x1720
+#	define RADEON_WAIT_CRTC_PFLIP		(1 << 0)
+#	define RADEON_WAIT_2D_IDLECLEAN		(1 << 16)
+#	define RADEON_WAIT_3D_IDLECLEAN		(1 << 17)
+#	define RADEON_WAIT_HOST_IDLECLEAN	(1 << 18)
+
+#define RADEON_RB3D_ZMASKOFFSET		0x1c34
+#define RADEON_RB3D_ZSTENCILCNTL	0x1c2c
+#	define RADEON_DEPTH_FORMAT_16BIT_INT_Z	(0 << 0)
+#	define RADEON_DEPTH_FORMAT_24BIT_INT_Z	(2 << 0)
+
+
+/* CP registers */
+#define RADEON_CP_ME_RAM_ADDR		0x07d4
+#define RADEON_CP_ME_RAM_RADDR		0x07d8
+#define RADEON_CP_ME_RAM_DATAH		0x07dc
+#define RADEON_CP_ME_RAM_DATAL		0x07e0
+
+#define RADEON_CP_RB_BASE		0x0700
+#define RADEON_CP_RB_CNTL		0x0704
+#define RADEON_CP_RB_RPTR_ADDR		0x070c
+#define RADEON_CP_RB_RPTR		0x0710
+#define RADEON_CP_RB_WPTR		0x0714
+
+#define RADEON_CP_RB_WPTR_DELAY		0x0718
+#	define RADEON_PRE_WRITE_TIMER_SHIFT	0
+#	define RADEON_PRE_WRITE_LIMIT_SHIFT	23
+
+#define RADEON_CP_IB_BASE		0x0738
+
+#define RADEON_CP_CSQ_CNTL		0x0740
+#	define RADEON_CSQ_CNT_PRIMARY_MASK	(0xff << 0)
+#	define RADEON_CSQ_PRIDIS_INDDIS		(0 << 28)
+#	define RADEON_CSQ_PRIPIO_INDDIS		(1 << 28)
+#	define RADEON_CSQ_PRIBM_INDDIS		(2 << 28)
+#	define RADEON_CSQ_PRIPIO_INDBM		(3 << 28)
+#	define RADEON_CSQ_PRIBM_INDBM		(4 << 28)
+#	define RADEON_CSQ_PRIPIO_INDPIO		(15 << 28)
+
+#define RADEON_AIC_CNTL			0x01d0
+#	define RADEON_PCIGART_TRANSLATE_EN	(1 << 0)
+
+/* CP command packets */
+#define RADEON_CP_PACKET0		0x00000000
+#	define RADEON_ONE_REG_WR		(1 << 15)
+#define RADEON_CP_PACKET1		0x40000000
+#define RADEON_CP_PACKET2		0x80000000
+#define RADEON_CP_PACKET3		0xC0000000
+#	define RADEON_3D_RNDR_GEN_INDX_PRIM	0x00002300
+#	define RADEON_WAIT_FOR_IDLE		0x00002600
+#	define RADEON_3D_DRAW_IMMD		0x00002900
+#	define RADEON_3D_CLEAR_ZMASK		0x00003200
+#	define RADEON_CNTL_HOSTDATA_BLT		0x00009400
+#	define RADEON_CNTL_PAINT_MULTI		0x00009A00
+#	define RADEON_CNTL_BITBLT_MULTI		0x00009B00
+
+#define RADEON_CP_PACKET_MASK		0xC0000000
+#define RADEON_CP_PACKET_COUNT_MASK	0x3fff0000
+#define RADEON_CP_PACKET0_REG_MASK	0x000007ff
+#define RADEON_CP_PACKET1_REG0_MASK	0x000007ff
+#define RADEON_CP_PACKET1_REG1_MASK	0x003ff800
+
+#define RADEON_VTX_Z_PRESENT			(1 << 31)
+
+#define RADEON_PRIM_TYPE_NONE			(0 << 0)
+#define RADEON_PRIM_TYPE_POINT			(1 << 0)
+#define RADEON_PRIM_TYPE_LINE			(2 << 0)
+#define RADEON_PRIM_TYPE_LINE_STRIP		(3 << 0)
+#define RADEON_PRIM_TYPE_TRI_LIST		(4 << 0)
+#define RADEON_PRIM_TYPE_TRI_FAN		(5 << 0)
+#define RADEON_PRIM_TYPE_TRI_STRIP		(6 << 0)
+#define RADEON_PRIM_TYPE_TRI_TYPE2		(7 << 0)
+#define RADEON_PRIM_TYPE_RECT_LIST		(8 << 0)
+#define RADEON_PRIM_TYPE_3VRT_POINT_LIST	(9 << 0)
+#define RADEON_PRIM_TYPE_3VRT_LINE_LIST		(10 << 0)
+#define RADEON_PRIM_WALK_IND			(1 << 4)
+#define RADEON_PRIM_WALK_LIST			(2 << 4)
+#define RADEON_PRIM_WALK_RING			(3 << 4)
+#define RADEON_COLOR_ORDER_BGRA			(0 << 6)
+#define RADEON_COLOR_ORDER_RGBA			(1 << 6)
+#define RADEON_MAOS_ENABLE			(1 << 7)
+#define RADEON_VTX_FMT_R128_MODE		(0 << 8)
+#define RADEON_VTX_FMT_RADEON_MODE		(1 << 8)
+#define RADEON_NUM_VERTICES_SHIFT		16
+
+#define RADEON_COLOR_FORMAT_CI8		2
+#define RADEON_COLOR_FORMAT_ARGB1555	3
+#define RADEON_COLOR_FORMAT_RGB565	4
+#define RADEON_COLOR_FORMAT_ARGB8888	6
+#define RADEON_COLOR_FORMAT_RGB332	7
+#define RADEON_COLOR_FORMAT_RGB8	9
+#define RADEON_COLOR_FORMAT_ARGB4444	15
+
+#define RADEON_TXF_8BPP_I		0
+#define RADEON_TXF_16BPP_AI88		1
+#define RADEON_TXF_8BPP_RGB332		2
+#define RADEON_TXF_16BPP_ARGB1555	3
+#define RADEON_TXF_16BPP_RGB565		4
+#define RADEON_TXF_16BPP_ARGB4444	5
+#define RADEON_TXF_32BPP_ARGB8888	6
+#define RADEON_TXF_32BPP_RGBA8888	7
+
+/* Constants */
+#define RADEON_MAX_USEC_TIMEOUT		100000	/* 100 ms */
+
+#define RADEON_LAST_FRAME_REG		RADEON_SCRATCH_REG0
+#define RADEON_LAST_DISPATCH_REG	RADEON_SCRATCH_REG1
+#define RADEON_LAST_CLEAR_REG		RADEON_SCRATCH_REG2
+#define RADEON_LAST_DISPATCH		1
+
+#define RADEON_MAX_VB_AGE		0x7fffffff
+#define RADEON_MAX_VB_VERTS		(0xffff)
+
+
+#define RADEON_BASE(reg)	((u32)(dev_priv->mmio->handle))
+#define RADEON_ADDR(reg)	(RADEON_BASE(reg) + reg)
+
+#define RADEON_DEREF(reg)	*(__volatile__ u32 *)RADEON_ADDR(reg)
+#define RADEON_READ(reg)	RADEON_DEREF(reg)
+#define RADEON_WRITE(reg,val)	do { RADEON_DEREF(reg) = val; } while (0)
+
+#define RADEON_DEREF8(reg)	*(__volatile__ u8 *)RADEON_ADDR(reg)
+#define RADEON_READ8(reg)	RADEON_DEREF8(reg)
+#define RADEON_WRITE8(reg,val)	do { RADEON_DEREF8(reg) = val; } while (0)
+
+#define RADEON_WRITE_PLL(addr,val)                                            \
+do {                                                                          \
+	RADEON_WRITE8(RADEON_CLOCK_CNTL_INDEX,                                \
+		      ((addr) & 0x1f) | RADEON_PLL_WR_EN);                    \
+	RADEON_WRITE(RADEON_CLOCK_CNTL_DATA, (val));                          \
+} while (0)
+
+extern int RADEON_READ_PLL(drm_device_t *dev, int addr);
+
+
+
+#define CP_PACKET0( reg, n )						\
+	(RADEON_CP_PACKET0 | ((n) << 16) | ((reg) >> 2))
+#define CP_PACKET0_TABLE( reg, n )					\
+	(RADEON_CP_PACKET0 | RADEON_ONE_REG_WR | ((n) << 16) | ((reg) >> 2))
+#define CP_PACKET1( reg0, reg1 )					\
+	(RADEON_CP_PACKET1 | (((reg1) >> 2) << 15) | ((reg0) >> 2))
+#define CP_PACKET2()							\
+	(RADEON_CP_PACKET2)
+#define CP_PACKET3( pkt, n )						\
+	(RADEON_CP_PACKET3 | (pkt) | ((n) << 16))
+
+
+/* ================================================================
+ * Engine control helper macros
+ */
+
+#define RADEON_WAIT_UNTIL_2D_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_3D_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_3D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_IDLE()					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( (RADEON_WAIT_2D_IDLECLEAN |				\
+		   RADEON_WAIT_3D_IDLECLEAN |				\
+		   RADEON_WAIT_HOST_IDLECLEAN) );			\
+} while (0)
+
+#define RADEON_WAIT_UNTIL_PAGE_FLIPPED()				\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_WAIT_UNTIL, 0 ) );			\
+	OUT_RING( RADEON_WAIT_CRTC_PFLIP );				\
+} while (0)
+
+#define RADEON_FLUSH_CACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB2D_DC_FLUSH );				\
+} while (0)
+
+#define RADEON_PURGE_CACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB2D_DC_FLUSH_ALL );				\
+} while (0)
+
+#define RADEON_FLUSH_ZCACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB3D_ZC_FLUSH );				\
+} while (0)
+
+#define RADEON_PURGE_ZCACHE()						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_RB3D_ZCACHE_CTLSTAT, 0 ) );	\
+	OUT_RING( RADEON_RB3D_ZC_FLUSH_ALL );				\
+} while (0)
+
+
+/* ================================================================
+ * Misc helper macros
+ */
+
+#define VB_AGE_CHECK_WITH_RET( dev_priv )				\
+do {									\
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;		\
+	if ( sarea_priv->last_dispatch >= RADEON_MAX_VB_AGE ) {		\
+		int __ret = radeon_do_cp_idle( dev_priv );		\
+		if ( __ret < 0 ) return __ret;				\
+		sarea_priv->last_dispatch = 0;				\
+		radeon_freelist_reset( dev );				\
+	}								\
+} while (0)
+
+#define RADEON_DISPATCH_AGE( age )					\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_DISPATCH_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+#define RADEON_FRAME_AGE( age )						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_FRAME_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+#define RADEON_CLEAR_AGE( age )						\
+do {									\
+	OUT_RING( CP_PACKET0( RADEON_LAST_CLEAR_REG, 0 ) );		\
+	OUT_RING( age );						\
+} while (0)
+
+
+/* ================================================================
+ * Ring control
+ */
+
+#define radeon_flush_write_combine()	mb()
+
+
+#define RADEON_VERBOSE	0
+
+#define RING_LOCALS	int write; unsigned int mask; volatile u32 *ring;
+
+#define BEGIN_RING( n ) do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "BEGIN_RING( %d ) in %s\n",			\
+			   n, __FUNCTION__ );				\
+	}								\
+	if ( dev_priv->ring.space < (n) * sizeof(u32) ) {		\
+		radeon_wait_ring( dev_priv, (n) * sizeof(u32) );	\
+	}								\
+	dev_priv->ring.space -= (n) * sizeof(u32);			\
+	ring = dev_priv->ring.start;					\
+	write = dev_priv->ring.tail;					\
+	mask = dev_priv->ring.tail_mask;				\
+} while (0)
+
+#define ADVANCE_RING() do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n",	\
+			  write, dev_priv->ring.tail );			\
+	}								\
+	radeon_flush_write_combine();					\
+	dev_priv->ring.tail = write;					\
+	RADEON_WRITE( RADEON_CP_RB_WPTR, write );			\
+} while (0)
+
+#define OUT_RING( x ) do {						\
+	if ( RADEON_VERBOSE ) {						\
+		DRM_INFO( "   OUT_RING( 0x%08x ) at 0x%x\n",		\
+			   (unsigned int)(x), write );			\
+	}								\
+	ring[write++] = (x);						\
+	write &= mask;							\
+} while (0)
+
+#define RADEON_PERFORMANCE_BOXES	0
+
+#endif /* __RADEON_DRV_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/radeon_state.c linux/drivers/char/drm-4.0/radeon_state.c
--- linux.orig/drivers/char/drm-4.0/radeon_state.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/radeon_state.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,1447 @@
+/* radeon_state.c -- State support for Radeon -*- linux-c -*-
+ *
+ * Copyright 2000 VA Linux Systems, Inc., Fremont, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Kevin E. Martin <martin@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "radeon_drv.h"
+#include "drm.h"
+#include <linux/delay.h>
+
+
+/* ================================================================
+ * CP hardware state programming functions
+ */
+
+static inline void radeon_emit_clip_rect( drm_radeon_private_t *dev_priv,
+					  drm_clip_rect_t *box )
+{
+	RING_LOCALS;
+
+	DRM_DEBUG( "   box:  x1=%d y1=%d  x2=%d y2=%d\n",
+		   box->x1, box->y1, box->x2, box->y2 );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_TOP_LEFT, 0 ) );
+	OUT_RING( (box->y1 << 16) | box->x1 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_WIDTH_HEIGHT, 0 ) );
+	OUT_RING( ((box->y2 - 1) << 16) | (box->x2 - 1) );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_context( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 14 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_MISC, 6 ) );
+	OUT_RING( ctx->pp_misc );
+	OUT_RING( ctx->pp_fog_color );
+	OUT_RING( ctx->re_solid_color );
+	OUT_RING( ctx->rb3d_blendcntl );
+	OUT_RING( ctx->rb3d_depthoffset );
+	OUT_RING( ctx->rb3d_depthpitch );
+	OUT_RING( ctx->rb3d_zstencilcntl );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 2 ) );
+	OUT_RING( ctx->pp_cntl );
+	OUT_RING( ctx->rb3d_cntl );
+	OUT_RING( ctx->rb3d_coloroffset );
+
+	OUT_RING( CP_PACKET0( RADEON_RB3D_COLORPITCH, 0 ) );
+	OUT_RING( ctx->rb3d_colorpitch );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_vertfmt( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_COORD_FMT, 0 ) );
+	OUT_RING( ctx->se_coord_fmt );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_line( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_LINE_PATTERN, 1 ) );
+	OUT_RING( ctx->re_line_pattern );
+	OUT_RING( ctx->re_line_state );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_LINE_WIDTH, 0 ) );
+	OUT_RING( ctx->se_line_width );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_bumpmap( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 5 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_LUM_MATRIX, 0 ) );
+	OUT_RING( ctx->pp_lum_matrix );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_ROT_MATRIX_0, 1 ) );
+	OUT_RING( ctx->pp_rot_matrix_0 );
+	OUT_RING( ctx->pp_rot_matrix_1 );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_masks( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_RB3D_STENCILREFMASK, 2 ) );
+	OUT_RING( ctx->rb3d_stencilrefmask );
+	OUT_RING( ctx->rb3d_ropcntl );
+	OUT_RING( ctx->rb3d_planemask );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_viewport( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 7 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_VPORT_XSCALE, 5 ) );
+	OUT_RING( ctx->se_vport_xscale );
+	OUT_RING( ctx->se_vport_xoffset );
+	OUT_RING( ctx->se_vport_yscale );
+	OUT_RING( ctx->se_vport_yoffset );
+	OUT_RING( ctx->se_vport_zscale );
+	OUT_RING( ctx->se_vport_zoffset );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_setup( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 4 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) );
+	OUT_RING( ctx->se_cntl );
+	OUT_RING( CP_PACKET0( RADEON_SE_CNTL_STATUS, 0 ) );
+	OUT_RING( ctx->se_cntl_status );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tcl( drm_radeon_private_t *dev_priv )
+{
+#ifdef TCL_ENABLE
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 29 );
+
+	OUT_RING( CP_PACKET0( RADEON_SE_TCL_MATERIAL_EMMISSIVE_RED, 27 ) );
+	OUT_RING( ctx->se_tcl_material_emmissive.red );
+	OUT_RING( ctx->se_tcl_material_emmissive.green );
+	OUT_RING( ctx->se_tcl_material_emmissive.blue );
+	OUT_RING( ctx->se_tcl_material_emmissive.alpha );
+	OUT_RING( ctx->se_tcl_material_ambient.red );
+	OUT_RING( ctx->se_tcl_material_ambient.green );
+	OUT_RING( ctx->se_tcl_material_ambient.blue );
+	OUT_RING( ctx->se_tcl_material_ambient.alpha );
+	OUT_RING( ctx->se_tcl_material_diffuse.red );
+	OUT_RING( ctx->se_tcl_material_diffuse.green );
+	OUT_RING( ctx->se_tcl_material_diffuse.blue );
+	OUT_RING( ctx->se_tcl_material_diffuse.alpha );
+	OUT_RING( ctx->se_tcl_material_specular.red );
+	OUT_RING( ctx->se_tcl_material_specular.green );
+	OUT_RING( ctx->se_tcl_material_specular.blue );
+	OUT_RING( ctx->se_tcl_material_specular.alpha );
+	OUT_RING( ctx->se_tcl_shininess );
+	OUT_RING( ctx->se_tcl_output_vtx_fmt );
+	OUT_RING( ctx->se_tcl_output_vtx_sel );
+	OUT_RING( ctx->se_tcl_matrix_select_0 );
+	OUT_RING( ctx->se_tcl_matrix_select_1 );
+	OUT_RING( ctx->se_tcl_ucp_vert_blend_ctl );
+	OUT_RING( ctx->se_tcl_texture_proc_ctl );
+	OUT_RING( ctx->se_tcl_light_model_ctl );
+	for ( i = 0 ; i < 4 ; i++ ) {
+		OUT_RING( ctx->se_tcl_per_light_ctl[i] );
+	}
+
+	ADVANCE_RING();
+#else
+	DRM_ERROR( "TCL not enabled!\n" );
+#endif
+}
+
+static inline void radeon_emit_misc( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_context_regs_t *ctx = &sarea_priv->context_state;
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 2 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_MISC, 0 ) );
+	OUT_RING( ctx->re_misc );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex0( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_0, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_0, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex1( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[1];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s: offset=0x%x\n", __FUNCTION__, tex->pp_txoffset );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_1, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_1, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_tex2( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_texture_regs_t *tex = &sarea_priv->tex_state[2];
+	RING_LOCALS;
+	DRM_DEBUG( "    %s\n", __FUNCTION__ );
+
+	BEGIN_RING( 9 );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_TXFILTER_2, 5 ) );
+	OUT_RING( tex->pp_txfilter );
+	OUT_RING( tex->pp_txformat );
+	OUT_RING( tex->pp_txoffset );
+	OUT_RING( tex->pp_txcblend );
+	OUT_RING( tex->pp_txablend );
+	OUT_RING( tex->pp_tfactor );
+
+	OUT_RING( CP_PACKET0( RADEON_PP_BORDER_COLOR_2, 0 ) );
+	OUT_RING( tex->pp_border_color );
+
+	ADVANCE_RING();
+}
+
+static inline void radeon_emit_state( drm_radeon_private_t *dev_priv )
+{
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	DRM_DEBUG( "%s: dirty=0x%08x\n", __FUNCTION__, dirty );
+
+	if ( dirty & RADEON_UPLOAD_CONTEXT ) {
+		radeon_emit_context( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_CONTEXT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_VERTFMT ) {
+		radeon_emit_vertfmt( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_VERTFMT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_LINE ) {
+		radeon_emit_line( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_LINE;
+	}
+
+	if ( dirty & RADEON_UPLOAD_BUMPMAP ) {
+		radeon_emit_bumpmap( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_BUMPMAP;
+	}
+
+	if ( dirty & RADEON_UPLOAD_MASKS ) {
+		radeon_emit_masks( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_MASKS;
+	}
+
+	if ( dirty & RADEON_UPLOAD_VIEWPORT ) {
+		radeon_emit_viewport( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_VIEWPORT;
+	}
+
+	if ( dirty & RADEON_UPLOAD_SETUP ) {
+		radeon_emit_setup( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_SETUP;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TCL ) {
+#ifdef TCL_ENABLE
+		radeon_emit_tcl( dev_priv );
+#endif
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TCL;
+	}
+
+	if ( dirty & RADEON_UPLOAD_MISC ) {
+		radeon_emit_misc( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_MISC;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX0 ) {
+		radeon_emit_tex0( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX0;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX1 ) {
+		radeon_emit_tex1( dev_priv );
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX1;
+	}
+
+	if ( dirty & RADEON_UPLOAD_TEX2 ) {
+#if 0
+		radeon_emit_tex2( dev_priv );
+#endif
+		sarea_priv->dirty &= ~RADEON_UPLOAD_TEX2;
+	}
+
+	sarea_priv->dirty &= ~(RADEON_UPLOAD_TEX0IMAGES |
+			       RADEON_UPLOAD_TEX1IMAGES |
+			       RADEON_UPLOAD_TEX2IMAGES |
+			       RADEON_REQUIRE_QUIESCENCE);
+}
+
+
+#if RADEON_PERFORMANCE_BOXES
+/* ================================================================
+ * Performance monitoring functions
+ */
+
+static void radeon_clear_box( drm_radeon_private_t *dev_priv,
+			      int x, int y, int w, int h,
+			      int r, int g, int b )
+{
+	u32 pitch, offset;
+	u32 color;
+	RING_LOCALS;
+
+	switch ( dev_priv->color_fmt ) {
+	case RADEON_COLOR_FORMAT_RGB565:
+		color = (((r & 0xf8) << 8) |
+			 ((g & 0xfc) << 3) |
+			 ((b & 0xf8) >> 3));
+		break;
+	case RADEON_COLOR_FORMAT_ARGB8888:
+	default:
+		color = (((0xff) << 24) | (r << 16) | (g <<  8) | b);
+		break;
+	}
+
+	offset = dev_priv->back_offset;
+	pitch = dev_priv->back_pitch >> 3;
+
+	BEGIN_RING( 6 );
+
+	OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+	OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+		  RADEON_GMC_BRUSH_SOLID_COLOR |
+		  (dev_priv->color_fmt << 8) |
+		  RADEON_GMC_SRC_DATATYPE_COLOR |
+		  RADEON_ROP3_P |
+		  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+	OUT_RING( (pitch << 22) | (offset >> 5) );
+	OUT_RING( color );
+
+	OUT_RING( (x << 16) | y );
+	OUT_RING( (w << 16) | h );
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_performance_boxes( drm_radeon_private_t *dev_priv )
+{
+	if ( atomic_read( &dev_priv->idle_count ) == 0 ) {
+		radeon_clear_box( dev_priv, 64, 4, 8, 8, 0, 255, 0 );
+	} else {
+		atomic_set( &dev_priv->idle_count, 0 );
+	}
+}
+
+#endif
+
+
+/* ================================================================
+ * CP command dispatch functions
+ */
+
+static void radeon_print_dirty( const char *msg, unsigned int flags )
+{
+	DRM_DEBUG( "%s: (0x%x) %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
+		   msg,
+		   flags,
+		   (flags & RADEON_UPLOAD_CONTEXT)     ? "context, " : "",
+		   (flags & RADEON_UPLOAD_VERTFMT)     ? "vertfmt, " : "",
+		   (flags & RADEON_UPLOAD_LINE)        ? "line, " : "",
+		   (flags & RADEON_UPLOAD_BUMPMAP)     ? "bumpmap, " : "",
+		   (flags & RADEON_UPLOAD_MASKS)       ? "masks, " : "",
+		   (flags & RADEON_UPLOAD_VIEWPORT)    ? "viewport, " : "",
+		   (flags & RADEON_UPLOAD_SETUP)       ? "setup, " : "",
+		   (flags & RADEON_UPLOAD_TCL)         ? "tcl, " : "",
+		   (flags & RADEON_UPLOAD_MISC)        ? "misc, " : "",
+		   (flags & RADEON_UPLOAD_TEX0)        ? "tex0, " : "",
+		   (flags & RADEON_UPLOAD_TEX1)        ? "tex1, " : "",
+		   (flags & RADEON_UPLOAD_TEX2)        ? "tex2, " : "",
+		   (flags & RADEON_UPLOAD_CLIPRECTS)   ? "cliprects, " : "",
+		   (flags & RADEON_REQUIRE_QUIESCENCE) ? "quiescence, " : "" );
+}
+
+static void radeon_cp_dispatch_clear( drm_device_t *dev,
+				      drm_radeon_clear_t *clear )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	unsigned int flags = clear->flags;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( dev_priv->page_flipping && dev_priv->current_page == 1 ) {
+		unsigned int tmp = flags;
+
+		flags &= ~(RADEON_FRONT | RADEON_BACK);
+		if ( tmp & RADEON_FRONT ) flags |= RADEON_BACK;
+		if ( tmp & RADEON_BACK )  flags |= RADEON_FRONT;
+	}
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch clear %d,%d-%d,%d flags 0x%x\n",
+			   x, y, w, h, flags );
+
+		if ( flags & (RADEON_FRONT | RADEON_BACK) ) {
+			BEGIN_RING( 4 );
+
+			/* Ensure the 3D stream is idle before doing a
+			 * 2D fill to clear the front or back buffer.
+			 */
+			RADEON_WAIT_UNTIL_3D_IDLE();
+
+			OUT_RING( CP_PACKET0( RADEON_DP_WRITE_MASK, 0 ) );
+			OUT_RING( sarea_priv->context_state.rb3d_planemask );
+
+			ADVANCE_RING();
+
+			/* Make sure we restore the 3D state next time.
+			 */
+			dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+							RADEON_UPLOAD_MASKS);
+		}
+
+		if ( flags & RADEON_FRONT ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+				  RADEON_GMC_BRUSH_SOLID_COLOR |
+				  (dev_priv->color_fmt << 8) |
+				  RADEON_GMC_SRC_DATATYPE_COLOR |
+				  RADEON_ROP3_P |
+				  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+			OUT_RING( dev_priv->front_pitch_offset );
+			OUT_RING( clear->clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+		}
+
+		if ( flags & RADEON_BACK ) {
+			BEGIN_RING( 6 );
+
+			OUT_RING( CP_PACKET3( RADEON_CNTL_PAINT_MULTI, 4 ) );
+			OUT_RING( RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+				  RADEON_GMC_BRUSH_SOLID_COLOR |
+				  (dev_priv->color_fmt << 8) |
+				  RADEON_GMC_SRC_DATATYPE_COLOR |
+				  RADEON_ROP3_P |
+				  RADEON_GMC_CLR_CMP_CNTL_DIS );
+
+			OUT_RING( dev_priv->back_pitch_offset );
+			OUT_RING( clear->clear_color );
+
+			OUT_RING( (x << 16) | y );
+			OUT_RING( (w << 16) | h );
+
+			ADVANCE_RING();
+
+		}
+
+		if ( flags & RADEON_DEPTH ) {
+			drm_radeon_depth_clear_t *depth_clear =
+			   &dev_priv->depth_clear;
+
+			if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+				radeon_emit_state( dev_priv );
+			}
+
+			/* FIXME: Render a rectangle to clear the depth
+			 * buffer.  So much for those "fast Z clears"...
+			 */
+			BEGIN_RING( 23 );
+
+			RADEON_WAIT_UNTIL_2D_IDLE();
+
+			OUT_RING( CP_PACKET0( RADEON_PP_CNTL, 1 ) );
+			OUT_RING( 0x00000000 );
+			OUT_RING( depth_clear->rb3d_cntl );
+			OUT_RING( CP_PACKET0( RADEON_RB3D_ZSTENCILCNTL, 0 ) );
+			OUT_RING( depth_clear->rb3d_zstencilcntl );
+			OUT_RING( CP_PACKET0( RADEON_RB3D_PLANEMASK, 0 ) );
+			OUT_RING( 0x00000000 );
+			OUT_RING( CP_PACKET0( RADEON_SE_CNTL, 0 ) );
+			OUT_RING( depth_clear->se_cntl );
+
+			OUT_RING( CP_PACKET3( RADEON_3D_DRAW_IMMD, 10 ) );
+			OUT_RING( RADEON_VTX_Z_PRESENT );
+			OUT_RING( (RADEON_PRIM_TYPE_RECT_LIST |
+				   RADEON_PRIM_WALK_RING |
+				   RADEON_MAOS_ENABLE |
+				   RADEON_VTX_FMT_RADEON_MODE |
+				   (3 << RADEON_NUM_VERTICES_SHIFT)) );
+
+			OUT_RING( clear->rect.ui[CLEAR_X1] );
+			OUT_RING( clear->rect.ui[CLEAR_Y1] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			OUT_RING( clear->rect.ui[CLEAR_X1] );
+			OUT_RING( clear->rect.ui[CLEAR_Y2] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			OUT_RING( clear->rect.ui[CLEAR_X2] );
+			OUT_RING( clear->rect.ui[CLEAR_Y2] );
+			OUT_RING( clear->rect.ui[CLEAR_DEPTH] );
+
+			ADVANCE_RING();
+
+			/* Make sure we restore the 3D state next time.
+			 */
+			dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+							RADEON_UPLOAD_SETUP |
+							RADEON_UPLOAD_MASKS);
+		}
+	}
+
+	/* Increment the clear counter.  The client-side 3D driver must
+	 * wait on this value before performing the clear ioctl.  We
+	 * need this because the card's so damned fast...
+	 */
+	dev_priv->sarea_priv->last_clear++;
+
+	BEGIN_RING( 4 );
+
+	RADEON_CLEAR_AGE( dev_priv->sarea_priv->last_clear );
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_swap( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int nbox = sarea_priv->nbox;
+	drm_clip_rect_t *pbox = sarea_priv->boxes;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+#if RADEON_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	radeon_cp_performance_boxes( dev_priv );
+#endif
+
+	/* Wait for the 3D stream to idle before dispatching the bitblt.
+	 * This will prevent data corruption between the two streams.
+	 */
+	BEGIN_RING( 2 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+
+	ADVANCE_RING();
+
+	for ( i = 0 ; i < nbox ; i++ ) {
+		int x = pbox[i].x1;
+		int y = pbox[i].y1;
+		int w = pbox[i].x2 - x;
+		int h = pbox[i].y2 - y;
+
+		DRM_DEBUG( "dispatch swap %d,%d-%d,%d\n",
+			   x, y, w, h );
+
+		BEGIN_RING( 7 );
+
+		OUT_RING( CP_PACKET3( RADEON_CNTL_BITBLT_MULTI, 5 ) );
+		OUT_RING( RADEON_GMC_SRC_PITCH_OFFSET_CNTL |
+			  RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+			  RADEON_GMC_BRUSH_NONE |
+			  (dev_priv->color_fmt << 8) |
+			  RADEON_GMC_SRC_DATATYPE_COLOR |
+			  RADEON_ROP3_S |
+			  RADEON_DP_SRC_SOURCE_MEMORY |
+			  RADEON_GMC_CLR_CMP_CNTL_DIS |
+			  RADEON_GMC_WR_MSK_DIS );
+
+		OUT_RING( dev_priv->back_pitch_offset );
+		OUT_RING( dev_priv->front_pitch_offset );
+
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (x << 16) | y );
+		OUT_RING( (w << 16) | h );
+
+		ADVANCE_RING();
+	}
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 4 );
+
+	RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame );
+	RADEON_WAIT_UNTIL_2D_IDLE();
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_flip( drm_device_t *dev )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: page=%d\n", __FUNCTION__, dev_priv->current_page );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+#if RADEON_PERFORMANCE_BOXES
+	/* Do some trivial performance monitoring...
+	 */
+	radeon_cp_performance_boxes( dev_priv );
+#endif
+
+	BEGIN_RING( 6 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+	RADEON_WAIT_UNTIL_PAGE_FLIPPED();
+
+	OUT_RING( CP_PACKET0( RADEON_CRTC_OFFSET, 0 ) );
+
+	if ( dev_priv->current_page == 0 ) {
+		OUT_RING( dev_priv->back_offset );
+		dev_priv->current_page = 1;
+	} else {
+		OUT_RING( dev_priv->front_offset );
+		dev_priv->current_page = 0;
+	}
+
+	ADVANCE_RING();
+
+	/* Increment the frame counter.  The client-side 3D driver must
+	 * throttle the framerate by waiting for this value before
+	 * performing the swapbuffer ioctl.
+	 */
+	dev_priv->sarea_priv->last_frame++;
+
+	BEGIN_RING( 2 );
+
+	RADEON_FRAME_AGE( dev_priv->sarea_priv->last_frame );
+
+	ADVANCE_RING();
+}
+
+static void radeon_cp_dispatch_vertex( drm_device_t *dev,
+				       drm_buf_t *buf )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->agp_buffers_offset + buf->offset;
+	int size = buf->used;
+	int prim = buf_priv->prim;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "%s: nbox=%d\n", __FUNCTION__, sarea_priv->nbox );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		radeon_print_dirty( "dispatch_vertex", sarea_priv->dirty );
+
+	if ( buf->used ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+			radeon_emit_state( dev_priv );
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				radeon_emit_clip_rect( dev_priv,
+						       &sarea_priv->boxes[i] );
+			}
+
+			/* Emit the vertex buffer rendering commands */
+			BEGIN_RING( 5 );
+
+			OUT_RING( CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, 3 ) );
+			OUT_RING( offset );
+			OUT_RING( size );
+			OUT_RING( format );
+			OUT_RING( prim | RADEON_PRIM_WALK_LIST |
+				  RADEON_COLOR_ORDER_RGBA |
+				  RADEON_VTX_FMT_RADEON_MODE |
+				  (size << RADEON_NUM_VERTICES_SHIFT) );
+
+			ADVANCE_RING();
+
+			i++;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+	sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+
+static void radeon_cp_dispatch_indirect( drm_device_t *dev,
+					 drm_buf_t *buf,
+					 int start, int end )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	RING_LOCALS;
+	DRM_DEBUG( "indirect: buf=%d s=0x%x e=0x%x\n",
+		   buf->idx, start, end );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( start != end ) {
+		int offset = (dev_priv->agp_buffers_offset
+			      + buf->offset + start);
+		int dwords = (end - start + 3) / sizeof(u32);
+
+		/* Indirect buffer data must be an even number of
+		 * dwords, so if we've been given an odd number we must
+		 * pad the data with a Type-2 CP packet.
+		 */
+		if ( dwords & 1 ) {
+			u32 *data = (u32 *)
+				((char *)dev_priv->buffers->handle
+				 + buf->offset + start);
+			data[dwords++] = RADEON_CP_PACKET2;
+		}
+
+		buf_priv->dispatched = 1;
+
+		/* Fire off the indirect buffer */
+		BEGIN_RING( 3 );
+
+		OUT_RING( CP_PACKET0( RADEON_CP_IB_BASE, 1 ) );
+		OUT_RING( offset );
+		OUT_RING( dwords );
+
+		ADVANCE_RING();
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the indirect buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		buf->used = 0;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+}
+
+static void radeon_cp_dispatch_indices( drm_device_t *dev,
+					drm_buf_t *buf,
+					int start, int end,
+					int count )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_buf_priv_t *buf_priv = buf->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	int format = sarea_priv->vc_format;
+	int offset = dev_priv->agp_buffers_offset;
+	int prim = buf_priv->prim;
+	u32 *data;
+	int dwords;
+	int i = 0;
+	RING_LOCALS;
+	DRM_DEBUG( "indices: s=%d e=%d c=%d\n", start, end, count );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	if ( 0 )
+		radeon_print_dirty( "dispatch_indices", sarea_priv->dirty );
+
+	if ( start != end ) {
+		buf_priv->dispatched = 1;
+
+		if ( sarea_priv->dirty & ~RADEON_UPLOAD_CLIPRECTS ) {
+			radeon_emit_state( dev_priv );
+		}
+
+		dwords = (end - start + 3) / sizeof(u32);
+
+		data = (u32 *)((char *)dev_priv->buffers->handle
+			       + buf->offset + start);
+
+		data[0] = CP_PACKET3( RADEON_3D_RNDR_GEN_INDX_PRIM, dwords-2 );
+
+		data[1] = offset;
+		data[2] = RADEON_MAX_VB_VERTS;
+		data[3] = format;
+		data[4] = (prim | RADEON_PRIM_WALK_IND |
+			   RADEON_COLOR_ORDER_RGBA |
+			   RADEON_VTX_FMT_RADEON_MODE |
+			   (count << RADEON_NUM_VERTICES_SHIFT) );
+
+		if ( count & 0x1 ) {
+			data[dwords-1] &= 0x0000ffff;
+		}
+
+		do {
+			/* Emit the next set of up to three cliprects */
+			if ( i < sarea_priv->nbox ) {
+				radeon_emit_clip_rect( dev_priv,
+						       &sarea_priv->boxes[i] );
+			}
+
+			radeon_cp_dispatch_indirect( dev, buf, start, end );
+
+			i++;
+		} while ( i < sarea_priv->nbox );
+	}
+
+	if ( buf_priv->discard ) {
+		buf_priv->age = dev_priv->sarea_priv->last_dispatch;
+
+		/* Emit the vertex buffer age */
+		BEGIN_RING( 2 );
+		RADEON_DISPATCH_AGE( buf_priv->age );
+		ADVANCE_RING();
+
+		buf->pending = 1;
+		/* FIXME: Check dispatched field */
+		buf_priv->dispatched = 0;
+	}
+
+	dev_priv->sarea_priv->last_dispatch++;
+
+	sarea_priv->dirty &= ~RADEON_UPLOAD_CLIPRECTS;
+	sarea_priv->nbox = 0;
+}
+
+static int radeon_cp_dispatch_blit( drm_device_t *dev,
+				    drm_radeon_blit_t *blit )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	u32 format;
+	u32 *data;
+	int dword_shift, dwords;
+	RING_LOCALS;
+	DRM_DEBUG( "blit: ofs=0x%x p=%d f=%d x=%hd y=%hd w=%hd h=%hd\n",
+		   blit->offset >> 10, blit->pitch, blit->format,
+		   blit->x, blit->y, blit->width, blit->height );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	/* The compiler won't optimize away a division by a variable,
+	 * even if the only legal values are powers of two.  Thus, we'll
+	 * use a shift instead.
+	 */
+	switch ( blit->format ) {
+	case RADEON_TXF_32BPP_ARGB8888:
+	case RADEON_TXF_32BPP_RGBA8888:
+		format = RADEON_COLOR_FORMAT_ARGB8888;
+		dword_shift = 0;
+		break;
+	case RADEON_TXF_16BPP_AI88:
+	case RADEON_TXF_16BPP_ARGB1555:
+	case RADEON_TXF_16BPP_RGB565:
+	case RADEON_TXF_16BPP_ARGB4444:
+		format = RADEON_COLOR_FORMAT_RGB565;
+		dword_shift = 1;
+		break;
+	case RADEON_TXF_8BPP_I:
+	case RADEON_TXF_8BPP_RGB332:
+		format = RADEON_COLOR_FORMAT_CI8;
+		dword_shift = 2;
+		break;
+	default:
+		DRM_ERROR( "invalid blit format %d\n", blit->format );
+		return -EINVAL;
+	}
+
+	/* Flush the pixel cache.  This ensures no pixel data gets mixed
+	 * up with the texture data from the host data blit, otherwise
+	 * part of the texture image may be corrupted.
+	 */
+	BEGIN_RING( 4 );
+
+	RADEON_FLUSH_CACHE();
+	RADEON_WAIT_UNTIL_IDLE();
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer.
+	 */
+	buf = dma->buflist[blit->idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", blit->idx );
+		return -EINVAL;
+	}
+
+	buf_priv->discard = 1;
+
+	dwords = (blit->width * blit->height) >> dword_shift;
+	if ( !dwords ) dwords = 1;
+
+	data = (u32 *)((char *)dev_priv->buffers->handle + buf->offset);
+
+	data[0] = CP_PACKET3( RADEON_CNTL_HOSTDATA_BLT, dwords + 6 );
+	data[1] = (RADEON_GMC_DST_PITCH_OFFSET_CNTL |
+		   RADEON_GMC_BRUSH_NONE |
+		   (format << 8) |
+		   RADEON_GMC_SRC_DATATYPE_COLOR |
+		   RADEON_ROP3_S |
+		   RADEON_DP_SRC_SOURCE_HOST_DATA |
+		   RADEON_GMC_CLR_CMP_CNTL_DIS |
+		   RADEON_GMC_WR_MSK_DIS);
+
+	data[2] = (blit->pitch << 22) | (blit->offset >> 10);
+	data[3] = 0xffffffff;
+	data[4] = 0xffffffff;
+	data[5] = (blit->y << 16) | blit->x;
+	data[6] = (blit->height << 16) | blit->width;
+	data[7] = dwords;
+
+	buf->used = (dwords + 8) * sizeof(u32);
+
+	radeon_cp_dispatch_indirect( dev, buf, 0, buf->used );
+
+	/* Flush the pixel cache after the blit completes.  This ensures
+	 * the texture data is written out to memory before rendering
+	 * continues.
+	 */
+	BEGIN_RING( 4 );
+
+	RADEON_FLUSH_CACHE();
+	RADEON_WAIT_UNTIL_2D_IDLE();
+
+	ADVANCE_RING();
+
+	return 0;
+}
+
+static void radeon_cp_dispatch_stipple( drm_device_t *dev, u32 *stipple )
+{
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	int i;
+	RING_LOCALS;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	radeon_update_ring_snapshot( dev_priv );
+
+	BEGIN_RING( 35 );
+
+	OUT_RING( CP_PACKET0( RADEON_RE_STIPPLE_ADDR, 0 ) );
+	OUT_RING( 0x00000000 );
+
+	OUT_RING( CP_PACKET0_TABLE( RADEON_RE_STIPPLE_DATA, 31 ) );
+	for ( i = 0 ; i < 32 ; i++ ) {
+		OUT_RING( stipple[i] );
+	}
+
+	ADVANCE_RING();
+}
+
+
+/* ================================================================
+ * IOCTL functions
+ */
+
+int radeon_cp_clear( struct inode *inode, struct file *filp,
+		     unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_radeon_clear_t clear;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &clear, (drm_radeon_clear_t *) arg,
+			     sizeof(clear) ) )
+		return -EFAULT;
+
+	if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
+
+	radeon_cp_dispatch_clear( dev, &clear );
+
+	return 0;
+}
+
+int radeon_cp_swap( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_radeon_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	DRM_DEBUG( "%s\n", __FUNCTION__ );
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( sarea_priv->nbox > RADEON_NR_SAREA_CLIPRECTS )
+		sarea_priv->nbox = RADEON_NR_SAREA_CLIPRECTS;
+
+	if ( !dev_priv->page_flipping ) {
+		radeon_cp_dispatch_swap( dev );
+		dev_priv->sarea_priv->dirty |= (RADEON_UPLOAD_CONTEXT |
+						RADEON_UPLOAD_MASKS);
+	} else {
+		radeon_cp_dispatch_flip( dev );
+	}
+
+	return 0;
+}
+
+int radeon_cp_vertex( struct inode *inode, struct file *filp,
+		      unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_vertex_t vertex;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &vertex, (drm_radeon_vertex_t *)arg,
+			     sizeof(vertex) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d count=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   vertex.idx, vertex.count, vertex.discard );
+
+	if ( vertex.idx < 0 || vertex.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   vertex.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( vertex.prim < 0 ||
+	     vertex.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) {
+		DRM_ERROR( "buffer prim %d\n", vertex.prim );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf = dma->buflist[vertex.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", vertex.idx );
+		return -EINVAL;
+	}
+
+	buf->used = vertex.count;
+	buf_priv->prim = vertex.prim;
+	buf_priv->discard = vertex.discard;
+
+	radeon_cp_dispatch_vertex( dev, buf );
+
+	return 0;
+}
+
+int radeon_cp_indices( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_indices_t elts;
+	int count;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &elts, (drm_radeon_indices_t *)arg,
+			     sizeof(elts) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d start=%d end=%d discard=%d\n",
+		   __FUNCTION__, current->pid,
+		   elts.idx, elts.start, elts.end, elts.discard );
+
+	if ( elts.idx < 0 || elts.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   elts.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+	if ( elts.prim < 0 ||
+	     elts.prim > RADEON_PRIM_TYPE_3VRT_LINE_LIST ) {
+		DRM_ERROR( "buffer prim %d\n", elts.prim );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf = dma->buflist[elts.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", elts.idx );
+		return -EINVAL;
+	}
+
+	count = (elts.end - elts.start) / sizeof(u16);
+	elts.start -= RADEON_INDEX_PRIM_OFFSET;
+
+	if ( elts.start & 0x7 ) {
+		DRM_ERROR( "misaligned buffer 0x%x\n", elts.start );
+		return -EINVAL;
+	}
+	if ( elts.start < buf->used ) {
+		DRM_ERROR( "no header 0x%x - 0x%x\n", elts.start, buf->used );
+		return -EINVAL;
+	}
+
+	buf->used = elts.end;
+	buf_priv->prim = elts.prim;
+	buf_priv->discard = elts.discard;
+
+	radeon_cp_dispatch_indices( dev, buf, elts.start, elts.end, count );
+
+	return 0;
+}
+
+int radeon_cp_blit( struct inode *inode, struct file *filp,
+		    unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_radeon_blit_t blit;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &blit, (drm_radeon_blit_t *)arg,
+			     sizeof(blit) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "%s: pid=%d index=%d\n",
+		   __FUNCTION__, current->pid, blit.idx );
+
+	if ( blit.idx < 0 || blit.idx > dma->buf_count ) {
+		DRM_ERROR( "sending %d buffers (of %d max)\n",
+			   blit.idx, dma->buf_count );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	return radeon_cp_dispatch_blit( dev, &blit );
+}
+
+int radeon_cp_stipple( struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_stipple_t stipple;
+	u32 mask[32];
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &stipple, (drm_radeon_stipple_t *)arg,
+			     sizeof(stipple) ) )
+		return -EFAULT;
+
+	if ( copy_from_user( &mask, stipple.mask,
+			     32 * sizeof(u32) ) )
+		return -EFAULT;
+
+	radeon_cp_dispatch_stipple( dev, mask );
+
+	return 0;
+}
+
+int radeon_cp_indirect( struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_radeon_private_t *dev_priv = dev->dev_private;
+	drm_device_dma_t *dma = dev->dma;
+	drm_buf_t *buf;
+	drm_radeon_buf_priv_t *buf_priv;
+	drm_radeon_indirect_t indirect;
+	RING_LOCALS;
+
+	if ( !_DRM_LOCK_IS_HELD( dev->lock.hw_lock->lock ) ||
+	     dev->lock.pid != current->pid ) {
+		DRM_ERROR( "%s called without lock held\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+	if ( !dev_priv || dev_priv->is_pci ) {
+		DRM_ERROR( "%s called with a PCI card\n", __FUNCTION__ );
+		return -EINVAL;
+	}
+
+	if ( copy_from_user( &indirect, (drm_radeon_indirect_t *)arg,
+			     sizeof(indirect) ) )
+		return -EFAULT;
+
+	DRM_DEBUG( "indirect: idx=%d s=%d e=%d d=%d\n",
+		   indirect.idx, indirect.start,
+		   indirect.end, indirect.discard );
+
+	if ( indirect.idx < 0 || indirect.idx >= dma->buf_count ) {
+		DRM_ERROR( "buffer index %d (of %d max)\n",
+			   indirect.idx, dma->buf_count - 1 );
+		return -EINVAL;
+	}
+
+	buf = dma->buflist[indirect.idx];
+	buf_priv = buf->dev_private;
+
+	if ( buf->pid != current->pid ) {
+		DRM_ERROR( "process %d using buffer owned by %d\n",
+			   current->pid, buf->pid );
+		return -EINVAL;
+	}
+	if ( buf->pending ) {
+		DRM_ERROR( "sending pending buffer %d\n", indirect.idx );
+		return -EINVAL;
+	}
+
+	if ( indirect.start < buf->used ) {
+		DRM_ERROR( "reusing indirect: start=0x%x actual=0x%x\n",
+			   indirect.start, buf->used );
+		return -EINVAL;
+	}
+
+	VB_AGE_CHECK_WITH_RET( dev_priv );
+
+	buf->used = indirect.end;
+	buf_priv->discard = indirect.discard;
+
+	/* Wait for the 3D stream to idle before the indirect buffer
+	 * containing 2D acceleration commands is processed.
+	 */
+	BEGIN_RING( 2 );
+
+	RADEON_WAIT_UNTIL_3D_IDLE();
+
+	ADVANCE_RING();
+
+	/* Dispatch the indirect buffer full of commands from the
+	 * X server.  This is insecure and is thus only available to
+	 * privileged clients.
+	 */
+	radeon_cp_dispatch_indirect( dev, buf, indirect.start, indirect.end );
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/tdfx_context.c linux/drivers/char/drm-4.0/tdfx_context.c
--- linux.orig/drivers/char/drm-4.0/tdfx_context.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/tdfx_context.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,219 @@
+/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*-
+ * Created: Thu Oct  7 10:50:22 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+extern drm_ctx_t tdfx_res_ctx;
+
+static int tdfx_alloc_queue(drm_device_t *dev)
+{
+	return drm_ctxbitmap_next(dev);
+}
+
+int tdfx_context_switch(drm_device_t *dev, int old, int new)
+{
+        char        buf[64];
+
+        atomic_inc(&dev->total_ctx);
+
+        if (test_and_set_bit(0, &dev->context_flag)) {
+                DRM_ERROR("Reentering -- FIXME\n");
+                return -EBUSY;
+        }
+
+#if DRM_DMA_HISTOGRAM
+        dev->ctx_start = get_cycles();
+#endif
+        
+        DRM_DEBUG("Context switch from %d to %d\n", old, new);
+
+        if (new == dev->last_context) {
+                clear_bit(0, &dev->context_flag);
+                return 0;
+        }
+        
+        if (drm_flags & DRM_FLAG_NOCTX) {
+                tdfx_context_switch_complete(dev, new);
+        } else {
+                sprintf(buf, "C %d %d\n", old, new);
+                drm_write_string(dev, buf);
+        }
+        
+        return 0;
+}
+
+int tdfx_context_switch_complete(drm_device_t *dev, int new)
+{
+        dev->last_context = new;  /* PRE/POST: This is the _only_ writer. */
+        dev->last_switch  = jiffies;
+        
+        if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) {
+                DRM_ERROR("Lock isn't held after context switch\n");
+        }
+
+				/* If a context switch is ever initiated
+                                   when the kernel holds the lock, release
+                                   that lock here. */
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles()
+                                                      - dev->ctx_start)]);
+                   
+#endif
+        clear_bit(0, &dev->context_flag);
+        wake_up(&dev->context_wait);
+        
+        return 0;
+}
+
+
+int tdfx_resctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_ctx_res_t	res;
+	drm_ctx_t	ctx;
+	int		i;
+
+	DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS);
+	if (copy_from_user(&res, (drm_ctx_res_t *)arg, sizeof(res)))
+		return -EFAULT;
+	if (res.count >= DRM_RESERVED_CONTEXTS) {
+		memset(&ctx, 0, sizeof(ctx));
+		for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) {
+			ctx.handle = i;
+			if (copy_to_user(&res.contexts[i],
+					 &i,
+					 sizeof(i)))
+				return -EFAULT;
+		}
+	}
+	res.count = DRM_RESERVED_CONTEXTS;
+	if (copy_to_user((drm_ctx_res_t *)arg, &res, sizeof(res)))
+		return -EFAULT;
+	return 0;
+}
+
+
+int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	if ((ctx.handle = tdfx_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) {
+				/* Skip kernel's context and get a new one. */
+		ctx.handle = tdfx_alloc_queue(dev);
+	}
+	DRM_DEBUG("%d\n", ctx.handle);
+	if (ctx.handle == -1) {
+		DRM_DEBUG("Not enough free contexts.\n");
+				/* Should this return -EBUSY instead? */
+		return -ENOMEM;
+	}
+   
+	if (copy_to_user((drm_ctx_t *)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_modctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	if (ctx.flags==_DRM_CONTEXT_PRESERVED)
+		tdfx_res_ctx.handle=ctx.handle;
+	return 0;
+}
+
+int tdfx_getctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	unsigned long arg)
+{
+	drm_ctx_t ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t*)arg, sizeof(ctx)))
+		return -EFAULT;
+	/* This is 0, because we don't handle any context flags */
+	ctx.flags = 0;
+	if (copy_to_user((drm_ctx_t*)arg, &ctx, sizeof(ctx)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_switchctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		   unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	return tdfx_context_switch(dev, dev->last_context, ctx.handle);
+}
+
+int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	tdfx_context_switch_complete(dev, ctx.handle);
+
+	return 0;
+}
+
+int tdfx_rmctx(struct inode *inode, struct file *filp, unsigned int cmd,
+	       unsigned long arg)
+{
+	drm_file_t      *priv   = filp->private_data;
+	drm_device_t    *dev    = priv->dev;
+	drm_ctx_t	ctx;
+
+	if (copy_from_user(&ctx, (drm_ctx_t *)arg, sizeof(ctx)))
+		return -EFAULT;
+	DRM_DEBUG("%d\n", ctx.handle);
+	drm_ctxbitmap_free(dev, ctx.handle);
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/tdfx_drv.c linux/drivers/char/drm-4.0/tdfx_drv.c
--- linux.orig/drivers/char/drm-4.0/tdfx_drv.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/tdfx_drv.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,697 @@
+/* tdfx_drv.c -- tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:38:32 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ *
+ */
+
+#include <linux/config.h>
+#include "drmP.h"
+#include "tdfx_drv.h"
+
+#define TDFX_NAME	 "tdfx"
+#define TDFX_DESC	 "3dfx Banshee/Voodoo3+"
+#define TDFX_DATE	 "20000928"
+#define TDFX_MAJOR	 1
+#define TDFX_MINOR	 0
+#define TDFX_PATCHLEVEL  0
+
+static drm_device_t	      tdfx_device;
+drm_ctx_t	              tdfx_res_ctx;
+
+static struct file_operations tdfx_fops = {
+#if LINUX_VERSION_CODE >= 0x020400
+				/* This started being used during 2.4.0-test */
+	owner:   THIS_MODULE,
+#endif
+	open:	 tdfx_open,
+	flush:	 drm_flush,
+	release: tdfx_release,
+	ioctl:	 tdfx_ioctl,
+	mmap:	 drm_mmap,
+	read:	 drm_read,
+	fasync:	 drm_fasync,
+	poll:	 drm_poll,
+};
+
+static struct miscdevice      tdfx_misc = {
+	minor: MISC_DYNAMIC_MINOR,
+	name:  TDFX_NAME,
+	fops:  &tdfx_fops,
+};
+
+static drm_ioctl_desc_t	      tdfx_ioctls[] = {
+	[DRM_IOCTL_NR(DRM_IOCTL_VERSION)]    = { tdfx_version,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)]  = { drm_getmagic,	  0, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)]  = { drm_irq_busid,	  0, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_BLOCK)]	     = { drm_block,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)]    = { drm_unblock,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)]    = { drm_addmap,	  1, 1 },
+
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)]    = { tdfx_addctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)]     = { tdfx_rmctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)]    = { tdfx_modctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)]    = { tdfx_getctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { tdfx_switchctx,  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)]    = { tdfx_newctx,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)]    = { tdfx_resctx,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)]   = { drm_adddraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)]    = { drm_rmdraw,	  1, 1 },
+	[DRM_IOCTL_NR(DRM_IOCTL_LOCK)]	     = { tdfx_lock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)]     = { tdfx_unlock,	  1, 0 },
+	[DRM_IOCTL_NR(DRM_IOCTL_FINISH)]     = { drm_finish,	  1, 0 },
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)]   = {drm_agp_acquire, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)]   = {drm_agp_release, 1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)]    = {drm_agp_enable,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)]      = {drm_agp_info,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)]     = {drm_agp_alloc,   1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)]      = {drm_agp_free,    1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)]      = {drm_agp_unbind,  1, 1},
+	[DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)]    = {drm_agp_bind,    1, 1},
+#endif
+};
+#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls)
+
+#ifdef MODULE
+static char		      *tdfx = NULL;
+#endif
+
+MODULE_AUTHOR("VA Linux Systems, Inc.");
+MODULE_LICENSE("GPL and additional rights");
+MODULE_DESCRIPTION("tdfx");
+MODULE_PARM(tdfx, "s");
+
+#ifndef MODULE
+/* tdfx_options is called by the kernel to parse command-line options
+ * passed via the boot-loader (e.g., LILO).  It calls the insmod option
+ * routine, drm_parse_drm.
+ */
+
+static int __init tdfx_options(char *str)
+{
+	drm_parse_options(str);
+	return 1;
+}
+
+__setup("tdfx=", tdfx_options);
+#endif
+
+static int tdfx_setup(drm_device_t *dev)
+{
+	int i;
+
+	atomic_set(&dev->ioctl_count, 0);
+	atomic_set(&dev->vma_count, 0);
+	dev->buf_use	  = 0;
+	atomic_set(&dev->buf_alloc, 0);
+
+	atomic_set(&dev->total_open, 0);
+	atomic_set(&dev->total_close, 0);
+	atomic_set(&dev->total_ioctl, 0);
+	atomic_set(&dev->total_irq, 0);
+	atomic_set(&dev->total_ctx, 0);
+	atomic_set(&dev->total_locks, 0);
+	atomic_set(&dev->total_unlocks, 0);
+	atomic_set(&dev->total_contends, 0);
+	atomic_set(&dev->total_sleeps, 0);
+
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		dev->magiclist[i].head = NULL;
+		dev->magiclist[i].tail = NULL;
+	}
+	dev->maplist	    = NULL;
+	dev->map_count	    = 0;
+	dev->vmalist	    = NULL;
+	dev->lock.hw_lock   = NULL;
+	init_waitqueue_head(&dev->lock.lock_queue);
+	dev->queue_count    = 0;
+	dev->queue_reserved = 0;
+	dev->queue_slots    = 0;
+	dev->queuelist	    = NULL;
+	dev->irq	    = 0;
+	dev->context_flag   = 0;
+	dev->interrupt_flag = 0;
+	dev->dma            = 0;
+	dev->dma_flag	    = 0;
+	dev->last_context   = 0;
+	dev->last_switch    = 0;
+	dev->last_checked   = 0;
+	init_timer(&dev->timer);
+	init_waitqueue_head(&dev->context_wait);
+
+	dev->ctx_start	    = 0;
+	dev->lck_start	    = 0;
+
+	dev->buf_rp	  = dev->buf;
+	dev->buf_wp	  = dev->buf;
+	dev->buf_end	  = dev->buf + DRM_BSZ;
+	dev->buf_async	  = NULL;
+	init_waitqueue_head(&dev->buf_readers);
+	init_waitqueue_head(&dev->buf_writers);
+
+	tdfx_res_ctx.handle=-1;
+
+	DRM_DEBUG("\n");
+
+	/* The kernel's context could be created here, but is now created
+	   in drm_dma_enqueue.	This is more resource-efficient for
+	   hardware that does not do DMA, but may mean that
+	   drm_select_queue fails between the time the interrupt is
+	   initialized and the time the queues are initialized. */
+
+	return 0;
+}
+
+
+static int tdfx_takedown(drm_device_t *dev)
+{
+	int		  i;
+	drm_magic_entry_t *pt, *next;
+	drm_map_t	  *map;
+	drm_vma_entry_t	  *vma, *vma_next;
+
+	DRM_DEBUG("\n");
+
+	down(&dev->struct_sem);
+	del_timer(&dev->timer);
+
+	if (dev->devname) {
+		drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER);
+		dev->devname = NULL;
+	}
+
+	if (dev->unique) {
+		drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER);
+		dev->unique = NULL;
+		dev->unique_len = 0;
+	}
+				/* Clear pid list */
+	for (i = 0; i < DRM_HASH_SIZE; i++) {
+		for (pt = dev->magiclist[i].head; pt; pt = next) {
+			next = pt->next;
+			drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
+		}
+		dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+	}
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+				/* Clear AGP information */
+	if (dev->agp) {
+		drm_agp_mem_t *temp;
+		drm_agp_mem_t *temp_next;
+
+		temp = dev->agp->memory;
+		while(temp != NULL) {
+			temp_next = temp->next;
+			drm_free_agp(temp->memory, temp->pages);
+			drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS);
+			temp = temp_next;
+		}
+		if (dev->agp->acquired) _drm_agp_release();
+	}
+#endif
+				/* Clear vma list (only built for debugging) */
+	if (dev->vmalist) {
+		for (vma = dev->vmalist; vma; vma = vma_next) {
+			vma_next = vma->next;
+			drm_free(vma, sizeof(*vma), DRM_MEM_VMAS);
+		}
+		dev->vmalist = NULL;
+	}
+
+				/* Clear map area and mtrr information */
+	if (dev->maplist) {
+		for (i = 0; i < dev->map_count; i++) {
+			map = dev->maplist[i];
+			switch (map->type) {
+			case _DRM_REGISTERS:
+			case _DRM_FRAME_BUFFER:
+#ifdef CONFIG_MTRR
+				if (map->mtrr >= 0) {
+					int retcode;
+					retcode = mtrr_del(map->mtrr,
+							   map->offset,
+							   map->size);
+					DRM_DEBUG("mtrr_del = %d\n", retcode);
+				}
+#endif
+				drm_ioremapfree(map->handle, map->size);
+				break;
+			case _DRM_SHM:
+				drm_free_pages((unsigned long)map->handle,
+					       drm_order(map->size)
+					       - PAGE_SHIFT,
+					       DRM_MEM_SAREA);
+				break;
+			case _DRM_AGP:
+				/* Do nothing here, because this is all
+                                   handled in the AGP/GART driver. */
+				break;
+			}
+			drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+		}
+		drm_free(dev->maplist,
+			 dev->map_count * sizeof(*dev->maplist),
+			 DRM_MEM_MAPS);
+		dev->maplist   = NULL;
+		dev->map_count = 0;
+	}
+
+	if (dev->lock.hw_lock) {
+		dev->lock.hw_lock    = NULL; /* SHM removed */
+		dev->lock.pid	     = 0;
+		wake_up_interruptible(&dev->lock.lock_queue);
+	}
+	up(&dev->struct_sem);
+
+	return 0;
+}
+
+/* tdfx_init is called via init_module at module load time, or via
+ * linux/init/main.c (this is not currently supported). */
+
+static int __init tdfx_init(void)
+{
+	int		      retcode;
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+
+	memset((void *)dev, 0, sizeof(*dev));
+	dev->count_lock	  = SPIN_LOCK_UNLOCKED;
+	sema_init(&dev->struct_sem, 1);
+
+#ifdef MODULE
+	drm_parse_options(tdfx);
+#endif
+
+	if ((retcode = misc_register(&tdfx_misc))) {
+		DRM_ERROR("Cannot register \"%s\"\n", TDFX_NAME);
+		return retcode;
+	}
+	dev->device = MKDEV(MISC_MAJOR, tdfx_misc.minor);
+	dev->name   = TDFX_NAME;
+
+	drm_mem_init();
+	drm_proc_init(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	dev->agp    = drm_agp_init();
+#endif
+	if((retcode = drm_ctxbitmap_init(dev))) {
+		DRM_ERROR("Cannot allocate memory for context bitmap.\n");
+		drm_proc_cleanup();
+		misc_deregister(&tdfx_misc);
+		tdfx_takedown(dev);
+		return retcode;
+	}
+
+	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
+		 TDFX_NAME,
+		 TDFX_MAJOR,
+		 TDFX_MINOR,
+		 TDFX_PATCHLEVEL,
+		 TDFX_DATE,
+		 tdfx_misc.minor);
+
+	return 0;
+}
+
+/* tdfx_cleanup is called via cleanup_module at module unload time. */
+
+static void __exit tdfx_cleanup(void)
+{
+	drm_device_t	      *dev = &tdfx_device;
+
+	DRM_DEBUG("\n");
+
+	drm_proc_cleanup();
+	if (misc_deregister(&tdfx_misc)) {
+		DRM_ERROR("Cannot unload module\n");
+	} else {
+		DRM_INFO("Module unloaded\n");
+	}
+	drm_ctxbitmap_cleanup(dev);
+	tdfx_takedown(dev);
+#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE)
+	if (dev->agp) {
+		drm_agp_uninit();
+		drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS);
+		dev->agp = NULL;
+	}
+#endif
+}
+
+module_init(tdfx_init);
+module_exit(tdfx_cleanup);
+
+
+int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd,
+		  unsigned long arg)
+{
+	drm_version_t version;
+	int	      len;
+
+	if (copy_from_user(&version,
+			   (drm_version_t *)arg,
+			   sizeof(version)))
+		return -EFAULT;
+
+#define DRM_COPY(name,value)				     \
+	len = strlen(value);				     \
+	if (len > name##_len) len = name##_len;		     \
+	name##_len = strlen(value);			     \
+	if (len && name) {				     \
+		if (copy_to_user(name, value, len))	     \
+			return -EFAULT;			     \
+	}
+
+	version.version_major	   = TDFX_MAJOR;
+	version.version_minor	   = TDFX_MINOR;
+	version.version_patchlevel = TDFX_PATCHLEVEL;
+
+	DRM_COPY(version.name, TDFX_NAME);
+	DRM_COPY(version.date, TDFX_DATE);
+	DRM_COPY(version.desc, TDFX_DESC);
+
+	if (copy_to_user((drm_version_t *)arg,
+			 &version,
+			 sizeof(version)))
+		return -EFAULT;
+	return 0;
+}
+
+int tdfx_open(struct inode *inode, struct file *filp)
+{
+	drm_device_t  *dev    = &tdfx_device;
+	int	      retcode = 0;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_open_helper(inode, filp, dev))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_open);
+		spin_lock(&dev->count_lock);
+		if (!dev->open_count++) {
+			spin_unlock(&dev->count_lock);
+			return tdfx_setup(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+	return retcode;
+}
+
+int tdfx_release(struct inode *inode, struct file *filp)
+{
+	drm_file_t    *priv   = filp->private_data;
+	drm_device_t  *dev;
+	int	      retcode = 0;
+
+	lock_kernel();
+	dev = priv->dev;
+
+	DRM_DEBUG("open_count = %d\n", dev->open_count);
+	if (!(retcode = drm_release(inode, filp))) {
+#if LINUX_VERSION_CODE < 0x020333
+		MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+		atomic_inc(&dev->total_close);
+		spin_lock(&dev->count_lock);
+		if (!--dev->open_count) {
+			if (atomic_read(&dev->ioctl_count) || dev->blocked) {
+				DRM_ERROR("Device busy: %d %d\n",
+					  atomic_read(&dev->ioctl_count),
+					  dev->blocked);
+				spin_unlock(&dev->count_lock);
+				unlock_kernel();
+				return -EBUSY;
+			}
+			spin_unlock(&dev->count_lock);
+			unlock_kernel();
+			return tdfx_takedown(dev);
+		}
+		spin_unlock(&dev->count_lock);
+	}
+
+	unlock_kernel();
+	return retcode;
+}
+
+/* tdfx_ioctl is called whenever a process performs an ioctl on /dev/drm. */
+
+int tdfx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+		unsigned long arg)
+{
+	int		 nr	 = DRM_IOCTL_NR(cmd);
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	int		 retcode = 0;
+	drm_ioctl_desc_t *ioctl;
+	drm_ioctl_t	 *func;
+
+	atomic_inc(&dev->ioctl_count);
+	atomic_inc(&dev->total_ioctl);
+	++priv->ioctl_count;
+
+	DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n",
+		  current->pid, cmd, nr, dev->device, priv->authenticated);
+
+	if (nr >= TDFX_IOCTL_COUNT) {
+		retcode = -EINVAL;
+	} else {
+		ioctl	  = &tdfx_ioctls[nr];
+		func	  = ioctl->func;
+
+		if (!func) {
+			DRM_DEBUG("no function\n");
+			retcode = -EINVAL;
+		} else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN))
+			    || (ioctl->auth_needed && !priv->authenticated)) {
+			retcode = -EACCES;
+		} else {
+			retcode = (func)(inode, filp, cmd, arg);
+		}
+	}
+
+	atomic_dec(&dev->ioctl_count);
+	return retcode;
+}
+
+int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd,
+	      unsigned long arg)
+{
+        drm_file_t        *priv   = filp->private_data;
+        drm_device_t      *dev    = priv->dev;
+        DECLARE_WAITQUEUE(entry, current);
+        int               ret   = 0;
+        drm_lock_t        lock;
+#if DRM_DMA_HISTOGRAM
+        cycles_t          start;
+
+        dev->lck_start = start = get_cycles();
+#endif
+
+        if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+        if (lock.context == DRM_KERNEL_CONTEXT) {
+                DRM_ERROR("Process %d using kernel context %d\n",
+                          current->pid, lock.context);
+                return -EINVAL;
+        }
+
+        DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n",
+                  lock.context, current->pid, dev->lock.hw_lock->lock,
+                  lock.flags);
+
+#if 0
+				/* dev->queue_count == 0 right now for
+                                   tdfx.  FIXME? */
+        if (lock.context < 0 || lock.context >= dev->queue_count)
+                return -EINVAL;
+#endif
+
+        if (!ret) {
+#if 0
+                if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)
+                    != lock.context) {
+                        long j = jiffies - dev->lock.lock_time;
+
+                        if (lock.context == tdfx_res_ctx.handle &&
+				j >= 0 && j < DRM_LOCK_SLICE) {
+                                /* Can't take lock if we just had it and
+                                   there is contention. */
+                                DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n",
+					lock.context, current->pid, j,
+					dev->lock.lock_time, jiffies);
+                                current->state = TASK_INTERRUPTIBLE;
+				current->policy |= SCHED_YIELD;
+                                schedule_timeout(DRM_LOCK_SLICE-j);
+				DRM_DEBUG("jiffies=%d\n", jiffies);
+                        }
+                }
+#endif
+                add_wait_queue(&dev->lock.lock_queue, &entry);
+                for (;;) {
+                        current->state = TASK_INTERRUPTIBLE;
+                        if (!dev->lock.hw_lock) {
+                                /* Device has been unregistered */
+                                ret = -EINTR;
+                                break;
+                        }
+                        if (drm_lock_take(&dev->lock.hw_lock->lock,
+                                          lock.context)) {
+                                dev->lock.pid       = current->pid;
+                                dev->lock.lock_time = jiffies;
+                                atomic_inc(&dev->total_locks);
+                                break;  /* Got lock */
+                        }
+
+                                /* Contention */
+                        atomic_inc(&dev->total_sleeps);
+#if 1
+			current->policy |= SCHED_YIELD;
+#endif
+                        schedule();
+                        if (signal_pending(current)) {
+                                ret = -ERESTARTSYS;
+                                break;
+                        }
+                }
+                current->state = TASK_RUNNING;
+                remove_wait_queue(&dev->lock.lock_queue, &entry);
+        }
+
+#if 0
+	if (!ret && dev->last_context != lock.context &&
+		lock.context != tdfx_res_ctx.handle &&
+		dev->last_context != tdfx_res_ctx.handle) {
+		add_wait_queue(&dev->context_wait, &entry);
+	        current->state = TASK_INTERRUPTIBLE;
+                /* PRE: dev->last_context != lock.context */
+	        tdfx_context_switch(dev, dev->last_context, lock.context);
+		/* POST: we will wait for the context
+                   switch and will dispatch on a later call
+                   when dev->last_context == lock.context
+                   NOTE WE HOLD THE LOCK THROUGHOUT THIS
+                   TIME! */
+		current->policy |= SCHED_YIELD;
+	        schedule();
+	        current->state = TASK_RUNNING;
+	        remove_wait_queue(&dev->context_wait, &entry);
+	        if (signal_pending(current)) {
+	                ret = -EINTR;
+	        } else if (dev->last_context != lock.context) {
+			DRM_ERROR("Context mismatch: %d %d\n",
+                        	dev->last_context, lock.context);
+	        }
+	}
+#endif
+
+        if (!ret) {
+		sigemptyset(&dev->sigmask);
+		sigaddset(&dev->sigmask, SIGSTOP);
+		sigaddset(&dev->sigmask, SIGTSTP);
+		sigaddset(&dev->sigmask, SIGTTIN);
+		sigaddset(&dev->sigmask, SIGTTOU);
+		dev->sigdata.context = lock.context;
+		dev->sigdata.lock    = dev->lock.hw_lock;
+		block_all_signals(drm_notifier, &dev->sigdata, &dev->sigmask);
+
+                if (lock.flags & _DRM_LOCK_READY) {
+				/* Wait for space in DMA/FIFO */
+		}
+                if (lock.flags & _DRM_LOCK_QUIESCENT) {
+				/* Make hardware quiescent */
+#if 0
+                        tdfx_quiescent(dev);
+#endif
+		}
+        }
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY/4;
+	}
+#endif
+        DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock");
+
+#if DRM_DMA_HISTOGRAM
+        atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]);
+#endif
+
+        return ret;
+}
+
+
+int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd,
+		 unsigned long arg)
+{
+	drm_file_t	  *priv	  = filp->private_data;
+	drm_device_t	  *dev	  = priv->dev;
+	drm_lock_t	  lock;
+
+	if (copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)))
+		return -EFAULT;
+
+	if (lock.context == DRM_KERNEL_CONTEXT) {
+		DRM_ERROR("Process %d using kernel context %d\n",
+			  current->pid, lock.context);
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("%d frees lock (%d holds)\n",
+		  lock.context,
+		  _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock));
+	atomic_inc(&dev->total_unlocks);
+	if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock))
+		atomic_inc(&dev->total_contends);
+	drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT);
+				/* FIXME: Try to send data to card here */
+	if (!dev->context_flag) {
+		if (drm_lock_free(dev, &dev->lock.hw_lock->lock,
+				  DRM_KERNEL_CONTEXT)) {
+			DRM_ERROR("\n");
+		}
+	}
+
+#if LINUX_VERSION_CODE < 0x020400
+	if (lock.context != tdfx_res_ctx.handle) {
+		current->counter = 5;
+		current->priority = DEF_PRIORITY;
+	}
+#endif
+
+	unblock_all_signals();
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/tdfx_drv.h linux/drivers/char/drm-4.0/tdfx_drv.h
--- linux.orig/drivers/char/drm-4.0/tdfx_drv.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/tdfx_drv.h	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,67 @@
+/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*-
+ * Created: Thu Oct  7 10:40:04 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Daryll Strauss <daryll@valinux.com>
+ * 
+ */
+
+#ifndef _TDFX_DRV_H_
+#define _TDFX_DRV_H_
+
+				/* tdfx_drv.c */
+extern int  tdfx_version(struct inode *inode, struct file *filp,
+			  unsigned int cmd, unsigned long arg);
+extern int  tdfx_open(struct inode *inode, struct file *filp);
+extern int  tdfx_release(struct inode *inode, struct file *filp);
+extern int  tdfx_ioctl(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_lock(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+extern int  tdfx_unlock(struct inode *inode, struct file *filp,
+			 unsigned int cmd, unsigned long arg);
+
+				/* tdfx_context.c */
+
+extern int  tdfx_resctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_addctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_modctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_getctx(struct inode *inode, struct file *filp,
+		        unsigned int cmd, unsigned long arg);
+extern int  tdfx_switchctx(struct inode *inode, struct file *filp,
+			   unsigned int cmd, unsigned long arg);
+extern int  tdfx_newctx(struct inode *inode, struct file *filp,
+			unsigned int cmd, unsigned long arg);
+extern int  tdfx_rmctx(struct inode *inode, struct file *filp,
+		       unsigned int cmd, unsigned long arg);
+
+extern int  tdfx_context_switch(drm_device_t *dev, int old, int new);
+extern int  tdfx_context_switch_complete(drm_device_t *dev, int new);
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/drm-4.0/vm.c linux/drivers/char/drm-4.0/vm.c
--- linux.orig/drivers/char/drm-4.0/vm.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/char/drm-4.0/vm.c	Wed Jan  9 21:56:59 2002
@@ -0,0 +1,370 @@
+/* vm.c -- Memory mapping for DRM -*- linux-c -*-
+ * Created: Mon Jan  4 08:58:31 1999 by faith@precisioninsight.com
+ *
+ * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
+ * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ * Authors:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *
+ */
+
+#define __NO_VERSION__
+#include "drmP.h"
+
+struct vm_operations_struct   drm_vm_ops = {
+	nopage:	 drm_vm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_shm_ops = {
+	nopage:	 drm_vm_shm_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_shm_lock_ops = {
+	nopage:	 drm_vm_shm_nopage_lock,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+struct vm_operations_struct   drm_vm_dma_ops = {
+	nopage:	 drm_vm_dma_nopage,
+	open:	 drm_vm_open,
+	close:	 drm_vm_close,
+};
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_nopage(struct vm_area_struct *vma,
+			    unsigned long address,
+			    int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_nopage(struct vm_area_struct *vma,
+			   unsigned long address,
+			   int write_access)
+#endif
+{
+	return NOPAGE_SIGBUS;		/* Disallow mremap */
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_shm_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+#if LINUX_VERSION_CODE >= 0x020300
+	drm_map_t	 *map	 = (drm_map_t *)vma->vm_private_data;
+#else
+	drm_map_t	 *map	 = (drm_map_t *)vma->vm_pte;
+#endif
+	unsigned long	 physical;
+	unsigned long	 offset;
+
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!map)    		   return NOPAGE_OOM;  /* Nothing allocated */
+
+	offset	 = address - vma->vm_start;
+	physical = (unsigned long)map->handle + offset;
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx => 0x%08lx\n", address, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+				     unsigned long address,
+				     int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_shm_nopage_lock(struct vm_area_struct *vma,
+				    unsigned long address,
+				    int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dev->lock.hw_lock)    return NOPAGE_OOM;  /* Nothing allocated */
+
+	offset	 = address - vma->vm_start;
+	page	 = offset >> PAGE_SHIFT;
+	physical = (unsigned long)dev->lock.hw_lock + offset;
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+#if LINUX_VERSION_CODE < 0x020317
+unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma,
+				unsigned long address,
+				int write_access)
+#else
+				/* Return type changed in 2.3.23 */
+struct page *drm_vm_dma_nopage(struct vm_area_struct *vma,
+			       unsigned long address,
+			       int write_access)
+#endif
+{
+	drm_file_t	 *priv	 = vma->vm_file->private_data;
+	drm_device_t	 *dev	 = priv->dev;
+	drm_device_dma_t *dma	 = dev->dma;
+	unsigned long	 physical;
+	unsigned long	 offset;
+	unsigned long	 page;
+
+	if (!dma)		   return NOPAGE_SIGBUS; /* Error */
+	if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */
+	if (!dma->pagelist)	   return NOPAGE_OOM ; /* Nothing allocated */
+
+	offset	 = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
+	page	 = offset >> PAGE_SHIFT;
+	physical = dma->pagelist[page] + (offset & (~PAGE_MASK));
+	atomic_inc(&virt_to_page(physical)->count); /* Dec. by kernel */
+
+	DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical);
+#if LINUX_VERSION_CODE < 0x020317
+	return physical;
+#else
+	return virt_to_page(physical);
+#endif
+}
+
+void drm_vm_open(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *vma_entry;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+	atomic_inc(&dev->vma_count);
+#if LINUX_VERSION_CODE < 0x020333
+				/* The map can exist after the fd is closed. */
+	MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+
+
+#if DRM_DEBUG_CODE
+	vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS);
+	if (vma_entry) {
+		down(&dev->struct_sem);
+		vma_entry->vma	= vma;
+		vma_entry->next = dev->vmalist;
+		vma_entry->pid	= current->pid;
+		dev->vmalist	= vma_entry;
+		up(&dev->struct_sem);
+	}
+#endif
+}
+
+void drm_vm_close(struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= vma->vm_file->private_data;
+	drm_device_t	*dev	= priv->dev;
+#if DRM_DEBUG_CODE
+	drm_vma_entry_t *pt, *prev;
+#endif
+
+	DRM_DEBUG("0x%08lx,0x%08lx\n",
+		  vma->vm_start, vma->vm_end - vma->vm_start);
+#if LINUX_VERSION_CODE < 0x020333
+	MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */
+#endif
+	atomic_dec(&dev->vma_count);
+
+#if DRM_DEBUG_CODE
+	down(&dev->struct_sem);
+	for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
+		if (pt->vma == vma) {
+			if (prev) {
+				prev->next = pt->next;
+			} else {
+				dev->vmalist = pt->next;
+			}
+			drm_free(pt, sizeof(*pt), DRM_MEM_VMAS);
+			break;
+		}
+	}
+	up(&dev->struct_sem);
+#endif
+}
+
+int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	 *priv	 = filp->private_data;
+	drm_device_t	 *dev;
+	drm_device_dma_t *dma;
+	unsigned long	 length	 = vma->vm_end - vma->vm_start;
+	
+	lock_kernel();
+	dev	 = priv->dev;
+	dma	 = dev->dma;
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+				/* Length must match exact page count */
+	if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
+		unlock_kernel();
+		return -EINVAL;
+	}
+	unlock_kernel();
+
+	vma->vm_ops   = &drm_vm_dma_ops;
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+	
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
+
+int drm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	drm_file_t	*priv	= filp->private_data;
+	drm_device_t	*dev	= priv->dev;
+	drm_map_t	*map	= NULL;
+	int		i;
+	
+	DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
+		  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+
+	if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma);
+
+				/* A sequential search of a linked list is
+				   fine here because: 1) there will only be
+				   about 5-10 entries in the list and, 2) a
+				   DRI client only has to do this mapping
+				   once, so it doesn't have to be optimized
+				   for performance, even if the list was a
+				   bit longer. */
+	for (i = 0; i < dev->map_count; i++) {
+		map = dev->maplist[i];
+		if (map->offset == VM_OFFSET(vma)) break;
+	}
+	
+	if (i >= dev->map_count) return -EINVAL;
+	if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
+		return -EPERM;
+
+				/* Check for valid size. */
+	if (map->size != vma->vm_end - vma->vm_start) return -EINVAL;
+	
+	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
+		vma->vm_flags &= VM_MAYWRITE;
+#if defined(__i386__)
+		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
+#else
+				/* Ye gads this is ugly.  With more thought
+                                   we could move this up higher and use
+                                   `protection_map' instead.  */
+		vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect(
+			__pte(pgprot_val(vma->vm_page_prot)))));
+#endif
+	}
+
+	switch (map->type) {
+	case _DRM_FRAME_BUFFER:
+	case _DRM_REGISTERS:
+	case _DRM_AGP:
+		if (VM_OFFSET(vma) >= __pa(high_memory)) {
+#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;
+			}
+#elif defined(__ia64__)
+			if (map->type != _DRM_AGP)
+				vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#endif
+			vma->vm_flags |= VM_IO;	/* not in core dump */
+		}
+		if (remap_page_range(vma->vm_start,
+				     VM_OFFSET(vma),
+				     vma->vm_end - vma->vm_start,
+				     vma->vm_page_prot))
+				return -EAGAIN;
+		DRM_DEBUG("   Type = %d; start = 0x%lx, end = 0x%lx,"
+			  " offset = 0x%lx\n",
+			  map->type,
+			  vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+		vma->vm_ops = &drm_vm_ops;
+		break;
+	case _DRM_SHM:
+		if (map->flags & _DRM_CONTAINS_LOCK)
+			vma->vm_ops = &drm_vm_shm_lock_ops;
+		else {
+			vma->vm_ops = &drm_vm_shm_ops;
+#if LINUX_VERSION_CODE >= 0x020300
+			vma->vm_private_data = (void *)map;
+#else
+			vma->vm_pte = (unsigned long)map;
+#endif
+		}
+
+				/* Don't let this area swap.  Change when
+				   DRM_KERNEL advisory is supported. */
+		vma->vm_flags |= VM_LOCKED;
+		break;
+	default:
+		return -EINVAL;	/* This should never happen. */
+	}
+	vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */
+
+#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */
+				/* In Linux 2.2.3 and above, this is
+				   handled in do_mmap() in mm/mmap.c. */
+	++filp->f_count;
+#endif
+	vma->vm_file  =	 filp;	/* Needed for drm_vm_open() */
+	drm_vm_open(vma);
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/eurotechwdt.c linux/drivers/char/eurotechwdt.c
--- linux.orig/drivers/char/eurotechwdt.c	Thu Oct 25 20:53:47 2001
+++ linux/drivers/char/eurotechwdt.c	Thu Jan 17 22:00:03 2002
@@ -233,7 +233,7 @@
         unsigned int cmd, unsigned long arg)
 {
    static struct watchdog_info ident = {
-      options		: WDIOF_CARDRESET,
+      options		: WDIOF_CARDRESET | WDIOF_SETTIMEOUT,
       firmware_version	: 1,
       identity		: "WDT Eurotech CPU-1220/1410"
    };
@@ -265,7 +265,10 @@
 
          eurwdt_timeout = time; 
          eurwdt_set_timeout(time); 
-         return 0;
+	 /* Fall */
+
+      case WDIOC_GETTIMEOUT:
+	 return put_user(eurwdt_timeout, (int *)arg);
    }
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/generic_serial.c linux/drivers/char/generic_serial.c
--- linux.orig/drivers/char/generic_serial.c	Thu Sep 13 22:21:32 2001
+++ linux/drivers/char/generic_serial.c	Mon Feb  4 17:22:38 2002
@@ -143,7 +143,12 @@
 		/* Can't copy more? break out! */
 		if (c <= 0) break;
 		if (from_user)
-			copy_from_user (port->xmit_buf + port->xmit_head, buf, c);
+                       if (copy_from_user (port->xmit_buf + port->xmit_head, 
+                                           buf, c)) {
+                               up (& port->port_write_sem);
+                               return -EFAULT;
+                       }
+
 		else
 			memcpy         (port->xmit_buf + port->xmit_head, buf, c);
 
@@ -214,8 +219,13 @@
 		while (1) {
 			c = count;
 
-			/* This is safe because we "OWN" the "head". Noone else can 
-			   change the "head": we own the port_write_sem. */
+			/* Note: This part can be done without
+			 * interrupt routine protection since
+			 * the interrupt routines may only modify
+			 * shared variables in safe ways, in the worst
+			 * case causing us to loop twice in the code
+			 * below. See comments below. */ 
+
 			/* Don't overrun the end of the buffer */
 			t = SERIAL_XMIT_SIZE - port->xmit_head;
 			if (t < c) c = t;
@@ -506,7 +516,7 @@
 
 void gs_shutdown_port (struct gs_port *port)
 {
-	long flags;
+	unsigned long flags;
 
 	func_enter();
 	
@@ -589,6 +599,7 @@
 	int    do_clocal = 0;
 	int    CD;
 	struct tty_struct *tty;
+	unsigned long flags;
 
 	func_enter ();
 
@@ -604,7 +615,7 @@
 	 * until it's done, and then try again.
 	 */
 	if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) {
-	  interruptible_sleep_on(&port->close_wait);
+		interruptible_sleep_on(&port->close_wait);
 		if (port->flags & ASYNC_HUP_NOTIFY)
 			return -EAGAIN;
 		else
@@ -668,10 +679,11 @@
 	add_wait_queue(&port->open_wait, &wait);
 
 	gs_dprintk (GS_DEBUG_BTR, "after add waitq.\n"); 
+	save_flags(flags);
 	cli();
 	if (!tty_hung_up_p(filp))
 		port->count--;
-	sti();
+	restore_flags(flags);
 	port->blocked_open++;
 	while (1) {
 		CD = port->rd->get_CD (port);
@@ -1003,7 +1015,8 @@
 {
 	struct serial_struct sio;
 
-	copy_from_user(&sio, sp, sizeof(struct serial_struct));
+	if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
+		return(-EFAULT);
 
 	if (!capable(CAP_SYS_ADMIN)) {
 		if ((sio.baud_base != port->baud_base) ||
@@ -1033,7 +1046,7 @@
  *      Generate the serial struct info.
  */
 
-void gs_getserial(struct gs_port *port, struct serial_struct *sp)
+int gs_getserial(struct gs_port *port, struct serial_struct *sp)
 {
 	struct serial_struct    sio;
 
@@ -1055,7 +1068,10 @@
 	if (port->rd->getserial)
 		port->rd->getserial (port, &sio);
 
-	copy_to_user(sp, &sio, sizeof(struct serial_struct));
+	if (copy_to_user(sp, &sio, sizeof(struct serial_struct)))
+		return -EFAULT;
+	return 0;
+
 }
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/i810-tco.c linux/drivers/char/i810-tco.c
--- linux.orig/drivers/char/i810-tco.c	Thu Sep 13 22:21:32 2001
+++ linux/drivers/char/i810-tco.c	Thu Jan 17 22:00:03 2002
@@ -181,7 +181,7 @@
 	/*
 	 *      Shut off the timer.
 	 */
-#ifdef CONFIG_WATCHDOG_NOWAYOUT
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
 	tco_timer_stop ();
 	timer_alive = 0;
 #endif	
@@ -208,8 +208,10 @@
 static int i810tco_ioctl (struct inode *inode, struct file *file,
 			  unsigned int cmd, unsigned long arg)
 {
+	int new_margin, u_margin;
+
 	static struct watchdog_info ident = {
-		0,
+		WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 		0,
 		"i810 TCO timer"
 	};
@@ -229,6 +231,19 @@
 	case WDIOC_KEEPALIVE:
 		tco_timer_reload ();
 		return 0;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(u_margin, (int *) arg))
+			return -EFAULT;
+		new_margin = (u_margin * 10 + 5) / 6;
+		if ((new_margin < 3) || (new_margin > 63))
+			return -EINVAL;
+		if (tco_timer_settimer((unsigned char)new_margin))
+		    return -EINVAL;
+		i810_margin = new_margin;
+		tco_timer_reload();
+		/* Fall */
+	case WDIOC_GETTIMEOUT:
+		return put_user((int)(i810_margin * 6 / 10), (int *) arg);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/ib700wdt.c linux/drivers/char/ib700wdt.c
--- linux.orig/drivers/char/ib700wdt.c	Thu Oct 11 16:07:00 2001
+++ linux/drivers/char/ib700wdt.c	Thu Jan 17 22:00:03 2002
@@ -87,11 +87,34 @@
  *
  */
 
+static int wd_times[] = {
+	30,	/* 0x0 */
+	28,	/* 0x1 */
+	26,	/* 0x2 */
+	24,	/* 0x3 */
+	22,	/* 0x4 */
+	20,	/* 0x5 */
+	18,	/* 0x6 */
+	16,	/* 0x7 */
+	14,	/* 0x8 */
+	12,	/* 0x9 */
+	10,	/* 0xA */
+	8,	/* 0xB */
+	6,	/* 0xC */
+	4,	/* 0xD */
+	2,	/* 0xE */
+	0,	/* 0xF */
+};
+
 #define WDT_STOP 0x441
 #define WDT_START 0x443
 
+/* Default timeout */
 #define WD_TIMO 0		/* 30 seconds +/- 20%, from table */
 
+static int wd_margin = WD_TIMO;
+
+
 /*
  *	Kernel methods.
  */
@@ -100,7 +123,7 @@
 ibwdt_ping(void)
 {
 	/* Write a watchdog value */
-	outb_p(WD_TIMO, WDT_START);
+	outb_p(wd_times[wd_margin], WDT_START);
 }
 
 static ssize_t
@@ -127,8 +150,10 @@
 ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	  unsigned long arg)
 {
+	int i, new_margin;
+
 	static struct watchdog_info ident = {
-		WDIOF_KEEPALIVEPING, 1, "IB700 WDT"
+		WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 1, "IB700 WDT"
 	};
 
 	switch (cmd) {
@@ -146,6 +171,22 @@
 	  ibwdt_ping();
 	  break;
 
+	case WDIOC_SETTIMEOUT:
+	  if (get_user(new_margin, (int *)arg))
+		  return -EFAULT;
+	  if ((new_margin < 0) || (new_margin > 30))
+		  return -EINVAL;
+	  for (i = 0x0F; i > -1; i--)
+		  if (wd_times[i] > new_margin)
+			  break;
+	  wd_margin = i;
+	  ibwdt_ping();
+	  /* Fall */
+
+	case WDIOC_GETTIMEOUT:
+	  return put_user(wd_times[wd_margin], (int *)arg);
+	  break;
+
 	default:
 	  return -ENOTTY;
 	}
@@ -182,7 +223,7 @@
 	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
 		spin_lock(&ibwdt_lock);
 #ifndef CONFIG_WATCHDOG_NOWAYOUT
-		outb_p(WD_TIMO, WDT_STOP);
+		outb_p(wd_times[wd_margin], WDT_STOP);
 #endif
 		ibwdt_is_open = 0;
 		spin_unlock(&ibwdt_lock);
@@ -201,7 +242,7 @@
 {
 	if (code == SYS_DOWN || code == SYS_HALT) {
 		/* Turn the WDT off */
-		outb_p(WD_TIMO, WDT_STOP);
+		outb_p(wd_times[wd_margin], WDT_STOP);
 	}
 	return NOTIFY_DONE;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/joystick/emu10k1-gp.c linux/drivers/char/joystick/emu10k1-gp.c
--- linux.orig/drivers/char/joystick/emu10k1-gp.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/char/joystick/emu10k1-gp.c	Wed Jan 23 20:55:51 2002
@@ -52,6 +52,7 @@
 	
 static struct pci_device_id emu_tbl[] __devinitdata = {
 	{ 0x1102, 0x7002, PCI_ANY_ID, PCI_ANY_ID }, /* SB Live! gameport */
+        { 0x1102, 0x7003, PCI_ANY_ID, PCI_ANY_ID }, /* Audigy! gameport */
 	{ 0, }
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- linux.orig/drivers/char/keyboard.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/keyboard.c	Tue Jan  8 18:08:44 2002
@@ -319,7 +319,7 @@
 			compute_shiftstate();
 			kbd->slockstate = 0; /* play it safe */
 #else
-			keysym =  U(plain_map[keycode]);
+			keysym = U(key_maps[0][keycode]);
 			type = KTYP(keysym);
 			if (type == KT_SHIFT)
 			  (*key_handler[type])(keysym & 0xff, up_flag);
@@ -750,7 +750,7 @@
 	    k = i*BITS_PER_LONG;
 	    for(j=0; j<BITS_PER_LONG; j++,k++)
 	      if(test_bit(k, key_down)) {
-		sym = U(plain_map[k]);
+		sym = U(key_maps[0][k]);
 		if(KTYP(sym) == KT_SHIFT || KTYP(sym) == KT_SLOCK) {
 		  val = KVAL(sym);
 		  if (val == KVAL(K_CAPSSHIFT))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/lp.c linux/drivers/char/lp.c
--- linux.orig/drivers/char/lp.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/lp.c	Mon Jan  7 13:38:04 2002
@@ -650,7 +650,7 @@
 	do {
 		/* Write the data, converting LF->CRLF as we go. */
 		ssize_t canwrite = count;
-		char *lf = strchr (s, '\n');
+		char *lf = memchr (s, '\n', count);
 		if (lf)
 			canwrite = lf - s;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/mwave/mwavedd.c linux/drivers/char/mwave/mwavedd.c
--- linux.orig/drivers/char/mwave/mwavedd.c	Thu Oct 11 16:14:32 2001
+++ linux/drivers/char/mwave/mwavedd.c	Wed Feb 13 17:37:40 2002
@@ -462,7 +462,7 @@
 * mwave_exit is called on module unload
 * mwave_exit is also used to clean up after an aborted mwave_init
 */
-static void __exit mwave_exit(void)
+static void mwave_exit(void)
 {
 	pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/mxser.c linux/drivers/char/mxser.c
--- linux.orig/drivers/char/mxser.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/mxser.c	Mon Jan 14 18:53:53 2002
@@ -614,38 +614,35 @@
 		n = (sizeof(mxser_pcibrds) / sizeof(mxser_pcibrds[0])) - 1;
 		index = 0;
 		for (b = 0; b < n; b++) {
-			pdev = pci_find_device(mxser_pcibrds[b].vendor,
-					       mxser_pcibrds[b].device, pdev);
-			if (!pdev || pci_enable_device(pdev))
-				continue;
-			hwconf.pdev = pdev;
-			printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n",
-				mxser_brdname[mxser_pcibrds[b].driver_data],
-				pdev->bus->number, PCI_SLOT(pdev->devfn));
-			if (m >= MXSER_BOARDS) {
-				printk("Too many Smartio family boards found (maximum %d),board not configured\n", MXSER_BOARDS);
-			} else {
-				retval = mxser_get_PCI_conf(pdev,
-				   mxser_pcibrds[b].driver_data, &hwconf);
-				if (retval < 0) {
-					if (retval == MXSER_ERR_IRQ)
-						printk("Invalid interrupt number,board not configured\n");
-					else if (retval == MXSER_ERR_IRQ_CONFLIT)
-						printk("Invalid interrupt number,board not configured\n");
-					else if (retval == MXSER_ERR_VECTOR)
-						printk("Invalid interrupt vector,board not configured\n");
-					else if (retval == MXSER_ERR_IOADDR)
-						printk("Invalid I/O address,board not configured\n");
+			while (pdev = pci_find_device(mxser_pcibrds[b].vendor, mxser_pcibrds[b].device, pdev)) 
+			{
+				if (pci_enable_device(pdev))
 					continue;
-
+				hwconf.pdev = pdev;
+				printk("Found MOXA %s board(BusNo=%d,DevNo=%d)\n",
+					mxser_brdname[mxser_pcibrds[b].driver_data],
+				pdev->bus->number, PCI_SLOT(pdev->devfn));
+				if (m >= MXSER_BOARDS) {
+					printk("Too many Smartio family boards found (maximum %d),board not configured\n", MXSER_BOARDS);
+				} else {
+					retval = mxser_get_PCI_conf(pdev, mxser_pcibrds[b].driver_data, &hwconf);
+					if (retval < 0) {
+						if (retval == MXSER_ERR_IRQ)
+							printk("Invalid interrupt number,board not configured\n");
+						else if (retval == MXSER_ERR_IRQ_CONFLIT)
+							printk("Invalid interrupt number,board not configured\n");	
+						else if (retval == MXSER_ERR_VECTOR)
+							printk("Invalid interrupt vector,board not configured\n");
+						else if (retval == MXSER_ERR_IOADDR)
+							printk("Invalid I/O address,board not configured\n");
+						continue;
+					}
+					if (mxser_initbrd(m, &hwconf) < 0)
+						continue;
+					mxser_getcfg(m, &hwconf);
+					m++;
 				}
-				if (mxser_initbrd(m, &hwconf) < 0)
-					continue;
-				mxser_getcfg(m, &hwconf);
-				m++;
-
 			}
-
 		}
 	}
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/n_hdlc.c linux/drivers/char/n_hdlc.c
--- linux.orig/drivers/char/n_hdlc.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/n_hdlc.c	Fri Dec 21 19:42:08 2001
@@ -9,7 +9,7 @@
  *	Al Longyear <longyear@netcom.com>, Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
  *
  * Original release 01/11/99
- * $Id: n_hdlc.c,v 3.2 2000/11/06 22:34:38 paul Exp $
+ * $Id: n_hdlc.c,v 3.3 2001/11/08 16:16:03 paulkf Exp $
  *
  * This code is released under the GNU General Public License (GPL)
  *
@@ -78,7 +78,7 @@
  */
 
 #define HDLC_MAGIC 0x239e
-#define HDLC_VERSION "3.2"
+#define HDLC_VERSION "$Revision: 3.3 $"
 
 #include <linux/version.h>
 #include <linux/config.h>
@@ -160,11 +160,6 @@
 	struct tty_struct *tty;		/* ptr to TTY structure	*/
 	struct tty_struct *backup_tty;	/* TTY to use if tty gets closed */
 	
-	/* Queues for select() functionality */
-	wait_queue_head_t read_wait;
-	wait_queue_head_t write_wait;
-	wait_queue_head_t poll_wait;
-
 	int		tbusy;		/* reentrancy flag for tx wakeup code */
 	int		woke_up;
 	N_HDLC_BUF	*tbuf;		/* currently transmitting tx buffer */
@@ -235,9 +230,8 @@
 		printk("%s(%d)n_hdlc_release() called\n",__FILE__,__LINE__);
 		
 	/* Ensure that the n_hdlcd process is not hanging on select()/poll() */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
-	wake_up_interruptible (&n_hdlc->write_wait);
+	wake_up_interruptible (&tty->read_wait);
+	wake_up_interruptible (&tty->write_wait);
 
 	if (tty != NULL && tty->disc_data == n_hdlc)
 		tty->disc_data = NULL;	/* Break the tty->n_hdlc link */
@@ -432,8 +426,7 @@
 			n_hdlc->tbuf = NULL;
 			
 			/* wait up sleeping writers */
-			wake_up_interruptible(&n_hdlc->write_wait);
-			wake_up_interruptible(&n_hdlc->poll_wait);
+			wake_up_interruptible(&tty->write_wait);
 	
 			/* get next pending transmit buffer */
 			tbuf = n_hdlc_buf_get(&n_hdlc->tx_buf_list);
@@ -575,8 +568,7 @@
 	n_hdlc_buf_put(&n_hdlc->rx_buf_list,buf);
 	
 	/* wake up any blocked reads and perform async signalling */
-	wake_up_interruptible (&n_hdlc->read_wait);
-	wake_up_interruptible (&n_hdlc->poll_wait);
+	wake_up_interruptible (&tty->read_wait);
 	if (n_hdlc->tty->fasync != NULL)
 		kill_fasync (&n_hdlc->tty->fasync, SIGIO, POLL_IN);
 
@@ -621,6 +613,9 @@
 	}
 
 	for (;;) {
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
+			return -EIO;
+
 		n_hdlc = tty2n_hdlc (tty);
 		if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
 			 tty != n_hdlc->tty)
@@ -634,7 +629,7 @@
 		if (file->f_flags & O_NONBLOCK)
 			return -EAGAIN;
 			
-		interruptible_sleep_on (&n_hdlc->read_wait);
+		interruptible_sleep_on (&tty->read_wait);
 		if (signal_pending(current))
 			return -EINTR;
 	}
@@ -703,7 +698,7 @@
 		count = maxframe;
 	}
 	
-	add_wait_queue(&n_hdlc->write_wait, &wait);
+	add_wait_queue(&tty->write_wait, &wait);
 	set_current_state(TASK_INTERRUPTIBLE);
 	
 	/* Allocate transmit buffer */
@@ -726,7 +721,7 @@
 	}
 
 	set_current_state(TASK_RUNNING);
-	remove_wait_queue(&n_hdlc->write_wait, &wait);
+	remove_wait_queue(&tty->write_wait, &wait);
 
 	if (!error) {		
 		/* Retrieve the user's buffer */
@@ -836,12 +831,14 @@
 	if (n_hdlc && n_hdlc->magic == HDLC_MAGIC && tty == n_hdlc->tty) {
 		/* queue current process into any wait queue that */
 		/* may awaken in the future (read and write) */
-		poll_wait(filp, &n_hdlc->poll_wait, wait);
+
+		poll_wait(filp, &tty->read_wait, wait);
+		poll_wait(filp, &tty->write_wait, wait);
 
 		/* set bits for operations that wont block */
 		if(n_hdlc->rx_buf_list.head)
 			mask |= POLLIN | POLLRDNORM;	/* readable */
-		if(tty->flags & (1 << TTY_OTHER_CLOSED))
+		if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
 			mask |= POLLHUP;
 		if(tty_hung_up_p(filp))
 			mask |= POLLHUP;
@@ -895,11 +892,7 @@
 	
 	/* Initialize the control block */
 	n_hdlc->magic  = HDLC_MAGIC;
-
 	n_hdlc->flags  = 0;
-	init_waitqueue_head(&n_hdlc->read_wait);
-	init_waitqueue_head(&n_hdlc->poll_wait);
-	init_waitqueue_head(&n_hdlc->write_wait);
 	
 	return n_hdlc;
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/ppdev.c linux/drivers/char/ppdev.c
--- linux.orig/drivers/char/ppdev.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/ppdev.c	Wed Jan  9 16:14:20 2002
@@ -324,6 +324,7 @@
 	case PPCLAIM:
 	    {
 		struct ieee1284_info *info;
+		int ret;
 
 		if (pp->flags & PP_CLAIMED) {
 			printk (KERN_DEBUG CHRDEV
@@ -339,7 +340,10 @@
 			}
 		}
 
-		parport_claim_or_block (pp->pdev);
+		ret = parport_claim_or_block (pp->pdev);
+		if (ret < 0)
+			return ret;
+
 		pp->flags |= PP_CLAIMED;
 
 		/* For interrupt-reporting to work, we need to be
@@ -433,8 +437,12 @@
 	    {
 		unsigned int modes;
 
-		modes = pp->pdev->port->modes;
-		if (copy_to_user ((unsigned int *)arg, &modes, sizeof (port->modes))) {
+		port = parport_find_number (minor);
+		if (!port)
+			return -ENODEV;
+
+		modes = port->modes;
+		if (copy_to_user ((unsigned int *)arg, &modes, sizeof (modes))) {
 			return -EFAULT;
 		}
 		return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/rio/rio_linux.c linux/drivers/char/rio/rio_linux.c
--- linux.orig/drivers/char/rio/rio_linux.c	Thu Oct 25 20:53:47 2001
+++ linux/drivers/char/rio/rio_linux.c	Mon Feb  4 17:22:38 2002
@@ -742,7 +742,7 @@
   case TIOCGSERIAL:
     if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
                           sizeof(struct serial_struct))) == 0)
-      gs_getserial(&PortP->gs, (struct serial_struct *) arg);
+      rc = gs_getserial(&PortP->gs, (struct serial_struct *) arg);
     break;
   case TCSBRK:
     if ( PortP->State & RIO_DELETED ) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/serial.c linux/drivers/char/serial.c
--- linux.orig/drivers/char/serial.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/serial.c	Wed Jan 23 21:43:25 2002
@@ -231,8 +231,8 @@
 #include <asm/irq.h>
 #include <asm/bitops.h>
 
-#ifdef CONFIG_MAC_SERIAL
-#define SERIAL_DEV_OFFSET	2
+#if defined(CONFIG_MAC_SERIAL)
+#define SERIAL_DEV_OFFSET	((_machine == _MACH_prep || _machine == _MACH_chrp) ? 0 : 2)
 #else
 #define SERIAL_DEV_OFFSET	0
 #endif
@@ -5393,6 +5393,7 @@
 #endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET;
+	serial_driver.name_base = SERIAL_DEV_OFFSET;
 	serial_driver.num = NR_PORTS;
 	serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
 	serial_driver.subtype = SERIAL_TYPE_NORMAL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/serial_tx3912.c linux/drivers/char/serial_tx3912.c
--- linux.orig/drivers/char/serial_tx3912.c	Fri Nov  9 22:01:21 2001
+++ linux/drivers/char/serial_tx3912.c	Mon Feb  4 17:22:38 2002
@@ -673,7 +673,7 @@
 	case TIOCGSERIAL:
 		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
 		                      sizeof(struct serial_struct))) == 0)
-			gs_getserial(&port->gs, (struct serial_struct *) arg);
+			rc = gs_getserial(&port->gs, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sh-sci.c linux/drivers/char/sh-sci.c
--- linux.orig/drivers/char/sh-sci.c	Mon Oct 15 20:36:48 2001
+++ linux/drivers/char/sh-sci.c	Mon Feb  4 17:22:38 2002
@@ -919,7 +919,7 @@
 	case TIOCGSERIAL:
 		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
 		                      sizeof(struct serial_struct))) == 0)
-			gs_getserial(&port->gs, (struct serial_struct *) arg);
+			rc = gs_getserial(&port->gs, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/shwdt.c linux/drivers/char/shwdt.c
--- linux.orig/drivers/char/shwdt.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/shwdt.c	Fri Dec 21 18:00:38 2001
@@ -10,7 +10,6 @@
  * 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/init.h>
@@ -177,7 +176,7 @@
  * 	sh_wdt_read - Read from Device
  *
  * 	@file: file handle of device
- * 	@char: buffer to write to
+ * 	@buf: buffer to write to
  * 	@count: length of buffer
  * 	@ppos: offset
  *
@@ -193,7 +192,7 @@
  * 	sh_wdt_write - Write to Device
  *
  * 	@file: file handle of device
- * 	@char: buffer to write
+ * 	@buf: buffer to write
  * 	@count: length of buffer
  * 	@ppos: offset
  *
@@ -269,7 +268,7 @@
 static int sh_wdt_notify_sys(struct notifier_block *this,
 			     unsigned long code, void *unused)
 {
-	if (code == SYS_DOWN || SYS_HALT) {
+	if (code == SYS_DOWN || code == SYS_HALT) {
 		sh_wdt_stop();
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/softdog.c linux/drivers/char/softdog.c
--- linux.orig/drivers/char/softdog.c	Sun Sep 30 19:26:05 2001
+++ linux/drivers/char/softdog.c	Thu Jan 17 22:00:03 2002
@@ -26,6 +26,11 @@
  *
  *  19980911 Alan Cox
  *	Made SMP safe for 2.3.x
+ *
+ *  20011127 Joel Becker (jlbec@evilplan.org>
+ *	Added soft_noboot; Allows testing the softdog trigger without 
+ *	requiring a recompile.
+ *	Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
  */
  
 #include <linux/module.h>
@@ -44,8 +49,14 @@
 #define TIMER_MARGIN	60		/* (secs) Default is 1 minute */
 
 static int soft_margin = TIMER_MARGIN;	/* in seconds */
+#ifdef ONLY_TESTING
+static int soft_noboot = 1;
+#else
+static int soft_noboot = 0;
+#endif  /* ONLY_TESTING */
 
 MODULE_PARM(soft_margin,"i");
+MODULE_PARM(soft_noboot,"i");
 MODULE_LICENSE("GPL");
 
 /*
@@ -66,13 +77,14 @@
  
 static void watchdog_fire(unsigned long data)
 {
-#ifdef ONLY_TESTING
-		printk(KERN_CRIT "SOFTDOG: Would Reboot.\n");
-#else
-	printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
-	machine_restart(NULL);
-	printk("WATCHDOG: Reboot didn't ?????\n");
-#endif
+	if (soft_noboot)
+		printk(KERN_CRIT "SOFTDOG: Triggered - Reboot ignored.\n");
+	else
+	{
+		printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
+		machine_restart(NULL);
+		printk("SOFTDOG: Reboot didn't ?????\n");
+	}
 }
 
 /*
@@ -128,8 +140,11 @@
 static int softdog_ioctl(struct inode *inode, struct file *file,
 	unsigned int cmd, unsigned long arg)
 {
+	int new_margin;
 	static struct watchdog_info ident = {
-		identity: "Software Watchdog",
+		WDIOF_SETTIMEOUT,
+		0,
+		"Software Watchdog"
 	};
 	switch (cmd) {
 		default:
@@ -144,6 +159,16 @@
 		case WDIOC_KEEPALIVE:
 			mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
 			return 0;
+		case WDIOC_SETTIMEOUT:
+			if (get_user(new_margin, (int *)arg))
+				return -EFAULT;
+			if (new_margin < 1)
+				return -EINVAL;
+			soft_margin = new_margin;
+			mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
+			/* Fall */
+		case WDIOC_GETTIMEOUT:
+			return put_user(soft_margin, (int *)arg);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sonypi.c linux/drivers/char/sonypi.c
--- linux.orig/drivers/char/sonypi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/sonypi.c	Thu Feb  7 16:58:52 2002
@@ -109,25 +109,29 @@
         return result;
 }
 
-static void sonypi_ecrset(u16 addr, u16 value) {
+static void sonypi_ecrset(u8 addr, u8 value) {
 
-	wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3);
-	outw_p(0x81, SONYPI_CST_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(addr, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(value, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-}
-
-static u16 sonypi_ecrget(u16 addr) {
-
-	wait_on_command(1, inw_p(SONYPI_CST_IOPORT) & 3);
-	outw_p(0x80, SONYPI_CST_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	outw_p(addr, SONYPI_DATA_IOPORT);
-	wait_on_command(0, inw_p(SONYPI_CST_IOPORT) & 2);
-	return inw_p(SONYPI_DATA_IOPORT);
+	wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3);
+	outb_p(0x81, SONYPI_CST_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(addr, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(value, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+}
+
+static u8 sonypi_ecrget(u8 addr) {
+
+	wait_on_command(1, inb_p(SONYPI_CST_IOPORT) & 3);
+	outb_p(0x80, SONYPI_CST_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	outb_p(addr, SONYPI_DATA_IOPORT);
+	wait_on_command(0, inb_p(SONYPI_CST_IOPORT) & 2);
+	return inb_p(SONYPI_DATA_IOPORT);
+}
+
+static u16 sonypi_ecrget16(u8 addr) {
+	return sonypi_ecrget(addr) | (sonypi_ecrget(addr + 1) << 8);
 }
 
 /* Initializes the device - this comes from the AML code in the ACPI bios */
@@ -286,19 +290,38 @@
 	sonypi_device.camera_power = 1;
 }
 
+/* sets the bluetooth subsystem power state */
+static void sonypi_setbluetoothpower(u8 state) {
+
+	state = (state != 0);
+	if (sonypi_device.bluetooth_power && state) 
+		return;
+	if (!sonypi_device.bluetooth_power && !state) 
+		return;
+	
+	sonypi_call2(0x96, state);
+	sonypi_call1(0x93);
+	sonypi_device.bluetooth_power = state;
+}
+
 /* Interrupt handler: some event is available */
 void sonypi_irq(int irq, void *dev_id, struct pt_regs *regs) {
 	u8 v1, v2, event = 0;
 	int i;
 	u8 sonypi_jogger_ev, sonypi_fnkey_ev;
+	u8 sonypi_capture_ev, sonypi_bluetooth_ev;
 
 	if (sonypi_device.model == SONYPI_DEVICE_MODEL_TYPE2) {
 		sonypi_jogger_ev = SONYPI_TYPE2_JOGGER_EV;
 		sonypi_fnkey_ev = SONYPI_TYPE2_FNKEY_EV;
+		sonypi_capture_ev = SONYPI_TYPE2_CAPTURE_EV;
+		sonypi_bluetooth_ev = SONYPI_TYPE2_BLUETOOTH_EV;
 	}
 	else {
 		sonypi_jogger_ev = SONYPI_TYPE1_JOGGER_EV;
 		sonypi_fnkey_ev = SONYPI_TYPE1_FNKEY_EV;
+		sonypi_capture_ev = SONYPI_TYPE1_CAPTURE_EV;
+		sonypi_bluetooth_ev = SONYPI_TYPE1_BLUETOOTH_EV;
 	}
 
 	v1 = inb_p(sonypi_device.ioport1);
@@ -318,7 +341,7 @@
 				goto found;
 			}
 	}
-	if ((v2 & SONYPI_CAPTURE_EV) == SONYPI_CAPTURE_EV) {
+	if ((v2 & sonypi_capture_ev) == sonypi_capture_ev) {
 		for (i = 0; sonypi_captureev[i].event; i++)
 			if (sonypi_captureev[i].data == v1) {
 				event = sonypi_captureev[i].event;
@@ -332,7 +355,7 @@
 				goto found;
 			}
 	}
-	if ((v2 & SONYPI_BLUETOOTH_EV) == SONYPI_BLUETOOTH_EV) {
+	if ((v2 & sonypi_bluetooth_ev) == sonypi_bluetooth_ev) {
 		for (i = 0; sonypi_blueev[i].event; i++)
 			if (sonypi_blueev[i].data == v1) {
 				event = sonypi_blueev[i].event;
@@ -510,24 +533,74 @@
 static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, 
 			     unsigned int cmd, unsigned long arg) {
 	int ret = 0;
-	u8 val;
+	u8 val8;
+	u16 val16;
 
 	down(&sonypi_device.lock);
 	switch (cmd) {
-		case SONYPI_IOCGBRT:
-			val = sonypi_ecrget(0x96) & 0xff;
-			if (copy_to_user((u8 *)arg, &val, sizeof(val))) {
-				ret = -EFAULT;
-				goto out;
-			}
-			break;
-		case SONYPI_IOCSBRT:
-			if (copy_from_user(&val, (u8 *)arg, sizeof(val))) {
-				ret = -EFAULT;
-				goto out;
-			}
-			sonypi_ecrset(0x96, val);
-			break;
+	case SONYPI_IOCGBRT:
+		val8 = sonypi_ecrget(0x96);
+		if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCSBRT:
+		if (copy_from_user(&val8, (u8 *)arg, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		sonypi_ecrset(0x96, val8);
+		break;
+	case SONYPI_IOCGBAT1CAP:
+		val16 = sonypi_ecrget16(0xb2);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT1REM:
+		val16 = sonypi_ecrget16(0xa2);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT2CAP:
+		val16 = sonypi_ecrget16(0xba);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBAT2REM:
+		val16 = sonypi_ecrget16(0xaa);
+		if (copy_to_user((u16 *)arg, &val16, sizeof(val16))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBATFLAGS:
+		val8 = sonypi_ecrget(0x81) & 0x07;
+		if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCGBLUE:
+		val8 = sonypi_device.bluetooth_power;
+		if (copy_to_user((u8 *)arg, &val8, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		break;
+	case SONYPI_IOCSBLUE:
+		if (copy_from_user(&val8, (u8 *)arg, sizeof(val8))) {
+			ret = -EFAULT;
+			goto out;
+		}
+		sonypi_setbluetoothpower(val8);
+		break;
 	default:
 		ret = -EINVAL;
 	}
@@ -562,6 +635,7 @@
 		sonypi_device.model = SONYPI_DEVICE_MODEL_TYPE2;
 	sonypi_initq();
 	init_MUTEX(&sonypi_device.lock);
+	sonypi_device.bluetooth_power = 0;
 	
 	if (pcidev && pci_enable_device(pcidev)) {
 		printk(KERN_ERR "sonypi: pci_enable_device failed\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sonypi.h linux/drivers/char/sonypi.h
--- linux.orig/drivers/char/sonypi.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/sonypi.h	Thu Feb  7 16:58:52 2002
@@ -34,8 +34,8 @@
 
 #ifdef __KERNEL__
 
-#define SONYPI_DRIVER_MAJORVERSION	1
-#define SONYPI_DRIVER_MINORVERSION	8
+#define SONYPI_DRIVER_MAJORVERSION	 1
+#define SONYPI_DRIVER_MINORVERSION	10
 
 #include <linux/types.h>
 #include <linux/pci.h>
@@ -132,15 +132,17 @@
 #define SONYPI_CAMERA_ROMVERSION 		9
 
 /* key press event data (ioport2) */
-#define SONYPI_TYPE1_JOGGER_EV	0x10
-#define SONYPI_TYPE2_JOGGER_EV	0x08
-#define SONYPI_CAPTURE_EV	0x60
-#define SONYPI_TYPE1_FNKEY_EV	0x20
-#define SONYPI_TYPE2_FNKEY_EV	0x08
-#define SONYPI_BLUETOOTH_EV	0x30
-#define SONYPI_TYPE1_PKEY_EV	0x40
-#define SONYPI_BACK_EV		0x08
-#define SONYPI_LID_EV		0x38
+#define SONYPI_TYPE1_JOGGER_EV		0x10
+#define SONYPI_TYPE2_JOGGER_EV		0x08
+#define SONYPI_TYPE1_CAPTURE_EV		0x60
+#define SONYPI_TYPE2_CAPTURE_EV		0x08
+#define SONYPI_TYPE1_FNKEY_EV		0x20
+#define SONYPI_TYPE2_FNKEY_EV		0x08
+#define SONYPI_TYPE1_BLUETOOTH_EV	0x30
+#define SONYPI_TYPE2_BLUETOOTH_EV	0x08
+#define SONYPI_TYPE1_PKEY_EV		0x40
+#define SONYPI_BACK_EV			0x08
+#define SONYPI_LID_EV			0x38
 
 struct sonypi_event {
 	u8	data;
@@ -203,6 +205,8 @@
 /* The set of possible bluetooth events */
 static struct sonypi_event sonypi_blueev[] = {
 	{ 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED },
+	{ 0x59, SONYPI_EVENT_BLUETOOTH_ON },
+	{ 0x5a, SONYPI_EVENT_BLUETOOTH_OFF },
 	{ 0x00, 0x00 }
 };
 
@@ -241,6 +245,7 @@
 	u16 ioport2;
 	u16 region_size;
 	int camera_power;
+	int bluetooth_power;
 	struct semaphore lock;
 	struct sonypi_queue queue;
 	int open_count;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/sx.c linux/drivers/char/sx.c
--- linux.orig/drivers/char/sx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/sx.c	Mon Feb  4 17:22:38 2002
@@ -1160,7 +1160,8 @@
 				/* DCD went UP */
 				if( (~(port->gs.flags & ASYNC_NORMAL_ACTIVE) || 
 						 ~(port->gs.flags & ASYNC_CALLOUT_ACTIVE)) &&
-						(sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED)) {
+						(sx_read_channel_byte(port, hi_hstat) != HS_IDLE_CLOSED) &&
+						!(port->gs.tty->termios->c_cflag & CLOCAL) ) {
 					/* Are we blocking in open?*/
 					sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD active, unblocking open\n");
 					wake_up_interruptible(&port->gs.open_wait);
@@ -1170,7 +1171,8 @@
 			} else {
 				/* DCD went down! */
 				if (!((port->gs.flags & ASYNC_CALLOUT_ACTIVE) &&
-				      (port->gs.flags & ASYNC_CALLOUT_NOHUP))) {
+				      (port->gs.flags & ASYNC_CALLOUT_NOHUP)) &&
+				    !(port->gs.tty->termios->c_cflag & CLOCAL) ) {
 					sx_dprintk (SX_DEBUG_MODEMSIGNALS, "DCD dropped. hanging up....\n");
 					tty_hangup (port->gs.tty);
 				} else {
@@ -1815,7 +1817,7 @@
 	case TIOCGSERIAL:
 		if ((rc = verify_area(VERIFY_WRITE, (void *) arg,
 		                      sizeof(struct serial_struct))) == 0)
-			gs_getserial(&port->gs, (struct serial_struct *) arg);
+			rc = gs_getserial(&port->gs, (struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
 		if ((rc = verify_area(VERIFY_READ, (void *) arg,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/synclink.c linux/drivers/char/synclink.c
--- linux.orig/drivers/char/synclink.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/char/synclink.c	Wed Feb 13 17:37:40 2002
@@ -940,7 +940,7 @@
 	name:		"synclink",
 	id_table:	synclink_pci_tbl,
 	probe:		synclink_init_one,
-	remove:		synclink_remove_one,
+	remove:		__devexit_p(synclink_remove_one),
 };
 
 static struct tty_driver serial_driver, callout_driver;
@@ -8219,7 +8219,7 @@
 	return 0;
 }
 
-static void __exit synclink_remove_one (struct pci_dev *dev)
+static void __devexit synclink_remove_one (struct pci_dev *dev)
 {
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux.orig/drivers/char/tty_io.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/char/tty_io.c	Mon Jan 14 18:53:53 2002
@@ -697,8 +697,13 @@
 {
 	ssize_t ret = 0, written = 0;
 	
-	if (down_interruptible(&tty->atomic_write)) {
-		return -ERESTARTSYS;
+	if (file->f_flags & O_NONBLOCK) {
+		if (down_trylock(&tty->atomic_write))
+			return -EAGAIN;
+	}
+	else {
+		if (down_interruptible(&tty->atomic_write))
+			return -ERESTARTSYS;
 	}
 	if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {
 		lock_kernel();
@@ -2192,6 +2197,11 @@
 #ifdef CONFIG_SERIAL_CONSOLE
 #if (defined(CONFIG_8xx) || defined(CONFIG_8260))
 	console_8xx_init();
+#elif defined(CONFIG_MAC_SERIAL) && defined(CONFIG_SERIAL)
+	if (_machine == _MACH_Pmac)
+ 		mac_scc_console_init();
+	else
+		serial_console_init();
 #elif defined(CONFIG_MAC_SERIAL)
  	mac_scc_console_init();
 #elif defined(CONFIG_PARISC)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/wdt.c linux/drivers/char/wdt.c
--- linux.orig/drivers/char/wdt.c	Fri Sep  7 16:28:38 2001
+++ linux/drivers/char/wdt.c	Thu Jan 17 22:00:03 2002
@@ -27,6 +27,7 @@
  *		Tim Hockin	:	Added insmod parameters, comment cleanup
  *					Parameterized timeout
  *		Tigran Aivazian	:	Restructured wdt_init() to handle failures
+ *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT
  */
 
 #include <linux/config.h>
@@ -60,8 +61,11 @@
 static int io=0x240;
 static int irq=11;
 
+/* Default margin */
 #define WD_TIMO (100*60)		/* 1 minute */
 
+static int wd_margin = WD_TIMO;
+
 #ifndef MODULE
 
 /**
@@ -216,7 +220,7 @@
 	/* Write a watchdog value */
 	inb_p(WDT_DC);
 	wdt_ctr_mode(1,2);
-	wdt_ctr_load(1,WD_TIMO);		/* Timeout */
+	wdt_ctr_load(1,wd_margin);		/* Timeout */
 	outb_p(0, WDT_DC);
 }
 
@@ -294,10 +298,13 @@
 static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	unsigned long arg)
 {
+	int new_margin;
+
 	static struct watchdog_info ident=
 	{
 		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
-			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
+			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
+			|WDIOF_SETTIMEOUT,
 		1,
 		"WDT500/501"
 	};
@@ -317,6 +324,17 @@
 		case WDIOC_KEEPALIVE:
 			wdt_ping();
 			return 0;
+		case WDIOC_SETTIMEOUT:
+			if (get_user(new_margin, (int *)arg))
+				return -EFAULT;
+			/* Arbitrary, can't find the card's limits */
+			if ((new_margin < 0) || (new_margin > 60))
+				return -EINVAL;
+			wd_margin = new_margin * 100;
+			wdt_ping();
+			/* Fall */
+		case WDIOC_GETTIMEOUT:
+			return put_user(wd_margin / 100, (int *)arg);
 	}
 }
 
@@ -349,7 +367,7 @@
 			wdt_ctr_mode(1,2);
 			wdt_ctr_mode(2,0);
 			wdt_ctr_load(0, 8948);		/* count at 100Hz */
-			wdt_ctr_load(1,WD_TIMO);	/* Timeout 120 seconds */
+			wdt_ctr_load(1,wd_margin);	/* Timeout 120 seconds */
 			wdt_ctr_load(2,65535);
 			outb_p(0, WDT_DC);	/* Enable */
 			return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/wdt285.c linux/drivers/char/wdt285.c
--- linux.orig/drivers/char/wdt285.c	Fri Sep  7 16:28:38 2001
+++ linux/drivers/char/wdt285.c	Thu Jan 17 22:00:03 2002
@@ -126,10 +126,10 @@
 static int watchdog_ioctl(struct inode *inode, struct file *file,
 	unsigned int cmd, unsigned long arg)
 {
-	int i;
+	int i, new_margin;
 	static struct watchdog_info ident=
 	{
-		0,
+		WDIOF_SETTIMEOUT,
 		0,
 		"Footbridge Watchdog"
 	};
@@ -149,6 +149,17 @@
 		case WDIOC_KEEPALIVE:
 			watchdog_ping();
 			return 0;
+		case WDIOC_SETTIMEOUT:
+			if (get_user(new_margin, (int *)arg))
+				return -EFAULT;
+			/* Arbitrary, can't find the card's limits */
+			if ((new_marg < 0) || (new_margin > 60))
+				return -EINVAL;
+			soft_margin = new_margin;
+			watchdog_ping();
+			/* Fall */
+		case WDIOC_GETTIMEOUT:
+			return put_user(soft_margin, (int *)arg);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/char/wdt_pci.c linux/drivers/char/wdt_pci.c
--- linux.orig/drivers/char/wdt_pci.c	Fri Sep  7 16:28:38 2001
+++ linux/drivers/char/wdt_pci.c	Thu Jan 17 22:00:03 2002
@@ -30,6 +30,7 @@
  *		Alan Cox	:	Split ISA and PCI cards into two drivers
  *		Jeff Garzik	:	PCI cleanups
  *		Tigran Aivazian	:	Restructured wdtpci_init_one() to handle failures
+ *		Joel Becker	:	Added WDIOC_GET/SETTIMEOUT
  */
 
 #include <linux/config.h>
@@ -81,8 +82,11 @@
 static int io=0x240;
 static int irq=11;
 
+/* Default timeout */
 #define WD_TIMO (100*60)		/* 1 minute */
 
+static int wd_margin = WD_TIMO;
+
 #ifndef MODULE
 
 /**
@@ -232,7 +236,7 @@
 	/* Write a watchdog value */
 	inb_p(WDT_DC);
 	wdtpci_ctr_mode(1,2);
-	wdtpci_ctr_load(1,WD_TIMO);		/* Timeout */
+	wdtpci_ctr_load(1,wd_margin);		/* Timeout */
 	outb_p(0, WDT_DC);
 }
 
@@ -310,10 +314,12 @@
 static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	unsigned long arg)
 {
+	int new_margin;
 	static struct watchdog_info ident=
 	{
 		WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER
-			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT,
+			|WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT
+			|WDIOF_SETTIMEOUT,
 		1,
 		"WDT500/501PCI"
 	};
@@ -333,6 +339,17 @@
 		case WDIOC_KEEPALIVE:
 			wdtpci_ping();
 			return 0;
+		case WDIOC_SETTIMEOUT:
+			if (get_user(new_margin, (int *)arg))
+				return -EFAULT;
+			/* Arbitrary, can't find the card's limits */
+			if ((new_margin < 0) || (new_margin > 60))
+				return -EINVAL;
+			wd_margin = new_margin * 100;
+			wdtpci_ping();
+			/* Fall */
+		case WDIOC_GETTIMEOUT:
+			return put_user(wd_margin, (int *)arg);
 	}
 }
 
@@ -385,7 +402,7 @@
 			wdtpci_ctr_mode(1,2);
 			wdtpci_ctr_mode(2,1);
 			wdtpci_ctr_load(0,20833);	/* count at 100Hz */
-			wdtpci_ctr_load(1,WD_TIMO);/* Timeout 60 seconds */
+			wdtpci_ctr_load(1,wd_margin);/* Timeout 60 seconds */
 			/* DO NOT LOAD CTR2 on PCI card! -- JPN */
 			outb_p(0, WDT_DC);	/* Enable */
 			return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/hotplug/cpqphp_core.c linux/drivers/hotplug/cpqphp_core.c
--- linux.orig/drivers/hotplug/cpqphp_core.c	Fri Nov  9 22:01:21 2001
+++ linux/drivers/hotplug/cpqphp_core.c	Wed Jan 16 20:13:22 2002
@@ -404,6 +404,10 @@
 		result = pci_hp_register (new_slot->hotplug_slot);
 		if (result) {
 			err ("pci_hp_register failed with error %d\n", result);
+			kfree (new_slot->hotplug_slot->info);
+			kfree (new_slot->hotplug_slot->name);
+			kfree (new_slot->hotplug_slot);
+			kfree (new_slot);
 			return result;
 		}
 		
@@ -429,6 +433,8 @@
 	while (old_slot) {
 		next_slot = old_slot->next;
 		pci_hp_deregister (old_slot->hotplug_slot);
+		kfree(old_slot->hotplug_slot->info);
+		kfree(old_slot->hotplug_slot->name);
 		kfree(old_slot->hotplug_slot);
 		kfree(old_slot);
 		old_slot = next_slot;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/Config.in linux/drivers/i2c/Config.in
--- linux.orig/drivers/i2c/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/i2c/Config.in	Wed Dec 26 16:31:13 2001
@@ -39,6 +39,10 @@
       fi
    fi
 
+   if [ "$CONFIG_ALL_PPC" = "y" ] ; then
+      dep_tristate 'Keywest I2C interface in Apple Core99 machines' CONFIG_I2C_KEYWEST $CONFIG_I2C
+   fi
+
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/Makefile linux/drivers/i2c/Makefile
--- linux.orig/drivers/i2c/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/i2c/Makefile	Wed Dec 26 16:31:13 2001
@@ -18,6 +18,7 @@
 obj-$(CONFIG_ITE_I2C_ALGO)	+= i2c-algo-ite.o
 obj-$(CONFIG_ITE_I2C_ADAP)	+= i2c-adap-ite.o
 obj-$(CONFIG_I2C_PROC)		+= i2c-proc.o
+obj-$(CONFIG_I2C_KEYWEST)	+= i2c-keywest.o
 
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/i2c-keywest.c linux/drivers/i2c/i2c-keywest.c
--- linux.orig/drivers/i2c/i2c-keywest.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/i2c/i2c-keywest.c	Mon Feb  4 18:47:24 2002
@@ -0,0 +1,680 @@
+/*
+    i2c Support for Apple Keywest I2C Bus Controller
+
+    Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+    Original work by
+    
+    Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+    Changes:
+
+    2001/12/13 BenH	New implementation
+    2001/12/15 BenH	Add support for "byte" and "quick"
+                        transfers. Add i2c_xfer routine.
+
+    My understanding of the various modes supported by keywest are:
+
+     - Dumb mode : not implemented, probably direct tweaking of lines
+     - Standard mode : simple i2c transaction of type
+         S Addr R/W A Data A Data ... T
+     - Standard sub mode : combined 8 bit subaddr write with data read
+         S Addr R/W A SubAddr A Data A Data ... T
+     - Combined mode : Subaddress and Data sequences appended with no stop
+         S Addr R/W A SubAddr S Addr R/W A Data A Data ... T
+
+    Currently, this driver uses only Standard mode for i2c xfer, and
+    smbus byte & quick transfers ; and uses StandardSub mode for
+    other smbus transfers instead of combined as we need that for the
+    sound driver to be happy
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+
+#include "i2c-keywest.h"
+
+#undef POLLED_MODE
+
+#define DBG(x...) do {\
+	if (debug > 0) \
+		printk(KERN_DEBUG "KW:" x); \
+	} while(0)
+
+
+MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
+MODULE_DESCRIPTION("I2C driver for Apple's Keywest");
+MODULE_LICENSE("GPL");
+MODULE_PARM(probe, "i");
+MODULE_PARM(debug, "i");
+EXPORT_NO_SYMBOLS;
+
+int probe = 0;
+int debug = 0;
+
+static struct keywest_iface *ifaces = NULL;
+
+#ifdef POLLED_MODE
+/* This isn't fast, but will go once I implement interrupt with
+ * proper timeout
+ */
+static u8
+wait_interrupt(struct keywest_iface* iface)
+{
+	int i;
+	u8 isr;
+	
+	for (i = 0; i < POLL_TIMEOUT; i++) {
+		isr = read_reg(reg_isr) & KW_I2C_IRQ_MASK;
+		if (isr != 0)
+			return isr;
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1);
+	}
+	return isr;
+}
+#endif /* POLLED_MODE */
+
+
+static void
+do_stop(struct keywest_iface* iface, int result)
+{
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_STOP);
+	iface->state = state_stop;
+	iface->result = result;
+}
+
+/* Main state machine for standard & standard sub mode */
+static void
+handle_interrupt(struct keywest_iface *iface, u8 isr)
+{
+	int ack;
+	
+	DBG("handle_interrupt(), got: %x, status: %x, state: %d\n",
+		isr, read_reg(reg_status), iface->state);
+	if (isr == 0 && iface->state != state_stop) {
+		do_stop(iface, -1);
+		return;
+	}
+	if (isr & KW_I2C_IRQ_STOP && iface->state != state_stop) {
+		iface->result = -1;
+		iface->state = state_stop;
+	}
+	switch(iface->state) {
+	case state_addr:
+		if (!(isr & KW_I2C_IRQ_ADDR)) {
+			do_stop(iface, -1);
+			break;
+		}
+		ack = read_reg(reg_status);
+		DBG("ack on set address: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Handle rw "quick" mode */
+		if (iface->datalen == 0)
+			do_stop(iface, 0);
+		else if (iface->read_write == I2C_SMBUS_READ) {
+			iface->state = state_read;
+			if (iface->datalen > 1)
+				write_reg(reg_control, read_reg(reg_control)
+					| KW_I2C_CTL_AAK);
+		} else {
+			iface->state = state_write;
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		}
+		
+		break;
+	case state_read:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		*(iface->data++) = read_reg(reg_data);
+		DBG("read byte: %x\n", *(iface->data-1));
+		iface->datalen--;
+		if (iface->datalen == 0)
+			iface->state = state_stop;
+		else
+			write_reg(reg_control, 0);
+		break;
+	case state_write:
+		if (!(isr & KW_I2C_IRQ_DATA)) {
+			do_stop(iface, -1);
+			break;
+		}
+		/* Check ack status */
+		ack = read_reg(reg_status);
+		DBG("ack on data write: %x\n", ack);
+		if ((ack & KW_I2C_STAT_LAST_AAK) == 0) {
+			do_stop(iface, -1);
+			break;
+		}
+		if (iface->datalen) {
+			DBG("write byte: %x\n", *(iface->data));
+			write_reg(reg_data, *(iface->data++));
+			iface->datalen--;
+		} else
+			do_stop(iface, 0);
+		break;
+		
+	case state_stop:
+		if (!(isr & KW_I2C_IRQ_STOP) && (++iface->stopretry) < 10)
+			do_stop(iface, -1);
+		else {
+			iface->state = state_idle;
+			write_reg(reg_control, 0x00);
+			write_reg(reg_ier, 0x00);
+#ifndef POLLED_MODE
+			complete(&iface->complete);
+#endif /* POLLED_MODE */			
+		}
+		break;
+	}
+	
+	write_reg(reg_isr, isr);
+}
+
+#ifndef POLLED_MODE
+
+/* Interrupt handler */
+static void
+keywest_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)dev_id;
+
+	spin_lock(&iface->lock);
+	del_timer(&iface->timeout_timer);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+static void
+keywest_timeout(unsigned long data)
+{
+	struct keywest_iface *iface = (struct keywest_iface *)data;
+
+	DBG("timeout !\n");
+	spin_lock_irq(&iface->lock);
+	handle_interrupt(iface, read_reg(reg_isr));
+	if (iface->state != state_idle) {
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+	}
+	spin_unlock(&iface->lock);
+}
+
+#endif /* POLLED_MODE */
+
+/*
+ * SMBUS-type transfer entrypoint
+ */
+static s32
+keywest_smbus_xfer(	struct i2c_adapter*	adap,
+			u16			addr,
+			unsigned short		flags,
+			char			read_write,
+			u8			command,
+			int			size,
+			union i2c_smbus_data*	data)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	int len;
+	u8* buffer;
+	u16 cur_word;
+	int rc = 0;
+
+	if (iface->state == state_dead)
+		return -1;
+		
+	/* Prepare datas & select mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	switch (size) {
+	    case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+	    	break;
+	    case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	    	break;
+	    case I2C_SMBUS_WORD_DATA:
+	    	len = 2;
+	    	cur_word = cpu_to_le16(data->word);
+	    	buffer = (u8 *)&cur_word;
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    case I2C_SMBUS_BLOCK_DATA:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+	    	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+		break;
+	    default:
+	    	return -1;
+	}
+
+	/* Original driver had this limitation */
+	if (len > 32)
+		len = 32;
+
+	down(&iface->sem);
+
+	DBG("chan: %d, addr: 0x%x, transfer len: %d, read: %d\n",
+		chan->chan_no, addr, len, read_write == I2C_SMBUS_READ);
+
+	iface->data = buffer;
+	iface->datalen = len;
+	iface->state = state_addr;
+	iface->result = 0;
+	iface->stopretry = 0;
+	iface->read_write = read_write;
+	
+	/* Setup channel & clear pending irqs */
+	write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+	write_reg(reg_isr, read_reg(reg_isr));
+	write_reg(reg_status, 0);
+
+	/* Set up address and r/w bit */
+	write_reg(reg_addr,
+		(addr << 1) | ((read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+	/* Set up the sub address */
+	if ((iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_STANDARDSUB
+	    || (iface->cur_mode & KW_I2C_MODE_MODE_MASK) == KW_I2C_MODE_COMBINED)
+		write_reg(reg_subaddr, command);
+
+	/* Arm timeout */
+	iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+	add_timer(&iface->timeout_timer);
+
+	/* Start sending address & enable interrupt*/
+	write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+	write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+	DBG("using polled mode...\n");
+	/* State machine, to turn into an interrupt handler */
+	while(iface->state != state_idle) {
+		u8 isr = wait_interrupt(iface);
+		handle_interrupt(iface, isr);
+	}
+#else /* POLLED_MODE */
+	DBG("using interrupt mode...\n");
+	wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+	rc = iface->result;	
+	DBG("transfer done, result: %d\n", rc);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && read_write == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+	
+	/* Release sem */
+	up(&iface->sem);
+	
+	return rc;
+}
+
+/*
+ * Generic i2c master transfer entrypoint
+ */
+static int
+keywest_xfer(	struct i2c_adapter *adap,
+		struct i2c_msg msgs[], 
+		int num)
+{
+	struct keywest_chan* chan = (struct keywest_chan*)adap->data;
+	struct keywest_iface* iface = chan->iface;
+	struct i2c_msg *pmsg;
+	int i, completed;
+	int rc = 0;
+
+	down(&iface->sem);
+
+	/* Set adapter to standard mode */
+	iface->cur_mode &= ~KW_I2C_MODE_MODE_MASK;
+	iface->cur_mode |= KW_I2C_MODE_STANDARD;
+
+	completed = 0;
+	for (i = 0; rc >= 0 && i < num;) {
+		u8 addr;
+		
+		pmsg = &msgs[i++];
+		addr = pmsg->addr;
+		if (pmsg->flags & I2C_M_TEN) {
+			printk(KERN_ERR "i2c-keywest: 10 bits addr not supported !\n");
+			rc = -EINVAL;
+			break;
+		}
+		DBG("xfer: chan: %d, doing %s %d bytes to 0x%02x - %d of %d messages\n",
+		     chan->chan_no,
+		     pmsg->flags & I2C_M_RD ? "read" : "write",
+                     pmsg->len, addr, i, num);
+    
+		/* Setup channel & clear pending irqs */
+		write_reg(reg_mode, iface->cur_mode | (chan->chan_no << 4));
+		write_reg(reg_isr, read_reg(reg_isr));
+		write_reg(reg_status, 0);
+		
+		iface->data = pmsg->buf;
+		iface->datalen = pmsg->len;
+		iface->state = state_addr;
+		iface->result = 0;
+		iface->stopretry = 0;
+		if (pmsg->flags & I2C_M_RD)
+			iface->read_write = I2C_SMBUS_READ;
+		else
+			iface->read_write = I2C_SMBUS_WRITE;
+
+		/* Set up address and r/w bit */
+		if (pmsg->flags & I2C_M_REV_DIR_ADDR)
+			addr ^= 1;		
+		write_reg(reg_addr,
+			(addr << 1) |
+			((iface->read_write == I2C_SMBUS_READ) ? 0x01 : 0x00));
+
+		/* Arm timeout */
+		iface->timeout_timer.expires = jiffies + POLL_TIMEOUT;
+		add_timer(&iface->timeout_timer);
+
+		/* Start sending address & enable interrupt*/
+		write_reg(reg_control, read_reg(reg_control) | KW_I2C_CTL_XADDR);
+		write_reg(reg_ier, KW_I2C_IRQ_MASK);
+
+#ifdef POLLED_MODE
+		DBG("using polled mode...\n");
+		/* State machine, to turn into an interrupt handler */
+		while(iface->state != state_idle) {
+			u8 isr = wait_interrupt(iface);
+			handle_interrupt(iface, isr);
+		}
+#else /* POLLED_MODE */88
+		DBG("using interrupt mode...\n");
+		wait_for_completion(&iface->complete);	
+#endif /* POLLED_MODE */	
+
+		rc = iface->result;
+		if (rc == 0)
+			completed++;
+		DBG("transfer done, result: %d\n", rc);
+	}
+
+	/* Release sem */
+	up(&iface->sem);
+
+	return completed;
+}
+
+static u32
+keywest_func(struct i2c_adapter * adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static void
+keywest_inc(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void
+keywest_dec(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm keywest_algorithm = {
+	name:		"Keywest i2c",
+	id:		I2C_ALGO_SMBUS,
+	smbus_xfer:	keywest_smbus_xfer,
+	master_xfer:	keywest_xfer,
+	functionality:	keywest_func,
+};
+
+
+static int
+create_iface(struct device_node* np)
+{
+	unsigned long steps, *psteps, *prate;
+	unsigned bsteps, tsize, i, nchan, addroffset;
+	struct keywest_iface* iface;
+	int rc;
+
+	psteps = (unsigned long *)get_property(np, "AAPL,address-step", NULL);
+	steps = psteps ? (*psteps) : 0x10;
+
+	/* Hrm... maybe we can be smarter here */
+	for (bsteps = 0; (steps & 0x01) == 0; bsteps++)
+		steps >>= 1;
+
+	if (!strcmp(np->parent->name, "uni-n")) {
+		nchan = 2;
+		addroffset = 3;
+	} else {
+		addroffset = 0;
+		nchan = 1;
+	}
+
+	tsize = sizeof(struct keywest_iface) +
+		(sizeof(struct keywest_chan) + 4) * nchan;
+	iface = (struct keywest_iface *) kmalloc(tsize, GFP_KERNEL);
+	if (iface == NULL) {
+		printk(KERN_ERR "i2c-keywest: can't allocate inteface !\n");
+		return -ENOMEM;
+	}
+	memset(iface, 0, tsize);
+	init_MUTEX(&iface->sem);
+	spin_lock_init(&iface->lock);
+	init_completion(&iface->complete);
+	iface->bsteps = bsteps;
+	iface->chan_count = nchan;
+	iface->state = state_idle;
+	iface->irq = np->intrs[0].line;
+	iface->channels = (struct keywest_chan *)
+		(((unsigned long)(iface + 1) + 3UL) & ~3UL);
+	iface->base = (unsigned long)ioremap(np->addrs[0].address + addroffset,
+						np->addrs[0].size);
+	if (iface->base == 0) {
+		printk(KERN_ERR "i2c-keywest: can't map inteface !\n");
+		kfree(iface);
+		return -ENOMEM;
+	}
+
+	init_timer(&iface->timeout_timer);
+	iface->timeout_timer.function = keywest_timeout;
+	iface->timeout_timer.data = (unsigned long)iface;
+
+	/* Select interface rate */
+	iface->cur_mode = KW_I2C_MODE_100KHZ;
+	prate = (unsigned long *)get_property(np, "AAPL,i2c-rate", NULL);
+	if (prate) switch(*prate) {
+	case 100:
+		iface->cur_mode = KW_I2C_MODE_100KHZ;
+		break;
+	case 50:
+		iface->cur_mode = KW_I2C_MODE_50KHZ;
+		break;
+	case 25:
+		iface->cur_mode = KW_I2C_MODE_25KHZ;
+		break;
+	default:
+		printk(KERN_WARNING "i2c-keywest: unknown rate %ldKhz, using 100KHz\n",
+			*prate);
+	}
+	
+	/* Select standard sub mode */
+	iface->cur_mode |= KW_I2C_MODE_STANDARDSUB;
+	
+	/* Write mode */
+	write_reg(reg_mode, iface->cur_mode);
+	
+	/* Switch interrupts off & clear them*/
+	write_reg(reg_ier, 0x00);
+	write_reg(reg_isr, KW_I2C_IRQ_MASK);
+
+#ifndef POLLED_MODE
+	/* Request chip interrupt */	
+	rc = request_irq(iface->irq, keywest_irq, 0, "keywest i2c", iface);
+	if (rc) {
+		printk(KERN_ERR "i2c-keywest: can't get IRQ %d !\n", iface->irq);
+		iounmap((void *)iface->base);
+		kfree(iface);
+		return -ENODEV;
+	}
+#endif /* POLLED_MODE */
+
+	for (i=0; i<nchan; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		u8 addr;
+		
+		sprintf(chan->adapter.name, "%s %d", np->parent->name, i);
+		chan->iface = iface;
+		chan->chan_no = i;
+		chan->adapter.id = I2C_ALGO_SMBUS;
+		chan->adapter.algo = &keywest_algorithm;
+		chan->adapter.algo_data = NULL;
+		chan->adapter.inc_use = keywest_inc;
+		chan->adapter.dec_use = keywest_dec;
+		chan->adapter.client_register = NULL;
+		chan->adapter.client_unregister = NULL;
+		chan->adapter.data = chan;
+
+		rc = i2c_add_adapter(&chan->adapter);
+		if (rc) {
+			printk("i2c-keywest.c: Adapter %s registration failed\n",
+				chan->adapter.name);
+			chan->adapter.data = NULL;
+		}
+		if (probe) {
+			printk("Probe: ");
+			for (addr = 0x00; addr <= 0x7f; addr++) {
+				if (i2c_smbus_xfer(&chan->adapter,addr,
+				    0,0,0,I2C_SMBUS_QUICK,NULL) >= 0)
+					printk("%02x ", addr);
+			}
+			printk("\n");
+		}
+	}
+
+	printk(KERN_INFO "Found KeyWest i2c on \"%s\", %d channel%s, stepping: %d bits\n",
+		np->parent->name, nchan, nchan > 1 ? "s" : "", bsteps);
+		
+	iface->next = ifaces;
+	ifaces = iface;
+	return 0;
+}
+
+static void
+dispose_iface(struct keywest_iface *iface)
+{
+	int i, rc;
+	
+	ifaces = iface->next;
+
+	/* Make sure we stop all activity */
+	down(&iface->sem);
+#ifndef POLLED_MODE
+	spin_lock_irq(&iface->lock);
+	while (iface->state != state_idle) {
+		spin_unlock_irq(&iface->lock);
+		schedule();
+		spin_lock_irq(&iface->lock);
+	}
+#endif /* POLLED_MODE */
+	iface->state = state_dead;
+#ifndef POLLED_MODE
+	spin_unlock_irq(&iface->lock);
+	free_irq(iface->irq, iface);
+#endif /* POLLED_MODE */
+	up(&iface->sem);
+
+	/* Release all channels */
+	for (i=0; i<iface->chan_count; i++) {
+		struct keywest_chan* chan = &iface->channels[i];
+		if (!chan->adapter.data)
+			continue;
+		rc = i2c_del_adapter(&chan->adapter);
+		chan->adapter.data = NULL;
+		/* We aren't that prepared to deal with this... */
+		if (rc)
+			printk("i2c-keywest.c: i2c_del_adapter failed, that's bad !\n");
+	}
+	iounmap((void *)iface->base);
+	kfree(iface);
+}
+
+static int __init
+i2c_keywest_init(void)
+{
+	struct device_node *np;
+	int rc = -ENODEV;
+	
+	np = find_compatible_devices("i2c", "keywest");
+	while (np != 0) {
+		if (np->n_addrs >= 1 && np->n_intrs >= 1)
+			rc = create_iface(np);
+		np = np->next;
+	}
+	if (ifaces)
+		rc = 0;
+	return rc;
+}
+
+static void __exit
+i2c_keywest_cleanup(void)
+{
+	while(ifaces)
+		dispose_iface(ifaces);
+}
+
+module_init(i2c_keywest_init);
+module_exit(i2c_keywest_cleanup);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/i2c-keywest.h linux/drivers/i2c/i2c-keywest.h
--- linux.orig/drivers/i2c/i2c-keywest.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/i2c/i2c-keywest.h	Wed Dec 26 16:31:13 2001
@@ -0,0 +1,111 @@
+#ifndef __I2C_KEYWEST_H__
+#define __I2C_KEYWEST_H__
+
+/* The Tumbler audio equalizer can be really slow sometimes */
+#define POLL_TIMEOUT		(2*HZ)
+
+/* Register indices */
+typedef enum {
+	reg_mode = 0,
+	reg_control,
+	reg_status,
+	reg_isr,
+	reg_ier,
+	reg_addr,
+	reg_subaddr,
+	reg_data
+} reg_t;
+
+
+/* Mode register */
+#define KW_I2C_MODE_100KHZ	0x00
+#define KW_I2C_MODE_50KHZ	0x01
+#define KW_I2C_MODE_25KHZ	0x02
+#define KW_I2C_MODE_DUMB	0x00
+#define KW_I2C_MODE_STANDARD	0x04
+#define KW_I2C_MODE_STANDARDSUB	0x08
+#define KW_I2C_MODE_COMBINED	0x0C
+#define KW_I2C_MODE_MODE_MASK	0x0C
+#define KW_I2C_MODE_CHAN_MASK	0xF0
+
+/* Control register */
+#define KW_I2C_CTL_AAK		0x01
+#define KW_I2C_CTL_XADDR	0x02
+#define KW_I2C_CTL_STOP		0x04
+#define KW_I2C_CTL_START	0x08
+
+/* Status register */
+#define KW_I2C_STAT_BUSY	0x01
+#define KW_I2C_STAT_LAST_AAK	0x02
+#define KW_I2C_STAT_LAST_RW	0x04
+#define KW_I2C_STAT_SDA		0x08
+#define KW_I2C_STAT_SCL		0x10
+
+/* IER & ISR registers */
+#define KW_I2C_IRQ_DATA		0x01
+#define KW_I2C_IRQ_ADDR		0x02
+#define KW_I2C_IRQ_STOP		0x04
+#define KW_I2C_IRQ_START	0x08
+#define KW_I2C_IRQ_MASK		0x0F
+
+/* Physical interface */
+struct keywest_iface
+{
+	unsigned long		base;
+	unsigned		bsteps;
+	int			irq;
+	struct semaphore	sem;
+	spinlock_t		lock;
+	struct keywest_chan*	channels;
+	unsigned		chan_count;
+	u8			cur_mode;
+	char			read_write;
+	u8*			data;
+	unsigned		datalen;
+	int			state;
+	int			result;
+	int			stopretry;
+	struct timer_list	timeout_timer;
+	struct completion	complete;
+	struct keywest_iface*	next;
+};
+
+enum {
+	state_idle,
+	state_addr,
+	state_read,
+	state_write,
+	state_stop,
+	state_dead
+};
+
+/* Channel on an interface */
+struct keywest_chan
+{
+	struct i2c_adapter	adapter;
+	struct keywest_iface*	iface;
+	unsigned		chan_no;
+};
+
+/* Register access */
+
+static inline u8 __read_reg(struct keywest_iface *iface, reg_t reg)
+{
+	return in_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps));
+}
+
+static inline void __write_reg(struct keywest_iface *iface, reg_t reg, u8 val)
+{
+	out_8(((volatile u8 *)iface->base)
+		+ (((unsigned)reg) << iface->bsteps), val);
+	(void)__read_reg(iface, reg);
+	udelay(10);
+}
+
+#define write_reg(reg, val)	__write_reg(iface, reg, val) 
+#define read_reg(reg)		__read_reg(iface, reg) 
+
+
+
+#endif /* __I2C_KEYWEST_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/i2c/i2c-proc.c linux/drivers/i2c/i2c-proc.c
--- linux.orig/drivers/i2c/i2c-proc.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/i2c/i2c-proc.c	Mon Jan 14 18:53:53 2002
@@ -119,6 +119,10 @@
 		sprintf(name_buffer, "%s-i2c-%d-%02x", prefix, id, addr);
 	}
 	*name = kmalloc(strlen(name_buffer) + 1, GFP_KERNEL);
+	if (!*name) {
+		printk (KERN_WARNING "i2c_create_name: not enough memory\n");
+		return -ENOMEM;
+	}
 	strcpy(*name, name_buffer);
 	return 0;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-cd.c linux/drivers/ide/ide-cd.c
--- linux.orig/drivers/ide/ide-cd.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/ide/ide-cd.c	Wed Jan  9 16:19:43 2002
@@ -1462,11 +1462,9 @@
 		ide_init_drive_cmd (&req);
 		req.cmd = PACKET_COMMAND;
 		req.buffer = (char *)pc;
-		if (ide_do_drive_cmd (drive, &req, ide_wait)) {
-			printk("%s: do_drive_cmd returned stat=%02x,err=%02x\n",
-				drive->name, req.buffer[0], req.buffer[1]);
-			/* FIXME: we should probably abort/retry or something */
-		}
+		ide_do_drive_cmd (drive, &req, ide_wait);
+		/* FIXME: we should probably abort/retry or something 
+		 * in case of failure */
 		if (pc->stat != 0) {
 			/* The request failed.  Retry if it was due to a unit
 			   attention status
@@ -2641,6 +2639,13 @@
 		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
 	if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup)
 		CDROM_CONFIG_FLAGS (drive)->close_tray = 0;
+
+	/* Some drives used by Apple don't advertise audio play
+	 * but they do support reading TOC & audio datas
+	 */
+	if (strcmp (drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 ||
+	    strcmp (drive->id->model, "MATSHITADVD-ROM SR-8186") == 0)
+		CDROM_CONFIG_FLAGS (drive)->audio_play = 1;
 
 #if ! STANDARD_ATAPI
 	if (cdi->sanyo_slot > 0) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-m8xx.c linux/drivers/ide/ide-m8xx.c
--- linux.orig/drivers/ide/ide-m8xx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/ide/ide-m8xx.c	Wed Dec 26 16:47:47 2001
@@ -1,8 +1,14 @@
 /*
- *
- *
  *  linux/drivers/ide/ide-m8xx.c
  *
+ *  Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de
+ *  Modified for direct IDE interface
+ *	by Thomas Lange, thomas@corelatus.com
+ *  Modified for direct IDE interface on 8xx without using the PCMCIA
+ *  controller
+ *	by Steven.Scholz@imc-berlin.de
+ *  Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups
+ *	by Mathew Locke <mattl@mvista.com>
  */
 
 #include <linux/config.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide-pmac.c linux/drivers/ide/ide-pmac.c
--- linux.orig/drivers/ide/ide-pmac.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/ide/ide-pmac.c	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/ide/ide-pmac.c		Version ?.??	Mar. 18, 2000
+ * linux/drivers/ide/ide-pmac.c
  *
  * Support for IDE interfaces on PowerMacs.
  * These IDE interfaces are memory-mapped and have a DBDMA channel
@@ -24,26 +24,29 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/ide.h>
-
+#include <linux/notifier.h>
+#include <linux/reboot.h>
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
 #include <asm/ide.h>
 #include <asm/mediabay.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
+#include <asm/sections.h>
+#include <asm/irq.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
-#include <asm/irq.h>
 #endif
 #include "ide_modes.h"
 
 extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc);
+extern void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq);
 
-#undef IDE_PMAC_DEBUG
+#define IDE_PMAC_DEBUG
 
-#define IDE_SYSCLK_NS		30
-#define IDE_SYSCLK_ULTRA_PS	0x1d4c /* (15 * 1000 / 2)*/
+#define DMA_WAIT_TIMEOUT	500
 
 struct pmac_ide_hwif {
 	ide_ioreg_t			regbase;
@@ -52,12 +55,14 @@
 	int				aapl_bus_id;
 	struct device_node*		node;
 	u32				timings[2];
+	struct resource*		reg_resource;
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 	volatile struct dbdma_regs*	dma_regs;
 	struct dbdma_cmd*		dma_table;
+	struct resource*		dma_resource;
 #endif
 	
-} pmac_ide[MAX_HWIFS];
+} pmac_ide[MAX_HWIFS] __pmacdata;
 
 static int pmac_ide_count;
 
@@ -65,33 +70,157 @@
 	controller_ohare,	/* OHare based */
 	controller_heathrow,	/* Heathrow/Paddington */
 	controller_kl_ata3,	/* KeyLargo ATA-3 */
-	controller_kl_ata4	/* KeyLargo ATA-4 */
+	controller_kl_ata4,	/* KeyLargo ATA-4 */
+	controller_kl_ata4_80	/* KeyLargo ATA-4 with 80 conductor cable */
 };
 
+/*
+ * Extra registers, both 32-bit little-endian
+ */
+#define IDE_TIMING_CONFIG	0x200
+#define IDE_INTERRUPT		0x300
+
+/*
+ * Timing configuration register definitions
+ */
+
+/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
+#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
+#define SYSCLK_TICKS_66(t)	(((t) + IDE_SYSCLK_66_NS - 1) / IDE_SYSCLK_66_NS)
+#define IDE_SYSCLK_NS		30	/* 33Mhz cell */
+#define IDE_SYSCLK_66_NS	15	/* 66Mhz cell */
+
+/* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on
+ * 40 connector cable and to 4 on 80 connector one.
+ * Clock unit is 15ns (66Mhz)
+ * 
+ * 3 Values can be programmed:
+ *  - Write data setup, which appears to match the cycle time. They
+ *    also call it DIOW setup.
+ *  - Ready to pause time (from spec)
+ *  - Address setup. That one is weird. I don't see where exactly
+ *    it fits in UDMA cycles, I got it's name from an obscure piece
+ *    of commented out code in Darwin. They leave it to 0, we do as
+ *    well, despite a comment that would lead to think it has a
+ *    min value of 45ns.
+ * Apple also add 60ns to the write data setup (or cycle time ?) on
+ * reads. I can't explain that, I tried it and it broke everything
+ * here.
+ */
+#define TR_66_UDMA_MASK			0xfff00000
+#define TR_66_UDMA_EN			0x00100000 /* Enable Ultra mode for DMA */
+#define TR_66_UDMA_ADDRSETUP_MASK	0xe0000000 /* Address setup */
+#define TR_66_UDMA_ADDRSETUP_SHIFT	29
+#define TR_66_UDMA_RDY2PAUS_MASK	0x1e000000 /* Ready 2 pause time */
+#define TR_66_UDMA_RDY2PAUS_SHIFT	25
+#define TR_66_UDMA_WRDATASETUP_MASK	0x01e00000 /* Write data setup time */
+#define TR_66_UDMA_WRDATASETUP_SHIFT	21
+#define TR_66_MDMA_MASK			0x000ffc00
+#define TR_66_MDMA_RECOVERY_MASK	0x000f8000
+#define TR_66_MDMA_RECOVERY_SHIFT	15
+#define TR_66_MDMA_ACCESS_MASK		0x00007c00
+#define TR_66_MDMA_ACCESS_SHIFT		10
+#define TR_66_PIO_MASK			0x000003ff
+#define TR_66_PIO_RECOVERY_MASK		0x000003e0
+#define TR_66_PIO_RECOVERY_SHIFT	5
+#define TR_66_PIO_ACCESS_MASK		0x0000001f
+#define TR_66_PIO_ACCESS_SHIFT		0
+
+/* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo
+ * Can do pio & mdma modes, clock unit is 30ns (33Mhz)
+ * 
+ * The access time and recovery time can be programmed. Some older
+ * Darwin code base limit OHare to 150ns cycle time. I decided to do
+ * the same here fore safety against broken old hardware ;)
+ * The HalfTick bit, when set, adds half a clock (15ns) to the access
+ * time and removes one from recovery. It's not supported on KeyLargo
+ * implementation afaik. The E bit appears to be set for PIO mode 0 and
+ * is used to reach long timings used in this mode.
+ */
+#define TR_33_MDMA_MASK			0x003ff800
+#define TR_33_MDMA_RECOVERY_MASK	0x001f0000
+#define TR_33_MDMA_RECOVERY_SHIFT	16
+#define TR_33_MDMA_ACCESS_MASK		0x0000f800
+#define TR_33_MDMA_ACCESS_SHIFT		11
+#define TR_33_MDMA_HALFTICK		0x00200000
+#define TR_33_PIO_MASK			0x000007ff
+#define TR_33_PIO_E			0x00000400
+#define TR_33_PIO_RECOVERY_MASK		0x000003e0
+#define TR_33_PIO_RECOVERY_SHIFT	5
+#define TR_33_PIO_ACCESS_MASK		0x0000001f
+#define TR_33_PIO_ACCESS_SHIFT		0
+
+/*
+ * Interrupt register definitions
+ */
+#define IDE_INTR_DMA			0x80000000
+#define IDE_INTR_DEVICE			0x40000000
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 
-typedef struct {
+/* Rounded Multiword DMA timings
+ * 
+ * I gave up finding a generic formula for all controller
+ * types and instead, built tables based on timing values
+ * used by Apple in Darwin's implementation.
+ */
+struct mdma_timings_t {
 	int	accessTime;
+	int	recoveryTime;
 	int	cycleTime;
-} pmac_ide_timing;
+};
+
+struct mdma_timings_t mdma_timings_33[] __pmacdata =
+{
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 135, 135, 270 },
+    { 120, 120, 240 },
+    { 105, 105, 210 },
+    {  90,  90, 180 },
+    {  75,  75, 150 },
+    {  75,  45, 120 },
+    {   0,   0,   0 }
+};
 
-/* Multiword DMA timings */
-static pmac_ide_timing mdma_timings[] =
+struct mdma_timings_t mdma_timings_33k[] __pmacdata =
 {
-    { 215,    480 },	/* Mode 0 */
-    {  80,    150 },	/*      1 */
-    {  70,    120 }	/*      2 */
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 150, 150, 300 },
+    { 120, 120, 240 },
+    {  90, 120, 210 },
+    {  90,  90, 180 },
+    {  90,  60, 150 },
+    {  90,  30, 120 },
+    {   0,   0,   0 }
 };
 
-/* Ultra DMA timings (for use when I know how to calculate them */
-static pmac_ide_timing udma_timings[] =
+struct mdma_timings_t mdma_timings_66[] __pmacdata =
 {
-    {   0,    114 },	/* Mode 0 */
-    {   0,     75 },	/*      1 */
-    {   0,     55 },	/*      2 */
-    {   100,   45 },	/*      3 */
-    {   100,   25 }	/*      4 */
+    { 240, 240, 480 },
+    { 180, 180, 360 },
+    { 135, 135, 270 },
+    { 120, 120, 240 },
+    { 105, 105, 210 },
+    {  90,  90, 180 },
+    {  90,  75, 165 },
+    {  75,  45, 120 },
+    {   0,   0,   0 }
+};
+
+/* Ultra DMA timings (rounded) */
+struct {
+	int	addrSetup; /* ??? */
+	int	rdy2pause;
+	int	wrDataSetup;
+} udma_timings[] __pmacdata =
+{
+    {   0, 180,  120 },	/* Mode 0 */
+    {   0, 150,  90 },	/*      1 */
+    {   0, 120,  60 },	/*      2 */
+    {   0, 90,   45 },	/*      3 */
+    {   0, 90,   30 }	/*      4 */
 };
 
 /* allow up to 256 DBDMA commands per xfer */
@@ -121,7 +250,14 @@
 };
 #endif /* CONFIG_PMAC_PBOOK */
 
-static int
+static int pmac_ide_notify_reboot(struct notifier_block *, unsigned long, void *);
+static struct notifier_block pmac_ide_reboot_notifier = {
+	pmac_ide_notify_reboot,
+	NULL,
+	0
+};
+
+static int __pmac
 pmac_ide_find(ide_drive_t *drive)
 {
 	ide_hwif_t *hwif = HWIF(drive);
@@ -140,7 +276,8 @@
  * N.B. this can't be an initfunc, because the media-bay task can
  * call ide_[un]register at any time.
  */
-void pmac_ide_init_hwif_ports(hw_regs_t *hw,
+void __pmac
+pmac_ide_init_hwif_ports(hw_regs_t *hw,
 			      ide_ioreg_t data_port, ide_ioreg_t ctrl_port,
 			      int *irq)
 {
@@ -198,24 +335,33 @@
 /* Setup timings for the selected drive (master/slave). I still need to verify if this
  * is enough, I beleive selectproc will be called whenever an IDE command is started,
  * but... */
-static void
+static void __pmac
 pmac_ide_selectproc(ide_drive_t *drive)
 {
 	int i = pmac_ide_find(drive);
 	if (i < 0)
 		return;
-			
-	if (drive->select.all & 0x10)
-		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[1]);
+
+	if (drive->select.b.unit & 0x01)
+		out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE),
+			pmac_ide[i].timings[1]);
 	else
-		out_le32((unsigned *)(IDE_DATA_REG + 0x200 + _IO_BASE), pmac_ide[i].timings[0]);
+		out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE),
+			pmac_ide[i].timings[0]);
+	(void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE));
 }
 
-/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */
-#define SYSCLK_TICKS(t)		(((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS)
-#define SYSCLK_TICKS_UDMA(t)	(((t) + IDE_SYSCLK_ULTRA_PS - 1) / IDE_SYSCLK_ULTRA_PS)
 
-static __inline__ int
+/* Note: We don't use the generic routine here because for some
+ * yet unexplained reasons, it cause some media-bay CD-ROMs to
+ * lockup the bus. Strangely, this new version of the code is
+ * almost identical to the generic one and works, I've not yet
+ * managed to figure out what bit is causing the lockup in the
+ * generic code, possibly a timing issue...
+ * 
+ * --BenH
+ */
+static int __pmac
 wait_for_ready(ide_drive_t *drive)
 {
 	/* Timeout bumped for some powerbooks */
@@ -241,51 +387,73 @@
 	return 0;
 }
 
-/* Note: We don't use the generic routine here because some of Apple's
- * controller seem to be very sensitive about how things are done.
- * We should probably set the NIEN bit, but that's an example of thing
- * that can cause the controller to hang under some circumstances when
- * done on the media-bay CD-ROM during boot. We do get occasional
- * spurrious interrupts because of that.
- * --BenH
- */
-static int
+static int __pmac
 pmac_ide_do_setfeature(ide_drive_t *drive, byte command)
 {
-	unsigned long flags;
 	int result = 1;
-
-	save_flags(flags);
-	cli();
+	unsigned long flags;
+	ide_hwif_t *hwif = HWIF(drive);
+	
+	disable_irq(hwif->irq);	/* disable_irq_nosync ?? */
 	udelay(1);
 	SELECT_DRIVE(HWIF(drive), drive);
 	SELECT_MASK(HWIF(drive), drive, 0);
 	udelay(1);
+	(void)GET_STAT(); /* Get rid of pending error state */
 	if(wait_for_ready(drive)) {
 		printk(KERN_ERR "pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n");
 		goto out;
 	}
-	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
+	udelay(10);
+	OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG);
 	OUT_BYTE(command, IDE_NSECTOR_REG);
+	OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG);
 	OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG);
 	udelay(1);
+	__save_flags(flags);	/* local CPU only */
+	ide__sti();		/* local CPU only -- for jiffies */
 	result = wait_for_ready(drive);
+	__restore_flags(flags); /* local CPU only */
+	OUT_BYTE(drive->ctl, IDE_CONTROL_REG);
 	if (result)
 		printk(KERN_ERR "pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n");
 out:
-	restore_flags(flags);
-	
+	SELECT_MASK(HWIF(drive), drive, 0);
+	if (result == 0) {
+		drive->id->dma_ultra &= ~0xFF00;
+		drive->id->dma_mword &= ~0x0F00;
+		drive->id->dma_1word &= ~0x0F00;
+		switch(command) {
+			case XFER_UDMA_7:   drive->id->dma_ultra |= 0x8080; break;
+			case XFER_UDMA_6:   drive->id->dma_ultra |= 0x4040; break;
+			case XFER_UDMA_5:   drive->id->dma_ultra |= 0x2020; break;
+			case XFER_UDMA_4:   drive->id->dma_ultra |= 0x1010; break;
+			case XFER_UDMA_3:   drive->id->dma_ultra |= 0x0808; break;
+			case XFER_UDMA_2:   drive->id->dma_ultra |= 0x0404; break;
+			case XFER_UDMA_1:   drive->id->dma_ultra |= 0x0202; break;
+			case XFER_UDMA_0:   drive->id->dma_ultra |= 0x0101; break;
+			case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break;
+			case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break;
+			case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break;
+			case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break;
+			case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break;
+			case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break;
+			default: break;
+		}
+	}
+	enable_irq(hwif->irq);
 	return result;
 }
 
 /* Calculate PIO timings */
-static void
+static void __pmac
 pmac_ide_tuneproc(ide_drive_t *drive, byte pio)
 {
 	ide_pio_data_t d;
 	int i;
 	u32 *timings;
-	int accessTicks, recTicks;
+	unsigned accessTicks, recTicks;
+	unsigned accessTime, recTime;
 	
 	i = pmac_ide_find(drive);
 	if (i < 0)
@@ -293,27 +461,44 @@
 		
 	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
 	accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[i].timings[1];
 	else
 		timings = &pmac_ide[i].timings[0];
-	
-	if (pmac_ide[i].kind == controller_kl_ata4) {
-		/* The "ata-4" IDE controller of Core99 machines */
-		accessTicks = SYSCLK_TICKS_UDMA(ide_pio_timings[pio].active_time * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(d.cycle_time * 1000) - accessTicks;
 
-		*timings = ((*timings) & 0x1FFFFFC00) | accessTicks | (recTicks << 5);
+	recTime = d.cycle_time - ide_pio_timings[pio].active_time
+			- ide_pio_timings[pio].setup_time;
+	recTime = max(recTime, 150U);
+	accessTime = ide_pio_timings[pio].active_time;
+	accessTime = max(accessTime, 150U);
+	if (pmac_ide[i].kind == controller_kl_ata4 ||
+		pmac_ide[i].kind == controller_kl_ata4_80) {
+		/* 66Mhz cell */
+		accessTicks = SYSCLK_TICKS_66(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		recTicks = SYSCLK_TICKS_66(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		*timings = ((*timings) & ~TR_66_PIO_MASK) |
+				(accessTicks << TR_66_PIO_ACCESS_SHIFT) |
+				(recTicks << TR_66_PIO_RECOVERY_SHIFT);
 	} else {
-		/* The old "ata-3" IDE controller */
-		accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time);
-		if (accessTicks < 4)
-			accessTicks = 4;
-		recTicks = SYSCLK_TICKS(d.cycle_time) - accessTicks - 4;
-		if (recTicks < 1)
-			recTicks = 1;
-	
-		*timings = ((*timings) & 0xFFFFFF800) | accessTicks | (recTicks << 5);
+		/* 33Mhz cell */
+		int ebit = 0;
+		accessTicks = SYSCLK_TICKS(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTicks = max(accessTicks, 4U);
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		recTicks = max(recTicks, 5U) - 4;
+		if (recTicks > 9) {
+			recTicks--; /* guess, but it's only for PIO0, so... */
+			ebit = 1;
+		}
+		*timings = ((*timings) & ~TR_33_PIO_MASK) |
+				(accessTicks << TR_33_PIO_ACCESS_SHIFT) |
+				(recTicks << TR_33_PIO_RECOVERY_SHIFT);
+		if (ebit)
+			*timings |= TR_33_PIO_E;
 	}
 
 #ifdef IDE_PMAC_DEBUG
@@ -326,70 +511,134 @@
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
-static int
-set_timings_udma(int intf, u32 *timings, byte speed)
+static int __pmac
+set_timings_udma(u32 *timings, byte speed)
 {
-	int cycleTime, accessTime;
-	int rdyToPauseTicks, cycleTicks;
-
-	if (pmac_ide[intf].kind != controller_kl_ata4)
-		return 1;
-		
-	cycleTime = udma_timings[speed & 0xf].cycleTime;
-	accessTime = udma_timings[speed & 0xf].accessTime;
+	unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks;
 
-	rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-	cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000);
-
-	*timings = ((*timings) & 0xe00fffff) |
-			((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20;
+	rdyToPauseTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].rdy2pause);
+	wrDataSetupTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].wrDataSetup);
+	addrTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].addrSetup);
+
+	*timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) |
+			(wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) | 
+			(rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) |
+			(addrTicks <<TR_66_UDMA_ADDRSETUP_SHIFT) |
+			TR_66_UDMA_EN;
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: Set UDMA timing for mode %d, reg: 0x%08x\n",
+		speed & 0xf,  *timings);
+#endif	
 
 	return 0;
 }
 
-static int
-set_timings_mdma(int intf, u32 *timings, byte speed)
+static int __pmac
+set_timings_mdma(int intf_type, u32 *timings, byte speed, int drive_cycle_time)
 {
-	int cycleTime, accessTime;
-	int accessTicks, recTicks;
+	int cycleTime, accessTime, recTime;
+	unsigned accessTicks, recTicks;
+	struct mdma_timings_t* tm;
+	int i;
 
-	/* Calculate accesstime and cycle time */
-	cycleTime = mdma_timings[speed & 0xf].cycleTime;
-	accessTime = mdma_timings[speed & 0xf].accessTime;
-	if ((pmac_ide[intf].kind == controller_ohare) && (cycleTime < 150))
+	/* Get default cycle time for mode */
+	switch(speed & 0xf) {
+		case 0: cycleTime = 480; break;
+		case 1: cycleTime = 150; break;
+		case 2: cycleTime = 120; break;
+		default:
+			return -1;
+	}
+	/* Adjust for drive */
+	if (drive_cycle_time && drive_cycle_time > cycleTime)
+		cycleTime = drive_cycle_time;
+	/* OHare limits according to some old Apple sources */	
+	if ((intf_type == controller_ohare) && (cycleTime < 150))
 		cycleTime = 150;
+	/* Get the proper timing array for this controller */
+	switch(intf_type) {
+		case controller_kl_ata4:
+		case controller_kl_ata4_80:
+			tm = mdma_timings_66;
+			break;
+		case controller_kl_ata3:
+			tm = mdma_timings_33k;
+			break;
+		default:
+			tm = mdma_timings_33;
+			break;
+	}
+	/* Lookup matching access & recovery times */
+	i = -1;
+	for (;;) {
+		if (tm[i+1].cycleTime < cycleTime)
+			break;
+		i++;
+	}
+	if (i < 0)
+		return -1;
+	cycleTime = tm[i].cycleTime;
+	accessTime = tm[i].accessTime;
+	recTime = tm[i].recoveryTime;
 
-	/* For ata-4 controller */
-	if (pmac_ide[intf].kind == controller_kl_ata4) {
-		accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks;
-		*timings = ((*timings) & 0xffe003ff) |
-			(accessTicks | (recTicks << 5)) << 10;
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n",
+		cycleTime, accessTime, recTime);
+#endif	
+	if (intf_type == controller_kl_ata4 || intf_type == controller_kl_ata4_80) {
+		/* 66Mhz cell */
+		accessTicks = SYSCLK_TICKS_66(accessTime);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTicks = max(accessTicks, 0x1U);
+		recTicks = SYSCLK_TICKS_66(recTime);
+		recTicks = min(recTicks, 0x1fU);
+		recTicks = max(recTicks, 0x3U);
+		/* Clear out mdma bits and disable udma */
+		*timings = ((*timings) & ~(TR_66_MDMA_MASK | TR_66_UDMA_MASK)) |
+			(accessTicks << TR_66_MDMA_ACCESS_SHIFT) |
+			(recTicks << TR_66_MDMA_RECOVERY_SHIFT);
+	} else if (intf_type == controller_kl_ata3) {
+		/* 33Mhz cell on KeyLargo */
+		accessTicks = SYSCLK_TICKS(accessTime);
+		accessTicks = max(accessTicks, 1U);
+		accessTicks = min(accessTicks, 0x1fU);
+		accessTime = accessTicks * IDE_SYSCLK_NS;
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = max(recTicks, 1U);
+		recTicks = min(recTicks, 0x1fU);
+		*timings = ((*timings) & ~TR_33_MDMA_MASK) |
+				(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
+				(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
 	} else {
+		/* 33Mhz cell on others */
 		int halfTick = 0;
 		int origAccessTime = accessTime;
-		int origCycleTime = cycleTime;
+		int origRecTime = recTime;
 		
 		accessTicks = SYSCLK_TICKS(accessTime);
-		if (accessTicks < 1)
-			accessTicks = 1;
+		accessTicks = max(accessTicks, 1U);
+		accessTicks = min(accessTicks, 0x1fU);
 		accessTime = accessTicks * IDE_SYSCLK_NS;
-		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
-		if (recTicks < 1)
-			recTicks = 1;
-		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
-
-		/* KeyLargo ata-3 don't support the half-tick stuff */
-		if ((pmac_ide[intf].kind != controller_kl_ata3) &&
-			(accessTicks > 1) &&
-			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
-			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
-            			halfTick    = 1;
-				accessTicks--;
-		}
-		*timings = ((*timings) & 0x7FF) |
-			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
+		recTicks = SYSCLK_TICKS(recTime);
+		recTicks = max(recTicks, 2U) - 1;
+		recTicks = min(recTicks, 0x1fU);
+		recTime = (recTicks + 1) * IDE_SYSCLK_NS;
+		if ((accessTicks > 1) &&
+		    ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
+		    ((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) {
+            		halfTick = 1;
+			accessTicks--;
+		}
+		*timings = ((*timings) & ~TR_33_MDMA_MASK) |
+				(accessTicks << TR_33_MDMA_ACCESS_SHIFT) |
+				(recTicks << TR_33_MDMA_RECOVERY_SHIFT);
+		if (halfTick)
+			*timings |= TR_33_MDMA_HALFTICK;
 	}
+#ifdef IDE_PMAC_DEBUG
+	printk(KERN_ERR "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
+		speed & 0xf,  *timings);
+#endif	
 	return 0;
 }
 #endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */
@@ -397,11 +646,11 @@
 /* You may notice we don't use this function on normal operation,
  * our, normal mdma function is supposed to be more precise
  */
-static int
+static int __pmac
 pmac_ide_tune_chipset (ide_drive_t *drive, byte speed)
 {
 	int intf		= pmac_ide_find(drive);
-	int unit		= (drive->select.all & 0x10) ? 1:0;
+	int unit		= (drive->select.b.unit & 0x01);
 	int ret			= 0;
 	u32 *timings;
 
@@ -414,19 +663,25 @@
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
 		case XFER_UDMA_4:
 		case XFER_UDMA_3:
+			if (pmac_ide[intf].kind != controller_kl_ata4_80)
+				return 1;		
 		case XFER_UDMA_2:
 		case XFER_UDMA_1:
 		case XFER_UDMA_0:
-			ret = set_timings_udma(intf, timings, speed);
+			if (pmac_ide[intf].kind != controller_kl_ata4 &&
+				pmac_ide[intf].kind != controller_kl_ata4_80)
+				return 1;		
+			ret = set_timings_udma(timings, speed);
 			break;
 		case XFER_MW_DMA_2:
 		case XFER_MW_DMA_1:
 		case XFER_MW_DMA_0:
+			ret = set_timings_mdma(pmac_ide[intf].kind, timings, speed, 0);
+			break;
 		case XFER_SW_DMA_2:
 		case XFER_SW_DMA_1:
 		case XFER_SW_DMA_0:
-			ret = set_timings_mdma(intf, timings, speed);
-			break;
+			return 1;
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 		case XFER_PIO_4:
 		case XFER_PIO_3:
@@ -451,13 +706,46 @@
 	return 0;
 }
 
-ide_ioreg_t
+static void __pmac
+sanitize_timings(int i)
+{
+	unsigned value;
+	
+	switch(pmac_ide[i].kind) {
+		case controller_kl_ata4:
+		case controller_kl_ata4_80:
+			value = 0x0008438c;
+			break;
+		case controller_kl_ata3:
+			value = 0x00084526;
+			break;
+		case controller_heathrow:
+		case controller_ohare:
+		default:
+			value = 0x00074526;
+			break;
+	}
+	pmac_ide[i].timings[0] = pmac_ide[i].timings[1] = value;
+}
+
+ide_ioreg_t __pmac
 pmac_ide_get_base(int index)
 {
 	return pmac_ide[index].regbase;
 }
 
-int
+int __pmac
+pmac_ide_check_base(ide_ioreg_t base)
+{
+	int ix;
+	
+ 	for (ix = 0; ix < MAX_HWIFS; ++ix)
+		if (base == pmac_ide[ix].regbase)
+			return ix;
+	return -1;
+}
+
+int __pmac
 pmac_ide_get_irq(ide_ioreg_t base)
 {
 	int ix;
@@ -468,7 +756,7 @@
 	return 0;
 }
 
-static int ide_majors[] = { 3, 22, 33, 34, 56, 57 };
+static int ide_majors[]  __pmacdata = { 3, 22, 33, 34, 56, 57 };
 
 kdev_t __init
 pmac_find_ide_boot(char *bootdevice, int n)
@@ -532,6 +820,7 @@
 
 	for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) {
 		struct device_node *tp;
+		struct pmac_ide_hwif* pmhw;
 		int *bidp;
 		int in_bay = 0;
 
@@ -560,8 +849,24 @@
 			++i;
 		if (i >= MAX_HWIFS)
 			break;
+		pmhw = &pmac_ide[i];
+
+		/*
+		 * Some older OFs have bogus sizes, causing request_OF_resource
+		 * to fail. We fix them up here
+		 */
+		if (np->addrs[0].size > 0x1000)
+			np->addrs[0].size = 0x1000;
+		if (np->n_addrs > 1 && np->addrs[1].size > 0x100)
+			np->addrs[1].size = 0x100;
+
+		pmhw->reg_resource = request_OF_resource(np, 0, "  (mac-io IDE IO)");
+		if (!pmhw->reg_resource) {
+			printk(KERN_ERR "ide-pmac(%s): can't request IO resource !\n", np->name);
+			continue;
+		}
 
-		base = (unsigned long) ioremap(np->addrs[0].address, 0x200) - _IO_BASE;
+		base = (unsigned long) ioremap(np->addrs[0].address, 0x400) - _IO_BASE;
 
 		/* XXX This is bogus. Should be fixed in the registry by checking
 		   the kind of host interrupt controller, a bit like gatwick
@@ -574,21 +879,30 @@
 		} else {
 			irq = np->intrs[0].line;
 		}
-		pmac_ide[i].regbase = base;
-		pmac_ide[i].irq = irq;
-		pmac_ide[i].node = np;
+		pmhw->regbase = base;
+		pmhw->irq = irq;
+		pmhw->node = np;
 		if (device_is_compatible(np, "keylargo-ata")) {
 			if (strcmp(np->name, "ata-4") == 0)
-				pmac_ide[i].kind = controller_kl_ata4;
+				pmhw->kind = controller_kl_ata4;
 			else
-				pmac_ide[i].kind = controller_kl_ata3;
+				pmhw->kind = controller_kl_ata3;
 		} else if (device_is_compatible(np, "heathrow-ata"))
-			pmac_ide[i].kind = controller_heathrow;
+			pmhw->kind = controller_heathrow;
 		else
-			pmac_ide[i].kind = controller_ohare;
+			pmhw->kind = controller_ohare;
 
 		bidp = (int *)get_property(np, "AAPL,bus-id", NULL);
-		pmac_ide[i].aapl_bus_id =  bidp ? *bidp : 0;
+		pmhw->aapl_bus_id =  bidp ? *bidp : 0;
+
+		if (pmhw->kind == controller_kl_ata4) {
+			char* cable = get_property(np, "cable-type", NULL);
+			if (cable && !strncmp(cable, "80-", 3))
+				pmhw->kind = controller_kl_ata4_80;
+		}
+
+		/* Make sure we have sane timings */
+		sanitize_timings(i);
 
 		if (np->parent && np->parent->name
 		    && strcasecmp(np->parent->name, "media-bay") == 0) {
@@ -596,39 +910,22 @@
 			media_bay_set_ide_infos(np->parent,base,irq,i);
 #endif /* CONFIG_PMAC_PBOOK */
 			in_bay = 1;
-		} else if (pmac_ide[i].kind == controller_ohare) {
+			if (!bidp)
+				pmhw->aapl_bus_id = 1;
+		} else if (pmhw->kind == controller_ohare) {
 			/* The code below is having trouble on some ohare machines
 			 * (timing related ?). Until I can put my hand on one of these
 			 * units, I keep the old way
 			 */
-			 feature_set(np, FEATURE_IDE0_enable);
+			ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1);
 		} else {
  			/* This is necessary to enable IDE when net-booting */
 			printk(KERN_INFO "pmac_ide: enabling IDE bus ID %d\n",
-				pmac_ide[i].aapl_bus_id);
-			switch(pmac_ide[i].aapl_bus_id) {
-			    case 0:
-				feature_set(np, FEATURE_IDE0_reset);
-				mdelay(10);
- 				feature_set(np, FEATURE_IDE0_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE0_reset);
-				break;
-			    case 1:
-				feature_set(np, FEATURE_IDE1_reset);
-				mdelay(10);
- 				feature_set(np, FEATURE_IDE1_enable);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE1_reset);
-				break;
-			    case 2:
-			    	/* This one exists only for KL, I don't know
-				   about any enable bit */
-				feature_set(np, FEATURE_IDE2_reset);
-				mdelay(10);
-				feature_clear(np, FEATURE_IDE2_reset);
-				break;
-			}
+				pmhw->aapl_bus_id);
+			ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 1);
+			ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmhw->aapl_bus_id, 1);
+			mdelay(10);
+			ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 0);
 			big_delay = 1;
 		}
 
@@ -637,6 +934,7 @@
 		memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports));
 		hwif->chipset = ide_pmac;
 		hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET] || in_bay;
+		hwif->udma_four = (pmhw->kind == controller_kl_ata4_80);
 #ifdef CONFIG_PMAC_PBOOK
 		if (in_bay && check_media_bay_by_base(base, MB_CD) == 0)
 			hwif->noprobe = 0;
@@ -658,6 +956,7 @@
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_register_sleep_notifier(&idepmac_sleep_notifier);
 #endif /* CONFIG_PMAC_PBOOK */
+	register_reboot_notifier(&pmac_ide_reboot_notifier);
 }
 
 #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
@@ -665,6 +964,12 @@
 static void __init 
 pmac_ide_setup_dma(struct device_node *np, int ix)
 {
+	pmac_ide[ix].dma_resource = request_OF_resource(np, 1, " (mac-io IDE DMA)");
+	if (!pmac_ide[ix].dma_resource) {
+		printk(KERN_ERR "ide-pmac(%s): can't request DMA resource !\n", np->name);
+		return;
+	}
+
 	pmac_ide[ix].dma_regs =
 		(volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200);
 
@@ -692,7 +997,7 @@
  * pmac_ide_build_dmatable builds the DBDMA command list
  * for a transfer and sets the DBDMA channel to point to it.
  */
-static int
+static int __pmac
 pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr)
 {
 	struct dbdma_cmd *table, *tstart;
@@ -703,6 +1008,13 @@
 	volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs;
 
 	table = tstart = (struct dbdma_cmd *) DBDMA_ALIGN(pmac_ide[ix].dma_table);
+
+#ifdef IDE_PMAC_DEBUG
+	if (in_le32(&dma->status) & (RUN|ACTIVE))
+		printk("ide-pmac: channel status not stopped ! (%x)\n",
+			in_le32(&dma->status));
+#endif	
+	/* Make sure channel is stopped and all error conditions are clear */
 	out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
 	while (in_le32(&dma->status) & RUN)
 		udelay(1);
@@ -734,6 +1046,10 @@
 		 * Note that one DBDMA command can transfer
 		 * at most 65535 bytes.
 		 */
+#ifdef IDE_PMAC_DEBUG
+		if (size & 0x01)
+			printk("ide-pmac: odd size transfer ! (%d)\n", size);
+#endif			
 		while (size) {
 			unsigned int tc = (size < 0xfe00)? size: 0xfe00;
 
@@ -782,12 +1098,14 @@
 }
 
 static __inline__ unsigned char
-udma_bits_to_command(unsigned char bits)
+udma_bits_to_command(unsigned char bits, int high_speed)
 {
-	if(bits & 0x10)
-		return XFER_UDMA_4;
-	if(bits & 0x08)
-		return XFER_UDMA_3;
+	if (high_speed) {
+		if(bits & 0x10)
+			return XFER_UDMA_4;
+		if(bits & 0x08)
+			return XFER_UDMA_3;
+	}
 	if(bits & 0x04)
 		return XFER_UDMA_2;
 	if(bits & 0x02)
@@ -798,14 +1116,13 @@
 }
 
 /* Calculate MultiWord DMA timings */
-static int
+static int __pmac
 pmac_ide_mdma_enable(ide_drive_t *drive, int idx)
 {
 	byte bits = drive->id->dma_mword & 0x07;
 	byte feature = dma_bits_to_command(bits);
 	u32 *timings;
-	int cycleTime, accessTime;
-	int accessTicks, recTicks;
+	int drive_cycle_time;
 	struct hd_driveid *id = drive->id;
 	int ret;
 
@@ -821,66 +1138,30 @@
 		drive->init_speed = feature;
 	
 	/* which drive is it ? */
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[idx].timings[1];
 	else
 		timings = &pmac_ide[idx].timings[0];
 
-	/* Calculate accesstime and cycle time */
-	cycleTime = mdma_timings[feature & 0xf].cycleTime;
-	accessTime = mdma_timings[feature & 0xf].accessTime;
+	/* Check if drive provide explicit cycle time */
 	if ((id->field_valid & 2) && (id->eide_dma_time))
-		cycleTime = id->eide_dma_time;
-	if ((pmac_ide[idx].kind == controller_ohare) && (cycleTime < 150))
-		cycleTime = 150;
+		drive_cycle_time = id->eide_dma_time;
+	else
+		drive_cycle_time = 0;
+
+	/* Calculate controller timings */
+	set_timings_mdma(pmac_ide[idx].kind, timings, feature, drive_cycle_time);
 
-	/* For ata-4 controller */
-	if (pmac_ide[idx].kind == controller_kl_ata4) {
-		accessTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-		recTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000) - accessTicks;
-		*timings = ((*timings) & 0xffe003ff) |
-			(accessTicks | (recTicks << 5)) << 10;
-	} else {
-		int halfTick = 0;
-		int origAccessTime = accessTime;
-		int origCycleTime = cycleTime;
-		
-		accessTicks = SYSCLK_TICKS(accessTime);
-		if (accessTicks < 1)
-			accessTicks = 1;
-		accessTime = accessTicks * IDE_SYSCLK_NS;
-		recTicks = SYSCLK_TICKS(cycleTime - accessTime) - 1;
-		if (recTicks < 1)
-			recTicks = 1;
-		cycleTime = (recTicks + 1 + accessTicks) * IDE_SYSCLK_NS;
-
-		/* KeyLargo ata-3 don't support the half-tick stuff */
-		if ((pmac_ide[idx].kind != controller_kl_ata3) &&
-			(accessTicks > 1) &&
-			((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) &&
-			((cycleTime - IDE_SYSCLK_NS) >= origCycleTime)) {
-            			halfTick    = 1;
-				accessTicks--;
-		}
-		*timings = ((*timings) & 0x7FF) |
-			(accessTicks | (recTicks << 5) | (halfTick << 10)) << 11;
-	}
-#ifdef IDE_PMAC_DEBUG
-	printk(KERN_INFO "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n",
-		feature & 0xf, *timings);
-#endif
 	drive->current_speed = feature;	
 	return 1;
 }
 
 /* Calculate Ultra DMA timings */
-static int
-pmac_ide_udma_enable(ide_drive_t *drive, int idx)
+static int __pmac
+pmac_ide_udma_enable(ide_drive_t *drive, int idx, int high_speed)
 {
 	byte bits = drive->id->dma_ultra & 0x1f;
-	byte feature = udma_bits_to_command(bits);
-	int cycleTime, accessTime;
-	int rdyToPauseTicks, cycleTicks;
+	byte feature = udma_bits_to_command(bits, high_speed);
 	u32 *timings;
 	int ret;
 
@@ -896,25 +1177,18 @@
 		drive->init_speed = feature;
 
 	/* which drive is it ? */
-	if (drive->select.all & 0x10)
+	if (drive->select.b.unit & 0x01)
 		timings = &pmac_ide[idx].timings[1];
 	else
 		timings = &pmac_ide[idx].timings[0];
 
-	cycleTime = udma_timings[feature & 0xf].cycleTime;
-	accessTime = udma_timings[feature & 0xf].accessTime;
-
-	rdyToPauseTicks = SYSCLK_TICKS_UDMA(accessTime * 1000);
-	cycleTicks = SYSCLK_TICKS_UDMA(cycleTime * 1000);
-
-	*timings = ((*timings) & 0xe00fffff) |
-			((cycleTicks << 1) | (rdyToPauseTicks << 5) | 1) << 20;
+	set_timings_udma(timings, feature);
 
 	drive->current_speed = feature;	
 	return 1;
 }
 
-static int
+static int __pmac
 pmac_ide_check_dma(ide_drive_t *drive)
 {
 	int ata4, udma, idx;
@@ -935,21 +1209,20 @@
 		enable = 0;
 
 	udma = 0;
-	ata4 = (pmac_ide[idx].kind == controller_kl_ata4);
+	ata4 = (pmac_ide[idx].kind == controller_kl_ata4 ||
+		pmac_ide[idx].kind == controller_kl_ata4_80);
 			
 	if(enable) {
 		if (ata4 && (drive->media == ide_disk) &&
-		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x17)) {
+		    (id->field_valid & 0x0004) && (id->dma_ultra & 0x1f)) {
 			/* UltraDMA modes. */
-			drive->using_dma = pmac_ide_udma_enable(drive, idx);
+			drive->using_dma = pmac_ide_udma_enable(drive, idx,
+				pmac_ide[idx].kind == controller_kl_ata4_80);
 		}
 		if (!drive->using_dma && (id->dma_mword & 0x0007)) {
 			/* Normal MultiWord DMA modes. */
 			drive->using_dma = pmac_ide_mdma_enable(drive, idx);
 		}
-		/* Without this, strange things will happen on Keylargo-based
-		 * machines
-		 */
 		OUT_BYTE(0, IDE_CONTROL_REG);
 		/* Apply settings to controller */
 		pmac_ide_selectproc(drive);
@@ -957,10 +1230,13 @@
 	return 0;
 }
 
-int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
+static int __pmac
+pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive)
 {
-	int ix, dstat, i;
+	int ix, dstat;
 	volatile struct dbdma_regs *dma;
+	byte unit = (drive->select.b.unit & 0x01);
+	byte ata4;
 
 	/* Can we stuff a pointer to our intf structure in config_data
 	 * or select_data in hwif ?
@@ -969,7 +1245,9 @@
 	if (ix < 0)
 		return 0;		
 	dma = pmac_ide[ix].dma_regs;
-
+	ata4 = (pmac_ide[ix].kind == controller_kl_ata4 ||
+		pmac_ide[ix].kind == controller_kl_ata4_80);
+	
 	switch (func) {
 	case ide_dma_off:
 		printk(KERN_INFO "%s: DMA disabled\n", drive->name);
@@ -984,6 +1262,13 @@
 	case ide_dma_write:
 		if (!pmac_ide_build_dmatable(drive, ix, func==ide_dma_write))
 			return 1;
+		/* Apple adds 60ns to wrDataSetup on reads */
+		if (ata4 && (pmac_ide[ix].timings[unit] & TR_66_UDMA_EN)) {
+			out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE),
+				pmac_ide[ix].timings[unit] + 
+				((func == ide_dma_read) ? 0x00800000UL : 0));
+			(void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE));
+		}
 		drive->waiting_for_dma = 1;
 		if (drive->media != ide_disk)
 			return 0;
@@ -992,35 +1277,56 @@
 			 IDE_COMMAND_REG);
 	case ide_dma_begin:
 		out_le32(&dma->control, (RUN << 16) | RUN);
+		/* Make sure it gets to the controller right now */
+		(void)in_le32(&dma->control);
 		break;
-	case ide_dma_end:
+	case ide_dma_end: /* returns 1 on error, 0 otherwise */
 		drive->waiting_for_dma = 0;
 		dstat = in_le32(&dma->status);
 		out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16));
 		/* verify good dma status */
 		return (dstat & (RUN|DEAD|ACTIVE)) != RUN;
-	case ide_dma_test_irq:
-		if ((in_le32(&dma->status) & (RUN|ACTIVE)) == RUN)
-			return 1;
-		/* That's a bit ugly and dangerous, but works in our case
-		 * to workaround a problem with the channel status staying
-		 * active if the drive returns an error
+	case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */
+		/* We have to things to deal with here:
+		 * 
+		 * - The dbdma won't stop if the command was started
+		 * but completed with an error without transfering all
+		 * datas. This happens when bad blocks are met during
+		 * a multi-block transfer.
+		 * 
+		 * - The dbdma fifo hasn't yet finished flushing to
+		 * to system memory when the disk interrupt occurs.
+		 * 
+		 * The trick here is to increment drive->waiting_for_dma,
+		 * and return as if no interrupt occured. If the counter
+		 * reach a certain timeout value, we then return 1. If
+		 * we really got the interrupt, it will happen right away
+		 * again.
+		 * Apple's solution here may be more elegant. They issue
+		 * a DMA channel interrupt (a separate irq line) via a DBDMA
+		 * NOP command just before the STOP, and wait for both the
+		 * disk and DBDMA interrupts to have completed.
 		 */
-		if (IDE_CONTROL_REG) {
-			byte stat;
-			stat = GET_ALTSTAT();
-			if (stat & ERR_STAT)
-				return 1;
-		}
-		/* In some edge cases, some datas may still be in the dbdma
-		 * engine fifo, we wait a bit for dbdma to complete
+		 
+		/* If ACTIVE is cleared, the STOP command have passed and
+		 * transfer is complete.
 		 */
-		while ((in_le32(&dma->status) & (RUN|ACTIVE)) != RUN) {
-			if (++i > 100)
-				return 0;
-			udelay(1);
+		if (!(in_le32(&dma->status) & ACTIVE))
+			return 1;
+		if (!drive->waiting_for_dma)
+			printk(KERN_WARNING "ide%d, ide_dma_test_irq \
+				called while not waiting\n", ix);
+
+		/* If dbdma didn't execute the STOP command yet, the
+		 * active bit is still set */
+		drive->waiting_for_dma++;
+		if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) {
+			printk(KERN_WARNING "ide%d, timeout waiting \
+				for dbdma command stop\n", ix);
+			return 1;
 		}
-		return 1;
+		udelay(1);
+		return 0;
 
 		/* Let's implement tose just in case someone wants them */
 	case ide_dma_bad_drive:
@@ -1041,8 +1347,8 @@
 }
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 
-#ifdef CONFIG_PMAC_PBOOK
-static void idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base)
+static void __pmac
+idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base)
 {
 	int j;
 	
@@ -1052,7 +1358,9 @@
 	switch (drive->media) {
 	case ide_disk:
 		/* Spin down the drive */
-		outb(0xa0, base+0x60);
+		outb(drive->select.all, base+0x60);
+		(void)inb(base+0x60);
+		udelay(100);
 		outb(0x0, base+0x30);
 		outb(0x0, base+0x20);
 		outb(0x0, base+0x40);
@@ -1076,8 +1384,10 @@
 	}
 }
 
-static void idepmac_wake_device(ide_drive_t *drive, int used_dma)
- {
+#ifdef CONFIG_PMAC_PBOOK
+static void __pmac
+idepmac_wake_device(ide_drive_t *drive, int used_dma)
+{
 	/* We force the IDE subdriver to check for a media change
 	 * This must be done first or we may lost the condition
 	 *
@@ -1101,12 +1411,15 @@
 		HWGROUP(drive)->busy = 1;
 		pmac_ide_check_dma(drive);
 		HWGROUP(drive)->busy = 0;
+		if (!list_empty(&drive->queue.queue_head))
+			ide_do_request(HWGROUP(drive), 0);
 		spin_unlock_irq(&io_request_lock);
 	}
 #endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */
 }
 
-static void idepmac_sleep_interface(int i, unsigned base, int mediabay)
+static void __pmac
+idepmac_sleep_interface(int i, unsigned base, int mediabay)
 {
 	struct device_node* np = pmac_ide[i].node;
 
@@ -1118,73 +1431,85 @@
 	if (mediabay)
 		return;
 	
-	/* Disable and reset the bus */
-	feature_set(np, FEATURE_IDE0_reset);
-	feature_clear(np, FEATURE_IDE0_enable);
-	switch(pmac_ide[i].aapl_bus_id) {
-	    case 0:
-		feature_set(np, FEATURE_IDE0_reset);
-		feature_clear(np, FEATURE_IDE0_enable);
-		break;
-	    case 1:
-		feature_set(np, FEATURE_IDE1_reset);
-		feature_clear(np, FEATURE_IDE1_enable);
-		break;
-	    case 2:
-		feature_set(np, FEATURE_IDE2_reset);
-		break;
-	}
+	/* Disable the bus */
+	ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 0);
 }
 
-static void idepmac_wake_interface(int i, unsigned long base, int mediabay)
+static void __pmac
+idepmac_wake_interface(int i, unsigned long base, int mediabay)
 {
 	struct device_node* np = pmac_ide[i].node;
 
 	if (!mediabay) {
 		/* Revive IDE disk and controller */
-		switch(pmac_ide[i].aapl_bus_id) {
-		    case 0:
-			feature_set(np, FEATURE_IDE0_reset);
-			feature_set(np, FEATURE_IOBUS_enable);
-			mdelay(10);
-	 		feature_set(np, FEATURE_IDE0_enable);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE0_reset);
-			break;
-		    case 1:
-			feature_set(np, FEATURE_IDE1_reset);
-			feature_set(np, FEATURE_IOBUS_enable);
-			mdelay(10);
-	 		feature_set(np, FEATURE_IDE1_enable);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE1_reset);
-			break;
-		    case 2:
-		    	/* This one exists only for KL, I don't know
-			   about any enable bit */
-			feature_set(np, FEATURE_IDE2_reset);
-			mdelay(10);
-			feature_clear(np, FEATURE_IDE2_reset);
-			break;
-		}
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 1);
+		ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 1);
+		mdelay(10);
+		ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 0);
 	}
+}
+
+static void
+idepmac_sleep_drive(ide_drive_t *drive, int idx, unsigned long base)
+{
+	int unlock = 0;
+
+	/* Wait for HW group to complete operations */
+	if (ide_spin_wait_hwgroup(drive)) {
+		// What can we do here ? Wake drive we had already
+		// put to sleep and return an error ?
+	} else {
+		unlock = 1;
+		/* Lock HW group */
+		HWGROUP(drive)->busy = 1;
+		/* Stop the device */
+		idepmac_sleep_device(drive, idx, base);
+	}
+	if (unlock)
+		spin_unlock_irq(&io_request_lock);
+}
+
+static void
+idepmac_wake_drive(ide_drive_t *drive, unsigned long base)
+{
+	unsigned long flags;
+	int j;
 	
 	/* Reset timings */
-	pmac_ide_selectproc(&ide_hwifs[i].drives[0]);
+	pmac_ide_selectproc(drive);
 	mdelay(10);
+	
+	/* Wait up to 20 seconds for the drive to be ready */
+	for (j = 0; j < 200; j++) {
+		int status;
+		mdelay(100);
+		outb(drive->select.all, base + 0x60);
+		if (inb(base + 0x60) != drive->select.all)
+			continue;
+		status = inb(base + 0x70);
+		if (!(status & BUSY_STAT))
+			break;
+	}
+
+	/* We resume processing on the HW group */
+	spin_lock_irqsave(&io_request_lock, flags);
+	HWGROUP(drive)->busy = 0;
+	if (!list_empty(&drive->queue.queue_head))
+		ide_do_request(HWGROUP(drive), 0);
+	spin_unlock_irqrestore(&io_request_lock, flags);			
 }
 
 /* Note: We support only master drives for now. This will have to be
  * improved if we want to handle sleep on the iMacDV where the CD-ROM
  * is a slave
  */
-static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
+static int __pmac
+idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
 	int i, ret;
 	unsigned long base;
-	unsigned long flags;
 	int big_delay;
-
+ 
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
 		break;
@@ -1193,34 +1518,19 @@
 	case PBOOK_SLEEP_NOW:
 		for (i = 0; i < pmac_ide_count; ++i) {
 			ide_hwif_t *hwif;
-			ide_drive_t *drive;
-			int unlock = 0;
+			int dn;
 
 			if ((base = pmac_ide[i].regbase) == 0)
-				continue;	
+				continue;
 
 			hwif = &ide_hwifs[i];
-			drive = &hwif->drives[0];
-			
-			if (drive->present) {
-				/* Wait for HW group to complete operations */
-				if (ide_spin_wait_hwgroup(drive)) {
-					// What can we do here ? Wake drive we had already
-					// put to sleep and return an error ?
-				} else {
-					unlock = 1;
-					/* Lock HW group */
-					HWGROUP(drive)->busy = 1;
-
-					/* Stop the device */
-					idepmac_sleep_device(drive, i, base);
-				
-				}
+			for (dn=0; dn<MAX_DRIVES; dn++) {
+				if (!hwif->drives[dn].present)
+					continue;
+				idepmac_sleep_drive(&hwif->drives[dn], i, base);
 			}
 			/* Disable irq during sleep */
 			disable_irq(pmac_ide[i].irq);
-			if (unlock)
-				spin_unlock_irq(&io_request_lock);
 			
 			/* Check if this is a media bay with an IDE device or not
 			 * a media bay.
@@ -1237,6 +1547,9 @@
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
 				
+			/* Make sure we have sane timings */		
+			sanitize_timings(i);
+
 			/* Check if this is a media bay with an IDE device or not
 			 * a media bay
 			 */
@@ -1253,46 +1566,79 @@
 	
 		for (i = 0; i < pmac_ide_count; ++i) {
 			ide_hwif_t *hwif;
-			ide_drive_t *drive;			
-			int j, used_dma;
+			int used_dma, dn;
+			int irq_on = 0;
 			
 			if ((base = pmac_ide[i].regbase) == 0)
 				continue;
 				
 			hwif = &ide_hwifs[i];
-			drive = &hwif->drives[0];
-
-			/* Wait for the drive to come up and set it's DMA */
-			if (drive->present) {
-				/* Wait up to 20 seconds */
-				for (j = 0; j < 200; j++) {
-					int status;
-					mdelay(100);
-					status = inb(base + 0x70);
-					if (!(status & BUSY_STAT))
-						break;
+			for (dn=0; dn<MAX_DRIVES; dn++) {
+				ide_drive_t *drive = &hwif->drives[dn];
+				if (!drive->present)
+					continue;
+				/* We don't have re-configured DMA yet */
+				used_dma = drive->using_dma;
+				drive->using_dma = 0;
+				idepmac_wake_drive(drive, base);
+				if (!irq_on) {
+					enable_irq(pmac_ide[i].irq);
+					irq_on = 1;
 				}
-			}
-			
-			/* We don't have re-configured DMA yet */
-			used_dma = drive->using_dma;
-			drive->using_dma = 0;
-
-			/* We resume processing on the HW group */
-			spin_lock_irqsave(&io_request_lock, flags);
-			enable_irq(pmac_ide[i].irq);
-			if (drive->present)
-				HWGROUP(drive)->busy = 0;
-			spin_unlock_irqrestore(&io_request_lock, flags);
-			
-			/* Wake the device
-			 * We could handle the slave here
-			 */
-			if (drive->present)
 				idepmac_wake_device(drive, used_dma);
+			}
+			if (!irq_on)
+				enable_irq(pmac_ide[i].irq);
 		}
 		break;
 	}
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+static int __pmac
+pmac_ide_notify_reboot(struct notifier_block *this, unsigned long code, void *x)
+{
+	int i, gotone;
+	unsigned long base;
+
+	if (code != SYS_HALT && code != SYS_POWER_OFF)
+		return 0;
+
+	gotone = 0;
+	for (i = 0; i < pmac_ide_count; ++i) {
+		ide_hwif_t *hwif;
+		ide_drive_t *drive;
+		int unlock = 0;
+		int dn;
+
+		if ((base = pmac_ide[i].regbase) == 0)
+				continue;	
+
+		hwif = &ide_hwifs[i];
+		for (dn=0; dn<MAX_DRIVES; dn++) {
+			drive = &hwif->drives[dn];
+			if (drive->present) {
+				gotone = 1;
+				/* Wait for HW group to complete operations */
+				if (ide_spin_wait_hwgroup(drive)) {
+					// What can we do here ? Wake drive we had already
+					// put to sleep and return an error ?
+				} else {
+					unlock = 1;
+					/* Lock HW group */
+					HWGROUP(drive)->busy = 1;
+
+					/* Stop the device */
+					idepmac_sleep_device(drive, i, base);
+				}
+			}
+			if (unlock)
+				spin_unlock_irq(&io_request_lock);
+		}
+	}
+	if (gotone)
+		mdelay(1000);
+		
+	return NOTIFY_DONE;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ide/ide.c linux/drivers/ide/ide.c
--- linux.orig/drivers/ide/ide.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/ide/ide.c	Tue Jan  8 18:08:44 2002
@@ -1367,7 +1367,10 @@
  * the driver.  This makes the driver much more friendlier to shared IRQs
  * than previous designs, while remaining 100% (?) SMP safe and capable.
  */
-static void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq)
+/* --BenH: made non-static as ide-pmac.c uses it to kick the hwgroup back
+ *         into life on wakeup from machine sleep.
+ */ 
+void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq)
 {
 	ide_drive_t	*drive;
 	ide_hwif_t	*hwif;
@@ -1884,7 +1887,6 @@
 		if (drive->part[p].nr_sects > 0) {
 			kdev_t devp = MKDEV(major, minor+p);
 			invalidate_device(devp, 1);
-			set_blocksize(devp, 1024);
 		}
 		drive->part[p].start_sect = 0;
 		drive->part[p].nr_sects   = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ieee1394/sbp2.c linux/drivers/ieee1394/sbp2.c
--- linux.orig/drivers/ieee1394/sbp2.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/ieee1394/sbp2.c	Mon Jan  7 14:06:24 2002
@@ -2773,7 +2773,9 @@
 	/*
 	 * Tell scsi stack that we're done with this command
 	 */
+	spin_lock_irq(&io_request_lock);
 	done (SCpnt);
+	spin_unlock_irq(&io_request_lock);
 
 	return;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/ieee1394/video1394.c linux/drivers/ieee1394/video1394.c
--- linux.orig/drivers/ieee1394/video1394.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/ieee1394/video1394.c	Mon Feb  4 16:55:13 2002
@@ -617,7 +617,7 @@
 		if (d->ir_prg[i][d->nb_cmd-1].status & 0xFFFF0000) {
 			reset_ir_status(d, i);
 			d->buffer_status[i] = VIDEO1394_BUFFER_READY;
-			get_fast_time(&d->buffer_time[i]);
+			do_gettimeofday(&d->buffer_time[i]);
 		}
 	}
 	spin_unlock(&d->lock);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/input/evdev.c linux/drivers/input/evdev.c
--- linux.orig/drivers/input/evdev.c	Sun Sep 30 19:26:05 2001
+++ linux/drivers/input/evdev.c	Mon Feb  4 16:55:13 2002
@@ -67,7 +67,7 @@
 
 	while (list) {
 
-		get_fast_time(&list->buffer[list->head].time);
+		do_gettimeofday(&list->buffer[list->head].time);
 		list->buffer[list->head].type = type;
 		list->buffer[list->head].code = code;
 		list->buffer[list->head].value = value;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/isdn/avmb1/capi.c linux/drivers/isdn/avmb1/capi.c
--- linux.orig/drivers/isdn/avmb1/capi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/isdn/avmb1/capi.c	Wed Feb 13 17:37:40 2002
@@ -87,10 +87,10 @@
 	struct capincci  *nccip;
 	unsigned int      minor;
 
-	__u16		 applid;
-	__u32		 ncci;
-	__u16		 datahandle;
-	__u16		 msgid;
+	u16  		 applid;
+	u32		 ncci;
+	u16		 datahandle;
+	u16		 msgid;
 
 	struct file      *file;
 	struct tty_struct *tty;
@@ -112,7 +112,7 @@
 	/* transmit path */
 	struct datahandle_queue {
 		    struct datahandle_queue *next;
-		    __u16                    datahandle;
+		    u16                      datahandle;
 	} *ackqueue;
 	int nack;
 
@@ -121,7 +121,7 @@
 
 struct capincci {
 	struct capincci *next;
-	__u32		 ncci;
+	u32		 ncci;
 	struct capidev	*cdev;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 	struct capiminor *minorp;
@@ -131,8 +131,8 @@
 struct capidev {
 	struct capidev *next;
 	struct file    *file;
-	__u16		applid;
-	__u16		errcode;
+	u16		applid;
+	u16		errcode;
 	unsigned int    minor;
 	unsigned        userflags;
 
@@ -166,7 +166,7 @@
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 /* -------- datahandles --------------------------------------------- */
 
-int capincci_add_ack(struct capiminor *mp, __u16 datahandle)
+static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct datahandle_queue *n, **pp;
 
@@ -184,7 +184,7 @@
 	return 0;
 }
 
-int capiminor_del_ack(struct capiminor *mp, __u16 datahandle)
+static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
 {
 	struct datahandle_queue **pp, *p;
 
@@ -200,7 +200,7 @@
 	return -1;
 }
 
-void capiminor_del_all_ack(struct capiminor *mp)
+static void capiminor_del_all_ack(struct capiminor *mp)
 {
 	struct datahandle_queue **pp, *p;
 
@@ -216,7 +216,7 @@
 
 /* -------- struct capiminor ---------------------------------------- */
 
-struct capiminor *capiminor_alloc(__u16 applid, __u32 ncci)
+static struct capiminor *capiminor_alloc(u16 applid, u32 ncci)
 {
 	struct capiminor *mp, **pp;
         unsigned int minor = 0;
@@ -257,7 +257,7 @@
 	return mp;
 }
 
-void capiminor_free(struct capiminor *mp)
+static void capiminor_free(struct capiminor *mp)
 {
 	struct capiminor **pp;
 
@@ -283,7 +283,7 @@
 	}
 }
 
-struct capiminor *capiminor_find(unsigned int minor)
+static struct capiminor *capiminor_find(unsigned int minor)
 {
 	struct capiminor *p;
 	for (p = minors; p && p->minor != minor; p = p->next)
@@ -294,7 +294,7 @@
 
 /* -------- struct capincci ----------------------------------------- */
 
-static struct capincci *capincci_alloc(struct capidev *cdev, __u32 ncci)
+static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *np, **pp;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -331,7 +331,7 @@
         return np;
 }
 
-static void capincci_free(struct capidev *cdev, __u32 ncci)
+static void capincci_free(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *np, **pp;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -375,7 +375,7 @@
 	}
 }
 
-struct capincci *capincci_find(struct capidev *cdev, __u32 ncci)
+static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
 {
 	struct capincci *p;
 
@@ -426,7 +426,7 @@
 	kmem_cache_free(capidev_cachep, cdev);
 }
 
-static struct capidev *capidev_find(__u16 applid)
+static struct capidev *capidev_find(u16 applid)
 {
 	struct capidev *p;
 	for (p=capidev_openlist; p; p = p->next) {
@@ -439,13 +439,13 @@
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 /* -------- handle data queue --------------------------------------- */
 
-struct sk_buff *
+static struct sk_buff *
 gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
 {
 	struct sk_buff *nskb;
 	nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
 	if (nskb) {
-		__u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+		u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
 		unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
 		capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
 		capimsg_setu16(s, 2, mp->applid);
@@ -458,11 +458,11 @@
 	return nskb;
 }
 
-int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
 {
 	struct sk_buff *nskb;
 	unsigned int datalen;
-	__u16 errcode, datahandle;
+	u16 errcode, datahandle;
 
 	datalen = skb->len - CAPIMSG_LEN(skb->data);
 	if (mp->tty) {
@@ -538,7 +538,7 @@
 	return -1;
 }
 
-void handle_minor_recv(struct capiminor *mp)
+static void handle_minor_recv(struct capiminor *mp)
 {
 	struct sk_buff *skb;
 	while ((skb = skb_dequeue(&mp->inqueue)) != 0) {
@@ -552,13 +552,13 @@
 	}
 }
 
-int handle_minor_send(struct capiminor *mp)
+static int handle_minor_send(struct capiminor *mp)
 {
 	struct sk_buff *skb;
-	__u16 len;
+	u16 len;
 	int count = 0;
-	__u16 errcode;
-	__u16 datahandle;
+	u16 errcode;
+	u16 datahandle;
 
 	if (mp->tty && mp->ttyoutstop) {
 #if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
@@ -569,7 +569,7 @@
 
 	while ((skb = skb_dequeue(&mp->outqueue)) != 0) {
 		datahandle = mp->datahandle;
-		len = (__u16)skb->len;
+		len = (u16)skb->len;
 		skb_push(skb, CAPI_DATA_B3_REQ_LEN);
 		memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
 		capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
@@ -578,7 +578,7 @@
 		capimsg_setu8 (skb->data, 5, CAPI_REQ);
 		capimsg_setu16(skb->data, 6, mp->msgid++);
 		capimsg_setu32(skb->data, 8, mp->ncci);	/* NCCI */
-		capimsg_setu32(skb->data, 12, (__u32) skb->data); /* Data32 */
+		capimsg_setu32(skb->data, 12, (u32) skb->data); /* Data32 */
 		capimsg_setu16(skb->data, 16, len);	/* Data length */
 		capimsg_setu16(skb->data, 18, datahandle);
 		capimsg_setu16(skb->data, 20, 0);	/* Flags */
@@ -620,16 +620,16 @@
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 /* -------- function called by lower level -------------------------- */
 
-static void capi_signal(__u16 applid, void *param)
+static void capi_signal(u16 applid, void *param)
 {
 	struct capidev *cdev = (struct capidev *)param;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
 	struct capiminor *mp;
-	__u16 datahandle;
+	u16 datahandle;
 #endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
 	struct capincci *np;
 	struct sk_buff *skb = 0;
-	__u32 ncci;
+	u32 ncci;
 
 	(void) (*capifuncs->capi_get_message) (applid, &skb);
 	if (!skb) {
@@ -758,7 +758,7 @@
 	struct capidev *cdev = (struct capidev *)file->private_data;
 	struct sk_buff *skb;
 	int retval;
-	__u16 mlen;
+	u16 mlen;
 
         if (ppos != &file->f_pos)
 		return -ESPIPE;
@@ -998,7 +998,7 @@
 						sizeof(ncci));
 			if (retval)
 				return -EFAULT;
-			nccip = capincci_find(cdev, (__u32) ncci);
+			nccip = capincci_find(cdev, (u32) ncci);
 			if (!nccip)
 				return 0;
 #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
@@ -1023,7 +1023,7 @@
 						sizeof(ncci));
 			if (retval)
 				return -EFAULT;
-			nccip = capincci_find(cdev, (__u32) ncci);
+			nccip = capincci_find(cdev, (u32) ncci);
 			if (!nccip || (mp = nccip->minorp) == 0)
 				return -ESRCH;
 			return mp->minor;
@@ -1272,7 +1272,7 @@
 
 /* -------- tty_operations for capincci ----------------------------- */
 
-int capinc_tty_open(struct tty_struct * tty, struct file * file)
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
 {
 	struct capiminor *mp;
 
@@ -1300,7 +1300,7 @@
 	return 0;
 }
 
-void capinc_tty_close(struct tty_struct * tty, struct file * file)
+static void capinc_tty_close(struct tty_struct * tty, struct file * file)
 {
 	struct capiminor *mp;
 
@@ -1325,8 +1325,8 @@
 #endif
 }
 
-int capinc_tty_write(struct tty_struct * tty, int from_user,
-		      const unsigned char *buf, int count)
+static int capinc_tty_write(struct tty_struct * tty, int from_user,
+			    const unsigned char *buf, int count)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1377,7 +1377,7 @@
 	return count;
 }
 
-void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+static void capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1414,7 +1414,7 @@
 	}
 }
 
-void capinc_tty_flush_chars(struct tty_struct *tty)
+static void capinc_tty_flush_chars(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	struct sk_buff *skb;
@@ -1440,7 +1440,7 @@
 	(void)handle_minor_recv(mp);
 }
 
-int capinc_tty_write_room(struct tty_struct *tty)
+static int capinc_tty_write_room(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	int room;
@@ -1458,7 +1458,7 @@
 	return room;
 }
 
-int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+static int capinc_tty_chars_in_buffer(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 	if (!mp || !mp->nccip) {
@@ -1476,7 +1476,7 @@
 	return mp->outbytes;
 }
 
-int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
 	int error = 0;
@@ -1488,14 +1488,14 @@
 	return error;
 }
 
-void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
+static void capinc_tty_set_termios(struct tty_struct *tty, struct termios * old)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_set_termios\n");
 #endif
 }
 
-void capinc_tty_throttle(struct tty_struct * tty)
+static void capinc_tty_throttle(struct tty_struct * tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1505,7 +1505,7 @@
 		mp->ttyinstop = 1;
 }
 
-void capinc_tty_unthrottle(struct tty_struct * tty)
+static void capinc_tty_unthrottle(struct tty_struct * tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1517,7 +1517,7 @@
 	}
 }
 
-void capinc_tty_stop(struct tty_struct *tty)
+static void capinc_tty_stop(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1528,7 +1528,7 @@
 	}
 }
 
-void capinc_tty_start(struct tty_struct *tty)
+static void capinc_tty_start(struct tty_struct *tty)
 {
 	struct capiminor *mp = (struct capiminor *)tty->driver_data;
 #ifdef _DEBUG_TTYFUNCS
@@ -1540,49 +1540,43 @@
 	}
 }
 
-void capinc_tty_hangup(struct tty_struct *tty)
+static void capinc_tty_hangup(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_hangup\n");
 #endif
 }
 
-void capinc_tty_break_ctl(struct tty_struct *tty, int state)
+static void capinc_tty_break_ctl(struct tty_struct *tty, int state)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
 #endif
 }
 
-void capinc_tty_flush_buffer(struct tty_struct *tty)
+static void capinc_tty_flush_buffer(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
 #endif
 }
 
-void capinc_tty_set_ldisc(struct tty_struct *tty)
+static void capinc_tty_set_ldisc(struct tty_struct *tty)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
 #endif
 }
 
-void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
 {
 #ifdef _DEBUG_TTYFUNCS
 	printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
 #endif
 }
 
-int capinc_tty_read_proc(char *page, char **start, off_t off,
-			  int count, int *eof, void *data)
-{
-	return 0;
-}
-
-int capinc_write_proc(struct file *file, const char *buffer,
-			  unsigned long count, void *data)
+static int capinc_tty_read_proc(char *page, char **start, off_t off,
+				int count, int *eof, void *data)
 {
 	return 0;
 }
@@ -1594,7 +1588,7 @@
 static struct termios *capinc_tty_termios[CAPINC_NR_PORTS];
 static struct termios *capinc_tty_termios_locked[CAPINC_NR_PORTS];
 
-int capinc_tty_init(void)
+static int capinc_tty_init(void)
 {
 	struct tty_driver *drv = &capinc_tty_driver;
 
@@ -1652,7 +1646,7 @@
 	return 0;
 }
 
-void capinc_tty_exit(void)
+static void capinc_tty_exit(void)
 {
 	struct tty_driver *drv = &capinc_tty_driver;
 	int retval;
@@ -1778,7 +1772,7 @@
 /* -------- init function and module interface ---------------------- */
 
 
-static void __exit alloc_exit(void)
+static void alloc_exit(void)
 {
 	if (capidev_cachep) {
 		(void)kmem_cache_destroy(capidev_cachep);
@@ -1844,7 +1838,7 @@
 	return 0;
 }
 
-static void lower_callback(unsigned int cmd, __u32 contr, void *data)
+static void lower_callback(unsigned int cmd, u32 contr, void *data)
 {
 	struct capi_ncciinfo *np;
 	struct capidev *cdev;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/isdn/hisax/hisax_fcpcipnp.c linux/drivers/isdn/hisax/hisax_fcpcipnp.c
--- linux.orig/drivers/isdn/hisax/hisax_fcpcipnp.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/isdn/hisax/hisax_fcpcipnp.c	Mon Feb  4 17:55:44 2002
@@ -20,6 +20,7 @@
  *
  * o POWER PC
  * o clean up debugging
+ * o tx_skb at PH_DEACTIVATE time
  */
 
 #include <linux/version.h>
@@ -370,9 +371,6 @@
 
 	DBG(0x40, "hdlc_fill_fifo");
 
-	if (!skb)
-		BUG();
-
 	if (skb->len == 0)
 		BUG();
 
@@ -581,14 +579,14 @@
 		adapter->write_ctrl(bcs, 5);
 		break;
 	case L1_MODE_TRANS:
-		bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
-		adapter->write_ctrl(bcs, 5);
-		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
-		adapter->write_ctrl(bcs, 1);
-		bcs->ctrl.sr.cmd = 0;
-		break;
 	case L1_MODE_HDLC:
-		bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+		bcs->rcvidx = 0;
+		bcs->tx_cnt = 0;
+		bcs->tx_skb = NULL;
+		if (mode == L1_MODE_TRANS)
+			bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+		else
+			bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
 		adapter->write_ctrl(bcs, 5);
 		bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
 		adapter->write_ctrl(bcs, 1);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/isdn/hisax/md5sums.asc linux/drivers/isdn/hisax/md5sums.asc
--- linux.orig/drivers/isdn/hisax/md5sums.asc	Wed Oct 24 22:34:51 2001
+++ linux/drivers/isdn/hisax/md5sums.asc	Tue Feb  5 17:27:00 2002
@@ -1,3 +1,6 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
 # This are valid md5sums for certificated HiSax driver.
 # The certification is valid only if the md5sums of all files match.
 # The certification is valid only for ELSA Microlink PCI,
@@ -6,17 +9,25 @@
 # in the moment.
 # Read ../../../Documentation/isdn/HiSax.cert for more informations.
 # 
-d08b59f56fb9ed1fbd17713342c75081  isac.c
-e81e6e96f307e55f8b9777aca2b356d9  isdnl1.c
-cfd2527d9fb01885484cba74bfc67121  isdnl2.c
-8c6829f11459f9d044b5768803fb646d  isdnl3.c
-d40f88dff4191d2660240749cbdcb688  tei.c
-3bd3bd05ee4cb25ffe046200b569a83a  callc.c
-d518f52402ebc3f1be84e09af375313c  cert.c
-c425de1f8be86e84006de63c9bb3cc5f  l3dss1.c
-4c411e29d4103ba60e9af4e3e1234a99  l3_1tr6.c
-68c6cc2784f208e3247a5a555918d014  elsa.c
-8d63a85d7222cf7b40e663e543191d8f  diva.c
-8c8cb4ce621fb84d8e337a696e75b0df  sedlbauer.c
-ebe5613d535748409407568435b2be97  hfc_pci.c
+cd4a9917e1147039d5dfc66440d42054  isac.c
+211840e78b56c9d4753be9c85da21a50  isdnl1.c
+5ce9b1fff42a02f9c2eb4fb81c701b1f  isdnl2.c
+6948de0c43513dd23c6706feb5fc2209  isdnl3.c
+3730780b69368218d756024165efea79  tei.c
+16e72710eb58da01415b877490f5d2ac  callc.c
+6abc55c77e0f3149ae9334f3257a1a1a  cert.c
+27bdb2800d4590e00da20eff241edc47  l3dss1.c
+df8bb877b854c4302d396b554e4e84ef  l3_1tr6.c
+3d22d2f2fe9af693eb3b471beecf596d  elsa.c
+fd6aedade5b5bbd73683a3de878b76d2  diva.c
+def8d69efd25186878dc123df73d9319  sedlbauer.c
+6c5bd0fdc59f33319823fa5367357983  hfc_pci.c
 # end of md5sums
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.0.6 (GNU/Linux)
+Comment: For info see http://www.gnupg.org
+
+iD8DBQE8UYO9DiY0VZsg4ukRAqLzAKCKK7rFSNAWpBMtpJZ0kYvF+y+b1wCZAY8u
+hf6AL732sGhUsfRpwwdENVU=
+=B3yd
+-----END PGP SIGNATURE-----
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/isdn/isdn_audio.c linux/drivers/isdn/isdn_audio.c
--- linux.orig/drivers/isdn/isdn_audio.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/isdn/isdn_audio.c	Wed Feb  6 20:47:54 2002
@@ -228,7 +228,7 @@
 	:	"memory", "ax");
 #else
 	while (n--)
-		*buff++ = table[*(unsigned char *)buff];
+		*buff = table[*(unsigned char *)buff], buff++;
 #endif
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/Makefile linux/drivers/macintosh/Makefile
--- linux.orig/drivers/macintosh/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/Makefile	Wed Dec 26 16:32:31 2001
@@ -32,9 +32,11 @@
 obj-$(CONFIG_MAC_HID)		+= mac_hid.o
 obj-$(CONFIG_INPUT_ADBHID)	+= adbhid.o
 obj-$(CONFIG_PPC_RTC)		+= rtc.o
+obj-$(CONFIG_ANSLCD)		+= ans-lcd.o
 
 obj-$(CONFIG_ADB_PMU)		+= via-pmu.o
 obj-$(CONFIG_ADB_CUDA)		+= via-cuda.o
+obj-$(CONFIG_PMAC_APM_EMU)	+= apm_emu.o
 
 obj-$(CONFIG_ADB)		+= adb.o
 obj-$(CONFIG_ADB_KEYBOARD)	+= mac_keyb.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/adb.c linux/drivers/macintosh/adb.c
--- linux.orig/drivers/macintosh/adb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/adb.c	Wed Dec 26 16:32:31 2001
@@ -34,6 +34,7 @@
 #include <linux/wait.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/completion.h>
 #include <asm/uaccess.h>
 #ifdef CONFIG_PPC
 #include <asm/prom.h>
@@ -78,7 +79,7 @@
 static int adb_inited = 0;
 static pid_t adb_probe_task_pid;
 static int adb_probe_task_flag;
-static wait_queue_head_t adb_probe_task_wq;
+static struct completion adb_probe_task_comp;
 static int sleepy_trackpad;
 int __adb_probe_sync;
 
@@ -318,7 +319,7 @@
 		if (machine_is_compatible("AAPL,PowerBook1998") ||
 			machine_is_compatible("PowerBook1,1"))
 			sleepy_trackpad = 1;
-		init_waitqueue_head(&adb_probe_task_wq);
+		init_completion(&adb_probe_task_comp);
 		adbdev_init();
 		adb_reset_bus();
 	}
@@ -435,7 +436,7 @@
 static void
 adb_probe_wakeup(struct adb_request *req)
 {
-	wake_up(&adb_probe_task_wq);
+	complete(&adb_probe_task_comp);
 }
 
 static struct adb_request adb_sreq;
@@ -484,20 +485,11 @@
 	if ((flags & ADBREQ_SYNC) &&
 	    (current->pid && adb_probe_task_pid &&
 	    adb_probe_task_pid == current->pid)) {
-		DECLARE_WAITQUEUE(wait, current);
 		req->done = adb_probe_wakeup;
-		add_wait_queue(&adb_probe_task_wq, &wait);
 		rc = adb_controller->send_request(req, 0);
 		if (rc || req->complete)
 			goto bail;
-		for (;;) {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			if (req->complete)
-				break;
-			schedule();
-		}
-		current->state = TASK_RUNNING;
-		remove_wait_queue(&adb_probe_task_wq, &wait);
+		wait_for_completion(&adb_probe_task_comp);
 		rc = 0;
 		goto bail;
 	}
@@ -707,17 +699,16 @@
 		return ret;
 
 	req = NULL;
+	spin_lock_irqsave(&state->lock, flags);
 	add_wait_queue(&state->wait_queue, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
 	for (;;) {
-		spin_lock_irqsave(&state->lock, flags);
 		req = state->completed;
 		if (req != NULL)
 			state->completed = req->next;
 		else if (atomic_read(&state->n_pending) == 0)
 			ret = -EIO;
-		spin_unlock_irqrestore(&state->lock, flags);
 		if (req != NULL || ret != 0)
 			break;
 		
@@ -729,12 +720,15 @@
 			ret = -ERESTARTSYS;
 			break;
 		}
+		spin_unlock_irqrestore(&state->lock, flags);
 		schedule();
+		spin_lock_irqsave(&state->lock, flags);
 	}
 
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&state->wait_queue, &wait);
-
+	spin_unlock_irqrestore(&state->lock, flags);
+	
 	if (ret)
 		return ret;
 
@@ -757,6 +751,8 @@
 
 	if (count < 2 || count > sizeof(req->data))
 		return -EINVAL;
+	if (adb_controller == NULL)
+		return -ENXIO;
 	ret = verify_area(VERIFY_READ, buf, count);
 	if (ret)
 		return ret;
@@ -776,7 +772,10 @@
 		goto out;
 
 	atomic_inc(&state->n_pending);
-	if (adb_controller == NULL) return -ENXIO;
+
+	/* If a probe is in progress, wait for it to complete */
+	while (adb_probe_task_pid != 0 || test_bit(0, &adb_probe_task_flag))
+		schedule();
 
 	/* Special case for ADB_BUSRESET request, all others are sent to
 	   the controller */
@@ -784,6 +783,8 @@
 		&&(req->data[1] == ADB_BUSRESET)) {
 		ret = do_adb_reset_bus();
 		atomic_dec(&state->n_pending);
+		if (ret == 0)
+			ret = count;
 		goto out;
 	} else {	
 		req->reply_expected = ((req->data[1] & 0xc) == 0xc);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/adbhid.c linux/drivers/macintosh/adbhid.c
--- linux.orig/drivers/macintosh/adbhid.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/adbhid.c	Wed Dec 26 16:32:31 2001
@@ -273,26 +273,39 @@
 		break;
 	case 0x1f: /* Powerbook button device */
 	  {
+	  	int down = (data[1] == (data[1] & 0xf));
 #ifdef CONFIG_PMAC_BACKLIGHT
 		int backlight = get_backlight_level();
-
+#endif
 		/*
 		 * XXX: Where is the contrast control for the passive?
 		 *  -- Cort
 		 */
 
-		switch (data[1]) {
+		switch (data[1] & 0x0f) {
 		case 0x8:	/* mute */
+			input_report_key(&adbhid[id]->input, KEY_MUTE,
+				data[1] == (data[1] & 0xf));
 			break;
 
-		case 0x7:	/* contrast decrease */
+		case 0x7:	/* volume decrease */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEDOWN,
+				data[1] == (data[1] & 0xf));
 			break;
 
-		case 0x6:	/* contrast increase */
+		case 0x6:	/* volume increase */
+			input_report_key(&adbhid[id]->input, KEY_VOLUMEUP,
+				data[1] == (data[1] & 0xf));
+ 			break;
+
+		case 0xb:	/* eject */
+			input_report_key(&adbhid[id]->input, KEY_EJECTCD,
+				data[1] == (data[1] & 0xf));
 			break;
 
+#ifdef CONFIG_PMAC_BACKLIGHT
 		case 0xa:	/* brightness decrease */
-			if (backlight < 0)
+			if (!down || backlight < 0)
 				break;
 			if (backlight > BACKLIGHT_OFF)
 				set_backlight_level(backlight-1);
@@ -301,15 +314,15 @@
 			break;
 
 		case 0x9:	/* brightness increase */
-			if (backlight < 0)
+			if (!down || backlight < 0)
 				break;
 			if (backlight < BACKLIGHT_MAX)
 				set_backlight_level(backlight+1);
 			else 
 				set_backlight_level(BACKLIGHT_MAX);
 			break;
-		}
 #endif /* CONFIG_PMAC_BACKLIGHT */
+		}
 	  }
 	  break;
 	}
@@ -504,6 +517,11 @@
 		case 0x1f: /* Powerbook button device */
 			sprintf(adbhid[id]->name, "ADB Powerbook buttons on ID %d:%d.%02x",
 				id, default_id, original_handler_id);
+			adbhid[id]->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+			set_bit(KEY_MUTE, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEUP, adbhid[id]->input.keybit);
+			set_bit(KEY_VOLUMEDOWN, adbhid[id]->input.keybit);
+			set_bit(KEY_EJECTCD, adbhid[id]->input.keybit);
 			break;
 		}
 		if (adbhid[id]->name[0])
@@ -542,16 +560,38 @@
 }
 
 
+static u16
+adbhid_input_reregister(int id, int default_id, int org_handler_id,
+			int cur_handler_id, int mk)
+{
+	if (adbhid[id]) {
+		if (adbhid[id]->input.idproduct !=
+		    ((id << 12)|(default_id << 8)|org_handler_id)) {
+			adbhid_input_unregister(id);
+			adbhid_input_register(id, default_id, org_handler_id,
+				cur_handler_id, mk);
+		}
+	} else
+		adbhid_input_register(id, default_id, org_handler_id,
+			cur_handler_id, mk);
+	return 1<<id;
+}
+
+static void
+adbhid_input_devcleanup(u16 exist)
+{
+	int i;
+	for(i=1; i<16; i++)
+	    if (adbhid[i] && !(exist&(1<<i)))
+		adbhid_input_unregister(i);
+}
+ 
 static void
 adbhid_probe(void)
 {
 	struct adb_request req;
 	int i, default_id, org_handler_id, cur_handler_id;
-
-	for (i = 1; i < 16; i++) {
-		if (adbhid[i])
-			adbhid_input_unregister(i);
-	}
+	u16 reg = 0;
 
 	adb_register(ADB_MOUSE, 0, &mouse_ids, adbhid_mouse_input);
 	adb_register(ADB_KEYBOARD, 0, &keyboard_ids, adbhid_keyboard_input);
@@ -580,14 +620,14 @@
 			printk("ADB keyboard at %d, handler 1\n", id);
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, cur_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, cur_handler_id, 0);
 	}
 
 	for (i = 0; i < buttons_ids.nids; i++) {
 		int id = buttons_ids.id[i];
 
 		adb_get_infos(id, &default_id, &org_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id, org_handler_id, 0);
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id, org_handler_id, 0);
 	}
 
 	/* Try to switch all mice to handler 4, or 2 for three-button
@@ -676,9 +716,10 @@
 		printk("\n");
 
 		adb_get_infos(id, &default_id, &cur_handler_id);
-		adbhid_input_register(id, default_id, org_handler_id,
+		reg |= adbhid_input_reregister(id, default_id, org_handler_id,
 				      cur_handler_id, mouse_kind);
         }
+	adbhid_input_devcleanup(reg);
 }
 
 static void 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/ans-lcd.c linux/drivers/macintosh/ans-lcd.c
--- linux.orig/drivers/macintosh/ans-lcd.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/macintosh/ans-lcd.c	Mon Jan  7 13:00:34 2002
@@ -0,0 +1,171 @@
+/*
+ * /dev/lcd driver for Apple Network Servers.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/ans-lcd.h>
+#include <asm/io.h>
+
+#define ANSLCD_ADDR		0xf301c000
+#define ANSLCD_CTRL_IX 0x00
+#define ANSLCD_DATA_IX 0x10
+
+static unsigned long anslcd_short_delay = 80;
+static unsigned long anslcd_long_delay = 3280;
+static volatile unsigned char* anslcd_ptr;
+
+#undef DEBUG
+
+static void __pmac
+anslcd_write_byte_ctrl ( unsigned char c )
+{
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: CTRL byte: %02x\n",c);
+#endif
+	out_8(anslcd_ptr + ANSLCD_CTRL_IX, c);
+	switch(c) {
+		case 1:
+		case 2:
+		case 3:
+			udelay(anslcd_long_delay); break;
+		default: udelay(anslcd_short_delay);
+	}
+}
+
+static void __pmac
+anslcd_write_byte_data ( unsigned char c )
+{
+	out_8(anslcd_ptr + ANSLCD_DATA_IX, c);
+	udelay(anslcd_short_delay);
+}
+
+static ssize_t __pmac
+anslcd_write( struct file * file, const char * buf, 
+				size_t count, loff_t *ppos )
+{
+	const char * p = buf;
+	int i;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: write\n");
+#endif
+
+	if ( verify_area(VERIFY_READ, buf, count) )
+		return -EFAULT;
+	for ( i = *ppos; count > 0; ++i, ++p, --count ) 
+	{
+		char c;
+		__get_user(c, p);
+		anslcd_write_byte_data( c );
+	}
+	*ppos = i;
+	return p - buf;
+}
+
+static int __pmac
+anslcd_ioctl( struct inode * inode, struct file * file,
+				unsigned int cmd, unsigned long arg )
+{
+	char ch, *temp;
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: ioctl(%d,%d)\n",cmd,arg);
+#endif
+
+	switch ( cmd )
+	{
+	case ANSLCD_CLEAR:
+		anslcd_write_byte_ctrl ( 0x38 );
+		anslcd_write_byte_ctrl ( 0x0f );
+		anslcd_write_byte_ctrl ( 0x06 );
+		anslcd_write_byte_ctrl ( 0x01 );
+		anslcd_write_byte_ctrl ( 0x02 );
+		return 0;
+	case ANSLCD_SENDCTRL:
+		temp = (char *) arg;
+		__get_user(ch, temp);
+		for (; ch; temp++) { /* FIXME: This is ugly, but should work, as a \0 byte is not a valid command code */
+			anslcd_write_byte_ctrl ( ch );
+			__get_user(ch, temp);
+		}
+		return 0;
+	case ANSLCD_SETSHORTDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_short_delay=arg;
+		return 0;
+	case ANSLCD_SETLONGDELAY:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		anslcd_long_delay=arg;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int __pmac
+anslcd_open( struct inode * inode, struct file * file )
+{
+	return 0;
+}
+
+struct file_operations anslcd_fops = {
+	write:	anslcd_write,
+	ioctl:	anslcd_ioctl,
+	open:	anslcd_open,
+};
+
+static struct miscdevice anslcd_dev = {
+	ANSLCD_MINOR,
+	"anslcd",
+	&anslcd_fops
+};
+
+const char anslcd_logo[] =	"********************"  /* Line #1 */
+				"*      LINUX!      *"  /* Line #3 */
+				"*    Welcome to    *"  /* Line #2 */
+				"********************"; /* Line #4 */
+
+int __init
+anslcd_init(void)
+{
+	int a;
+	struct device_node* node;
+
+	node = find_devices("lcd");
+	if (!node || !node->parent)
+		return -ENODEV;
+	if (strcmp(node->parent->name, "gc"))
+		return -ENODEV;
+
+	anslcd_ptr = (volatile unsigned char*)ioremap(ANSLCD_ADDR, 0x20);
+	
+	misc_register(&anslcd_dev);
+
+#ifdef DEBUG
+	printk(KERN_DEBUG "LCD: init\n");
+#endif
+
+	anslcd_write_byte_ctrl ( 0x38 );
+	anslcd_write_byte_ctrl ( 0x0c );
+	anslcd_write_byte_ctrl ( 0x06 );
+	anslcd_write_byte_ctrl ( 0x01 );
+	anslcd_write_byte_ctrl ( 0x02 );
+	for(a=0;a<80;a++) {
+		anslcd_write_byte_data(anslcd_logo[a]);
+	}
+	return 0;
+}
+
+__initcall(anslcd_init);
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/apm_emu.c linux/drivers/macintosh/apm_emu.c
--- linux.orig/drivers/macintosh/apm_emu.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/macintosh/apm_emu.c	Mon Jan 21 18:49:12 2002
@@ -0,0 +1,546 @@
+/* APM emulation layer for PowerMac
+ * 
+ * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ *
+ * Lots of code inherited from apm.c, see appropriate notice in
+ *  arch/i386/kernel/apm.c
+ *
+ * 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, 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.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/fcntl.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/proc_fs.h>
+#include <linux/miscdevice.h>
+#include <linux/apm_bios.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+
+#include <linux/adb.h>
+#include <linux/pmu.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(args...) printk(KERN_DEBUG args)
+//#define DBG(args...) xmon_printf(args)
+#else
+#define DBG(args...) do { } while (0)
+#endif
+
+/*
+ * The apm_bios device is one of the misc char devices.
+ * This is its minor number.
+ */
+#define	APM_MINOR_DEV	134
+
+/*
+ * Maximum number of events stored
+ */
+#define APM_MAX_EVENTS		20
+
+#define FAKE_APM_BIOS_VERSION	0x0101
+
+#define APM_USER_NOTIFY_TIMEOUT	(5*HZ)
+
+/*
+ * The per-file APM data
+ */
+struct apm_user {
+	int		magic;
+	struct apm_user *	next;
+	int		suser: 1;
+	int		suspend_waiting: 1;
+	int		suspends_pending;
+	int		suspends_read;
+	int		event_head;
+	int		event_tail;
+	apm_event_t	events[APM_MAX_EVENTS];
+};
+
+/*
+ * The magic number in apm_user
+ */
+#define APM_BIOS_MAGIC		0x4101
+
+/*
+ * Local variables
+ */
+static int			suspends_pending;
+
+static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
+static struct apm_user *	user_list;
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier apm_sleep_notifier = {
+	apm_notify_sleep,
+	SLEEP_LEVEL_USERLAND,
+};
+
+static char			driver_version[] = "0.5";	/* no spaces */
+
+#ifdef DEBUG
+static char *	apm_event_name[] = {
+	"system standby",
+	"system suspend",
+	"normal resume",
+	"critical resume",
+	"low battery",
+	"power status change",
+	"update time",
+	"critical suspend",
+	"user standby",
+	"user suspend",
+	"system standby resume",
+	"capabilities change"
+};
+#define NR_APM_EVENT_NAME	\
+		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
+
+#endif
+
+static int queue_empty(struct apm_user *as)
+{
+	return as->event_head == as->event_tail;
+}
+
+static apm_event_t get_queued_event(struct apm_user *as)
+{
+	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+	return as->events[as->event_tail];
+}
+
+static void queue_event(apm_event_t event, struct apm_user *sender)
+{
+	struct apm_user *	as;
+
+	DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
+	if (user_list == NULL)
+		return;
+	for (as = user_list; as != NULL; as = as->next) {
+		if (as == sender)
+			continue;
+		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
+		if (as->event_head == as->event_tail) {
+			static int notified;
+
+			if (notified++ == 0)
+			    printk(KERN_ERR "apm_emu: an event queue overflowed\n");
+			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
+		}
+		as->events[as->event_head] = event;
+		if (!as->suser)
+			continue;
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_pending++;
+			suspends_pending++;
+			break;
+		case APM_NORMAL_RESUME:
+			as->suspend_waiting = 0;
+			break;
+		}
+	}
+	wake_up_interruptible(&apm_waitqueue);
+}
+
+static int check_apm_user(struct apm_user *as, const char *func)
+{
+	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+		printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t do_read(struct file *fp, char *buf, size_t count, loff_t *ppos)
+{
+	struct apm_user *	as;
+	int			i;
+	apm_event_t		event;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = fp->private_data;
+	if (check_apm_user(as, "read"))
+		return -EIO;
+	if (count < sizeof(apm_event_t))
+		return -EINVAL;
+	if (queue_empty(as)) {
+		if (fp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&apm_waitqueue, &wait);
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (queue_empty(as) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+	}
+	i = count;
+	while ((i >= sizeof(event)) && !queue_empty(as)) {
+		event = get_queued_event(as);
+		DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
+		if (copy_to_user(buf, &event, sizeof(event))) {
+			if (i < count)
+				break;
+			return -EFAULT;
+		}
+		switch (event) {
+		case APM_SYS_SUSPEND:
+		case APM_USER_SUSPEND:
+			as->suspends_read++;
+			break;
+		}
+		buf += sizeof(event);
+		i -= sizeof(event);
+	}
+	if (i < count)
+		return count - i;
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+static unsigned int do_poll(struct file *fp, poll_table * wait)
+{
+	struct apm_user * as;
+
+	as = fp->private_data;
+	if (check_apm_user(as, "poll"))
+		return 0;
+	poll_wait(fp, &apm_waitqueue, wait);
+	if (!queue_empty(as))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static int do_ioctl(struct inode * inode, struct file *filp,
+		    u_int cmd, u_long arg)
+{
+	struct apm_user *	as;
+	DECLARE_WAITQUEUE(wait, current);
+
+	as = filp->private_data;
+	if (check_apm_user(as, "ioctl"))
+		return -EIO;
+	if (!as->suser)
+		return -EPERM;
+	switch (cmd) {
+	case APM_IOC_SUSPEND:
+		/* If a suspend message was sent to userland, we
+		 * consider this as a confirmation message
+		 */
+		if (as->suspends_read > 0) {
+			as->suspends_read--;
+			as->suspends_pending--;
+			suspends_pending--;
+		} else {
+			// Route to PMU suspend ?
+			break;
+		}
+		as->suspend_waiting = 1;
+		add_wait_queue(&apm_waitqueue, &wait);
+		DBG("apm_emu: ioctl waking up sleep waiter !\n");
+		wake_up(&apm_suspend_waitqueue);
+		mb();
+		while(as->suspend_waiting && !signal_pending(current)) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&apm_waitqueue, &wait);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int do_release(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = filp->private_data;
+	if (check_apm_user(as, "release"))
+		return 0;
+	filp->private_data = NULL;
+	lock_kernel();
+	if (as->suspends_pending > 0) {
+		suspends_pending -= as->suspends_pending;
+		if (suspends_pending <= 0)
+			wake_up(&apm_suspend_waitqueue);
+	}
+	if (user_list == as)
+		user_list = as->next;
+	else {
+		struct apm_user *	as1;
+
+		for (as1 = user_list;
+		     (as1 != NULL) && (as1->next != as);
+		     as1 = as1->next)
+			;
+		if (as1 == NULL)
+			printk(KERN_ERR "apm: filp not in user list\n");
+		else
+			as1->next = as->next;
+	}
+	unlock_kernel();
+	kfree(as);
+	return 0;
+}
+
+static int do_open(struct inode * inode, struct file * filp)
+{
+	struct apm_user *	as;
+
+	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
+	if (as == NULL) {
+		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
+		       sizeof(*as));
+		return -ENOMEM;
+	}
+	as->magic = APM_BIOS_MAGIC;
+	as->event_tail = as->event_head = 0;
+	as->suspends_pending = 0;
+	as->suspends_read = 0;
+	/*
+	 * XXX - this is a tiny bit broken, when we consider BSD
+         * process accounting. If the device is opened by root, we
+	 * instantly flag that we used superuser privs. Who knows,
+	 * we might close the device immediately without doing a
+	 * privileged operation -- cevans
+	 */
+	as->suser = capable(CAP_SYS_ADMIN);
+	as->next = user_list;
+	user_list = as;
+	filp->private_data = as;
+
+	DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
+
+	return 0;
+}
+
+/* Wait for all clients to ack the suspend request. APM API
+ * doesn't provide a way to NAK, but this could be added
+ * here.
+ */
+static int wait_all_suspend(void)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&apm_suspend_waitqueue, &wait);
+	DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
+	while(suspends_pending > 0) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&apm_suspend_waitqueue, &wait);
+
+	DBG("apm_emu: wait_all_suspend() - complete !\n");
+	
+	return 1;
+}
+
+static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
+{
+	switch(when) {
+		case PBOOK_SLEEP_REQUEST:
+			queue_event(APM_SYS_SUSPEND, NULL);
+			if (!wait_all_suspend())
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+		case PBOOK_WAKE:
+			queue_event(APM_NORMAL_RESUME, NULL);
+			break;
+	}
+	return PBOOK_SLEEP_OK;
+}
+
+#define APM_CRITICAL		10
+#define APM_LOW			30
+
+static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
+{
+	/* Arguments, with symbols from linux/apm_bios.h.  Information is
+	   from the Get Power Status (0x0a) call unless otherwise noted.
+
+	   0) Linux driver version (this will change if format changes)
+	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
+	   2) APM flags from APM Installation Check (0x00):
+	      bit 0: APM_16_BIT_SUPPORT
+	      bit 1: APM_32_BIT_SUPPORT
+	      bit 2: APM_IDLE_SLOWS_CLOCK
+	      bit 3: APM_BIOS_DISABLED
+	      bit 4: APM_BIOS_DISENGAGED
+	   3) AC line status
+	      0x00: Off-line
+	      0x01: On-line
+	      0x02: On backup power (BIOS >= 1.1 only)
+	      0xff: Unknown
+	   4) Battery status
+	      0x00: High
+	      0x01: Low
+	      0x02: Critical
+	      0x03: Charging
+	      0x04: Selected battery not present (BIOS >= 1.2 only)
+	      0xff: Unknown
+	   5) Battery flag
+	      bit 0: High
+	      bit 1: Low
+	      bit 2: Critical
+	      bit 3: Charging
+	      bit 7: No system battery
+	      0xff: Unknown
+	   6) Remaining battery life (percentage of charge):
+	      0-100: valid
+	      -1: Unknown
+	   7) Remaining battery life (time units):
+	      Number of remaining minutes or seconds
+	      -1: Unknown
+	   8) min = minutes; sec = seconds */
+
+	unsigned short  ac_line_status = 0xff;
+	unsigned short  battery_status = 0xff;
+	unsigned short  battery_flag   = 0xff;
+	int		percentage     = -1;
+	int             time_units     = -1;
+	int		real_count     = 0;
+	int		i;
+	char *		p = buf;
+
+	ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
+	for (i=0; i<pmu_battery_count; i++) {
+		if (percentage < 0)
+			percentage = 0;
+		if (time_units < 0)
+			time_units = 0;
+		if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
+			percentage += (pmu_batteries[i].charge * 100) /
+				pmu_batteries[i].max_charge;
+			/* hrm... should we provide the remaining charge
+			 * time when AC is plugged ? If yes, just remove
+			 * that test --BenH
+			 */
+			if (!ac_line_status)
+				time_units += pmu_batteries[i].time_remaining / 60;
+			real_count++;
+			if (!(pmu_batteries[i].flags & PMU_BATT_CHARGING))
+				battery_flag &= ~0x08;
+		}
+	}
+	if (real_count) {
+		percentage /= real_count;
+		if (battery_flag & 0x08) {
+			battery_status = 0x03;
+			battery_flag = 0x08;
+		} else if (percentage <= APM_CRITICAL) {
+			battery_status = 0x02;
+			battery_flag = 0x04;
+		} else if (percentage <= APM_LOW) {
+			battery_status = 0x01;
+			battery_flag = 0x02;
+		} else {
+			battery_status = 0x00;
+			battery_flag = 0x01;
+		}
+	}
+	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
+		     driver_version,
+		     (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
+		     FAKE_APM_BIOS_VERSION & 0xff,
+		     0,
+		     ac_line_status,
+		     battery_status,
+		     battery_flag,
+		     percentage,
+		     time_units,
+		     "min");
+
+	return p - buf;
+}
+
+static struct file_operations apm_bios_fops = {
+	owner:		THIS_MODULE,
+	read:		do_read,
+	poll:		do_poll,
+	ioctl:		do_ioctl,
+	open:		do_open,
+	release:	do_release,
+};
+
+static struct miscdevice apm_device = {
+	APM_MINOR_DEV,
+	"apm_bios",
+	&apm_bios_fops
+};
+
+static int __init apm_emu_init(void)
+{
+	struct proc_dir_entry *apm_proc;
+
+	if (sys_ctrler != SYS_CTRLER_PMU) {
+		printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
+		return -ENODEV;
+	}
+		
+	apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
+	if (apm_proc)
+		SET_MODULE_OWNER(apm_proc);
+
+	misc_register(&apm_device);
+
+	pmu_register_sleep_notifier(&apm_sleep_notifier);
+
+	printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
+
+	return 0;
+}
+
+static void __exit apm_emu_exit(void)
+{
+	pmu_unregister_sleep_notifier(&apm_sleep_notifier);
+	misc_deregister(&apm_device);
+	remove_proc_entry("apm", NULL);
+
+	printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
+}
+
+module_init(apm_emu_init);
+module_exit(apm_emu_exit);
+
+MODULE_AUTHOR("Benjamin Herrenschmidt");
+MODULE_DESCRIPTION("APM emulation layer for PowerMac");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/mac_hid.c linux/drivers/macintosh/mac_hid.c
--- linux.orig/drivers/macintosh/mac_hid.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/mac_hid.c	Wed Dec 26 16:32:31 2001
@@ -200,15 +200,15 @@
 	0, 0, 0, KEY_KPCOMMA, 0, KEY_INTL3, 0, 0,		/* 0x00-0x07 */
 	0, 0, 0, 0, KEY_LANG1, KEY_LANG2, 0, 0,			/* 0x08-0x0f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x10-0x17 */
-	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, 0, 0,		/* 0x18-0x1f */
-	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x20-0x27 */
+	0, 0, 0, 0, KEY_KPENTER, KEY_RIGHTCTRL, KEY_VOLUMEUP, 0,/* 0x18-0x1f */
+	0, 0, 0, 0, 0, KEY_VOLUMEDOWN, KEY_MUTE, 0,		/* 0x20-0x27 */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x28-0x2f */
 	0, 0, 0, 0, 0, KEY_KPSLASH, 0, KEY_SYSRQ,		/* 0x30-0x37 */
-	KEY_RIGHTALT, 0, 0, 0, 0, 0, 0, 0,			/* 0x38-0x3f */
+	KEY_RIGHTALT, 0, 0, KEY_EJECTCD, 0, 0, 0, 0,		/* 0x38-0x3f */
 	0, 0, 0, 0, 0, 0, 0, KEY_HOME,				/* 0x40-0x47 */
 	KEY_UP, KEY_PAGEUP, 0, KEY_LEFT, 0, KEY_RIGHT, 0, KEY_END, /* 0x48-0x4f */
 	KEY_DOWN, KEY_PAGEDOWN, KEY_INSERT, KEY_DELETE, 0, 0, 0, 0, /* 0x50-0x57 */
-	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, 0, 0, /* 0x58-0x5f */
+	0, 0, 0, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_COMPOSE, KEY_POWER, 0, /* 0x58-0x5f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x60-0x67 */
 	0, 0, 0, 0, 0, 0, 0, KEY_MACRO,				/* 0x68-0x6f */
 	0, 0, 0, 0, 0, 0, 0, 0,					/* 0x70-0x77 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/macserial.c linux/drivers/macintosh/macserial.c
--- linux.orig/drivers/macintosh/macserial.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/macserial.c	Mon Feb  4 18:47:24 2002
@@ -40,7 +40,8 @@
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/bitops.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 #ifdef CONFIG_KGDB
@@ -59,6 +60,7 @@
 #endif
 
 #define SUPPORT_SERIAL_DMA
+#define MACSERIAL_VERSION	"2.0"
 
 /*
  * It would be nice to dynamically allocate everything that
@@ -1031,7 +1033,7 @@
 {
 	unsigned long flags;
 
-	OPNDBG("setting up ttys%d SCC...\n", info->line);
+	OPNDBG("setting up ttyS%d SCC...\n", info->line);
 
 	save_flags(flags); cli(); /* Disable interrupts */
 
@@ -1179,7 +1181,7 @@
 	}
 
 	memset(info->curregs, 0, sizeof(info->curregs));
-	memset(info->curregs, 0, sizeof(info->pendregs));
+	memset(info->pendregs, 0, sizeof(info->pendregs));
 
 	info->flags &= ~ZILOG_INITIALIZED;
 }
@@ -1194,69 +1196,34 @@
 {
 	int delay = 0;
 
-	if (feature_test(info->dev_node, FEATURE_Serial_enable) < 0)
-		return 0;	/* don't have serial power control */
-
-	/* The timings looks strange but that's the ones MacOS seems
-	   to use for the internal modem. I think we can use a lot faster
-	   ones, at least whe not using the modem, this should be tested.
-	 */
 	if (state) {
-		PWRDBG("ttyS%02d: powering up hardware\n", info->line);
-		if (feature_test(info->dev_node, FEATURE_Serial_enable) == 0) {
-			feature_set(info->dev_node, FEATURE_Serial_enable);
-			mdelay(10);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(10);
-		}
-		if (info->zs_chan_a == info->zs_channel)
-			feature_set(info->dev_node, FEATURE_Serial_IO_A);
-		else
-			feature_set(info->dev_node, FEATURE_Serial_IO_B);
-		delay = 10;
-		if (info->is_cobalt_modem) {
-			feature_set_modem_power(info->dev_node, 1);
+		PWRDBG("ttyS%d: powering up hardware\n", info->line);
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 1);
+		if (info->is_internal_modem) {
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 1);
 			delay = 2500;	/* wait for 2.5s before using */
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(1);
-#endif /* CONFIG_PMAC_PBOOK */
+		} else if (info->is_irda)
+			mdelay(50);	/* Do better here once the problems
+			                 * with blocking have been ironed out
+			                 */
 	} else {
 		/* TODO: Make that depend on a timer, don't power down
 		 * immediately
 		 */
-		PWRDBG("ttyS%02d: shutting down hardware\n", info->line);
-		if (info->is_cobalt_modem) {
-			PWRDBG("ttyS%02d: shutting down modem\n", info->line);
-			feature_set_modem_power(info->dev_node, 0);
-		}
-#ifdef CONFIG_PMAC_PBOOK
-		if (info->is_irda)
-			pmu_enable_irled(0);
-#endif /* CONFIG_PMAC_PBOOK */
-
-		if (info->zs_chan_a == info->zs_channel && !info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel A\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_A);
-		} else if (!info->is_irda) {
-			PWRDBG("ttyS%02d: shutting down SCC channel B\n", info->line);
-			feature_clear(info->dev_node, FEATURE_Serial_IO_B);
-		}
-		/* XXX for now, shut down SCC core only on powerbooks */
-		if (is_powerbook
-		    && !(feature_test(info->dev_node, FEATURE_Serial_IO_A) ||
-			 feature_test(info->dev_node, FEATURE_Serial_IO_B))) {
-			PWRDBG("ttyS%02d: shutting down SCC core\n", info->line);
-			feature_set(info->dev_node, FEATURE_Serial_reset);
-			mdelay(15);
-			feature_clear(info->dev_node, FEATURE_Serial_reset);
-			mdelay(25);
-			feature_clear(info->dev_node, FEATURE_Serial_enable);
-			mdelay(5);
-		}
+		PWRDBG("ttyS%d: shutting down hardware\n", info->line);
+		if (info->is_internal_modem) {
+			PWRDBG("ttyS%d: shutting down modem\n", info->line);
+			pmac_call_feature(
+				PMAC_FTR_MODEM_ENABLE,
+				info->dev_node, 0, 0);
+		}
+		pmac_call_feature(
+			PMAC_FTR_SCC_ENABLE,
+			info->dev_node, info->port_type, 0);
 	}
 	return delay;
 }
@@ -1989,7 +1956,7 @@
 		return;
 	}
 
-	OPNDBG("rs_close ttys%d, count = %d\n", info->line, info->count);
+	OPNDBG("rs_close ttyS%d, count = %d\n", info->line, info->count);
 	if ((tty->count == 1) && (info->count != 1)) {
 		/*
 		 * Uh, oh.  tty->count is 1, which means that the tty
@@ -2004,7 +1971,7 @@
 	}
 	if (--info->count < 0) {
 		printk(KERN_ERR "rs_close: bad serial port count for "
-				"ttys%d: %d\n", info->line, info->count);
+				"ttyS%d: %d\n", info->line, info->count);
 		info->count = 0;
 	}
 	if (info->count) {
@@ -2221,7 +2188,7 @@
 	 */
 	retval = 0;
 	add_wait_queue(&info->open_wait, &wait);
-	OPNDBG("block_til_ready before block: ttys%d, count = %d\n",
+	OPNDBG("block_til_ready before block: ttyS%d, count = %d\n",
 	       info->line, info->count);
 	cli();
 	if (!tty_hung_up_p(filp)) 
@@ -2256,7 +2223,7 @@
 			retval = -ERESTARTSYS;
 			break;
 		}
-		OPNDBG("block_til_ready blocking: ttys%d, count = %d\n",
+		OPNDBG("block_til_ready blocking: ttyS%d, count = %d\n",
 		       info->line, info->count);
 		schedule();
 	}
@@ -2265,7 +2232,7 @@
 	if (!tty_hung_up_p(filp))
 		info->count++;
 	info->blocked_open--;
-	OPNDBG("block_til_ready after blocking: ttys%d, count = %d\n",
+	OPNDBG("block_til_ready after blocking: ttyS%d, count = %d\n",
 	       info->line, info->count);
 	if (retval)
 		return retval;
@@ -2366,7 +2333,7 @@
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 
-	OPNDBG("rs_open ttys%d successful...\n", info->line);
+	OPNDBG("rs_open ttyS%d successful...\n", info->line);
 	return 0;
 }
 
@@ -2374,14 +2341,14 @@
 
 static void show_serial_version(void)
 {
-	printk(KERN_INFO "PowerMac Z8530 serial driver version 2.0\n");
+	printk(KERN_INFO "PowerMac Z8530 serial driver version " MACSERIAL_VERSION "\n");
 }
 
 /*
  * Initialize one channel, both the mac_serial and mac_zschannel
  * structs.  We use the dev_node field of the mac_serial struct.
  */
-static void
+static int
 chan_init(struct mac_serial *zss, struct mac_zschannel *zs_chan,
 	  struct mac_zschannel *zs_chan_a)
 {
@@ -2411,12 +2378,15 @@
 
 	/* setup misc varariables */
 	zss->kgdb_channel = 0;
-	zss->is_cobalt_modem = device_is_compatible(ch, "cobalt");
 
-	/* XXX tested only with wallstreet PowerBook,
-	   should do no harm anyway */
+	/* For now, we assume you either have a slot-names property
+	 * with "Modem" in it, or your channel is compatible with
+	 * "cobalt". Might need additional fixups
+	 */
+	zss->is_internal_modem = device_is_compatible(ch, "cobalt");
 	conn = get_property(ch, "AAPL,connector", &len);
 	zss->is_irda = conn && (strcmp(conn, "infrared") == 0);
+	zss->port_type = PMAC_SCC_ASYNC;
 	/* 1999 Powerbook G3 has slot-names property instead */
 	slots = (struct slot_names_prop *)get_property(ch, "slot-names", &len);
 	if (slots && slots->count > 0) {
@@ -2425,8 +2395,29 @@
 		else if (strcmp(slots->name, "Modem") == 0)
 			zss->is_internal_modem = 1;
 	}
+	if (zss->is_irda)
+		zss->port_type = PMAC_SCC_IRDA;
+	if (zss->is_internal_modem) {
+		struct device_node* i2c_modem = find_devices("i2c-modem");
+		if (i2c_modem) {
+			char* mid = get_property(i2c_modem, "modem-id", NULL);
+			if (mid) switch(*mid) {
+			case 0x04 :
+			case 0x05 :
+			case 0x07 :
+			case 0x08 :
+			case 0x0b :
+			case 0x0c :
+				zss->port_type = PMAC_SCC_I2S1;
+			}
+			printk(KERN_INFO "macserial: i2c-modem detected, id: %d\n",
+				mid ? (*mid) : 0);
+		} else {
+			printk(KERN_INFO "macserial: serial modem detected\n");
+		}
+	}
 
-	if (zss->has_dma) {
+	while (zss->has_dma) {
 		zss->dma_priv = NULL;
 		/* it seems that the last two addresses are the
 		   DMA controllers */
@@ -2437,11 +2428,69 @@
 		zss->tx_dma_irq = ch->intrs[1].line;
 		zss->rx_dma_irq = ch->intrs[2].line;
 		spin_lock_init(&zss->rx_dma_lock);
+		break;
 	}
 
 	init_timer(&zss->powerup_timer);
 	zss->powerup_timer.function = powerup_done;
 	zss->powerup_timer.data = (unsigned long) zss;
+	return 0;
+}
+
+/*
+ * /proc fs routines. TODO: Add status lines & error stats
+ */
+static inline int
+line_info(char *buf, struct mac_serial *info)
+{
+	int		ret=0;
+	unsigned char* connector;
+	int lenp;
+
+	ret += sprintf(buf, "%d: port:0x%X irq:%d", info->line, info->port, info->irq);
+
+	connector = get_property(info->dev_node, "AAPL,connector", &lenp);
+	if (connector)
+		ret+=sprintf(buf+ret," con:%s ", connector);
+	if (info->is_internal_modem) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (internal modem)");
+	}
+	if (info->is_irda) {
+		if (!connector)
+			ret+=sprintf(buf+ret," con:");
+		ret+=sprintf(buf+ret,"%s", " (IrDA)");
+	}
+	ret+=sprintf(buf+ret,"\n");
+
+	return ret;
+}
+
+int macserial_read_proc(char *page, char **start, off_t off, int count,
+		 int *eof, void *data)
+{
+	int l, len = 0;
+	off_t	begin = 0;
+	struct mac_serial *info;
+
+	len += sprintf(page, "serinfo:1.0 driver:" MACSERIAL_VERSION "\n");
+	for (info = zs_chain; info && len < 4000; info = info->zs_next) {
+		l = line_info(page + len, info);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
 }
 
 /* Ask the PROM how many Z8530s we have and initialize their zs_channels */
@@ -2496,13 +2545,15 @@
 			continue;
 
 		/* set up A side */
-		chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan);
+		if (chan_init(&zs_soft[chip + chan_a_index], zs_chan, zs_chan))
+			continue;
 		++zs_chan;
 
 		/* set up B side, if it exists */
 		if (nchan > 1)
-			chan_init(&zs_soft[chip + 1 - chan_a_index],
-				  zs_chan, zs_chan - 1);
+			if (chan_init(&zs_soft[chip + 1 - chan_a_index],
+				  zs_chan, zs_chan - 1))
+				continue;
 		++zs_chan;
 	}
 	*pp = 0;
@@ -2528,13 +2579,35 @@
 	if (zs_chain == 0)
 		probe_sccs();
 
-	/* XXX assume it's a powerbook if we have a via-pmu */
+	/* XXX assume it's a powerbook if we have a via-pmu
+	 * 
+	 * This is OK for core99 machines as well.
+	 */
 	is_powerbook = find_devices("via-pmu") != 0;
 
-	/* Register the interrupt handler for each one */
+	/* Register the interrupt handler for each one
+	 * We also request the OF resources here as probe_sccs()
+	 * might be called too early for that
+	 */
 	save_flags(flags); cli();
 	for (i = 0; i < zs_channels_found; ++i) {
+		struct device_node* ch = zs_soft[i].dev_node;
+		if (!request_OF_resource(ch, 0, NULL)) {
+			printk(KERN_ERR "macserial: can't request IO resource !\n");
+			return -ENODEV;
+		}
 		if (zs_soft[i].has_dma) {
+			if (!request_OF_resource(ch, ch->n_addrs - 2, " (tx dma)")) {
+				printk(KERN_ERR "macserial: can't request TX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
+			if (!request_OF_resource(ch, ch->n_addrs - 1, " (rx dma)")) {
+				release_OF_resource(ch, ch->n_addrs - 2);
+				printk(KERN_ERR "macserial: can't request RX DMA resource !\n");
+				zs_soft[i].has_dma = 0;
+				goto no_dma;
+			}
 			if (request_irq(zs_soft[i].tx_dma_irq, rs_txdma_irq, 0,
 					"SCC-txdma", &zs_soft[i]))
 				printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2546,6 +2619,7 @@
 				       zs_soft[i].rx_dma_irq);
 			disable_irq(zs_soft[i].rx_dma_irq);
 		}
+no_dma:		
 		if (request_irq(zs_soft[i].irq, rs_interrupt, 0,
 				"SCC", &zs_soft[i]))
 			printk(KERN_ERR "macserial: can't get irq %d\n",
@@ -2561,6 +2635,7 @@
 
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "macserial";
 #ifdef CONFIG_DEVFS_FS
 	serial_driver.name = "tts/%d";
 #else
@@ -2597,6 +2672,7 @@
 	serial_driver.hangup = rs_hangup;
 	serial_driver.break_ctl = rs_break;
 	serial_driver.wait_until_sent = rs_wait_until_sent;
+	serial_driver.read_proc = macserial_read_proc;
 
 	/*
 	 * The callout device is just like normal device except for
@@ -2610,6 +2686,8 @@
 #endif /* CONFIG_DEVFS_FS */
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
 
 	if (tty_register_driver(&serial_driver))
 		panic("Couldn't register serial driver\n");
@@ -2676,21 +2754,11 @@
 		connector = get_property(info->dev_node, "AAPL,connector", &lenp);
 		if (connector)
 			printk(", port = %s", connector);
-		if (info->is_cobalt_modem)
-			printk(" (cobalt modem)");
-		else if (info->is_internal_modem)
+		if (info->is_internal_modem)
 			printk(" (internal modem)");
 		if (info->is_irda)
 			printk(" (IrDA)");
 		printk("\n");
-
-#ifndef CONFIG_XMON
-#ifdef CONFIG_KGDB
-		if (!info->kgdb_channel)
-#endif /* CONFIG_KGDB */
-			/* By default, disable the port */
-			set_scc_power(info, 0);
-#endif /* CONFIG_XMON */
  	}
 	tmp_buf = 0;
 
@@ -2711,6 +2779,12 @@
 		if (zs_soft[i].has_dma) {
 			free_irq(zs_soft[i].tx_dma_irq, &zs_soft[i]);
 			free_irq(zs_soft[i].rx_dma_irq, &zs_soft[i]);
+		}
+		release_OF_resource(zs_soft[i].dev_node, 0);
+		if (zs_soft[i].has_dma) {
+			struct device_node* ch = zs_soft[i].dev_node;
+			release_OF_resource(ch, ch->n_addrs - 2);
+			release_OF_resource(ch, ch->n_addrs - 1);
 		}
 	}
 	restore_flags(flags);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/macserial.h linux/drivers/macintosh/macserial.h
--- linux.orig/drivers/macintosh/macserial.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/macserial.h	Wed Dec 26 16:32:31 2001
@@ -111,8 +111,8 @@
 	char kgdb_channel;  /* Kgdb is running on this channel */
 	char is_cons;       /* Is this our console. */
 	char is_internal_modem; /* is connected to an internal modem */
-	char is_cobalt_modem;	/* is a gatwick-based cobalt modem */
 	char is_irda;		/* is connected to an IrDA codec */
+	int port_type;		/* Port type for pmac_feature */
 	unsigned char tx_active; /* character is being xmitted */
 	unsigned char tx_stopped; /* output is suspended */
 	unsigned char power_wait; /* waiting for power-up delay to expire */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/mediabay.c linux/drivers/macintosh/mediabay.c
--- linux.orig/drivers/macintosh/mediabay.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/mediabay.c	Wed Dec 26 16:32:31 2001
@@ -25,9 +25,13 @@
 #include <asm/prom.h>
 #include <asm/pgtable.h>
 #include <asm/io.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/mediabay.h>
 #include <asm/sections.h>
+#include <asm/ohare.h>
+#include <asm/heathrow.h>
+#include <asm/keylargo.h>
 #include <linux/adb.h>
 #include <linux/pmu.h>
 
@@ -40,7 +44,7 @@
 #endif
 
 #undef MB_USE_INTERRUPTS
-#undef MB_DEBUG
+#define MB_DEBUG
 #define MB_IGNORE_SIGNALS
 
 #ifdef MB_DEBUG
@@ -49,24 +53,46 @@
 #define MBDBG(fmt, arg...)	do { } while (0)
 #endif
 
-struct media_bay_hw {
-	unsigned char	b0;
-	unsigned char	contents;
-	unsigned char	b2;
-	unsigned char	b3;
+/* Type of media bay */
+enum {
+	mb_ohare,
+	mb_heathrow,
+	mb_keylargo
+};
+
+#define MB_FCR32(bay, r)	((bay)->base + ((r) >> 2))
+#define MB_FCR8(bay, r)		(((volatile u8*)((bay)->base)) + (r))
+
+#define MB_IN32(bay,r)		(in_le32(MB_FCR32(bay,r)))
+#define MB_OUT32(bay,r,v)	(out_le32(MB_FCR32(bay,r), (v)))
+#define MB_BIS(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) | (v)))
+#define MB_BIC(bay,r,v)		(MB_OUT32((bay), (r), MB_IN32((bay), r) & ~(v)))
+#define MB_IN8(bay,r)		(in_8(MB_FCR8(bay,r)))
+#define MB_OUT8(bay,r,v)	(out_8(MB_FCR8(bay,r), (v)))
+
+struct media_bay_info;
+
+struct mb_ops {
+	char*	name;
+	u8	(*content)(struct media_bay_info* bay);
+	void	(*power)(struct media_bay_info* bay, int on_off);
+	int	(*setup_bus)(struct media_bay_info* bay, u8 device_id);
+	void	(*un_reset)(struct media_bay_info* bay);
+	void	(*un_reset_ide)(struct media_bay_info* bay);
 };
 
 struct media_bay_info {
-	volatile struct media_bay_hw*	addr;
-	volatile u8*			extint_gpio;
+	volatile u32*			base;
 	int				content_id;
 	int				state;
 	int				last_value;
 	int				value_count;
 	int				timer;
 	struct device_node*		dev_node;
-	int				pismo;	/* New PowerBook3,1 */
-	int				gpio_cache;
+	int				mb_type;
+	struct mb_ops*			ops;
+	int				index;
+	int				cached_gpio;
 #ifdef CONFIG_BLK_DEV_IDE
 	unsigned long			cd_base;
 	int 				cd_index;
@@ -77,33 +103,12 @@
 
 #define MAX_BAYS	2
 
-static volatile struct media_bay_info media_bays[MAX_BAYS];
+static struct media_bay_info media_bays[MAX_BAYS];
 int media_bay_count = 0;
 
-inline int mb_content(volatile struct media_bay_info *bay)
-{
-        if (bay->pismo) {
-                unsigned char new_gpio = in_8(bay->extint_gpio + 0xe) & 2;
-                if (new_gpio) {
-                	bay->gpio_cache = new_gpio;
-                        return MB_NO;
-                } else if (bay->gpio_cache != new_gpio) {
-                        /* make sure content bits are set */
-                        feature_set(bay->dev_node, FEATURE_Mediabay_content);
-                        udelay(5);
-                        bay->gpio_cache = new_gpio;
-                }
-                return (in_le32((unsigned*)bay->addr) >> 4) & 0xf;
-        } else {
-                int cont = (in_8(&bay->addr->contents) >> 4) & 7;
-                return (cont == 7) ? MB_NO : cont;
-        }
-}
-
 #ifdef CONFIG_BLK_DEV_IDE
 /* check the busy bit in the media-bay ide interface
    (assumes the media-bay contains an ide device) */
-//#define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0xc0) == 0x40)
 #define MB_IDE_READY(i)	((inb(media_bays[i].cd_base + 0x70) & 0x80) == 0)
 #endif
 
@@ -116,7 +121,7 @@
  * Consider the media-bay ID value stable if it is the same for
  * this number of milliseconds
  */
-#define MB_STABLE_DELAY	40
+#define MB_STABLE_DELAY	100
 
 /* Wait after powering up the media bay this delay in ms
  * timeout bumped for some powerbooks
@@ -166,175 +171,259 @@
 	mb_powering_down	/* Powering down (avoid too fast down/up) */
 };
 
-static void poll_media_bay(int which);
-static void set_media_bay(int which, int id);
-static void set_mb_power(int which, int onoff);
-static void media_bay_step(int i);
-static int media_bay_task(void *);
+#define MB_POWER_SOUND		0x08
+#define MB_POWER_FLOPPY		0x04
+#define MB_POWER_ATA		0x02
+#define MB_POWER_PCI		0x01
+#define MB_POWER_OFF		0x00
 
-#ifdef MB_USE_INTERRUPTS
-static void media_bay_intr(int irq, void *devid, struct pt_regs *regs);
-#endif
+/*
+ * Functions for polling content of media bay
+ */
+ 
+static u8 __pmac
+ohare_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, OHARE_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+heathrow_mb_content(struct media_bay_info *bay)
+{
+	return (MB_IN32(bay, HEATHROW_MBCR) >> 12) & 7;
+}
+
+static u8 __pmac
+keylargo_mb_content(struct media_bay_info *bay)
+{
+	int new_gpio;
+
+	new_gpio = MB_IN8(bay, KL_GPIO_MEDIABAY_IRQ) & KEYLARGO_GPIO_INPUT_DATA;
+	if (new_gpio) {
+		bay->cached_gpio = new_gpio;
+		return MB_NO;
+	} else if (bay->cached_gpio != new_gpio) {
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+		(void)MB_IN32(bay, KEYLARGO_MBCR);
+		udelay(5);
+		bay->cached_gpio = new_gpio;
+	}
+	return (MB_IN32(bay, KEYLARGO_MBCR) >> 4) & 7;
+}
 
 /*
- * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
- * register is always set when there is something in the media bay.
- * This causes problems for the interrupt code if we attach an interrupt
- * handler to the media-bay interrupt, because it tends to go into
- * an infinite loop calling the media bay interrupt handler.
- * Therefore we do it all by polling the media bay once each tick.
+ * Functions for powering up/down the bay, puts the bay device
+ * into reset state as well
  */
 
-void __pmac
-media_bay_init(void)
+static void __pmac
+ohare_mb_power(struct media_bay_info* bay, int on_off)
 {
-	struct device_node *np;
-	int		n,i;
-	
-	for (i=0; i<MAX_BAYS; i++) {
-		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
-		media_bays[i].content_id	= -1;
-#ifdef CONFIG_BLK_DEV_IDE
-		media_bays[i].cd_index		= -1;
-#endif
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIC(bay, OHARE_FCR, OH_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, OHARE_FCR, OH_BAY_DEV_MASK);
+		MB_BIC(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, OHARE_FCR, OH_BAY_POWER_N);
+		MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
+		MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
 	}
-	
-	np = find_devices("media-bay");
-	n = 0;
-	while(np && (n<MAX_BAYS)) {
-		if (np->n_addrs == 0)
-			continue;
-		media_bays[n].addr = (volatile struct media_bay_hw *)
-			ioremap(np->addrs[0].address, sizeof(struct media_bay_hw));
+	MB_BIC(bay, OHARE_MBCR, 0x00000F00);
+}
 
-		media_bays[n].pismo = device_is_compatible(np, "keylargo-media-bay");
-		if (media_bays[n].pismo) {
-			if (!np->parent || strcmp(np->parent->name, "mac-io")) {
-				printk(KERN_ERR "Pismo media-bay has no mac-io parent !\n");
-				continue;
-			}
-			media_bays[n].extint_gpio = ioremap(np->parent->addrs[0].address
-				+ 0x58, 0x10);
-		}
+static void __pmac
+heathrow_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, HEATHROW_FCR, HRW_BAY_DEV_MASK);
+		MB_BIC(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_POWER_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+		MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+	}
+	MB_BIC(bay, HEATHROW_MBCR, 0x00000F00);
+}
 
-#ifdef MB_USE_INTERRUPTS
-		if (np->n_intrs == 0) {
-			printk(KERN_ERR "media bay %d has no irq\n",n);
-			continue;
-		}
+static void __pmac
+keylargo_mb_power(struct media_bay_info* bay, int on_off)
+{
+	if (on_off) {
+		/* Power up device, assert it's reset line */
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+            	MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+	} else {
+		/* Disable all devices */
+		MB_BIC(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_MASK);
+		MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+		/* Cut power from bay, release reset line */
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_POWER);
+		MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+		MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+	}
+	MB_BIC(bay, KEYLARGO_MBCR, 0x0000000F);
+}
 
-		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
-			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
-				np->intrs[0].line, n);
-			continue;
-		}
-#endif	
-		media_bay_count++;
-	
-		media_bays[n].dev_node		= np;
+/*
+ * Functions for configuring the media bay for a given type of device,
+ * enable the related busses
+ */
 
-		/* Force an immediate detect */
-		set_mb_power(n,0);
-		mdelay(MB_POWER_DELAY);
-		if(!media_bays[n].pismo)
-			out_8(&media_bays[n].addr->contents, 0x70);
-		mdelay(MB_STABLE_DELAY);
-		media_bays[n].content_id = MB_NO;
-		media_bays[n].last_value = mb_content(&media_bays[n]);
-		media_bays[n].value_count = MS_TO_HZ(MB_STABLE_DELAY);
-		media_bays[n].state = mb_empty;
-		do {
-			mdelay(1000/HZ);
-			media_bay_step(n);
-		} while((media_bays[n].state != mb_empty) &&
-			(media_bays[n].state != mb_up));
+static int __pmac
+ohare_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, OHARE_FCR, OH_FLOPPY_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, OHARE_FCR, OH_IDE1_RESET_N);
+			MB_BIS(bay, OHARE_FCR, OH_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, OHARE_FCR, OH_BAY_PCI_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-		n++;
-		np=np->next;
+static int __pmac
+heathrow_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_FD:
+		case MB_FD1:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_FLOPPY_ENABLE);
+			MB_BIS(bay, HEATHROW_FCR, HRW_SWIM_ENABLE);
+			return 0;
+		case MB_CD:
+			MB_BIC(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_IDE_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, HEATHROW_FCR, HRW_BAY_PCI_ENABLE);
+			return 0;
 	}
+	return -ENODEV;
+}
 
-	if (media_bay_count)
-	{
-		printk(KERN_INFO "Registered %d media-bay(s)\n", media_bay_count);
+static int __pmac
+keylargo_mb_setup_bus(struct media_bay_info* bay, u8 device_id)
+{
+	switch(device_id) {
+		case MB_CD:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_IDE_ENABLE);
+			MB_BIC(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+			MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_ENABLE);
+			return 0;
+		case MB_PCI:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_PCI_ENABLE);
+			return 0;
+		case MB_SOUND:
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_SOUND_ENABLE);
+			return 0;
+	}
+	return -ENODEV;
+}
 
-#ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&mb_sleep_notifier);
-#endif /* CONFIG_PMAC_PBOOK */
+/*
+ * Functions for tweaking resets
+ */
 
-		kernel_thread(media_bay_task, NULL,
-			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-	}
+static void __pmac
+ohare_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_BAY_RESET_N);
 }
 
-#ifdef MB_USE_INTERRUPTS
 static void __pmac
-media_bay_intr(int irq, void *devid, struct pt_regs *regs)
+heathrow_mb_un_reset(struct media_bay_info* bay)
 {
+	MB_BIS(bay, HEATHROW_FCR, HRW_BAY_RESET_N);
+}
+
+static void __pmac
+keylargo_mb_un_reset(struct media_bay_info* bay)
+{
+	MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_DEV_RESET);
+}
+
+static void __pmac
+ohare_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, OHARE_FCR, OH_IDE1_RESET_N);
+}
+
+static void __pmac
+heathrow_mb_un_reset_ide(struct media_bay_info* bay)
+{
+	MB_BIS(bay, HEATHROW_FCR, HRW_IDE1_RESET_N);
 }
-#endif
 
 static void __pmac
-set_mb_power(int which, int onoff)
+keylargo_mb_un_reset_ide(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info*	mb = &media_bays[which];
+	MB_BIS(bay, KEYLARGO_FCR1, KL1_EIDE0_RESET_N);
+}
 
+static inline void __pmac
+set_mb_power(struct media_bay_info* bay, int onoff)
+{
+	/* Power up up and assert the bay reset line */
 	if (onoff) {
-		feature_set(mb->dev_node, FEATURE_Mediabay_power);
-		udelay(10);
-		feature_set(mb->dev_node, FEATURE_Mediabay_reset);
-		udelay(10);
-		mb->state = mb_powering_up;
-		MBDBG("mediabay%d: powering up\n", which);
-	} else {
-		feature_clear(mb->dev_node, FEATURE_Mediabay_floppy_enable);
-		if (mb->pismo)
-			feature_clear(mb->dev_node, FEATURE_IDE0_enable);
-		else
-			feature_clear(mb->dev_node, FEATURE_IDE1_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_IDE_switch);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_PCI_enable);
-		feature_clear(mb->dev_node, FEATURE_SWIM3_enable);
-		feature_clear(mb->dev_node, FEATURE_Mediabay_power);
-		mb->state = mb_powering_down;
-		MBDBG("mediabay%d: powering down\n", which);
+		bay->ops->power(bay, 1);
+		bay->state = mb_powering_up;
+		MBDBG("mediabay%d: powering up\n", bay->index);
+	} else { 
+		/* Make sure everything is powered down & disabled */
+		bay->ops->power(bay, 0);
+		bay->state = mb_powering_down;
+		MBDBG("mediabay%d: powering down\n", bay->index);
 	}
-	mb->timer = MS_TO_HZ(MB_POWER_DELAY);
+	bay->timer = MS_TO_HZ(MB_POWER_DELAY);
 }
 
 static void __pmac
-set_media_bay(int which, int id)
+poll_media_bay(struct media_bay_info* bay)
 {
-	volatile struct media_bay_info* bay;
+	int id = bay->ops->content(bay);
 
-	bay = &media_bays[which];
-	
-	switch (id) {
-	case MB_CD:
-		if (bay->pismo) {
-			feature_set(bay->dev_node, FEATURE_Mediabay_IDE_switch);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE0_reset);
-		} else {
-			feature_set(bay->dev_node, FEATURE_IDE1_enable);
-			udelay(10);
-			feature_set(bay->dev_node, FEATURE_IDE1_reset);
+	if (id == bay->last_value) {
+	    if (id != bay->content_id
+	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
+	        /* If the device type changes without going thru "MB_NO", we force
+	           a pass by "MB_NO" to make sure things are properly reset */
+	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
+	            id = MB_NO;
+		    MBDBG("mediabay%d: forcing MB_NO\n", bay->index);
 		}
-		printk(KERN_INFO "media bay %d contains a CD-ROM drive\n", which);
-		break;
-	case MB_FD:
-	case MB_FD1:
-		feature_set(bay->dev_node, FEATURE_Mediabay_floppy_enable);
-		feature_set(bay->dev_node, FEATURE_SWIM3_enable);
-		printk(KERN_INFO "media bay %d contains a floppy disk drive\n", which);
-		break;
-	case MB_NO:
-		break;
-	default:
-		printk(KERN_INFO "media bay %d contains an unknown device (%d)\n",
-		       which, id);
-		break;
+		MBDBG("mediabay%d: switching to %d\n", bay->index, id);
+		set_mb_power(bay, id != MB_NO);
+		bay->content_id = id;
+		if (id == MB_NO) {
+#ifdef CONFIG_BLK_DEV_IDE
+		    bay->cd_retry = 0;
+#endif
+		    printk(KERN_INFO "media bay %d is empty\n", bay->index);
+		}
+ 	    }
+	} else {
+		bay->last_value = id;
+		bay->value_count = 0;
 	}
 }
 
@@ -412,11 +501,11 @@
 static void __pmac
 media_bay_step(int i)
 {
-	volatile struct media_bay_info* bay = &media_bays[i];
+	struct media_bay_info* bay = &media_bays[i];
 
 	/* We don't poll when powering down */
 	if (bay->state != mb_powering_down)
-	    poll_media_bay(i);
+	    poll_media_bay(bay);
 
 	/* If timer expired or polling IDE busy, run state machine */
 	if ((bay->state != mb_ide_waiting) && (bay->timer != 0) && ((--bay->timer) != 0))
@@ -424,13 +513,17 @@
 
 	switch(bay->state) {
 	case mb_powering_up:
-	    	set_media_bay(i, bay->last_value);
+	    	if (bay->ops->setup_bus(bay, bay->last_value) < 0) {
+			MBDBG("mediabay%d: device not supported (kind:%d)\n", i, bay->content_id);
+	    		set_mb_power(bay, 0);
+	    		break;
+	    	}
 	    	bay->timer = MS_TO_HZ(MB_RESET_DELAY);
 	    	bay->state = mb_enabling_bay;
 		MBDBG("mediabay%d: enabling (kind:%d)\n", i, bay->content_id);
 		break;
 	case mb_enabling_bay:
-	    	feature_clear(bay->dev_node, FEATURE_Mediabay_reset);
+		bay->ops->un_reset(bay);
 	    	bay->timer = MS_TO_HZ(MB_SETUP_DELAY);
 	    	bay->state = mb_resetting;
 		MBDBG("mediabay%d: waiting reset (kind:%d)\n", i, bay->content_id);
@@ -444,16 +537,13 @@
 	    	}
 #ifdef CONFIG_BLK_DEV_IDE
 		MBDBG("mediabay%d: waiting IDE reset (kind:%d)\n", i, bay->content_id);
-		if (bay->pismo)
-	    		feature_clear(bay->dev_node, FEATURE_IDE0_reset);
-		else
-	    		feature_clear(bay->dev_node, FEATURE_IDE1_reset);
+		bay->ops->un_reset_ide(bay);
 	    	bay->timer = MS_TO_HZ(MB_IDE_WAIT);
 	    	bay->state = mb_ide_resetting;
 #else
 		printk(KERN_DEBUG "media-bay %d is ide (not compiled in kernel)\n", i);
-		set_mb_power(i, 0);
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+		set_mb_power(bay, 0);
+#endif /* CONFIG_BLK_DEV_IDE */
 	    	break;
 	    
 #ifdef CONFIG_BLK_DEV_IDE
@@ -481,7 +571,7 @@
 				/* We eventually do a retry */
 				bay->cd_retry++;
 				printk("IDE register error\n");
-				set_mb_power(i, 0);
+				set_mb_power(bay, 0);
 			} else {
 				printk(KERN_DEBUG "media-bay %d is ide %d\n", i, bay->cd_index);
 				MBDBG("mediabay %d IDE ready\n", i);
@@ -491,10 +581,10 @@
 	    	if (bay->timer == 0) {
 			printk("\nIDE Timeout in bay %d !\n", i);
 			MBDBG("mediabay%d: nIDE Timeout !\n", i);
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 	    	}
 		break;
-#endif // #ifdef CONFIG_BLK_DEV_IDE
+#endif /* CONFIG_BLK_DEV_IDE */
 
 	case mb_powering_down:
 	    	bay->state = mb_empty;
@@ -514,7 +604,7 @@
 				bay->content_id = MB_NO;
 			}
 	    	}
-#endif	    
+#endif /* CONFIG_BLK_DEV_IDE */    
 		MBDBG("mediabay%d: end of power down\n", i);
 	    	break;
 	}
@@ -526,7 +616,7 @@
  * with the IDE driver.  It needs to be a thread because
  * ide_register can't be called from interrupt context.
  */
-int __pmac
+static int __pmac
 media_bay_task(void *x)
 {
 	int	i;
@@ -547,37 +637,12 @@
 	}
 }
 
-void __pmac
-poll_media_bay(int which)
+#ifdef MB_USE_INTERRUPTS
+static void __pmac
+media_bay_intr(int irq, void *devid, struct pt_regs *regs)
 {
-	volatile struct media_bay_info* bay = &media_bays[which];
-	int id = mb_content(bay);
-
-	if (id == bay->last_value) {
-	    if (id != bay->content_id
-	        && ++bay->value_count >= MS_TO_HZ(MB_STABLE_DELAY)) {
-	        /* If the device type changes without going thru "MB_NO", we force
-	           a pass by "MB_NO" to make sure things are properly reset */
-	        if ((id != MB_NO) && (bay->content_id != MB_NO)) {
-	            id = MB_NO;
-		    MBDBG("mediabay%d: forcing MB_NO\n", which);
-		}
-		MBDBG("mediabay%d: switching to %d\n", which, id);
-		set_mb_power(which, id != MB_NO);
-		bay->content_id = id;
-		if (id == MB_NO) {
-#ifdef CONFIG_BLK_DEV_IDE
-		    bay->cd_retry = 0;
-#endif
-		    printk(KERN_INFO "media bay %d is empty\n", which);
-		}
- 	    }
-	} else {
-		bay->last_value = id;
-		bay->value_count = 0;
-	}
 }
-
+#endif
 
 #ifdef CONFIG_PMAC_PBOOK
 /*
@@ -586,7 +651,7 @@
 int __pmac
 mb_notify_sleep(struct pmu_sleep_notifier *self, int when)
 {
-	volatile struct media_bay_info* bay;
+	struct media_bay_info* bay;
 	int i;
 	
 	switch (when) {
@@ -597,7 +662,7 @@
 	case PBOOK_SLEEP_NOW:
 		for (i=0; i<media_bay_count; i++) {
 			bay = &media_bays[i];
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(10);
 		}
 		break;
@@ -609,14 +674,11 @@
 			   they seem to help the 3400 get it right.
 			 */
 			/* Force MB power to 0 */
-			set_mb_power(i, 0);
+			set_mb_power(bay, 0);
 			mdelay(MB_POWER_DELAY);
-			if (!bay->pismo)
-				out_8(&bay->addr->contents, 0x70);
-			mdelay(MB_STABLE_DELAY);
-			if (mb_content(bay) != bay->content_id)
+			if (bay->ops->content(bay) != bay->content_id)
 				continue;
-			set_mb_power(i, 1);
+			set_mb_power(bay, 1);
 			bay->last_value = bay->content_id;
 			bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
 			bay->timer = MS_TO_HZ(MB_POWER_DELAY);
@@ -634,4 +696,135 @@
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
+
+
+/* Definitions of "ops" structures.
+ */
+static struct mb_ops ohare_mb_ops __pmacdata = {
+	name:		"Ohare",
+	content:	ohare_mb_content,
+	power:		ohare_mb_power,
+	setup_bus:	ohare_mb_setup_bus,
+	un_reset:	ohare_mb_un_reset,
+	un_reset_ide:	ohare_mb_un_reset_ide,
+};
+
+static struct mb_ops heathrow_mb_ops __pmacdata = {
+	name:		"Heathrow",
+	content:	heathrow_mb_content,
+	power:		heathrow_mb_power,
+	setup_bus:	heathrow_mb_setup_bus,
+	un_reset:	heathrow_mb_un_reset,
+	un_reset_ide:	heathrow_mb_un_reset_ide,
+};
+
+static struct mb_ops keylargo_mb_ops __pmacdata = {
+	name:		"KeyLargo",
+	content:	keylargo_mb_content,
+	power:		keylargo_mb_power,
+	setup_bus:	keylargo_mb_setup_bus,
+	un_reset:	keylargo_mb_un_reset,
+	un_reset_ide:	keylargo_mb_un_reset_ide,
+};
+
+/*
+ * It seems that the bit for the media-bay interrupt in the IRQ_LEVEL
+ * register is always set when there is something in the media bay.
+ * This causes problems for the interrupt code if we attach an interrupt
+ * handler to the media-bay interrupt, because it tends to go into
+ * an infinite loop calling the media bay interrupt handler.
+ * Therefore we do it all by polling the media bay once each tick.
+ */
+
+void __pmac
+media_bay_init(void)
+{
+	struct device_node *np;
+	int		n,i;
+	
+	for (i=0; i<MAX_BAYS; i++) {
+		memset((char *)&media_bays[i], 0, sizeof(struct media_bay_info));
+		media_bays[i].content_id	= -1;
+#ifdef CONFIG_BLK_DEV_IDE
+		media_bays[i].cd_index		= -1;
+#endif
+	}
+	
+	np = find_devices("media-bay");
+	n = 0;
+	while(np && (n<MAX_BAYS)) {
+		struct media_bay_info* bay = &media_bays[n];
+		if (!np->parent || np->n_addrs == 0 || !request_OF_resource(np, 0, NULL)) {
+			np = np->next;
+			printk(KERN_ERR "media-bay: Can't request IO resource !\n");
+			continue;
+		}
+		bay->mb_type = mb_ohare;
+
+		if (device_is_compatible(np, "keylargo-media-bay")) {
+			bay->mb_type = mb_keylargo;
+			bay->ops = &keylargo_mb_ops;
+		} else if (device_is_compatible(np, "heathrow-media-bay")) {
+			bay->mb_type = mb_heathrow;
+			bay->ops = &heathrow_mb_ops;
+		} else if (device_is_compatible(np, "ohare-media-bay")) {
+			bay->mb_type = mb_ohare;
+			bay->ops = &ohare_mb_ops;
+		} else {
+			printk(KERN_ERR "mediabay: Unknown bay type !\n");
+			np = np->next;
+			continue;
+		}
+		bay->base = (volatile u32*)ioremap(np->parent->addrs[0].address, 0x1000);
+
+		/* Enable probe logic on keylargo */
+		if (bay->mb_type == mb_keylargo)
+			MB_BIS(bay, KEYLARGO_MBCR, KL_MBCR_MB0_ENABLE);
+#ifdef MB_USE_INTERRUPTS
+		if (np->n_intrs == 0) {
+			printk(KERN_ERR "media bay %d has no irq\n",n);
+			np = np->next;
+			continue;
+		}
+
+		if (request_irq(np->intrs[0].line, media_bay_intr, 0, "Media bay", (void *)n)) {
+			printk(KERN_ERR "Couldn't get IRQ %d for media bay %d\n",
+				np->intrs[0].line, n);
+			np = np->next;
+			continue;
+		}
+#endif	
+		media_bay_count++;
+	
+		printk(KERN_INFO "mediabay%d: Registered %s media-bay\n", n, bay->ops->name);
+		bay->dev_node = np;
+		bay->index = n;
+
+		/* Force an immediate detect */
+		set_mb_power(bay, 0);
+		mdelay(MB_POWER_DELAY);
+		bay->content_id = MB_NO;
+		bay->last_value = bay->ops->content(bay);
+		bay->value_count = MS_TO_HZ(MB_STABLE_DELAY);
+		bay->state = mb_empty;
+		do {
+			mdelay(1000/HZ);
+			media_bay_step(n);
+		} while((bay->state != mb_empty) &&
+			(bay->state != mb_up));
+
+		n++;
+		np=np->next;
+	}
+
+	if (media_bay_count)
+	{
+#ifdef CONFIG_PMAC_PBOOK
+		pmu_register_sleep_notifier(&mb_sleep_notifier);
+#endif /* CONFIG_PMAC_PBOOK */
+
+		kernel_thread(media_bay_task, NULL,
+			      CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	}
+}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/rtc.c linux/drivers/macintosh/rtc.c
--- linux.orig/drivers/macintosh/rtc.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/rtc.c	Wed Dec 26 16:32:31 2001
@@ -34,14 +34,13 @@
 void get_rtc_time(struct rtc_time *t)
 {
 	unsigned long nowtime;
-    
+
 	nowtime = (ppc_md.get_rtc_time)();
 
 	to_tm(nowtime, t);
 
 	t->tm_year -= 1900;
-	t->tm_mon -= 1;
-	t->tm_wday -= 1;
+	t->tm_mon -= 1; /* Make sure userland has a 0-based month */
 }
 
 /* Set the current date and time in the real time clock. */
@@ -49,7 +48,8 @@
 {
 	unsigned long nowtime;
 
-	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	nowtime = mktime(t->tm_year+1900, t->tm_mon+1, t->tm_mday,
+			t->tm_hour, t->tm_min, t->tm_sec);
 
 	(ppc_md.set_rtc_time)(nowtime);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/via-cuda.c linux/drivers/macintosh/via-cuda.c
--- linux.orig/drivers/macintosh/via-cuda.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/via-cuda.c	Wed Dec 26 16:32:31 2001
@@ -191,6 +191,8 @@
     if (via == NULL)
 	return -ENODEV;
 
+    request_OF_resource(vias, 0, NULL);
+
     if (request_irq(CUDA_IRQ, cuda_interrupt, 0, "ADB", cuda_interrupt)) {
 	printk(KERN_ERR "cuda_init: can't get irq %d\n", CUDA_IRQ);
 	return -EAGAIN;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/macintosh/via-pmu.c linux/drivers/macintosh/via-pmu.c
--- linux.orig/drivers/macintosh/via-pmu.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/macintosh/via-pmu.c	Mon Jan 21 18:49:12 2002
@@ -9,9 +9,9 @@
  * and the RTC (real time clock) chip.
  *
  * Copyright (C) 1998 Paul Mackerras and Fabio Riccardi.
+ * Copyright (C) 2001 Benjamin Herrenschmidt
  * 
- * todo: - Check this driver for smp safety (new Core99 motherboards).
- *       - Cleanup synchro between VIA interrupt and GPIO-based PMU
+ * todo: - Cleanup synchro between VIA interrupt and GPIO-based PMU
  *         interrupt.
  *
  *
@@ -45,10 +45,12 @@
 #include <asm/sections.h>
 #include <asm/irq.h>
 #include <asm/hardirq.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
+#include <asm/sections.h>
 #include <asm/cputable.h>
+#include <asm/time.h>
 #ifdef CONFIG_PMAC_BACKLIGHT
 #include <asm/backlight.h>
 #endif
@@ -56,9 +58,13 @@
 /* Some compile options */
 #undef SUSPEND_USES_PMU
 #define DEBUG_SLEEP
+#undef HACKED_PCI_SAVE
 
 /* Misc minor number allocated for /dev/pmu */
-#define PMU_MINOR	154
+#define PMU_MINOR		154
+
+/* How many iterations between battery polls */
+#define BATTERY_POLLING_COUNT	2
 
 static volatile unsigned char *via;
 
@@ -108,7 +114,7 @@
 static struct adb_request *current_req;
 static struct adb_request *last_req;
 static struct adb_request *req_awaiting_reply;
-static unsigned char interrupt_data[32];
+static unsigned char interrupt_data[256]; /* Made bigger: I've been told that might happen */
 static unsigned char *reply_ptr;
 static int data_index;
 static int data_len;
@@ -126,9 +132,26 @@
 static int pmu_version;
 static int drop_interrupts;
 #ifdef CONFIG_PMAC_PBOOK
+static int option_lid_wakeup = 1;
 static int sleep_in_progress;
+static int can_sleep;
+#endif /* CONFIG_PMAC_PBOOK */
+
+static struct proc_dir_entry *proc_pmu_root;
+static struct proc_dir_entry *proc_pmu_info;
+static struct proc_dir_entry *proc_pmu_options;
+
+#ifdef CONFIG_PMAC_PBOOK
+int pmu_battery_count;
+int pmu_cur_battery;
+unsigned int pmu_power_flags;
+struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+static int query_batt_timer = BATTERY_POLLING_COUNT;
+static struct adb_request batt_req;
+static struct proc_dir_entry *proc_pmu_batt[PMU_MAX_BATTERIES];
 #endif /* CONFIG_PMAC_PBOOK */
 
+int __fake_sleep;
 int asleep;
 struct notifier_block *sleep_notifier_list;
 
@@ -153,15 +176,22 @@
 static void pmu_done(struct adb_request *req);
 static void pmu_handle_data(unsigned char *data, int len,
 			    struct pt_regs *regs);
-static void set_volume(int level);
 static void gpio1_interrupt(int irq, void *arg, struct pt_regs *regs);
+static int proc_get_info(char *page, char **start, off_t off,
+			  int count, int *eof, void *data);
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int pmu_set_backlight_level(int level, void* data);
 static int pmu_set_backlight_enable(int on, int level, void* data);
 #endif /* CONFIG_PMAC_BACKLIGHT */
 #ifdef CONFIG_PMAC_PBOOK
 static void pmu_pass_intr(unsigned char *data, int len);
-#endif
+static int proc_get_batt(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+#endif /* CONFIG_PMAC_PBOOK */
+static int proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data);
+static int proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data);
 
 #ifdef CONFIG_ADB
 struct adb_driver via_pmu_driver = {
@@ -309,6 +339,10 @@
 	} else
 		pmu_kind = PMU_UNKNOWN;
 
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,-1) >= 0)
+		can_sleep = 1;
+#endif /* CONFIG_PMAC_PBOOK */
 	via = (volatile unsigned char *) ioremap(vias->addrs->address, 0x2000);
 
 	out_8(&via[IER], IER_CLR | 0x7f);	/* disable all intrs */
@@ -362,9 +396,14 @@
 	if (vias == NULL)
 		return -ENODEV;
 
+	request_OF_resource(vias, 0, NULL);
+
 	bright_req_1.complete = 1;
 	bright_req_2.complete = 1;
 	bright_req_3.complete = 1;
+#ifdef CONFIG_PMAC_PBOOK
+	batt_req.complete = 1;
+#endif
 
 	if (request_irq(vias->intrs[0].line, via_pmu_interrupt, 0, "VIA-PMU",
 			(void *)0)) {
@@ -388,6 +427,46 @@
 	register_backlight_controller(&pmu_backlight_controller, NULL, "pmu");
 #endif /* CONFIG_PMAC_BACKLIGHT */
 
+#ifdef CONFIG_PMAC_PBOOK
+  	if (machine_is_compatible("AAPL,3400/2400") ||
+  		machine_is_compatible("AAPL,3500"))
+		pmu_battery_count = 1;
+	else if (machine_is_compatible("AAPL,PowerBook1998") ||
+		machine_is_compatible("PowerBook1,1"))
+		pmu_battery_count = 2;
+	else {
+		struct device_node* prim = find_devices("power-mgt");
+		u32 *prim_info = NULL;
+		if (prim)
+			prim_info = (u32 *)get_property(prim, "prim-info", NULL);
+		if (prim_info) {
+			/* Other stuffs here yet unknown */
+			pmu_battery_count = (prim_info[6] >> 16) & 0xff;
+		}
+	}
+#endif /* CONFIG_PMAC_PBOOK */
+	/* Create /proc/pmu */
+	proc_pmu_root = proc_mkdir("pmu", 0);
+	if (proc_pmu_root) {
+		int i;
+		proc_pmu_info = create_proc_read_entry("info", 0, proc_pmu_root,
+					proc_get_info, NULL);
+#ifdef CONFIG_PMAC_PBOOK
+		for (i=0; i<pmu_battery_count; i++) {
+			char title[16];
+			sprintf(title, "battery_%d", i);
+			proc_pmu_batt[i] = create_proc_read_entry(title, 0, proc_pmu_root,
+						proc_get_batt, (void *)i);
+		}
+#endif /* CONFIG_PMAC_PBOOK */
+		proc_pmu_options = create_proc_entry("options", 0600, proc_pmu_root);
+		if (proc_pmu_options) {
+			proc_pmu_options->nlink = 1;
+			proc_pmu_options->read_proc = proc_read_options;
+			proc_pmu_options->write_proc = proc_write_options;
+		}
+	}
+
 	/* Make sure PMU settle down before continuing. This is _very_ important
 	 * since the IDE probe may shut interrupts down for quite a bit of time. If
 	 * a PMU communication is pending while this happens, the PMU may timeout
@@ -448,8 +527,8 @@
 	pmu_request(&req, NULL, 1, PMU_GET_VERSION);
 	while (!req.complete)
 		pmu_poll();
-	if (req.reply_len > 1)
-		pmu_version = req.reply[1];
+	if (req.reply_len > 0)
+		pmu_version = req.reply[0];
 
 	return 1;
 }
@@ -460,6 +539,263 @@
 	return pmu_kind;
 }
 
+#ifdef CONFIG_PMAC_PBOOK
+
+/* 
+ * WARNING ! This code probably needs some debugging... -- BenH.
+ */
+static void __pmac
+done_battery_state_ohare(struct adb_request* req)
+{
+	unsigned int bat_flags = 0;
+	int current = 0;
+	unsigned int capa, max, voltage, time;
+	int lrange[] = { 0, 275, 850, 1680, 2325, 
+				2765, 3160, 3500, 3830, 4115, 
+				4360, 4585, 4795, 4990, 5170, 
+				5340, 5510, 5710, 5930, 6150, 
+				6370, 6500
+				};
+	
+	if (req->reply[0] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+	if (req->reply[0] & 0x04) {
+		int vb, i, j, charge, pcharge;
+		bat_flags |= PMU_BATT_PRESENT;
+		vb = (req->reply[1] << 8) | req->reply[2];
+		voltage = ((vb * 2650) + 726650)/100;
+		current = *((signed char *)&req->reply[5]);
+		if ((req->reply[0] & 0x01) == 0 && (current > 200))
+			vb += (current - 200) * 15;
+		else if (req->reply[0] & 0x02)
+			vb = (vb - 10) * 100;
+	  	i = (33000 - vb) / 10;
+	  	j = i - (i % 100);
+	  	if (j <= 0)
+	       		charge = 0;
+	  	else if (j >= 21)
+	       		charge = 650000;
+	  	else
+			charge = (lrange[j + 1] - lrange[j]) * (i - j) + (lrange[j] * 100);
+		charge = (1000 - charge / 650) / 10;
+		if (req->reply[0] & 0x40) {
+			pcharge = (req->reply[6] << 8) + req->reply[7];
+			if (pcharge > 6500)
+				pcharge = 6500;
+			pcharge *= 100;
+			pcharge = (1000 - pcharge / 650) / 10;
+			if (pcharge < charge)
+				charge = pcharge;
+		}
+		capa = charge;
+		max = 100;
+		time = (charge * 274) / current;
+		current = -current;
+		
+	} else
+		capa = max = current = voltage = time = 0;
+
+	if ((req->reply[0] & 0x02) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+	if (req->reply[0] & 0x04) /* CHECK THIS ONE */
+		bat_flags |= PMU_BATT_PRESENT;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	pmu_batteries[pmu_cur_battery].time_remaining = time;
+}
+
+static void __pmac
+done_battery_state_smart(struct adb_request* req)
+{
+	/* format:
+	 *  [0] : format of this structure (known: 3,4,5)
+	 *  [1] : flags
+	 *  
+	 *  format 3 & 4:
+	 *  
+	 *  [2] : charge
+	 *  [3] : max charge
+	 *  [4] : current
+	 *  [5] : voltage
+	 *  
+	 *  format 5:
+	 *  
+	 *  [2][3] : charge
+	 *  [4][5] : max charge
+	 *  [6][7] : current
+	 *  [8][9] : voltage
+	 */
+	 
+	unsigned int bat_flags = 0;
+	int current;
+	unsigned int capa, max, voltage;
+	
+	if (req->reply[1] & 0x01)
+		pmu_power_flags |= PMU_PWR_AC_PRESENT;
+	else
+		pmu_power_flags &= ~PMU_PWR_AC_PRESENT;
+
+
+	if (req->reply[1] & 0x04) {
+		bat_flags |= PMU_BATT_PRESENT;
+		switch(req->reply[0]) {
+			case 3:
+			case 4: capa = req->reply[2];
+				max = req->reply[3];
+				current = *((signed char *)&req->reply[4]);
+				voltage = req->reply[5];
+				break;
+			case 5: capa = (req->reply[2] << 8) | req->reply[3];
+				max = (req->reply[4] << 8) | req->reply[5];
+				current = *((signed short *)&req->reply[6]);
+				voltage = (req->reply[8] << 8) | req->reply[9];
+				break;
+			default:
+				printk(KERN_WARNING "pmu.c : unrecognized battery info, len: %d, %02x %02x %02x %02x\n",
+					req->reply_len, req->reply[0], req->reply[1], req->reply[2], req->reply[3]);
+				break;
+		}
+	} else
+		capa = max = current = voltage = 0;
+
+	if ((req->reply[1] & 0x01) && (current > 0))
+		bat_flags |= PMU_BATT_CHARGING;
+
+	pmu_batteries[pmu_cur_battery].flags = bat_flags;
+	pmu_batteries[pmu_cur_battery].charge = capa;
+	pmu_batteries[pmu_cur_battery].max_charge = max;
+	pmu_batteries[pmu_cur_battery].current = current;
+	pmu_batteries[pmu_cur_battery].voltage = voltage;
+	if (current) {
+		if ((req->reply[1] & 0x01) && (current > 0))
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= ((max-capa) * 3600) / current;
+		else
+			pmu_batteries[pmu_cur_battery].time_remaining
+				= (capa * 3600) / (-current);
+	} else
+		pmu_batteries[pmu_cur_battery].time_remaining = 0;
+
+	pmu_cur_battery = (pmu_cur_battery + 1) % pmu_battery_count;
+}
+
+static void __pmac
+query_battery_state(void)
+{
+	if (!batt_req.complete)
+		return;
+	if (pmu_kind == PMU_OHARE_BASED)
+		pmu_request(&batt_req, done_battery_state_ohare,
+			1, PMU_BATTERY_STATE);
+	else
+		pmu_request(&batt_req, done_battery_state_smart,
+			2, PMU_SMART_BATTERY_STATE, pmu_cur_battery+1);
+}
+
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_get_info(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	char* p = page;
+
+	p += sprintf(p, "PMU driver version     : %d\n", PMU_DRIVER_VERSION);
+	p += sprintf(p, "PMU firmware version   : %02x\n", pmu_version);
+#ifdef CONFIG_PMAC_PBOOK
+	p += sprintf(p, "AC Power               : %d\n",
+		((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0));
+	p += sprintf(p, "Battery count          : %d\n", pmu_battery_count);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+
+#ifdef CONFIG_PMAC_PBOOK
+static int
+proc_get_batt(char *page, char **start, off_t off,
+		int count, int *eof, void *data)
+{
+	int batnum = (int)data;
+	char *p = page;
+	
+	p += sprintf(p, "\n");
+	p += sprintf(p, "flags      : %08x\n",
+		pmu_batteries[batnum].flags);
+	p += sprintf(p, "charge     : %d\n",
+		pmu_batteries[batnum].charge);
+	p += sprintf(p, "max_charge : %d\n",
+		pmu_batteries[batnum].max_charge);
+	p += sprintf(p, "current    : %d\n",
+		pmu_batteries[batnum].current);
+	p += sprintf(p, "voltage    : %d\n",
+		pmu_batteries[batnum].voltage);
+	p += sprintf(p, "time rem.  : %d\n",
+		pmu_batteries[batnum].time_remaining);
+
+	return p - page;
+}
+#endif /* CONFIG_PMAC_PBOOK */
+
+static int
+proc_read_options(char *page, char **start, off_t off,
+			int count, int *eof, void *data)
+{
+	char *p = page;
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		p += sprintf(p, "lid_wakeup=%d\n", option_lid_wakeup);
+#endif /* CONFIG_PMAC_PBOOK */
+
+	return p - page;
+}
+			
+static int
+proc_write_options(struct file *file, const char *buffer,
+			unsigned long count, void *data)
+{
+	char tmp[33];
+	char *label, *val;
+	unsigned long fcount = count;
+	
+	if (!count)
+		return -EINVAL;
+	if (count > 32)
+		count = 32;
+	if (copy_from_user(tmp, buffer, count))
+		return -EFAULT;
+	tmp[count] = 0;
+
+	label = tmp;
+	while(*label == ' ')
+		label++;
+	val = label;
+	while(*val && (*val != '=')) {
+		if (*val == ' ')
+			*val = 0;
+		val++;
+	}
+	if ((*val) == 0)
+		return -EINVAL;
+	*(val++) = 0;
+	while(*val == ' ')
+		val++;
+#ifdef CONFIG_PMAC_PBOOK
+	if (pmu_kind == PMU_KEYLARGO_BASED && can_sleep)
+		if (!strcmp(label, "lid_wakeup"))
+			option_lid_wakeup = ((*val) == '1');
+#endif /* CONFIG_PMAC_PBOOK */
+	return fcount;
+}
+
 #ifdef CONFIG_ADB
 /* Send an ADB command */
 static int __openfirmware
@@ -622,11 +958,7 @@
 	for (i = 0; i < nbytes; ++i)
 		req->data[i] = va_arg(list, int);
 	va_end(list);
-	if (pmu_data_len[req->data[0]][1] != 0) {
-		req->reply[0] = ADB_RET_OK;
-		req->reply_len = 1;
-	} else
-		req->reply_len = 0;
+	req->reply_len = 0;
 	req->reply_expected = 0;
 	return pmu_queue_request(req);
 }
@@ -835,27 +1167,22 @@
 	spin_lock_irqsave(&pmu_lock, flags);
 	++disable_poll;
 		
-	while ((intr = in_8(&via[IFR])) != 0) {
+	for (;;) {
+		intr = in_8(&via[IFR]) & (SR_INT | CB1_INT);
+		if (intr == 0)
+			break;
 		if (++nloop > 1000) {
 			printk(KERN_DEBUG "PMU: stuck in intr loop, "
-			       "intr=%x pmu_state=%d\n", intr, pmu_state);
+			       "intr=%x, ier=%x pmu_state=%d\n",
+			       intr, in_8(&via[IER]), pmu_state);
 			break;
 		}
 		out_8(&via[IFR], intr);
 		if (intr & SR_INT)
 			pmu_sr_intr(regs);
-		else if (intr & CB1_INT)
+		if (intr & CB1_INT)
 			adb_int_pending = 1;
 	}
-	/* This is not necessary except if synchronous ADB requests are done
-	 * with interrupts off, which should not happen. Since I'm not sure
-	 * this "wiring" will remain, I'm commenting it out for now. Please do
-	 * not remove. -- BenH.
-	 */
-#if 0
-	if (gpio_reg && !pmu_suspended && (in_8(gpio_reg + 0x9) & 0x02) == 0)
-		adb_int_pending = 1;
-#endif
 
 	if (pmu_state == idle) {
 		if (adb_int_pending) {
@@ -866,9 +1193,8 @@
 			wait_for_ack();
 			send_byte(PMU_INT_ACK);
 			adb_int_pending = 0;
-		} else if (current_req) {
+		} else if (current_req)
 			pmu_start();
-		}
 	}
 	
 	--disable_poll;
@@ -878,8 +1204,10 @@
 static void __openfirmware
 gpio1_interrupt(int irq, void *arg, struct pt_regs *regs)
 {
-	adb_int_pending = 1;
-	via_pmu_interrupt(0, 0, 0);
+	if ((in_8(gpio_reg + 0x9) & 0x02) == 0) {
+		adb_int_pending = 1;
+		via_pmu_interrupt(0, 0, 0);
+	}
 }
 
 static void __openfirmware
@@ -1040,16 +1368,25 @@
 				adb_input(data+1, len-1, regs, 1);
 #endif /* CONFIG_ADB */		
 		}
-	} else if (data[0] == 0x08 && len == 3) {
-		/* sound/brightness buttons pressed */
+	} else {
+		/* Sound/brightness button pressed */
+		if ((data[0] & PMU_INT_SNDBRT) && len == 3) {
 #ifdef CONFIG_PMAC_BACKLIGHT
-		set_backlight_level(data[1] >> 4);
+			set_backlight_level(data[1] >> 4);
 #endif
-		set_volume(data[2]);
-	} else {
+		}
 #ifdef CONFIG_PMAC_PBOOK
-		pmu_pass_intr(data, len);
-#endif
+		/* Environement or tick interrupt, query batteries */
+		if (pmu_battery_count && (data[0] & PMU_INT_TICK)) {
+			if ((--query_batt_timer) == 0) {
+				query_battery_state();
+				query_batt_timer = BATTERY_POLLING_COUNT;
+			}
+		} else if (pmu_battery_count && (data[0] & PMU_INT_ENVIRONMENT))
+			query_battery_state();
+ 		if (data[0])
+			pmu_pass_intr(data, len);
+#endif /* CONFIG_PMAC_PBOOK */
 	}
 }
 
@@ -1116,11 +1453,6 @@
 		pmu_poll();
 }
 
-static void __openfirmware
-set_volume(int level)
-{
-}
-
 void __openfirmware
 pmu_restart(void)
 {
@@ -1266,10 +1598,14 @@
  * PCI devices which may get powered off when we sleep.
  */
 static struct pci_save {
+#ifndef HACKED_PCI_SAVE
 	u16	command;
 	u16	cache_lat;
 	u16	intr;
 	u32	rom_address;
+#else
+	u32	config[16];
+#endif	
 } *pbook_pci_saves;
 static int n_pbook_pci_saves;
 
@@ -1293,14 +1629,24 @@
 		return;
 
 	pci_for_each_dev(pd) {
+#ifndef HACKED_PCI_SAVE
 		pci_read_config_word(pd, PCI_COMMAND, &ps->command);
 		pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
 		pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
 		pci_read_config_dword(pd, PCI_ROM_ADDRESS, &ps->rom_address);
+#else
+		int i;
+		for (i=1;i<16;i++)
+			pci_read_config_dword(pd, i<<4, &ps->config[i]);
+#endif
 		++ps;
 	}
 }
 
+/* For this to work, we must take care of a few things: If gmac was enabled
+ * during boot, it will be in the pci dev list. If it's disabled at this point
+ * (and it will probably be), then you can't access it's config space.
+ */
 static void __openfirmware
 pbook_pci_restore(void)
 {
@@ -1310,7 +1656,13 @@
 	int j;
 
 	pci_for_each_dev(pd) {
+#ifdef HACKED_PCI_SAVE
+		int i;
 		ps++;
+		for (i=2;i<16;i++)
+			pci_write_config_dword(pd, i<<4, ps->config[i]);
+		pci_write_config_dword(pd, 4, ps->config[1]);
+#else
 		if (ps->command == 0)
 			continue;
 		pci_read_config_word(pd, PCI_COMMAND, &cmd);
@@ -1330,8 +1682,8 @@
 				ps->intr);
 			pci_write_config_word(pd, PCI_COMMAND, ps->command);
 			break;
-			/* other header types not restored at present */
 		}
+#endif	
 	}
 }
 
@@ -1370,7 +1722,7 @@
 	}
 	mdelay(50);
 }
-#endif /* DEBUG_SLEEP */
+#endif
 
 /*
  * Put the powerbook to sleep.
@@ -1403,6 +1755,15 @@
 	out_8(&via[IER], IER_SET | SR_INT | CB1_INT);
 }
 
+static inline void wakeup_decrementer(void)
+{
+	set_dec(tb_ticks_per_jiffy);
+	/* No currently-supported powerbook has a 601,
+	 * so use get_tbl, not native
+	 */
+	last_jiffy_stamp(0) = tb_last_stamp = get_tbl();
+}
+
 #define	GRACKLE_PM	(1<<7)
 #define GRACKLE_DOZE	(1<<5)
 #define	GRACKLE_NAP	(1<<4)
@@ -1436,6 +1797,10 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + (HZ/2); time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
@@ -1443,13 +1808,9 @@
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/2); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 	
 	/* Turn off various things. Darwin does some retry tests here... */
@@ -1495,7 +1856,7 @@
 	/* The VIA is supposed not to be restored correctly*/
 	save_via_state();
 	/* We shut down some HW */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	pci_read_config_word(grackle, 0x70, &pmcr1);
 	/* Apparently, MacOS uses NAP mode for Grackle ??? */
@@ -1512,7 +1873,7 @@
 	pci_write_config_word(grackle, 0x70, pmcr1);
 
 	/* Make sure the PMU is idle */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	restore_via_state();
 	
 	/* Restore L2 cache */
@@ -1522,11 +1883,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Power things up */
 	pmu_request(&req, NULL, 2, PMU_SET_INTR_MASK, 0xfc);
 	while (!req.complete)
@@ -1558,6 +1914,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1571,7 +1931,7 @@
 	struct adb_request req;
 	int ret, timeout;
 	
-	if (!feature_can_sleep()) {
+	if (!can_sleep) {
 		printk(KERN_ERR "Sleep mode not supported on this machine\n");
 		return -ENOSYS;
 	}
@@ -1591,20 +1951,19 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + HZ; time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
 		printk("pmu: sleep failed\n");
 		return -EBUSY;
 	}
-
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + HZ; time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Tell PMU what events will wake us up */
@@ -1614,7 +1973,8 @@
 		pmu_poll();
 
 	pmu_request(&req, NULL, 4, PMU_POWER_EVENTS, PMU_PWR_SET_WAKEUP_EVENTS,
-		0, PMU_PWR_WAKEUP_KEY | PMU_PWR_WAKEUP_LID_OPEN);
+		0, PMU_PWR_WAKEUP_KEY |
+		(option_lid_wakeup ? PMU_PWR_WAKEUP_LID_OPEN : 0));
 	while (!req.complete)
 		pmu_poll();
 		
@@ -1651,10 +2011,12 @@
 	/* Save the state of PCI config space for some slots */
 	//pbook_pci_save();
 
-	/* Ask the PMU to put us to sleep */
-	pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
-	while (!req.complete && pmu_state != idle)
-		pmu_poll();
+	if (!__fake_sleep) {
+		/* Ask the PMU to put us to sleep */
+		pmu_request(&req, NULL, 5, PMU_SLEEP, 'M', 'A', 'T', 'T');
+		while (!req.complete && pmu_state != idle)
+			pmu_poll();
+	}
 
 	out_8(&via[B], in_8(&via[B]) | TREQ);
 	wait_for_ack();
@@ -1666,13 +2028,16 @@
 	 * talk to the PMU after this, so I moved it to _after_ sending the
 	 * sleep command to it. Still need to be checked.
 	 */
-	feature_prepare_for_sleep();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
 
 	/* Call low-level ASM sleep handler */
-	low_sleep_handler();
+	if (__fake_sleep)
+		mdelay(5000);
+	else
+		low_sleep_handler();
 
 	/* Restore Apple core ASICs state */
-	feature_wake_up();
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 
 	/* Restore VIA */
 	restore_via_state();
@@ -1694,11 +2059,6 @@
 	/* Restore userland MMU context */
 	set_context(current->active_mm->context, current->active_mm->pgd);
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* Tell PMU we are ready */
 	pmu_request(&req, NULL, 2, PMU_SYSTEM_READY, 2);
 	while (!req.complete)
@@ -1725,6 +2085,10 @@
 	/* Leave some time for HW to settle down */
 	mdelay(100);
 
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1766,6 +2130,10 @@
 	 * vmalloc's are done before actual sleep of block drivers */
 	fsync_dev(0);
 
+	/* Give the disks a little time to actually finish writing */
+	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
+		mb();
+
 	/* Sleep can fail now. May not be very robust but useful for debugging */
 	ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE);
 	if (ret != PBOOK_SLEEP_OK) {
@@ -1773,13 +2141,9 @@
 		return -EBUSY;
 	}
 
-	/* Give the disks a little time to actually finish writing */
-	for (wait = jiffies + (HZ/4); time_before(jiffies, wait); )
-		mb();
-
 	/* Wait for completion of async backlight requests */
 	while (!bright_req_1.complete || !bright_req_2.complete ||
-		!bright_req_3.complete)
+		!bright_req_3.complete || !batt_req.complete)
 		pmu_poll();
 
 	/* Disable all interrupts except pmu */
@@ -1811,6 +2175,8 @@
 	while (!sleep_req.complete)
 		mb();
 
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,1);
+
 	/* displacement-flush the L2 cache - necessary? */
 	for (p = KERNELBASE; p < KERNELBASE + 0x100000; p += 0x1000)
 		i = *(volatile int *)p;
@@ -1825,20 +2191,23 @@
 
 	/* OK, we're awake again, start restoring things */
 	out_be32(mem_ctrl_sleep, 0x3f);
+	pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0);
 	pbook_pci_restore();
 
 	/* wait for the PMU interrupt sequence to complete */
 	while (asleep)
 		mb();
 
-	/* Re-enable DEC interrupts and kick DEC */
-	asm volatile("mtdec %0" : : "r" (0x7fffffff));
-	sti();
-	asm volatile("mtdec %0" : : "r" (0x10000000));
-
 	/* reenable interrupts */
 	pmac_sleep_restore_intrs();
 
+	/* Leave some time for HW to settle down */
+	mdelay(100);
+
+	/* Restart jiffies & scheduling */
+	wakeup_decrementer();
+	sti();
+
 	/* Notify drivers */
 	broadcast_wake();
 
@@ -1877,6 +2246,7 @@
 	spin_lock_irqsave(&all_pvt_lock, flags);
 	for (list = &all_pmu_pvt; (list = list->next) != &all_pmu_pvt; ) {
 		pp = list_entry(list, struct pmu_private, list);
+		spin_lock(&pp->lock);
 		i = pp->rb_put + 1;
 		if (i >= RB_SIZE)
 			i = 0;
@@ -1887,6 +2257,7 @@
 			pp->rb_put = i;
 			wake_up_interruptible(&pp->wait);
 		}
+		spin_unlock(&pp->lock);
 	}
 	spin_unlock_irqrestore(&all_pvt_lock, flags);
 }
@@ -1914,6 +2285,7 @@
 {
 	struct pmu_private *pp = file->private_data;
 	DECLARE_WAITQUEUE(wait, current);
+	unsigned long flags;
 	int ret;
 
 	if (count < 1 || pp == 0)
@@ -1922,38 +2294,41 @@
 	if (ret)
 		return ret;
 
+	spin_lock_irqsave(&pp->lock, flags);
 	add_wait_queue(&pp->wait, &wait);
 	current->state = TASK_INTERRUPTIBLE;
 
 	for (;;) {
 		ret = -EAGAIN;
-		spin_lock(&pp->lock);
 		if (pp->rb_get != pp->rb_put) {
 			int i = pp->rb_get;
 			struct rb_entry *rp = &pp->rb_buf[i];
 			ret = rp->len;
+			spin_unlock_irqrestore(&pp->lock, flags);
 			if (ret > count)
 				ret = count;
 			if (ret > 0 && copy_to_user(buf, rp->data, ret))
 				ret = -EFAULT;
 			if (++i >= RB_SIZE)
 				i = 0;
+			spin_lock_irqsave(&pp->lock, flags);
 			pp->rb_get = i;
 		}
-		spin_unlock(&pp->lock);
 		if (ret >= 0)
 			break;
-
 		if (file->f_flags & O_NONBLOCK)
 			break;
 		ret = -ERESTARTSYS;
 		if (signal_pending(current))
 			break;
+		spin_unlock_irqrestore(&pp->lock, flags);
 		schedule();
+		spin_lock_irqsave(&pp->lock, flags);
 	}
 	current->state = TASK_RUNNING;
 	remove_wait_queue(&pp->wait, &wait);
-
+	spin_unlock_irqrestore(&pp->lock, flags);
+	
 	return ret;
 }
 
@@ -1967,14 +2342,15 @@
 {
 	struct pmu_private *pp = filp->private_data;
 	unsigned int mask = 0;
-
+	unsigned long flags;
+	
 	if (pp == 0)
 		return 0;
 	poll_wait(filp, &pp->wait, wait);
-	spin_lock(&pp->lock);
+	spin_lock_irqsave(&pp->lock, flags);
 	if (pp->rb_get != pp->rb_put)
 		mask |= POLLIN;
-	spin_unlock(&pp->lock);
+	spin_unlock_irqrestore(&pp->lock, flags);
 	return mask;
 }
 
@@ -2025,7 +2401,7 @@
 		sleep_in_progress = 0;
 		return error;
 	case PMU_IOC_CAN_SLEEP:
-		return put_user(feature_can_sleep(), (__u32 *)arg);
+		return put_user((u32)can_sleep, (__u32 *)arg);
 
 #ifdef CONFIG_PMAC_BACKLIGHT
 	/* Backlight should have its own device or go via
@@ -2155,5 +2531,8 @@
 EXPORT_SYMBOL(pmu_register_sleep_notifier);
 EXPORT_SYMBOL(pmu_unregister_sleep_notifier);
 EXPORT_SYMBOL(pmu_enable_irled);
+EXPORT_SYMBOL(pmu_battery_count);
+EXPORT_SYMBOL(pmu_batteries);
+EXPORT_SYMBOL(pmu_power_flags);
 #endif /* CONFIG_PMAC_PBOOK */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/md/lvm-fs.c linux/drivers/md/lvm-fs.c
--- linux.orig/drivers/md/lvm-fs.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/md/lvm-fs.c	Thu Jan 17 20:01:28 2002
@@ -65,9 +65,7 @@
 
 static void _show_uuid(const char *src, char *b, char *e);
 
-#if 0
 static devfs_handle_t lvm_devfs_handle;
-#endif
 static devfs_handle_t vg_devfs_handle[MAX_VG];
 static devfs_handle_t ch_devfs_handle[MAX_VG];
 static devfs_handle_t lv_devfs_handle[MAX_LV];
@@ -81,13 +79,11 @@
 void __init lvm_init_fs() {
 	struct proc_dir_entry *pde;
 
-/* User-space has already registered this */
-#if 0
+	/*  Must create device node. Think about "devfs=only" situation  */
 	lvm_devfs_handle = devfs_register(
 		0 , "lvm", 0, LVM_CHAR_MAJOR, 0,
 		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
 		&lvm_chr_fops, NULL);
-#endif
 
 	lvm_proc_dir = create_proc_entry(LVM_DIR, S_IFDIR, &proc_root);
 	if (lvm_proc_dir) {
@@ -99,9 +95,7 @@
 }
 
 void lvm_fin_fs() {
-#if 0
 	devfs_unregister (lvm_devfs_handle);
-#endif
 
 	remove_proc_entry(LVM_GLOBAL, lvm_proc_dir);
 	remove_proc_entry(LVM_VG_SUBDIR, lvm_proc_dir);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/md/md.c linux/drivers/md/md.c
--- linux.orig/drivers/md/md.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/md/md.c	Mon Jan 14 23:22:48 2002
@@ -1000,6 +1000,11 @@
 	struct md_list_head *tmp;
 	mdk_rdev_t *rdev;
 
+	if (!mddev->sb_dirty) {
+		printk("hm, md_update_sb() called without ->sb_dirty == 1, from %p.\n", __builtin_return_address(0));
+		return 0;
+	}
+	mddev->sb_dirty = 0;
 repeat:
 	mddev->sb->utime = CURRENT_TIME;
 	if ((++mddev->sb->events_lo)==0)
@@ -1724,6 +1729,7 @@
 	}
 
 	mddev->sb->state &= ~(1 << MD_SB_CLEAN);
+	mddev->sb_dirty = 1;
 	md_update_sb(mddev);
 
 	/*
@@ -1841,6 +1847,7 @@
 				printk(KERN_INFO "md: marking sb clean...\n");
 				mddev->sb->state |= 1 << MD_SB_CLEAN;
 			}
+			mddev->sb_dirty = 1;
 			md_update_sb(mddev);
 		}
 		if (ro)
@@ -2464,7 +2471,6 @@
 	mddev->sb->working_disks++;
 
 	mddev->sb_dirty = 1;
-
 	md_update_sb(mddev);
 
 	/*
@@ -3515,6 +3521,8 @@
 			continue;
 		if (sb->active_disks == sb->raid_disks)
 			continue;
+		if (mddev->sb_dirty)
+			md_update_sb(mddev);
 		if (!sb->spare_disks) {
 			printk(KERN_ERR "md%d: no spare disk to reconstruct array! "
 			       "-- continuing in degraded mode\n", mdidx(mddev));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/md/multipath.c linux/drivers/md/multipath.c
--- linux.orig/drivers/md/multipath.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/md/multipath.c	Mon Jan 14 23:22:48 2002
@@ -701,11 +701,8 @@
 		md_spin_unlock_irqrestore(&retry_list_lock, flags);
 
 		mddev = mp_bh->mddev;
-		if (mddev->sb_dirty) {
-			printk(KERN_INFO "dirty sb detected, updating.\n");
-			mddev->sb_dirty = 0;
+		if (mddev->sb_dirty)
 			md_update_sb(mddev);
-		}
 		bh = &mp_bh->bh_req;
 		dev = bh->b_dev;
 		
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/md/raid1.c linux/drivers/md/raid1.c
--- linux.orig/drivers/md/raid1.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/md/raid1.c	Mon Jan 14 23:22:48 2002
@@ -1136,11 +1136,8 @@
 		md_spin_unlock_irqrestore(&retry_list_lock, flags);
 
 		mddev = r1_bh->mddev;
-		if (mddev->sb_dirty) {
-			printk(KERN_INFO "raid1: dirty sb detected, updating.\n");
-			mddev->sb_dirty = 0;
+		if (mddev->sb_dirty)
 			md_update_sb(mddev);
-		}
 		bh = &r1_bh->bh_req;
 		switch(r1_bh->cmd) {
 		case SPECIAL:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/md/raid5.c linux/drivers/md/raid5.c
--- linux.orig/drivers/md/raid5.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/md/raid5.c	Mon Jan 14 23:22:48 2002
@@ -1293,10 +1293,8 @@
 
 	handled = 0;
 
-	if (mddev->sb_dirty) {
-		mddev->sb_dirty = 0;
+	if (mddev->sb_dirty)
 		md_update_sb(mddev);
-	}
 	md_spin_lock_irq(&conf->device_lock);
 	while (1) {
 		struct list_head *first;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/radio/radio-gemtek-pci.c linux/drivers/media/radio/radio-gemtek-pci.c
--- linux.orig/drivers/media/radio/radio-gemtek-pci.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/media/radio/radio-gemtek-pci.c	Mon Jan 14 18:53:53 2002
@@ -221,6 +221,7 @@
 		case VIDIOCGTUNER:
 		{
 			struct video_tuner t;
+			int signal;
 
 			if ( copy_from_user( &t, arg, sizeof( struct video_tuner ) ) )
 				return -EFAULT;
@@ -228,11 +229,12 @@
 			if ( t.tuner ) 
 				return -EINVAL;
 
+			signal = gemtek_pci_getsignal( card );
 			t.rangelow = GEMTEK_PCI_RANGE_LOW;
 			t.rangehigh = GEMTEK_PCI_RANGE_HIGH;
-			t.flags = VIDEO_TUNER_LOW;
+			t.flags = VIDEO_TUNER_LOW | (7 << signal) ;
 			t.mode = VIDEO_MODE_AUTO;
-			t.signal = 0xFFFF * gemtek_pci_getsignal( card );
+			t.signal = 0xFFFF * signal;
 			strcpy( t.name, "FM" );
 
 			if ( copy_to_user( arg, &t, sizeof( struct video_tuner ) ) )
@@ -282,6 +284,7 @@
 			a.flags |= VIDEO_AUDIO_MUTABLE;
 			a.volume = 1;
 			a.step = 65535;
+                        a.mode = (1 << gemtek_pci_getsignal( card ));
 			strcpy( a.name, "Radio" );
 
 			if ( copy_to_user( arg, &a, sizeof( struct video_audio ) ) )
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/radio/radio-sf16fmi.c linux/drivers/media/radio/radio-sf16fmi.c
--- linux.orig/drivers/media/radio/radio-sf16fmi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/media/radio/radio-sf16fmi.c	Mon Jan 14 18:53:53 2002
@@ -299,6 +299,7 @@
 	while (id_table[i].card_vendor != 0 && dev == NULL) {
 		dev = isapnp_find_dev(NULL, id_table[i].vendor,
 				      id_table[i].function, NULL);
+		i++;
 	}
 
 	if (!dev)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/video/bttv-driver.c linux/drivers/media/video/bttv-driver.c
--- linux.orig/drivers/media/video/bttv-driver.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/media/video/bttv-driver.c	Wed Feb 13 17:37:44 2002
@@ -2820,7 +2820,7 @@
  *	Scan for a Bt848 card, request the irq and map the io memory 
  */
 
-static void __devexit bttv_remove(struct pci_dev *pci_dev)
+static void bttv_remove(struct pci_dev *pci_dev)
 {
         u8 command;
         int j;
@@ -3025,7 +3025,7 @@
         name:     "bttv",
         id_table: bttv_pci_tbl,
         probe:    bttv_probe,
-        remove:   __devexit_p(bttv_remove),
+        remove:   bttv_remove,
 };
 
 int bttv_init_module(void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/video/planb.c linux/drivers/media/video/planb.c
--- linux.orig/drivers/media/video/planb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/media/video/planb.c	Tue Jan  8 18:08:44 2002
@@ -90,11 +90,10 @@
 static int planb_open(struct video_device *, int);
 static void planb_close(struct video_device *);
 static int planb_ioctl(struct video_device *, unsigned int, void *);
-static int planb_init_done(struct video_device *);
 static int planb_mmap(struct video_device *, const char *, unsigned long);
 static void planb_irq(int, void *, struct pt_regs *);
 static void release_planb(void);
-int init_planbs(struct video_init *);
+static int init_planbs(void);
 
 /* ------------------ PlanB Internal Functions ------------------ */
 static int planb_prepare_open(struct planb *);
@@ -2079,7 +2078,6 @@
 #endif
 	pb->tab_size = PLANB_MAXLINES + 40;
 	pb->suspend = 0;
-	pb->lock = 0;
 	init_MUTEX(&pb->lock);
 	pb->ch1_cmd = 0;
 	pb->ch2_cmd = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/video/zr36067.c linux/drivers/media/video/zr36067.c
--- linux.orig/drivers/media/video/zr36067.c	Fri Nov  9 22:01:22 2001
+++ linux/drivers/media/video/zr36067.c	Mon Feb  4 16:55:14 2002
@@ -2609,7 +2609,7 @@
 		}
 		frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
 		gbuf = &zr->jpg_gbuf[frame];
-		get_fast_time(&gbuf->bs.timestamp);
+		do_gettimeofday(&gbuf->bs.timestamp);
 
 		if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
 			gbuf->bs.length = (stat_com & 0x7fffff) >> 1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/media/video/zr36120.c linux/drivers/media/video/zr36120.c
--- linux.orig/drivers/media/video/zr36120.c	Fri Nov  9 22:01:22 2001
+++ linux/drivers/media/video/zr36120.c	Wed Feb 13 17:37:40 2002
@@ -2025,7 +2025,7 @@
 }
 
 static
-void __exit release_zoran(int max)
+void release_zoran(int max)
 {
 	struct zoran *ztv;
 	int i;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/message/i2o/i2o_block.c linux/drivers/message/i2o/i2o_block.c
--- linux.orig/drivers/message/i2o/i2o_block.c	Thu Oct 25 20:58:35 2001
+++ linux/drivers/message/i2o/i2o_block.c	Wed Feb 13 17:44:49 2002
@@ -282,6 +282,7 @@
 	
 	if(req->cmd == READ)
 	{
+		DEBUG("READ\n");
 		__raw_writel(I2O_CMD_BLOCK_READ<<24|HOST_TID<<12|tid, msg+4);
 		while(bh!=NULL)
 		{
@@ -321,6 +322,7 @@
 	}
 	else if(req->cmd == WRITE)
 	{
+		DEBUG("WRITE\n");
 		__raw_writel(I2O_CMD_BLOCK_WRITE<<24|HOST_TID<<12|tid, msg+4);
 		while(bh!=NULL)
 		{
@@ -415,6 +417,7 @@
 	 * It is now ok to complete the request.
 	 */
 	end_that_request_last( req );
+	DEBUG("IO COMPLETED\n");
 }
 
 /*
@@ -493,7 +496,7 @@
 	__raw_writel(i2ob_context|(unit<<8), msg+8);
 	__raw_writel(0, msg+12);
 	__raw_writel(60<<16, msg+16);
-	
+	DEBUG("FLUSH");
 	i2o_post_message(c,m);
 	return 0;
 }
@@ -522,6 +525,7 @@
 	 */
 	if(m[0] & (1<<13))
 	{
+		DEBUG("FAIL");
 		/*
 		 * FAILed message from controller
 		 * We increment the error count and abort it
@@ -561,7 +565,7 @@
 	{
 		spin_lock_irqsave(&io_request_lock, flags);
 		dev->constipated=0;
-		DEBUG(("unconstipated\n"));
+		DEBUG("unconstipated\n");
 		if(i2ob_backlog_request(c, dev)==0)
 			i2ob_request(dev->req_queue);
 		spin_unlock_irqrestore(&io_request_lock, flags);
@@ -861,7 +865,7 @@
 			 * and tell them to fix their firmware :)
 			 */
 			default:
-				printk(KERN_INFO "%s: Received event %d we didn't register for\n"
+				printk(KERN_INFO "%s: Received event 0x%X we didn't register for\n"
 					KERN_INFO "   Blame the I2O card manufacturer 8)\n", 
 					i2ob_dev[unit].i2odev->dev_name, evt);
 				break;
@@ -1205,9 +1209,6 @@
 	if(!dev->i2odev)
 		return 0;
 
-	/* Sync the device so we don't get errors */
-	fsync_dev(inode->i_rdev);
-
 	if (dev->refcnt <= 0)
 		printk(KERN_ALERT "i2ob_release: refcount(%d) <= 0\n", dev->refcnt);
 	dev->refcnt--;
@@ -1470,19 +1471,16 @@
 {
 	int i;
 
-	i2ob_queues[unit] = (struct i2ob_iop_queue*)
-		kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC);
+	i2ob_queues[unit] = (struct i2ob_iop_queue*) kmalloc(sizeof(struct i2ob_iop_queue), GFP_ATOMIC);
 	if(!i2ob_queues[unit])
 	{
-		printk(KERN_WARNING
-			"Could not allocate request queue for I2O block device!\n");
+		printk(KERN_WARNING "Could not allocate request queue for I2O block device!\n");
 		return -1;
 	}
 
 	for(i = 0; i< MAX_I2OB_DEPTH; i++)
 	{
-		i2ob_queues[unit]->request_queue[i].next = 
-			&i2ob_queues[unit]->request_queue[i+1];
+		i2ob_queues[unit]->request_queue[i].next =  &i2ob_queues[unit]->request_queue[i+1];
 		i2ob_queues[unit]->request_queue[i].num = i;
 	}
 	
@@ -1507,7 +1505,6 @@
 static request_queue_t* i2ob_get_queue(kdev_t dev)
 {
 	int unit = MINOR(dev)&0xF0;
-
 	return i2ob_dev[unit].req_queue;
 }
 
@@ -1530,34 +1527,34 @@
 		if(c==NULL)
 			continue;
 
-	/*
-	 *    The device list connected to the I2O Controller is doubly linked
-	 * Here we traverse the end of the list , and start claiming devices
-	 * from that end. This assures that within an I2O controller atleast
-	 * the newly created volumes get claimed after the older ones, thus
-	 * mapping to same major/minor (and hence device file name) after 
-	 * every reboot.
-	 * The exception being: 
-	 * 1. If there was a TID reuse.
-	 * 2. There was more than one I2O controller. 
-	 */
-
-	if(!bios)
-	{
-		for (d=c->devices;d!=NULL;d=d->next)
-		if(d->next == NULL)
-			b = d;
-	}
-	else
-		b = c->devices;
+		/*
+		 *    The device list connected to the I2O Controller is doubly linked
+		 * Here we traverse the end of the list , and start claiming devices
+		 * from that end. This assures that within an I2O controller atleast
+		 * the newly created volumes get claimed after the older ones, thus
+		 * mapping to same major/minor (and hence device file name) after 
+		 * every reboot.
+		 * The exception being: 
+		 * 1. If there was a TID reuse.
+		 * 2. There was more than one I2O controller. 
+		 */
 
-	while(b != NULL)
-	{
-		d=b;
-		if(bios)
-			b = b->next;
+		if(!bios)
+		{
+			for (d=c->devices;d!=NULL;d=d->next)
+			if(d->next == NULL)
+				b = d;
+		}
 		else
-			b = b->prev;
+			b = c->devices;
+
+		while(b != NULL)
+		{
+			d=b;
+			if(bios)
+				b = b->next;
+			else
+				b = b->prev;
 
 			if(d->lct_data.class_id!=I2O_CLASS_RANDOM_BLOCK_STORAGE)
 				continue;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/message/i2o/i2o_config.c linux/drivers/message/i2o/i2o_config.c
--- linux.orig/drivers/message/i2o/i2o_config.c	Mon Oct 22 15:39:56 2001
+++ linux/drivers/message/i2o/i2o_config.c	Wed Feb 13 17:44:49 2002
@@ -45,7 +45,7 @@
 static spinlock_t i2o_config_lock = SPIN_LOCK_UNLOCKED;
 struct wait_queue *i2o_wait_queue;
 
-#define MODINC(x,y) (x = x++ % y)
+#define MODINC(x,y) ((x) = ((x) + 1) % (y))
 
 struct i2o_cfg_info
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/message/i2o/i2o_core.c linux/drivers/message/i2o/i2o_core.c
--- linux.orig/drivers/message/i2o/i2o_core.c	Mon Oct 22 15:39:56 2001
+++ linux/drivers/message/i2o/i2o_core.c	Wed Feb 13 17:44:49 2002
@@ -208,7 +208,7 @@
  
 static DECLARE_MUTEX(evt_sem);
 static DECLARE_COMPLETION(evt_dead);
-DECLARE_WAIT_QUEUE_HEAD(evt_wait);
+static DECLARE_WAIT_QUEUE_HEAD(evt_wait);
 
 static struct notifier_block i2o_reboot_notifier =
 {
@@ -1184,7 +1184,8 @@
 	{
 		struct i2o_handler *i;
 		/* Map the message from the page frame map to kernel virtual */
-		m=(struct i2o_message *)(mv - (unsigned long)c->page_frame_map + (unsigned long)c->page_frame);
+		/* m=(struct i2o_message *)(mv - (unsigned long)c->page_frame_map + (unsigned long)c->page_frame); */
+		m=(struct i2o_message *)bus_to_virt(mv);
 		msg=(u32*)m;
 
 		/*
@@ -2558,6 +2559,7 @@
 int i2o_post_wait_mem(struct i2o_controller *c, u32 *msg, int len, int timeout, void *mem1, void *mem2)
 {
 	DECLARE_WAIT_QUEUE_HEAD(wq_i2o_post);
+	DECLARE_WAITQUEUE(wait, current);
 	int complete = 0;
 	int status;
 	unsigned long flags = 0;
@@ -2598,12 +2600,19 @@
 	 *	complete will be zero.  From the point post_this returns
 	 *	the wait_data may have been deleted.
 	 */
+
+	add_wait_queue(&wq_i2o_post, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
 	if ((status = i2o_post_this(c, msg, len))==0) {
-		sleep_on_timeout(&wq_i2o_post, HZ * timeout);
+		schedule_timeout(HZ * timeout);
 	}  
 	else
+	{
+		remove_wait_queue(&wq_i2o_post, &wait);
 		return -EIO;
-		
+	}
+	remove_wait_queue(&wq_i2o_post, &wait);
+
 	if(signal_pending(current))
 		status = -EINTR;
 		
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
--- linux.orig/drivers/mtd/devices/blkmtd.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/mtd/devices/blkmtd.c	Wed Dec 26 15:53:59 2001
@@ -1,10 +1,11 @@
 /* 
- * $Id: blkmtd.c,v 1.3 2001/10/02 15:33:20 dwmw2 Exp $
+ * $Id: blkmtd.c,v 1.7 2001/11/10 17:06:30 spse Exp $
+ *
  * blkmtd.c - use a block device as a fake MTD
  *
  * Author: Simon Evans <spse@secret.org.uk>
  *
- * Copyright (C) 2001 Simon Evans <spse@secret.org.uk>
+ * Copyright (C) 2001 Simon Evans
  * 
  * Licence: GPL
  *
@@ -13,7 +14,7 @@
  *       cache to cache access. Writes update the page cache with the
  *       new data but make a copy of the new page(s) and then a kernel
  *       thread writes pages out to the device in the background. This
- *       ensures tht writes are order even if a page is updated twice.
+ *       ensures that writes are order even if a page is updated twice.
  *       Also, since pages in the page cache are never marked as dirty,
  *       we dont have to worry about writepage() being called on some 
  *       random page which may not be in the write order.
@@ -30,7 +31,7 @@
  *       small memory systems and too small on large memory systems.
  * 
  *       Page cache usage may still be a bit wrong. Check we are doing
- *       everything proberly.
+ *       everything properly.
  * 
  *       Somehow allow writes to dirty the page cache so we dont use too
  *       much memory making copies of outgoing pages. Need to handle case
@@ -39,15 +40,9 @@
  * 
  *       Reading should read multiple pages at once rather than using 
  *       readpage() for each one. This is easy and will be fixed asap.
- *
- *       Dont run the write_thread if readonly. This is also easy and will
- *       be fixed asap.
- * 
- *       Even though the multiple erase regions are used if the default erase
- *       block size doesnt match the device properly, erases currently wont
- *       work on the last page if it is not a full page.
  */
 
+
 #include <linux/config.h>
 #include <linux/module.h>
 
@@ -59,20 +54,31 @@
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 
+#ifdef CONFIG_MTD_DEBUG
+#ifdef CONFIG_PROC_FS
+#  include <linux/proc_fs.h>
+#  define BLKMTD_PROC_DEBUG
+   static struct proc_dir_entry *blkmtd_proc;
+#endif
+#endif
+
+
 /* Default erase size in K, always make it a multiple of PAGE_SIZE */
 #define CONFIG_MTD_BLKDEV_ERASESIZE 128
-#define VERSION "1.1"
+#define VERSION "1.7"
 extern int *blk_size[];
 extern int *blksize_size[];
 
 /* Info for the block device */
 typedef struct mtd_raw_dev_data_s {
   struct block_device *binding;
-  int sector_size, sector_bits, total_sectors;
+  int sector_size, sector_bits;
+  int partial_last_page;   // 0 if device ends on page boundary, else page no of last page
+  int last_page_sectors;   // Number of sectors in last page if partial_last_page != 0
   size_t totalsize;
   int readonly;
   struct address_space as;
-  struct file *file;
+  struct mtd_info mtd_info;
 } mtd_raw_dev_data_t;
 
 /* Info for each queue item in the write queue */
@@ -85,24 +91,28 @@
 } mtdblkdev_write_queue_t;
 
 
+/* Our erase page - always remains locked. */
+static struct page *erase_page;
+
 /* Static info about the MTD, used in cleanup_module */
-static struct mtd_info *mtd_info;
+static mtd_raw_dev_data_t *mtd_rawdevice;
 
 /* Write queue fixed size */
 #define WRITE_QUEUE_SZ 512
 
 /* Storage for the write queue */
-static mtdblkdev_write_queue_t write_queue[WRITE_QUEUE_SZ];
+static mtdblkdev_write_queue_t *write_queue;
+static int write_queue_sz = WRITE_QUEUE_SZ;
 static int volatile write_queue_head;
 static int volatile write_queue_tail;
 static int volatile write_queue_cnt;
 static spinlock_t mbd_writeq_lock = SPIN_LOCK_UNLOCKED;
 
 /* Tell the write thread to finish */
-static volatile int write_task_finish = 0;
+static volatile int write_task_finish;
 
 /* ipc with the write thread */
-#if LINUX_VERSION_CODE > 0x020300
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
 static DECLARE_MUTEX_LOCKED(thread_sem);
 static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
 static DECLARE_WAIT_QUEUE_HEAD(mtbd_sync_wq);
@@ -113,13 +123,14 @@
 #endif
 
 
-
 /* Module parameters passed by insmod/modprobe */
 char *device;    /* the block device to use */
 int erasesz;     /* optional default erase size */
 int ro;          /* optional read only flag */
 int bs;          /* optionally force the block size (avoid using) */
 int count;       /* optionally force the block count (avoid using) */
+int wqs;         /* optionally set the write queue size */
+
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 MODULE_LICENSE("GPL");
@@ -135,10 +146,10 @@
 MODULE_PARM_DESC(bs, "force the block size in bytes");
 MODULE_PARM(count, "i");
 MODULE_PARM_DESC(count, "force the block count");
+MODULE_PARM(wqs, "i");
 #endif
 
 
-
 /* Page cache stuff */
 
 /* writepage() - should never be called - catch it anyway */
@@ -150,13 +161,13 @@
 
 
 /* readpage() - reads one page from the block device */                 
-static int blkmtd_readpage(struct file *file, struct page *page)
+static int blkmtd_readpage(mtd_raw_dev_data_t *rawdevice, struct page *page)
 {  
   int err;
   int sectornr, sectors, i;
   struct kiobuf *iobuf;
-  mtd_raw_dev_data_t *rawdevice = (mtd_raw_dev_data_t *)file->private_data;
   kdev_t dev;
+  unsigned long *blocks;
 
   if(!rawdevice) {
     printk("blkmtd: readpage: PANIC file->private_data == NULL\n");
@@ -168,7 +179,7 @@
 	bdevname(dev), page, page->index);
 
   if(Page_Uptodate(page)) {
-    DEBUG(1, "blkmtd: readpage page %ld is already upto date\n", page->index);
+    DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
     UnlockPage(page);
     return 0;
   }
@@ -184,8 +195,9 @@
       mtdblkdev_write_queue_t *item = &write_queue[i];
       if(page->index >= item->pagenr && page->index < item->pagenr+item->pagecnt) {
 	/* yes it is */
-	int index = item->pagenr - page->index;
-	DEBUG(1, "blkmtd: readpage: found page %ld in outgoing write queue\n",
+	int index = page->index - item->pagenr;
+		
+	DEBUG(2, "blkmtd: readpage: found page %ld in outgoing write queue\n",
 	      page->index);
 	if(item->iserase) {
 	  memset(page_address(page), 0xff, PAGE_SIZE);
@@ -199,7 +211,7 @@
 	return 0;
       }
       i++;
-      i %= WRITE_QUEUE_SZ;
+      i %= write_queue_sz;
     }
   }
   spin_unlock(&mbd_writeq_lock);
@@ -208,8 +220,24 @@
   DEBUG(3, "blkmtd: readpage: getting kiovec\n");
   err = alloc_kiovec(1, &iobuf);
   if (err) {
+    printk("blkmtd: cant allocate kiobuf\n");
+    SetPageError(page);
     return err;
   }
+
+  /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long));
+  if(blocks == NULL) {
+    printk("blkmtd: cant allocate iobuf blocks\n");
+    free_kiovec(1, &iobuf);
+    SetPageError(page);
+    return -ENOMEM;
+  }
+#else 
+  blocks = iobuf->blocks;
+#endif
+
   iobuf->offset = 0;
   iobuf->nr_pages = 1;
   iobuf->length = PAGE_SIZE;
@@ -217,16 +245,34 @@
   iobuf->maplist[0] = page;
   sectornr = page->index << (PAGE_SHIFT - rawdevice->sector_bits);
   sectors = 1 << (PAGE_SHIFT - rawdevice->sector_bits);
+  if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) {
+    DEBUG(3, "blkmtd: handling partial last page\n");
+    sectors = rawdevice->last_page_sectors;
+  }
   DEBUG(3, "blkmtd: readpage: sectornr = %d sectors = %d\n", sectornr, sectors);
   for(i = 0; i < sectors; i++) {
-    iobuf->blocks[i] = sectornr++;
+    blocks[i] = sectornr++;
   }
+  /* If only a partial page read in, clear the rest of the page */
+  if(rawdevice->partial_last_page && page->index == rawdevice->partial_last_page) {
+    int offset = rawdevice->last_page_sectors << rawdevice->sector_bits;
+    int count = PAGE_SIZE-offset;
+    DEBUG(3, "blkmtd: clear last partial page: offset = %d count = %d\n", offset, count);
+    memset(page_address(page)+offset, 0, count);
+    sectors = rawdevice->last_page_sectors;
+  }
+
 
   DEBUG(3, "bklmtd: readpage: starting brw_kiovec\n");
-  err = brw_kiovec(READ, 1, &iobuf, dev, iobuf->blocks, rawdevice->sector_size);
+  err = brw_kiovec(READ, 1, &iobuf, dev, blocks, rawdevice->sector_size);
   DEBUG(3, "blkmtd: readpage: finished, err = %d\n", err);
   iobuf->locked = 0;
   free_kiovec(1, &iobuf);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  kfree(blocks);
+#endif
+
   if(err != PAGE_SIZE) {
     printk("blkmtd: readpage: error reading page %ld\n", page->index);
     memset(page_address(page), 0, PAGE_SIZE);
@@ -246,7 +292,7 @@
                     
 static struct address_space_operations blkmtd_aops = {
   writepage:     blkmtd_writepage,
-  readpage:      blkmtd_readpage,
+  readpage:      NULL,
 }; 
 
 
@@ -256,6 +302,7 @@
   int err;
   struct task_struct *tsk = current;
   struct kiobuf *iobuf;
+  unsigned long *blocks;
 
   DECLARE_WAITQUEUE(wait, tsk);
   DEBUG(1, "blkmtd: writetask: starting (pid = %d)\n", tsk->pid);
@@ -268,8 +315,23 @@
   spin_unlock_irq(&tsk->sigmask_lock);
   exit_sighand(tsk);
 
-  if(alloc_kiovec(1, &iobuf))
+  if(alloc_kiovec(1, &iobuf)) {
+    printk("blkmtd: write_queue_task cant allocate kiobuf\n");
     return 0;
+  }
+
+  /* Pre 2.4.4 doesnt have space for the block list in the kiobuf */ 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long));
+  if(blocks == NULL) {
+    printk("blkmtd: write_queue_task cant allocate iobuf blocks\n");
+    free_kiovec(1, &iobuf);
+    return 0;
+  }
+#else 
+  blocks = iobuf->blocks;
+#endif
+
   DEBUG(2, "blkmtd: writetask: entering main loop\n");
   add_wait_queue(&thr_wq, &wait);
 
@@ -280,30 +342,39 @@
       /* If nothing in the queue, wake up anyone wanting to know when there
 	 is space in the queue then sleep for 2*HZ */
       spin_unlock(&mbd_writeq_lock);
-      DEBUG(3, "blkmtd: writetask: queue empty\n");
+      DEBUG(4, "blkmtd: writetask: queue empty\n");
       if(waitqueue_active(&mtbd_sync_wq))
 	 wake_up(&mtbd_sync_wq);
       interruptible_sleep_on_timeout(&thr_wq, 2*HZ);
-      DEBUG(3, "blkmtd: writetask: woken up\n");
+      DEBUG(4, "blkmtd: writetask: woken up\n");
       if(write_task_finish)
 	break;
     } else {
       /* we have stuff to write */
       mtdblkdev_write_queue_t *item = &write_queue[write_queue_tail];
       struct page **pages = item->pages;
-      int pagecnt = item->pagecnt;
-      int pagenr = item->pagenr;
+
       int i;
+      int sectornr = item->pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits);
+      int sectorcnt = item->pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits);
       int max_sectors = KIO_MAX_SECTORS >> (item->rawdevice->sector_bits - 9);
       kdev_t dev = to_kdev_t(item->rawdevice->binding->bd_dev);
-  
+
+      /* If we are writing to the last page on the device and it doesnt end
+       * on a page boundary, subtract the number of sectors that dont exist.
+       */
+      if(item->rawdevice->partial_last_page && 
+	 (item->pagenr + item->pagecnt -1) == item->rawdevice->partial_last_page) {
+	sectorcnt -= (1 << (PAGE_SHIFT - item->rawdevice->sector_bits));
+	sectorcnt += item->rawdevice->last_page_sectors;
+      }
 
       DEBUG(3, "blkmtd: writetask: got %d queue items\n", write_queue_cnt);
       set_current_state(TASK_RUNNING);
       spin_unlock(&mbd_writeq_lock);
 
-      DEBUG(2, "blkmtd: write_task: writing pagenr = %d pagecnt = %d", 
-	    item->pagenr, item->pagecnt);
+      DEBUG(2, "blkmtd: writetask: writing pagenr = %d pagecnt = %d sectornr = %d sectorcnt = %d\n", 
+	    item->pagenr, item->pagecnt, sectornr, sectorcnt);
 
       iobuf->offset = 0;
       iobuf->locked = 1;
@@ -311,31 +382,33 @@
       /* Loop through all the pages to be written in the queue item, remembering
 	 we can only write KIO_MAX_SECTORS at a time */
 	 
-      while(pagecnt) {
-	int sectornr = pagenr << (PAGE_SHIFT - item->rawdevice->sector_bits);
-	int sectorcnt = pagecnt << (PAGE_SHIFT - item->rawdevice->sector_bits);
+      while(sectorcnt) {
 	int cursectors = (sectorcnt < max_sectors) ? sectorcnt : max_sectors;
 	int cpagecnt = (cursectors << item->rawdevice->sector_bits) + PAGE_SIZE-1;
 	cpagecnt >>= PAGE_SHIFT;
 	
-	for(i = 0; i < cpagecnt; i++) 
+	for(i = 0; i < cpagecnt; i++) {
+	  if(item->iserase) {
+	    iobuf->maplist[i] = erase_page;
+	  } else {
 	    iobuf->maplist[i] = *(pages++);
+	  }
+	}
 	
 	for(i = 0; i < cursectors; i++) {
-	  iobuf->blocks[i] = sectornr++;
+	  blocks[i] = sectornr++;
 	}
 	
 	iobuf->nr_pages = cpagecnt;
 	iobuf->length = cursectors << item->rawdevice->sector_bits;
 	DEBUG(3, "blkmtd: write_task: about to kiovec\n");
-	err = brw_kiovec(WRITE, 1, &iobuf, dev, iobuf->blocks, item->rawdevice->sector_size);
+	err = brw_kiovec(WRITE, 1, &iobuf, dev, blocks, item->rawdevice->sector_size);
 	DEBUG(3, "bklmtd: write_task: done, err = %d\n", err);
 	if(err != (cursectors << item->rawdevice->sector_bits)) {
 	  /* if an error occured - set this to exit the loop */
-	  pagecnt = 0;
+	  sectorcnt = 0;
 	} else {
-	  pagenr += cpagecnt;
-	  pagecnt -= cpagecnt;
+	  sectorcnt -= cursectors;
 	}
       }
 
@@ -345,12 +418,14 @@
       spin_lock(&mbd_writeq_lock);
       write_queue_cnt--;
       write_queue_tail++;
-      write_queue_tail %= WRITE_QUEUE_SZ;
-      for(i = 0 ; i < item->pagecnt; i++) {
-	UnlockPage(item->pages[i]);
-	__free_pages(item->pages[i], 0);
+      write_queue_tail %= write_queue_sz;
+      if(!item->iserase) {
+	for(i = 0 ; i < item->pagecnt; i++) {
+	  UnlockPage(item->pages[i]);
+	  __free_pages(item->pages[i], 0);
+	}
+	kfree(item->pages);
       }
-      kfree(item->pages);
       item->pages = NULL;
       spin_unlock(&mbd_writeq_lock);
       /* Tell others there is some space in the write queue */
@@ -361,6 +436,11 @@
   remove_wait_queue(&thr_wq, &wait);
   DEBUG(1, "blkmtd: writetask: exiting\n");
   free_kiovec(1, &iobuf);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+  kfree(blocks);
+#endif
+
   /* Tell people we have exitd */
   up(&thread_sem);
   return 0;
@@ -372,43 +452,45 @@
 			    int pagenr, int pagecnt, int iserase)
 {
   struct page *outpage;
-  struct page **new_pages;
+  struct page **new_pages = NULL;
   mtdblkdev_write_queue_t *item;
   int i;
   DECLARE_WAITQUEUE(wait, current);
-  DEBUG(2, "mtdblkdev: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
+  DEBUG(2, "blkmtd: queue_page_write: adding pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
 
   if(!pagecnt)
     return 0;
 
-  if(pages == NULL)
+  if(pages == NULL && !iserase)
     return -EINVAL;
 
   /* create a array for the list of pages */
-  new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
-  if(new_pages == NULL)
-    return -ENOMEM;
+  if(!iserase) {
+    new_pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
+    if(new_pages == NULL)
+      return -ENOMEM;
 
-  /* make copies of the pages in the page cache */
-  for(i = 0; i < pagecnt; i++) {
-    outpage = alloc_pages(GFP_KERNEL, 0);
-    if(!outpage) {
-      while(i--) {
-	UnlockPage(new_pages[i]);
-	__free_pages(new_pages[i], 0);
+    /* make copies of the pages in the page cache */
+    for(i = 0; i < pagecnt; i++) {
+      outpage = alloc_pages(GFP_KERNEL, 0);
+      if(!outpage) {
+	while(i--) {
+	  UnlockPage(new_pages[i]);
+	  __free_pages(new_pages[i], 0);
+	}
+	kfree(new_pages);
+	return -ENOMEM;
       }
-      kfree(new_pages);
-      return -ENOMEM;
+      lock_page(outpage);
+      memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE);
+      new_pages[i] = outpage;
     }
-    lock_page(outpage);
-    memcpy(page_address(outpage), page_address(pages[i]), PAGE_SIZE);
-    new_pages[i] = outpage;
   }
 
   /* wait until there is some space in the write queue */
  test_lock:
   spin_lock(&mbd_writeq_lock);
-  if(write_queue_cnt == WRITE_QUEUE_SZ) {
+  if(write_queue_cnt == write_queue_sz) {
     spin_unlock(&mbd_writeq_lock);
     DEBUG(3, "blkmtd: queue_page: Queue full\n");
     current->state = TASK_UNINTERRUPTIBLE;
@@ -417,11 +499,11 @@
     schedule();
     current->state = TASK_RUNNING;
     remove_wait_queue(&mtbd_sync_wq, &wait);
-    DEBUG(3, "blkmtd: queue_page: Queue has %d items in it\n", write_queue_cnt);
+    DEBUG(3, "blkmtd: queue_page_write: Queue has %d items in it\n", write_queue_cnt);
     goto test_lock;
   }
 
-  DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", 
+  DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", 
 	write_queue_head, write_queue_tail, write_queue_cnt);
 
   /* fix up the queue item */
@@ -433,9 +515,9 @@
   item->iserase = iserase;
 
   write_queue_head++;
-  write_queue_head %= WRITE_QUEUE_SZ;
+  write_queue_head %= write_queue_sz;
   write_queue_cnt++;
-  DEBUG(3, "blkmtd: queue_write_page: qhead: %d qtail: %d qcnt: %d\n", 
+  DEBUG(3, "blkmtd: queue_page_write: qhead: %d qtail: %d qcnt: %d\n", 
 	write_queue_head, write_queue_tail, write_queue_cnt);
   spin_unlock(&mbd_writeq_lock);
   DEBUG(2, "blkmtd: queue_page_write: finished\n");
@@ -447,6 +529,8 @@
 static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
   mtd_raw_dev_data_t *rawdevice = mtd->priv;
+  struct mtd_erase_region_info *einfo = mtd->eraseregions;
+  int numregions = mtd->numeraseregions;
   size_t from;
   u_long len;
   int err = 0;
@@ -462,17 +546,23 @@
   from = instr->addr;
   len = instr->len;
 
-  /* check page alignment of start and length */
-  DEBUG(2, "blkmtd: erase: dev = `%s' from = %d len = %ld\n",
+  /* check erase region has valid start and length */
+  DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
 	bdevname(rawdevice->binding->bd_dev), from, len);
-  if(from % PAGE_SIZE) {
-    printk("blkmtd: erase: addr not page aligned (addr = %d)\n", from);
-    instr->state = MTD_ERASE_FAILED;
-    err = -EIO;
+  while(numregions) {
+    DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
+	  einfo->offset, einfo->erasesize, einfo->numblocks);
+    if(from >= einfo->offset && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
+      if(len == einfo->erasesize && ( (from - einfo->offset) % einfo->erasesize == 0))
+	break;
+    }
+    numregions--;
+    einfo++;
   }
 
-  if(len % PAGE_SIZE) {
-    printk("blkmtd: erase: len not a whole number of pages (len = %ld)\n", len);
+  if(!numregions) {
+    /* Not a valid erase block */
+    printk("blkmtd: erase: invalid erase request 0x%lX @ 0x%08X\n", len, from);
     instr->state = MTD_ERASE_FAILED;
     err = -EIO;
   }
@@ -483,9 +573,14 @@
     struct page *page, **pages;
     int i = 0;
 
+    /* Handle the last page of the device not being whole */
+    if(len < PAGE_SIZE)
+      len = PAGE_SIZE;
+
     pagenr = from >> PAGE_SHIFT;
     pagecnt = len >> PAGE_SHIFT;
     DEBUG(3, "blkmtd: erase: pagenr = %d pagecnt = %d\n", pagenr, pagecnt);
+
     pages = kmalloc(pagecnt * sizeof(struct page *), GFP_KERNEL);
     if(pages == NULL) {
       err = -ENOMEM;
@@ -493,9 +588,10 @@
       goto erase_out;
     }
 
+
     while(pagecnt) {
       /* get the page via the page cache */
-      DEBUG(3, "blkmtd: erase: doing grap_cache_page() for page %d\n", pagenr);
+      DEBUG(3, "blkmtd: erase: doing grab_cache_page() for page %d\n", pagenr);
       page = grab_cache_page(&rawdevice->as, pagenr);
       if(!page) {
 	DEBUG(3, "blkmtd: erase: grab_cache_page() failed for page %d\n", pagenr);
@@ -511,7 +607,7 @@
       i++;
     }
     DEBUG(3, "blkmtd: erase: queuing page write\n");
-    err = queue_page_write(rawdevice, pages, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1);
+    err = queue_page_write(rawdevice, NULL, from >> PAGE_SHIFT, len >> PAGE_SHIFT, 1);
     pagecnt = len >> PAGE_SHIFT;
     if(!err) {
       while(pagecnt--) {
@@ -567,7 +663,7 @@
     struct page *page;
     int cpylen;
     DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
     if(IS_ERR(page)) {
       return PTR_ERR(page);
     }
@@ -683,7 +779,7 @@
     struct page *page;
     
     DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", pagenr, len1, offset);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
 
     if(IS_ERR(page)) {
       kfree(pages);
@@ -716,6 +812,7 @@
       memcpy(page_address(page), buf, PAGE_SIZE);
       pages[pagecnt++] = page;
       UnlockPage(page);
+      SetPageUptodate(page);
       pagenr++;
       pagesc--;
       buf += PAGE_SIZE;
@@ -728,7 +825,7 @@
     /* do the third region */
     struct page *page;
     DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", pagenr, len3);
-    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice->file);
+    page = read_cache_page(&rawdevice->as, pagenr, (filler_t *)blkmtd_readpage, rawdevice);
     if(IS_ERR(page)) {
       err = PTR_ERR(page);
       goto write_err;
@@ -766,6 +863,10 @@
 static void blkmtd_sync(struct mtd_info *mtd)
 {
   DECLARE_WAITQUEUE(wait, current);
+  mtd_raw_dev_data_t *rawdevice = mtd->priv;
+  if(rawdevice->readonly)
+    return;
+
   DEBUG(2, "blkmtd: sync: called\n");
 
  stuff_inq:
@@ -784,36 +885,144 @@
   }
   spin_unlock(&mbd_writeq_lock);
 
-  DEBUG(2, "blkmtdL sync: finished\n");
+  DEBUG(2, "blkmtd: sync: finished\n");
 }
 
+
+#ifdef BLKMTD_PROC_DEBUG
+/* procfs stuff */
+static int blkmtd_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+  int clean = 0, dirty = 0, locked = 0;
+  struct list_head *temp;
+  int i, len, pages = 0, cnt;
+  MOD_INC_USE_COUNT;
+  spin_lock(&mbd_writeq_lock);
+  cnt = write_queue_cnt;
+  i = write_queue_tail;
+  while(cnt) {
+    if(!write_queue[i].iserase)
+      pages += write_queue[i].pagecnt;
+    i++;
+    i %= write_queue_sz;
+    cnt--;
+  }
+
+  /* Count the size of the page lists */
+  list_for_each(temp, &mtd_rawdevice->as.clean_pages) {
+    clean++;
+  }
+  list_for_each(temp, &mtd_rawdevice->as.dirty_pages) {
+    dirty++;
+  }
+  list_for_each(temp, &mtd_rawdevice->as.locked_pages) {
+    locked++;
+  }
+
+  len = sprintf(page, "Write queue head: %d\nWrite queue tail: %d\n"
+		"Write queue count: %d\nPages in queue: %d (%dK)\n"
+		"Clean Pages: %d\nDirty Pages: %d\nLocked Pages: %d\n"
+		"nrpages: %ld\n",
+		write_queue_head, write_queue_tail, write_queue_cnt,
+		pages, pages << (PAGE_SHIFT-10), clean, dirty, locked,
+		mtd_rawdevice->as.nrpages);
+  if(len <= count)
+    *eof = 1;
+  spin_unlock(&mbd_writeq_lock);
+  MOD_DEC_USE_COUNT;
+  return len;
+}
+#endif
+
+
 /* Cleanup and exit - sync the device and kill of the kernel thread */
 static void __exit cleanup_blkmtd(void)
 {
-  if (mtd_info) {
-    mtd_raw_dev_data_t *rawdevice = mtd_info->priv;
-    // sync the device
-    if (rawdevice) {
-      blkmtd_sync(mtd_info);
+#ifdef BLKMTD_PROC_DEBUG
+  if(blkmtd_proc) {
+    remove_proc_entry("blkmtd_debug", NULL);
+  }
+#endif
+
+  if (mtd_rawdevice) {
+    /* sync the device */
+    if (!mtd_rawdevice->readonly) {
+      blkmtd_sync(&mtd_rawdevice->mtd_info);
       write_task_finish = 1;
       wake_up_interruptible(&thr_wq);
       down(&thread_sem);
-      if(rawdevice->binding != NULL)
-	blkdev_put(rawdevice->binding, BDEV_RAW);
-      filp_close(rawdevice->file, NULL);
-      kfree(mtd_info->priv);
     }
-    if(mtd_info->eraseregions)
-      kfree(mtd_info->eraseregions);
-    del_mtd_device(mtd_info);
-    kfree(mtd_info);
-    mtd_info = NULL;
+    del_mtd_device(&mtd_rawdevice->mtd_info);
+    if(mtd_rawdevice->binding != NULL)
+      blkdev_put(mtd_rawdevice->binding, BDEV_RAW);
+
+    if(mtd_rawdevice->mtd_info.eraseregions)
+      kfree(mtd_rawdevice->mtd_info.eraseregions);
+    if(mtd_rawdevice->mtd_info.name)
+      kfree(mtd_rawdevice->mtd_info.name);
+
+    kfree(mtd_rawdevice);
+  }
+  if(write_queue)
+    kfree(write_queue);
+
+  if(erase_page) {
+    UnlockPage(erase_page);
+    __free_pages(erase_page, 0);
   }
   printk("blkmtd: unloaded for %s\n", device);
 }
 
 extern struct module __this_module;
 
+#ifndef MODULE
+
+/* Handle kernel boot params */
+
+
+static int __init param_blkmtd_device(char *str)
+{
+  device = str;
+  return 1;
+}
+
+
+static int __init param_blkmtd_erasesz(char *str)
+{
+  erasesz = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_ro(char *str)
+{
+  ro = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_bs(char *str)
+{
+  bs = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+
+static int __init param_blkmtd_count(char *str)
+{
+  count = simple_strtol(str, NULL, 0);
+  return 1;
+}
+
+__setup("blkmtd_device=", param_blkmtd_device);
+__setup("blkmtd_erasesz=", param_blkmtd_erasesz);
+__setup("blkmtd_ro=", param_blkmtd_ro);
+__setup("blkmtd_bs=", param_blkmtd_bs);
+__setup("blkmtd_count=", param_blkmtd_count);
+
+#endif
+
+
 /* for a given size and initial erase size, calculate the number and size of each
    erase region */
 static int __init calc_erase_regions(struct mtd_erase_region_info *info, size_t erase_size, size_t total_size)
@@ -842,12 +1051,16 @@
 }
 
 
+extern kdev_t name_to_kdev_t(char *line) __init;
+
 /* Startup */
 static int __init init_blkmtd(void)
 {
+#ifdef MODULE
   struct file *file = NULL;
   struct inode *inode;
-  mtd_raw_dev_data_t *rawdevice = NULL;
+#endif
+
   int maj, min;
   int i, blocksize, blocksize_bits;
   loff_t size = 0;
@@ -856,15 +1069,12 @@
   kdev_t rdev;
   int err;
   int mode;
-  int totalsize = 0, total_sectors = 0;
   int regions;
 
-  mtd_info = NULL;
-
-  // Check args
+  /* Check args */
   if(device == 0) {
     printk("blkmtd: error, missing `device' name\n");
-    return 1;
+    return -EINVAL;
   }
 
   if(ro)
@@ -873,12 +1083,25 @@
   if(erasesz)
     erase_size = erasesz;
 
-  DEBUG(1, "blkmtd: got device = `%s' erase size = %dK readonly = %s\n", device, erase_size, readonly ? "yes" : "no");
-  // Get a handle on the device
+  if(wqs) {
+    if(wqs < 16) 
+      wqs = 16;
+    if(wqs > 4*WRITE_QUEUE_SZ)
+      wqs = 4*WRITE_QUEUE_SZ;
+    write_queue_sz = wqs;
+  }
+
+  DEBUG(1, "blkmtd: device = `%s' erase size = %dK readonly = %s queue size = %d\n",
+	device, erase_size, readonly ? "yes" : "no", write_queue_sz);
+  /* Get a handle on the device */
   mode = (readonly) ? O_RDONLY : O_RDWR;
+
+#ifdef MODULE
+
   file = filp_open(device, mode, 0);
   if(IS_ERR(file)) {
-    DEBUG(2, "blkmtd: open_namei returned %ld\n", PTR_ERR(file));
+    printk("blkmtd: error, cant open device %s\n", device);
+    DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file));
     return 1;
   }
   
@@ -891,11 +1114,19 @@
     return 1;
   }
   rdev = inode->i_rdev;
-  //filp_close(file, NULL);
-  DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
-	 MAJOR(rdev), MINOR(rdev));
+  filp_close(file, NULL);
+#else
+  rdev = name_to_kdev_t(device);
+#endif
+
   maj = MAJOR(rdev);
   min = MINOR(rdev);
+  DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", maj, min);
+
+  if(!rdev) {
+    printk("blkmtd: bad block device: `%s'\n", device);
+    return 1;
+  }
 
   if(maj == MTD_BLOCK_MAJOR) {
     printk("blkmtd: attempting to use an MTD device as a block device\n");
@@ -927,116 +1158,159 @@
       size = ((loff_t) blk_size[maj][min] << BLOCK_SIZE_BITS) >> blocksize_bits;
     }
   }
-  total_sectors = size;
   size *= blocksize;
-  totalsize = size;
   DEBUG(1, "blkmtd: size = %ld\n", (long int)size);
 
   if(size == 0) {
     printk("blkmtd: cant determine size\n");
     return 1;
   }
-  rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL);
-  if(rawdevice == NULL) {
+
+  mtd_rawdevice = (mtd_raw_dev_data_t *)kmalloc(sizeof(mtd_raw_dev_data_t), GFP_KERNEL);
+  if(mtd_rawdevice == NULL) {
     err = -ENOMEM;
     goto init_err;
   }
-  memset(rawdevice, 0, sizeof(mtd_raw_dev_data_t));
-  // get the block device
-  rawdevice->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
-  err = blkdev_get(rawdevice->binding, mode, 0, BDEV_RAW);
+  memset(mtd_rawdevice, 0, sizeof(mtd_raw_dev_data_t));
+  /* get the block device */
+  mtd_rawdevice->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
+  err = blkdev_get(mtd_rawdevice->binding, mode, 0, BDEV_RAW);
   if (err) {
     goto init_err;
   }
-  rawdevice->totalsize = totalsize;
-  rawdevice->total_sectors = total_sectors;
-  rawdevice->sector_size = blocksize;
-  rawdevice->sector_bits = blocksize_bits;
-  rawdevice->readonly = readonly;
-
-  DEBUG(2, "sector_size = %d, sector_bits = %d\n", rawdevice->sector_size, rawdevice->sector_bits);
-
-  mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
-  if (mtd_info == NULL) {
-    err = -ENOMEM;
+  mtd_rawdevice->totalsize = size;
+  mtd_rawdevice->sector_size = blocksize;
+  mtd_rawdevice->sector_bits = blocksize_bits;
+  mtd_rawdevice->readonly = readonly;
+
+  /* See if device ends on page boundary */
+  if(size % PAGE_SIZE) {
+    mtd_rawdevice->partial_last_page = size >> PAGE_SHIFT;
+    mtd_rawdevice->last_page_sectors = (size & (PAGE_SIZE-1)) >> blocksize_bits;
+  }
+
+  DEBUG(2, "sector_size = %d, sector_bits = %d, partial_last_page = %d last_page_sectors = %d\n", 
+	mtd_rawdevice->sector_size, mtd_rawdevice->sector_bits,
+	mtd_rawdevice->partial_last_page, mtd_rawdevice->last_page_sectors);
+
+  /* Setup the MTD structure */
+  /* make the name contain the block device in */
+  mtd_rawdevice->mtd_info.name = kmalloc(9 + strlen(device), GFP_KERNEL);
+  if(mtd_rawdevice->mtd_info.name == NULL)
     goto init_err;
-  }
-  memset(mtd_info, 0, sizeof(*mtd_info));
 
-  // Setup the MTD structure
-  mtd_info->name = "blkmtd block device";
+  sprintf(mtd_rawdevice->mtd_info.name, "blkmtd: %s", device);
   if(readonly) {
-    mtd_info->type = MTD_ROM;
-    mtd_info->flags = MTD_CAP_ROM;
-    mtd_info->erasesize = erase_size << 10;
+    mtd_rawdevice->mtd_info.type = MTD_ROM;
+    mtd_rawdevice->mtd_info.flags = MTD_CAP_ROM;
+    mtd_rawdevice->mtd_info.erasesize = erase_size << 10;
   } else {
-    mtd_info->type = MTD_RAM;
-    mtd_info->flags = MTD_CAP_RAM;
-    mtd_info->erasesize = erase_size << 10;
-  }
-  mtd_info->size = size;
-  mtd_info->erase = blkmtd_erase;
-  mtd_info->read = blkmtd_read;
-  mtd_info->write = blkmtd_write;
-  mtd_info->sync = blkmtd_sync;
-  mtd_info->point = 0;
-  mtd_info->unpoint = 0;
+    mtd_rawdevice->mtd_info.type = MTD_RAM;
+    mtd_rawdevice->mtd_info.flags = MTD_CAP_RAM;
+    mtd_rawdevice->mtd_info.erasesize = erase_size << 10;
+  }
+  mtd_rawdevice->mtd_info.size = size;
+  mtd_rawdevice->mtd_info.erase = blkmtd_erase;
+  mtd_rawdevice->mtd_info.read = blkmtd_read;
+  mtd_rawdevice->mtd_info.write = blkmtd_write;
+  mtd_rawdevice->mtd_info.sync = blkmtd_sync;
+  mtd_rawdevice->mtd_info.point = 0;
+  mtd_rawdevice->mtd_info.unpoint = 0;
 
-  mtd_info->priv = rawdevice;
+  mtd_rawdevice->mtd_info.priv = mtd_rawdevice;
   regions = calc_erase_regions(NULL, erase_size << 10, size);
   DEBUG(1, "blkmtd: init: found %d erase regions\n", regions);
-  mtd_info->eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
-  if(mtd_info->eraseregions == NULL) {
+  mtd_rawdevice->mtd_info.eraseregions = kmalloc(regions * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+  if(mtd_rawdevice->mtd_info.eraseregions == NULL) {
+    err = -ENOMEM;
+    goto init_err;
   }
-  mtd_info->numeraseregions = regions;
-  calc_erase_regions(mtd_info->eraseregions, erase_size << 10, size);
+  mtd_rawdevice->mtd_info.numeraseregions = regions;
+  calc_erase_regions(mtd_rawdevice->mtd_info.eraseregions, erase_size << 10, size);
 
   /* setup the page cache info */
-  INIT_LIST_HEAD(&rawdevice->as.clean_pages);
-  INIT_LIST_HEAD(&rawdevice->as.dirty_pages);
-  INIT_LIST_HEAD(&rawdevice->as.locked_pages);
-  rawdevice->as.nrpages = 0;
-  rawdevice->as.a_ops = &blkmtd_aops;
-  rawdevice->as.host = inode;
-  rawdevice->as.i_mmap = NULL;
-  rawdevice->as.i_mmap_shared = NULL;
-  spin_lock_init(&rawdevice->as.i_shared_lock);
-  rawdevice->as.gfp_mask = GFP_KERNEL;
-  rawdevice->file = file;
-
-  file->private_data = rawdevice;
+  
+  mtd_rawdevice->as.nrpages = 0;
+  INIT_LIST_HEAD(&mtd_rawdevice->as.clean_pages);
+  INIT_LIST_HEAD(&mtd_rawdevice->as.dirty_pages);
+  INIT_LIST_HEAD(&mtd_rawdevice->as.locked_pages);
+  mtd_rawdevice->as.host = NULL;
+  spin_lock_init(&(mtd_rawdevice->as.i_shared_lock));
+
+  mtd_rawdevice->as.a_ops = &blkmtd_aops;
+  mtd_rawdevice->as.i_mmap = NULL;
+  mtd_rawdevice->as.i_mmap_shared = NULL;
+  mtd_rawdevice->as.gfp_mask = GFP_KERNEL;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-   mtd_info->module = THIS_MODULE;			
+  mtd_rawdevice->mtd_info.module = THIS_MODULE;			
 #endif
-   if (add_mtd_device(mtd_info)) {
-     err = -EIO;
-     goto init_err;
-   }
-   init_waitqueue_head(&thr_wq);
-   init_waitqueue_head(&mtbd_sync_wq);
-   DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task);
-   DEBUG(2, "blkmtd: init: starting kernel task\n");
-   kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
-   DEBUG(2, "blkmtd: init: started\n");
-   printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n", VERSION, device, erase_size, (readonly) ? "(read-only)" : "");
-   return 0;
+  if (add_mtd_device(&mtd_rawdevice->mtd_info)) {
+    err = -EIO;
+    goto init_err;
+  }
 
+  if(!mtd_rawdevice->readonly) {
+    /* Allocate the write queue */
+    write_queue = kmalloc(write_queue_sz * sizeof(mtdblkdev_write_queue_t), GFP_KERNEL);
+    if(!write_queue) {
+      err = -ENOMEM;
+      goto init_err;
+    }
+    /* Set up the erase page */
+    erase_page = alloc_pages(GFP_KERNEL, 0);
+    if(erase_page == NULL) {
+      err = -ENOMEM;
+      goto init_err;
+    }
+    memset(page_address(erase_page), 0xff, PAGE_SIZE);
+    lock_page(erase_page);
+
+    init_waitqueue_head(&thr_wq);
+    init_waitqueue_head(&mtbd_sync_wq);
+    DEBUG(3, "blkmtd: init: kernel task @ %p\n", write_queue_task);
+    DEBUG(2, "blkmtd: init: starting kernel task\n");
+    kernel_thread(write_queue_task, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+    DEBUG(2, "blkmtd: init: started\n");
+    printk("blkmtd loaded: version = %s using %s erase_size = %dK %s\n",
+	   VERSION, device, erase_size, (readonly) ? "(read-only)" : "");
+  }
+
+#ifdef BLKMTD_PROC_DEBUG
+  /* create proc entry */
+  DEBUG(2, "Creating /proc/blkmtd_debug\n");
+  blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444,
+				       NULL, blkmtd_proc_read, NULL);
+  if(blkmtd_proc == NULL) {
+    printk("Cant create /proc/blkmtd_debug\n");
+  } else {
+    blkmtd_proc->owner = THIS_MODULE;
+  }
+#endif
+  
+  /* Everything is ok if we got here */
+  return 0;
+  
  init_err:
-   if(!rawdevice) {
-     if(rawdevice->binding) 
-       blkdev_put(rawdevice->binding, BDEV_RAW);
 
-     kfree(rawdevice);
-     rawdevice = NULL;
-   }
-   if(mtd_info) {
-     if(mtd_info->eraseregions)
-       kfree(mtd_info->eraseregions);
-     kfree(mtd_info);
-     mtd_info = NULL;
-   }
-   return err;
+  if(mtd_rawdevice) {
+    if(mtd_rawdevice->mtd_info.eraseregions)
+      kfree(mtd_rawdevice->mtd_info.eraseregions);
+    if(mtd_rawdevice->mtd_info.name)
+      kfree(mtd_rawdevice->mtd_info.name);
+    if(mtd_rawdevice->binding) 
+      blkdev_put(mtd_rawdevice->binding, BDEV_RAW);
+    kfree(mtd_rawdevice);
+  }
+
+  if(write_queue) {
+    kfree(write_queue);
+    write_queue = NULL;
+  }
+
+  if(erase_page) 
+    __free_pages(erase_page, 0);
+  return err;
 }
 
 module_init(init_blkmtd);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c
--- linux.orig/drivers/mtd/maps/elan-104nc.c	Thu Oct  4 22:14:59 2001
+++ linux/drivers/mtd/maps/elan-104nc.c	Wed Feb 13 17:37:40 2002
@@ -213,7 +213,7 @@
 /* MTD device for all of the flash. */
 static struct mtd_info *all_mtd;
 
-static void __exit cleanup_elan_104nc(void)
+static void cleanup_elan_104nc(void)
 {
 	if( all_mtd ) {
 		del_mtd_partitions( all_mtd );
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c
--- linux.orig/drivers/mtd/maps/sbc_gxx.c	Thu Oct  4 22:14:59 2001
+++ linux/drivers/mtd/maps/sbc_gxx.c	Wed Feb 13 17:37:40 2002
@@ -221,7 +221,7 @@
 /* MTD device for all of the flash. */
 static struct mtd_info *all_mtd;
 
-static void __exit cleanup_sbc_gxx(void)
+static void cleanup_sbc_gxx(void)
 {
 	if( all_mtd ) {
 		del_mtd_partitions( all_mtd );
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c
--- linux.orig/drivers/mtd/nftlmount.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/mtd/nftlmount.c	Wed Jan  9 17:33:00 2002
@@ -4,7 +4,7 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: nftlmount.c,v 1.23 2001/09/19 21:42:32 dwmw2 Exp $
+ * $Id: nftlmount.c,v 1.25 2001/11/30 16:46:27 dwmw2 Exp $
  *
  * 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
@@ -39,7 +39,7 @@
 
 #define SECTORSIZE 512
 
-char nftlmountrev[]="$Revision: 1.23 $";
+char nftlmountrev[]="$Revision: 1.25 $";
 
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *	various device information of the NFTL partition and Bad Unit Table. Update
@@ -94,11 +94,11 @@
 			continue;
 		}
 
-#if 1 /* Some people seem to have devices without ECC or erase marks
+#if 0 /* Some people seem to have devices without ECC or erase marks
 	 on the Media Header blocks. There are enough other sanity
 	 checks in here that we can probably do without it.
       */
-		if (le16_to_cpu ((h1.EraseMark | h1.EraseMark1) != ERASE_MARK)) {
+		if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
 			       block * nftl->EraseSize, nftl->mtd->index, 
 			       le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c501.c linux/drivers/net/3c501.c
--- linux.orig/drivers/net/3c501.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c501.c	Fri Dec 21 18:50:47 2001
@@ -87,8 +87,12 @@
  *
  */
 
+#define DRV_NAME	"3c501"
+#define DRV_VERSION	"2001/11/17"
+
+
 static const char version[] =
-    "3c501.c: 2000/02/08 Alan Cox (alan@redhat.com).\n";
+	DRV_NAME ".c: " DRV_VERSION " Alan Cox (alan@redhat.com).\n";
 
 /*
  *	Braindamage remaining:
@@ -108,7 +112,9 @@
 #include <linux/errno.h>
 #include <linux/config.h>	/* for CONFIG_IP_MULTICAST */
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 
@@ -139,12 +145,14 @@
 static int  el1_close(struct net_device *dev);
 static struct net_device_stats *el1_get_stats(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 #define EL1_IO_EXTENT	16
 
 #ifndef EL_DEBUG
 #define EL_DEBUG  0	/* use 0 for production, 1 for devel., >2 for debug */
 #endif			/* Anything above 5 is wordy death! */
+#define debug el_debug
 static int el_debug = EL_DEBUG;
 
 /*
@@ -377,6 +385,7 @@
 	dev->stop = &el1_close;
 	dev->get_stats = &el1_get_stats;
 	dev->set_multicast_list = &set_multicast_list;
+	dev->do_ioctl = netdev_ioctl;
 
 	/*
 	 *	Setup the generic properties
@@ -915,6 +924,86 @@
 	}
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 
 static struct net_device dev_3c501 = {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c503.c linux/drivers/net/3c503.c
--- linux.orig/drivers/net/3c503.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c503.c	Fri Dec 21 18:50:47 2001
@@ -29,11 +29,17 @@
     Paul Gortmaker	: add support for the 2nd 8kB of RAM on 16 bit cards.
     Paul Gortmaker	: multiple card support for module users.
     rjohnson@analogic.com : Fix up PIO interface for efficient operation.
+    Jeff Garzik		: ethtool support
 
 */
 
+#define DRV_NAME	"3c503"
+#define DRV_VERSION	"1.10a"
+#define DRV_RELDATE	"11/17/2001"
+
+
 static const char version[] =
-    "3c503.c:v1.10 9/23/93  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+    DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "  Donald Becker (becker@scyld.com)\n";
 
 #include <linux/module.h>
 
@@ -45,7 +51,9 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/init.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/byteorder.h>
@@ -74,6 +82,7 @@
 			   int ring_offset);
 static void el2_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
 			 int ring_page);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* This routine probes for a memory-mapped 3c503 board by looking for
@@ -301,6 +310,7 @@
 
     dev->open = &el2_open;
     dev->stop = &el2_close;
+    dev->do_ioctl = &netdev_ioctl;
 
     if (dev->mem_start)
 	printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
@@ -607,6 +617,71 @@
     outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
     return;
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 #ifdef MODULE
 #define MAX_EL2_CARDS	4	/* Max number of EL2 cards per module */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c505.c linux/drivers/net/3c505.c
--- linux.orig/drivers/net/3c505.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c505.c	Fri Dec 21 18:50:47 2001
@@ -35,8 +35,13 @@
  *                      Philip Blundell <Philip.Blundell@pobox.com>
  *              Multicard/soft configurable dma channel/rev 2 hardware support
  *                      by Christopher Collins <ccollins@pcug.org.au>
+ *		Ethtool support (jgarzik), 11/17/2001
  */
 
+#define DRV_NAME	"3c505"
+#define DRV_VERSION	"1.10a"
+
+
 /* Theory of operation:
  *
  * The 3c505 is quite an intelligent board.  All communication with it is done
@@ -103,6 +108,9 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -148,10 +156,11 @@
  *********************************************************/
 
 #ifdef ELP_DEBUG
-static const int elp_debug = ELP_DEBUG;
+static int elp_debug = ELP_DEBUG;
 #else
-static const int elp_debug;
+static int elp_debug;
 #endif
+#define debug elp_debug
 
 /*
  *  0 = no messages (well, some)
@@ -1260,6 +1269,87 @@
 	}
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 /******************************************************
  *
  * initialise Etherlink Plus board
@@ -1280,6 +1370,7 @@
 	dev->tx_timeout = elp_timeout;			/* local */
 	dev->watchdog_timeo = 10*HZ;
 	dev->set_multicast_list = elp_set_mc_list;	/* local */
+	dev->do_ioctl = netdev_ioctl;			/* local */
 
 	/* Setup the generic properties */
 	ether_setup(dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c507.c linux/drivers/net/3c507.c
--- linux.orig/drivers/net/3c507.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c507.c	Fri Dec 21 18:50:47 2001
@@ -25,8 +25,12 @@
 	The statistics need to be updated correctly.
 */
 
+#define DRV_NAME		"3c507"
+#define DRV_VERSION		"1.10a"
+#define DRV_RELDATE		"11/17/2001"
+
 static const char version[] =
-	"3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+	DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Donald Becker (becker@scyld.com)\n";
 
 
 #include <linux/module.h>
@@ -52,6 +56,9 @@
 #include <linux/in.h>
 #include <linux/string.h>
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -70,6 +77,8 @@
 #define NET_DEBUG 1
 #endif
 static unsigned int net_debug = NET_DEBUG;
+#define debug net_debug
+
 
 /* A zero-terminated list of common I/O addresses to be probed. */
 static unsigned int netcard_portlist[] __initdata =
@@ -296,6 +305,7 @@
 
 static void hardware_send_packet(struct net_device *dev, void *buf, short length);
 static void init_82586_mem(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* Check for a network adaptor of this type, and return '0' iff one exists.
@@ -427,6 +437,7 @@
 	dev->get_stats	= el16_get_stats;
 	dev->tx_timeout = el16_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->do_ioctl = netdev_ioctl;
 
 	ether_setup(dev);	/* Generic ethernet behaviour */
 
@@ -864,6 +875,88 @@
 	lp->rx_head = rx_head;
 	lp->rx_tail = rx_tail;
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
+
 #ifdef MODULE
 static struct net_device dev_3c507;
 static int io = 0x300;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c509.c linux/drivers/net/3c509.c
--- linux.orig/drivers/net/3c509.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c509.c	Mon Jan 14 17:27:24 2002
@@ -43,8 +43,14 @@
 		v1.18 12Mar2001 Andrew Morton <andrewm@uow.edu.au>
 			- Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
 			- Reviewed against 1.18 from scyld.com
+		v1.18a 17Nov2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+			- ethtool support
 */
 
+#define DRV_NAME	"3c509"
+#define DRV_VERSION	"1.18a"
+#define DRV_RELDATE	"17Nov2001"
+
 /* A few values that may be tweaked. */
 
 /* Time in jiffies before concluding the transmitter is hung. */
@@ -70,12 +76,14 @@
 #include <linux/skbuff.h>
 #include <linux/delay.h>	/* for udelay() */
 #include <linux/spinlock.h>
+#include <linux/ethtool.h>
 
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 
-static char versionA[] __initdata = "3c509.c:1.18 12Mar2001 becker@scyld.com\n";
+static char versionA[] __initdata = DRV_NAME ".c:" DRV_VERSION " " DRV_RELDATE "becker@scyld.com\n";
 static char versionB[] __initdata = "http://www.scyld.com/network/3c509.html\n";
 
 #ifdef EL3_DEBUG
@@ -84,6 +92,7 @@
 static int el3_debug = 2;
 #endif
 
+
 /* To minimize the size of the driver source I only define operating
    constants if they are used several times.  You'll need the manual
    anyway if you want to understand driver details. */
@@ -158,6 +167,7 @@
 static int el3_close(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
 static void el3_tx_timeout (struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 #ifdef CONFIG_MCA
 struct el3_mca_adapters_struct {
@@ -513,6 +523,7 @@
 	dev->set_multicast_list = &set_multicast_list;
 	dev->tx_timeout = el3_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
+	dev->do_ioctl = netdev_ioctl;
 
 	/* Fill in the generic fields of the device structure. */
 	ether_setup(dev);
@@ -1003,6 +1014,85 @@
 	return 0;
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = el3_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		el3_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 /* Parameters that may be passed into the module. */
 static int debug = -1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c515.c linux/drivers/net/3c515.c
--- linux.orig/drivers/net/3c515.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c515.c	Fri Dec 21 18:50:47 2001
@@ -16,9 +16,17 @@
 	2/2/00- Added support for kernel-level ISAPnP 
 		by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
 	Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
+	
+	11/17/2001 - Added ethtool support (jgarzik)
+
 */
 
-static char *version = "3c515.c:v0.99-sn 2000/02/12 becker@cesdis.gsfc.nasa.gov and others\n";
+#define DRV_NAME		"3c515"
+#define DRV_VERSION		"0.99t"
+#define DRV_RELDATE		"17-Nov-2001"
+
+static char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " becker@scyld.com and others\n";
 
 #define CORKSCREW 1
 
@@ -63,6 +71,9 @@
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -393,6 +404,7 @@
 static void update_stats(int addr, struct net_device *dev);
 static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 
 /* 
@@ -721,6 +733,7 @@
 	dev->stop = &corkscrew_close;
 	dev->get_stats = &corkscrew_get_stats;
 	dev->set_multicast_list = &set_rx_mode;
+	dev->do_ioctl = netdev_ioctl;
 
 	return 0;
 }
@@ -1591,6 +1604,87 @@
 
 	outw(new_mode, ioaddr + EL3_CMD);
 }
+
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "ISA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = corkscrew_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		corkscrew_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 
 #ifdef MODULE
 void cleanup_module(void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c523.c linux/drivers/net/3c523.c
--- linux.orig/drivers/net/3c523.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c523.c	Fri Dec 21 18:50:47 2001
@@ -81,10 +81,15 @@
    added option to disable multicast as is causes problems
        Ganesh Sittampalam <ganesh.sittampalam@magdalen.oxford.ac.uk>
        Stuart Adamson <stuart.adamson@compsoc.net>
+   Nov 2001
+   added support for ethtool (jgarzik)
 	
    $Header: /fsys2/home/chrisb/linux-1.3.59-MCA/drivers/net/RCS/3c523.c,v 1.1 1996/02/05 01:53:46 chrisb Exp chrisb $
  */
 
+#define DRV_NAME		"3c523"
+#define DRV_VERSION		"17-Nov-2001"
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -95,6 +100,9 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/mca.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/processor.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -182,6 +190,7 @@
 #ifdef ELMC_MULTICAST
 static void set_multicast_list(struct net_device *dev);
 #endif
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 /* helper-functions */
 static int init586(struct net_device *dev);
@@ -563,7 +572,8 @@
 #else
 	dev->set_multicast_list = NULL;
 #endif
-
+	dev->do_ioctl = netdev_ioctl;
+	
 	ether_setup(dev);
 
 	/* note that we haven't actually requested the IRQ from the kernel.
@@ -1214,6 +1224,69 @@
 }
 #endif
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 /*************************************************************************/
 
 #ifdef MODULE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/3c527.c linux/drivers/net/3c527.c
--- linux.orig/drivers/net/3c527.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/3c527.c	Fri Dec 21 18:50:47 2001
@@ -16,8 +16,12 @@
  *
  */
 
+#define DRV_NAME		"3c527"
+#define DRV_VERSION		"0.6a"
+#define DRV_RELDATE		"2001/11/17"
+
 static const char *version =
-	"3c527.c:v0.6 2001/03/03 Richard Proctor (rnp@netlink.co.nz)\n";
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " Richard Proctor (rnp@netlink.co.nz)\n";
 
 /**
  * DOC: Traps for the unwary
@@ -90,6 +94,9 @@
 #include <linux/in.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -108,7 +115,7 @@
  * The name of the card. Is used for messages and in the requests for
  * io regions, irqs and dma channels
  */
-static const char* cardname = "3c527";
+static const char* cardname = DRV_NAME;
 
 /* use 0 for production, 1 for verification, >2 for debug */
 #ifndef NET_DEBUG
@@ -213,6 +220,7 @@
 static struct	net_device_stats *mc32_get_stats(struct net_device *dev);
 static void	mc32_set_multicast_list(struct net_device *dev);
 static void	mc32_reset_multicast_list(struct net_device *dev);
+static int	netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 /**
  * mc32_probe 	-	Search for supported boards
@@ -502,7 +510,7 @@
 	dev->set_multicast_list = mc32_set_multicast_list;
 	dev->tx_timeout		= mc32_timeout;
 	dev->watchdog_timeo	= HZ*5;	/* Board does all the work */
-
+	dev->do_ioctl		= netdev_ioctl;
 	
 	lp->xceiver_state = HALTED; 
 	
@@ -1644,6 +1652,86 @@
 	do_mc32_set_multicast_list(dev,1);
 }
 
+/**
+ * netdev_ethtool_ioctl: Handle network interface SIOCETHTOOL ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @useraddr: userspace address to which data is to be read and returned
+ *
+ * Process the various commands of the SIOCETHTOOL interface.
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "MCA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = mc32_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		mc32_debug = edata.data;
+		return 0;
+	}
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
+ * netdev_ioctl: Handle network interface ioctls
+ * @dev: network interface on which out-of-band action is to be performed
+ * @rq: user request data
+ * @cmd: command issued by user
+ *
+ * Process the various out-of-band ioctls passed to this driver.
+ */
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc = 0;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
+}
+ 
 #ifdef MODULE
 
 static struct net_device this_device;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/8139cp.c linux/drivers/net/8139cp.c
--- linux.orig/drivers/net/8139cp.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/8139cp.c	Mon Jan 14 17:27:24 2002
@@ -20,7 +20,6 @@
 	TODO, in rough priority order:
 	* dev->tx_timeout
 	* LinkChg interrupt
-	* ETHTOOL_[GS]SET
 	* Support forcing media type with a module parameter,
 	  like dl2k.c/sundance.c
 	* Implement PCI suspend/resume
@@ -33,18 +32,19 @@
 	* Rx checksumming
 	* Tx checksumming
 	* ETHTOOL_GREGS, ETHTOOL_[GS]WOL,
-	  ETHTOOL_[GS]MSGLVL, ETHTOOL_NWAY_RST
 	* Jumbo frames / dev->change_mtu
 	* Investigate using skb->priority with h/w VLAN priority
 	* Investigate using High Priority Tx Queue with skb->priority
 	* Adjust Rx FIFO threshold and Max Rx DMA burst on Rx FIFO error
 	* Adjust Tx FIFO threshold and Max Tx DMA burst on Tx FIFO error
+        * Implement Tx software interrupt mitigation via
+	          Tx descriptor bit
 
  */
 
 #define DRV_NAME		"8139cp"
-#define DRV_VERSION		"0.0.5"
-#define DRV_RELDATE		"Oct 19, 2001"
+#define DRV_VERSION		"0.0.6"
+#define DRV_RELDATE		"Nov 19, 2001"
 
 
 #include <linux/module.h>
@@ -55,6 +55,7 @@
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 
@@ -76,17 +77,6 @@
 MODULE_PARM (multicast_filter_limit, "i");
 MODULE_PARM_DESC (multicast_filter_limit, "8139cp maximum number of filtered multicast addresses");
 
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
-#if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \
-        || defined(__sparc_) || defined(__ia64__) \
-        || defined(__sh__) || defined(__mips__)
-static int rx_copybreak = 1518;
-#else
-static int rx_copybreak = 100;
-#endif
-MODULE_PARM (rx_copybreak, "i");
-MODULE_PARM_DESC (rx_copybreak, "8139cp Breakpoint at which Rx packets are copied");
-
 #define PFX			DRV_NAME ": "
 
 #define CP_DEF_MSG_ENABLE	(NETIF_MSG_DRV		| \
@@ -104,10 +94,10 @@
 	(((CP)->tx_tail <= (CP)->tx_head) ?			\
 	  (CP)->tx_tail + (CP_TX_RING_SIZE - 1) - (CP)->tx_head :	\
 	  (CP)->tx_tail - (CP)->tx_head - 1)
-#define CP_CHIP_VERSION		0x76
 
 #define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer.*/
 #define RX_OFFSET		2
+#define CP_INTERNAL_PHY		32
 
 /* The following settings are log_2(bytes)-4:  0 == 16 bytes .. 6==1024, 7==end of packet. */
 #define RX_FIFO_THRESH		5	/* Rx buffer level before first PCI xfer.  */
@@ -136,6 +126,11 @@
 	Config3		= 0x59, /* Config3 */
 	Config4		= 0x5A, /* Config4 */
 	MultiIntr	= 0x5C, /* Multiple interrupt select */
+	BasicModeCtrl	= 0x62,	/* MII BMCR */
+	BasicModeStatus	= 0x64, /* MII BMSR */
+	NWayAdvert	= 0x66, /* MII ADVERTISE */
+	NWayLPAR	= 0x68, /* MII LPA */
+	NWayExpansion	= 0x6A, /* MII Expansion */
 	Config5		= 0xD8,	/* Config5 */
 	TxPoll		= 0xD9,	/* Tell chip to check Tx descriptors for work */
 	CpCmd		= 0xE0, /* C+ Command register (C+ mode only) */
@@ -289,6 +284,8 @@
 
 	struct sk_buff		*frag_skb;
 	unsigned		dropping_frag : 1;
+
+	struct mii_if_info	mii_if;
 };
 
 #define cpr8(reg)	readb(cp->regs + (reg))
@@ -333,8 +330,8 @@
 	netif_rx (skb);
 }
 
-static inline void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
-				   u32 status, u32 len)
+static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
+			    u32 status, u32 len)
 {
 	if (netif_msg_rx_err (cp))
 		printk (KERN_DEBUG
@@ -428,8 +425,8 @@
 	while (rx_work--) {
 		u32 status, len;
 		dma_addr_t mapping;
-		struct sk_buff *skb, *copy_skb;
-		unsigned copying_skb, buflen;
+		struct sk_buff *skb, *new_skb;
+		unsigned buflen;
 
 		skb = cp->rx_skb[rx_tail].skb;
 		if (!skb)
@@ -452,43 +449,30 @@
 			goto rx_next;
 		}
 
-		copying_skb = (len <= rx_copybreak);
-
 		if (netif_msg_rx_status(cp))
-			printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d copying? %d\n",
-			       cp->dev->name, rx_tail, status, len,
-			       copying_skb);
-
-		buflen = copying_skb ? len : cp->rx_buf_sz;
-		copy_skb = dev_alloc_skb (buflen + RX_OFFSET);
-		if (!copy_skb) {
+			printk(KERN_DEBUG "%s: rx slot %d status 0x%x len %d\n",
+			       cp->dev->name, rx_tail, status, len);
+
+		buflen = cp->rx_buf_sz + RX_OFFSET;
+		new_skb = dev_alloc_skb (buflen);
+		if (!new_skb) {
 			cp->net_stats.rx_dropped++;
 			goto rx_next;
 		}
 
-		skb_reserve(copy_skb, RX_OFFSET);
-		copy_skb->dev = cp->dev;
-
-		if (!copying_skb) {
-			pci_unmap_single(cp->pdev, mapping,
-					 buflen, PCI_DMA_FROMDEVICE);
-			skb->ip_summed = CHECKSUM_NONE;
-			skb_trim(skb, len);
-
-			mapping =
-			cp->rx_skb[rx_tail].mapping =
-				pci_map_single(cp->pdev, copy_skb->data,
-					       buflen, PCI_DMA_FROMDEVICE);
-			cp->rx_skb[rx_tail].skb = copy_skb;
-			skb_put(copy_skb, buflen);
-		} else {
-			skb_put(copy_skb, len);
-			pci_dma_sync_single(cp->pdev, mapping, len, PCI_DMA_FROMDEVICE);
-			memcpy(copy_skb->data, skb->data, len);
+		skb_reserve(new_skb, RX_OFFSET);
+		new_skb->dev = cp->dev;
 
-			/* We'll reuse the original ring buffer. */
-			skb = copy_skb;
-		}
+		pci_unmap_single(cp->pdev, mapping,
+				 buflen, PCI_DMA_FROMDEVICE);
+		skb->ip_summed = CHECKSUM_NONE;
+		skb_put(skb, len);
+
+		mapping =
+		cp->rx_skb[rx_tail].mapping =
+			pci_map_single(cp->pdev, new_skb->tail,
+				       buflen, PCI_DMA_FROMDEVICE);
+		cp->rx_skb[rx_tail].skb = new_skb;
 
 		cp_rx_skb(cp, skb);
 
@@ -899,10 +883,9 @@
 
 		skb->dev = cp->dev;
 		skb_reserve(skb, RX_OFFSET);
-		skb_put(skb, cp->rx_buf_sz);
 
 		cp->rx_skb[i].mapping = pci_map_single(cp->pdev,
-			skb->data, cp->rx_buf_sz, PCI_DMA_FROMDEVICE);
+			skb->tail, cp->rx_buf_sz, PCI_DMA_FROMDEVICE);
 		cp->rx_skb[i].skb = skb;
 		cp->rx_skb[i].frag = 0;
 
@@ -1025,6 +1008,39 @@
 	return 0;
 }
 
+static char mii_2_8139_map[8] = {
+	BasicModeCtrl,
+	BasicModeStatus,
+	0,
+	0,
+	NWayAdvert,
+	NWayLPAR,
+	NWayExpansion,
+	0
+};
+
+static int mdio_read(struct net_device *dev, int phy_id, int location)
+{
+	struct cp_private *cp = dev->priv;
+
+	return location < 8 && mii_2_8139_map[location] ?
+	       readw(cp->regs + mii_2_8139_map[location]) : 0;
+}
+
+
+static void mdio_write(struct net_device *dev, int phy_id, int location,
+		       int value)
+{
+	struct cp_private *cp = dev->priv;
+
+	if (location == 0) {
+		cpw8(Cfg9346, Cfg9346_Unlock);
+		cpw16(BasicModeCtrl, value);
+		cpw8(Cfg9346, Cfg9346_Lock);
+	} else if (location < 8 && mii_2_8139_map[location])
+		cpw16(mii_2_8139_map[location], value);
+}
+
 static int cp_ethtool_ioctl (struct cp_private *cp, void *useraddr)
 {
 	u32 ethcmd;
@@ -1032,21 +1048,71 @@
 	/* dev_ioctl() in ../../net/core/dev.c has already checked
 	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
 
-	if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+	if (get_user(ethcmd, (u32 *)useraddr))
 		return -EFAULT;
 
 	switch (ethcmd) {
 
-	case ETHTOOL_GDRVINFO:
-		{
-			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
-			strcpy (info.driver, DRV_NAME);
-			strcpy (info.version, DRV_VERSION);
-			strcpy (info.bus_info, cp->pdev->slot_name);
-			if (copy_to_user (useraddr, &info, sizeof (info)))
-				return -EFAULT;
-			return 0;
-		}
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, cp->pdev->slot_name);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&cp->lock);
+		mii_ethtool_gset(&cp->mii_if, &ecmd);
+		spin_unlock_irq(&cp->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&cp->lock);
+		r = mii_ethtool_sset(&cp->mii_if, &ecmd);
+		spin_unlock_irq(&cp->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&cp->mii_if);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&cp->mii_if);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = cp->msg_enable;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		cp->msg_enable = edata.data;
+		return 0;
+	}
 
 	default:
 		break;
@@ -1061,6 +1127,9 @@
 	struct cp_private *cp = dev->priv;
 	int rc = 0;
 
+	if (!netif_running(dev))
+		return -EINVAL;
+
 	switch (cmd) {
 	case SIOCETHTOOL:
 		return cp_ethtool_ioctl(cp, (void *) rq->ifr_data);
@@ -1173,6 +1242,10 @@
 	cp->dev = dev;
 	cp->msg_enable = (debug < 0 ? CP_DEF_MSG_ENABLE : debug);
 	spin_lock_init (&cp->lock);
+	cp->mii_if.dev = dev;
+	cp->mii_if.mdio_read = mdio_read;
+	cp->mii_if.mdio_write = mdio_write;
+	cp->mii_if.phy_id = CP_INTERNAL_PHY;
 
 	rc = pci_enable_device(pdev);
 	if (rc)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/8139too.c linux/drivers/net/8139too.c
--- linux.orig/drivers/net/8139too.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/8139too.c	Wed Feb 13 17:44:34 2002
@@ -3,7 +3,7 @@
 	8139too.c: A RealTek RTL-8139 Fast Ethernet driver for Linux.
 
 	Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
-	Copyright 2000,2001 Jeff Garzik
+	Copyright 2000-2002 Jeff Garzik
 
 	Much code comes from Donald Becker's rtl8139.c driver,
 	versions 1.13 and older.  This driver was originally based
@@ -92,7 +92,7 @@
 */
 
 #define DRV_NAME	"8139too"
-#define DRV_VERSION	"0.9.22"
+#define DRV_VERSION	"0.9.24"
 
 
 #include <linux/config.h>
@@ -159,6 +159,9 @@
    The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
 static int multicast_filter_limit = 32;
 
+/* bitmapped message enable number */
+static int debug = -1;
+
 /* Size of the in-memory receive ring. */
 #define RX_BUF_LEN_IDX	2	/* 0==8K, 1==16K, 2==32K, 3==64K */
 #define RX_BUF_LEN	(8192 << RX_BUF_LEN_IDX)
@@ -212,6 +215,8 @@
 	ADDTRON8139,
 	DFE538TX,
 	DFE690TXD,
+	FE2000VX,
+	ALLIED8139,
 	RTL8129,
 } board_t;
 
@@ -229,6 +234,8 @@
 	{ "Addtron Technolgy 8139 10/100BaseTX", RTL8139_CAPS },
 	{ "D-Link DFE-538TX (RealTek RTL8139)", RTL8139_CAPS },
 	{ "D-Link DFE-690TXD (RealTek RTL8139)", RTL8139_CAPS },
+	{ "AboCom FE2000VX (RealTek RTL8139)", RTL8139_CAPS },
+	{ "Allied Telesyn 8139 CardBus", RTL8139_CAPS },
 	{ "RealTek RTL8129", RTL8129_CAPS },
 };
 
@@ -242,6 +249,8 @@
 	{0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ADDTRON8139 },
 	{0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE538TX },
 	{0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DFE690TXD },
+	{0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, FE2000VX },
+	{0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ALLIED8139 },
 
 #ifdef CONFIG_8139TOO_8129
 	{0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
@@ -253,6 +262,7 @@
 	 */
 	{PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
 	{PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, DFE538TX },
+	{PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, FE2000VX },
 
 	{0,}
 };
@@ -525,6 +535,7 @@
 	unsigned long early_rx;
 	unsigned long tx_buf_mapped;
 	unsigned long tx_timeouts;
+	unsigned long rx_lost_in_ring;
 };
 
 struct rtl8139_private {
@@ -543,12 +554,8 @@
 	dma_addr_t tx_bufs_dma;
 	signed char phys[4];		/* MII device addresses. */
 	char twistie, twist_row, twist_col;	/* Twister tune state. */
-	unsigned int full_duplex:1;	/* Full-duplex operation requested. */
-	unsigned int duplex_lock:1;
 	unsigned int default_port:4;	/* Last dev->if_port value. */
-	unsigned int media2:4;	/* Secondary monitored media port. */
 	unsigned int medialock:1;	/* Don't sense media type. */
-	unsigned int mediasense:1;	/* Media sensing in progress. */
 	spinlock_t lock;
 	chip_t chipset;
 	pid_t thr_pid;
@@ -557,6 +564,7 @@
 	u32 rx_config;
 	struct rtl_extra_stats xstats;
 	int time_to_die;
+	struct mii_if_info mii;
 };
 
 MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>");
@@ -567,6 +575,8 @@
 MODULE_PARM (max_interrupt_work, "i");
 MODULE_PARM (media, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM (full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
+MODULE_PARM (debug, "i");
+MODULE_PARM_DESC (debug, "8139too bitmapped message enable number");
 MODULE_PARM_DESC (multicast_filter_limit, "8139too maximum number of filtered multicast addresses");
 MODULE_PARM_DESC (max_interrupt_work, "8139too maximum events handled per interrupt");
 MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps");
@@ -948,6 +958,9 @@
 	spin_lock_init (&tp->lock);
 	init_waitqueue_head (&tp->thr_wait);
 	init_completion (&tp->thr_exited);
+	tp->mii.dev = dev;
+	tp->mii.mdio_read = mdio_read;
+	tp->mii.mdio_write = mdio_write;
 
 	/* dev is fully set up and ready to use now */
 	DPRINTK("about to register device named %s (%p)...\n", dev->name, dev);
@@ -999,18 +1012,18 @@
 	/* The lower four bits are the media type. */
 	option = (board_idx >= MAX_UNITS) ? 0 : media[board_idx];
 	if (option > 0) {
-		tp->full_duplex = (option & 0x210) ? 1 : 0;
+		tp->mii.full_duplex = (option & 0x210) ? 1 : 0;
 		tp->default_port = option & 0xFF;
 		if (tp->default_port)
 			tp->medialock = 1;
 	}
 	if (board_idx < MAX_UNITS  &&  full_duplex[board_idx] > 0)
-		tp->full_duplex = full_duplex[board_idx];
-	if (tp->full_duplex) {
+		tp->mii.full_duplex = full_duplex[board_idx];
+	if (tp->mii.full_duplex) {
 		printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
 		/* Changing the MII-advertised media because might prevent
 		   re-connection. */
-		tp->duplex_lock = 1;
+		tp->mii.duplex_lock = 1;
 	}
 	if (tp->default_port) {
 		printk(KERN_INFO "  Forcing %dMbps %s-duplex operation.\n",
@@ -1267,7 +1280,7 @@
 
 	}
 
-	tp->full_duplex = tp->duplex_lock;
+	tp->mii.full_duplex = tp->mii.duplex_lock;
 	tp->tx_flag = (TX_FIFO_THRESH << 11) & 0x003f0000;
 	tp->twistie = 1;
 	tp->time_to_die = 0;
@@ -1279,7 +1292,7 @@
 			" GP Pins %2.2x %s-duplex.\n",
 			dev->name, pci_resource_start (tp->pci_dev, 1),
 			dev->irq, RTL_R8 (MediaStatus),
-			tp->full_duplex ? "full" : "half");
+			tp->mii.full_duplex ? "full" : "half");
 
 	tp->thr_pid = kernel_thread (rtl8139_thread, dev, CLONE_FS | CLONE_FILES);
 	if (tp->thr_pid < 0)
@@ -1295,18 +1308,18 @@
 	struct rtl8139_private *tp = dev->priv;
 
 	if (tp->phys[0] >= 0) {
-		u16 mii_reg5 = mdio_read(dev, tp->phys[0], 5);
-		if (mii_reg5 == 0xffff)
+		u16 mii_lpa = mdio_read(dev, tp->phys[0], MII_LPA);
+		if (mii_lpa == 0xffff)
 			;					/* Not there */
-		else if ((mii_reg5 & 0x0100) == 0x0100
-				 || (mii_reg5 & 0x00C0) == 0x0040)
-			tp->full_duplex = 1;
+		else if ((mii_lpa & LPA_100FULL) == LPA_100FULL
+				 || (mii_lpa & 0x00C0) == LPA_10FULL)
+			tp->mii.full_duplex = 1;
 
 		printk (KERN_INFO"%s: Setting %s%s-duplex based on"
 				" auto-negotiated partner ability %4.4x.\n",
-		        dev->name, mii_reg5 == 0 ? "" :
-				(mii_reg5 & 0x0180) ? "100mbps " : "10mbps ",
-			tp->full_duplex ? "full" : "half", mii_reg5);
+		        dev->name, mii_lpa == 0 ? "" :
+				(mii_lpa & 0x0180) ? "100mbps " : "10mbps ",
+			tp->mii.full_duplex ? "full" : "half", mii_lpa);
 	}
 }
 
@@ -1494,30 +1507,30 @@
 				 struct rtl8139_private *tp,
 				 void *ioaddr)
 {
-	int mii_reg5;
+	int mii_lpa;
 
-	mii_reg5 = mdio_read (dev, tp->phys[0], 5);
+	mii_lpa = mdio_read (dev, tp->phys[0], MII_LPA);
 
-	if (!tp->duplex_lock && mii_reg5 != 0xffff) {
-		int duplex = (mii_reg5 & 0x0100)
-		    || (mii_reg5 & 0x01C0) == 0x0040;
-		if (tp->full_duplex != duplex) {
-			tp->full_duplex = duplex;
+	if (!tp->mii.duplex_lock && mii_lpa != 0xffff) {
+		int duplex = (mii_lpa & LPA_100FULL)
+		    || (mii_lpa & 0x01C0) == 0x0040;
+		if (tp->mii.full_duplex != duplex) {
+			tp->mii.full_duplex = duplex;
 
-			if (mii_reg5) {
+			if (mii_lpa) {
 				printk (KERN_INFO
 					"%s: Setting %s-duplex based on MII #%d link"
 					" partner ability of %4.4x.\n",
 					dev->name,
-					tp->full_duplex ? "full" : "half",
-					tp->phys[0], mii_reg5);
+					tp->mii.full_duplex ? "full" : "half",
+					tp->phys[0], mii_lpa);
 			} else {
 				printk(KERN_INFO"%s: media is unconnected, link down, or incompatible connection\n",
 				       dev->name);
 			}
 #if 0
 			RTL_W8 (Cfg9346, Cfg9346_Unlock);
-			RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
+			RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
 			RTL_W8 (Cfg9346, Cfg9346_Lock);
 #endif
 		}
@@ -1751,23 +1764,36 @@
 			    struct rtl8139_private *tp, void *ioaddr)
 {
 	u8 tmp8;
+#ifndef CONFIG_8139_NEW_RX_RESET
 	int tmp_work;
+#endif
 
 	DPRINTK ("%s: Ethernet frame had errors, status %8.8x.\n",
 	         dev->name, rx_status);
-	if (rx_status & RxTooLong) {
-		DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
-			 dev->name, rx_status);
-		/* A.C.: The chip hangs here. */
-	}
 	tp->stats.rx_errors++;
-	if (rx_status & (RxBadSymbol | RxBadAlign))
-		tp->stats.rx_frame_errors++;
-	if (rx_status & (RxRunt | RxTooLong))
-		tp->stats.rx_length_errors++;
-	if (rx_status & RxCRCErr)
-		tp->stats.rx_crc_errors++;
+	if (!(rx_status & RxStatusOK)) {
+		if (rx_status & RxTooLong) {
+			DPRINTK ("%s: Oversized Ethernet frame, status %4.4x!\n",
+			 	dev->name, rx_status);
+			/* A.C.: The chip hangs here. */
+		}
+		if (rx_status & (RxBadSymbol | RxBadAlign))
+			tp->stats.rx_frame_errors++;
+		if (rx_status & (RxRunt | RxTooLong))
+			tp->stats.rx_length_errors++;
+		if (rx_status & RxCRCErr)
+			tp->stats.rx_crc_errors++;
+	} else {
+		tp->xstats.rx_lost_in_ring++;
+	}
 
+#ifdef CONFIG_8139_NEW_RX_RESET
+	tmp8 = RTL_R8 (ChipCmd);
+	RTL_W8 (ChipCmd, tmp8 & ~CmdRxEnb);
+	RTL_W8 (ChipCmd, tmp8);
+	RTL_W32 (RxConfig, tp->rx_config);
+	tp->cur_rx = 0;
+#else
 	/* Reset the receiver, based on RealTek recommendation. (Bug?) */
 
 	/* disable receive */
@@ -1812,6 +1838,7 @@
 
 	/* A.C.: Reset the multicast list. */
 	__set_rx_mode (dev);
+#endif
 }
 
 static void rtl8139_rx_interrupt (struct net_device *dev,
@@ -1946,13 +1973,13 @@
 	    (tp->drv_flags & HAS_LNK_CHNG)) {
 		/* Really link-change on new chips. */
 		int lpar = RTL_R16 (NWayLPAR);
-		int duplex = (lpar & 0x0100) || (lpar & 0x01C0) == 0x0040
-				|| tp->duplex_lock;
-		if (tp->full_duplex != duplex) {
-			tp->full_duplex = duplex;
+		int duplex = (lpar & LPA_100FULL) || (lpar & 0x01C0) == 0x0040
+				|| tp->mii.duplex_lock;
+		if (tp->mii.full_duplex != duplex) {
+			tp->mii.full_duplex = duplex;
 #if 0
 			RTL_W8 (Cfg9346, Cfg9346_Unlock);
-			RTL_W8 (Config1, tp->full_duplex ? 0x60 : 0x20);
+			RTL_W8 (Config1, tp->mii.full_duplex ? 0x60 : 0x20);
 			RTL_W8 (Cfg9346, Cfg9346_Lock);
 #endif
 		}
@@ -2111,48 +2138,6 @@
 }
 
 
-/* Get the ethtool settings.  Assumes that eset points to kernel
-   memory, *eset has been initialized as {ETHTOOL_GSET}, and other
-   threads or interrupts aren't messing with the 8139.  */
-static void netdev_get_eset (struct net_device *dev, struct ethtool_cmd *eset)
-{
-	struct rtl8139_private *np = dev->priv;
-	void *ioaddr = np->mmio_addr;
-	u16 advert;
-
-	eset->supported = SUPPORTED_10baseT_Half
-		      	| SUPPORTED_10baseT_Full
-		      	| SUPPORTED_100baseT_Half
-		      	| SUPPORTED_100baseT_Full
-		      	| SUPPORTED_Autoneg
-		      	| SUPPORTED_TP;
-
-	eset->advertising = ADVERTISED_TP | ADVERTISED_Autoneg;
-	advert = mdio_read (dev, np->phys[0], 4);
-	if (advert & 0x0020)
-		eset->advertising |= ADVERTISED_10baseT_Half;
-	if (advert & 0x0040)
-		eset->advertising |= ADVERTISED_10baseT_Full;
-	if (advert & 0x0080)
-		eset->advertising |= ADVERTISED_100baseT_Half;
-	if (advert & 0x0100)
-		eset->advertising |= ADVERTISED_100baseT_Full;
-
-	eset->speed = (RTL_R8 (MediaStatus) & 0x08) ? 10 : 100;
-	/* (KON)FIXME: np->full_duplex is set or reset by the thread,
-	   which means this always shows half duplex if the interface
-	   isn't up yet, even if it has already autonegotiated.  */
-	eset->duplex = np->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
-	eset->port = PORT_TP;
-	/* (KON)FIXME: Is np->phys[0] correct?  starfire.c uses that.  */
-	eset->phy_address = np->phys[0];
-	eset->transceiver = XCVR_INTERNAL;
-	eset->autoneg = (mdio_read (dev, np->phys[0], 0) & 0x1000) != 0;
-	eset->maxtxpkt = 1;
-	eset->maxrxpkt = 1;
-}
-
-
 /* Get the ethtool Wake-on-LAN settings.  Assumes that wol points to
    kernel memory, *wol has been initialized as {ETHTOOL_GWOL}, and
    other threads or interrupts aren't messing with the 8139.  */
@@ -2227,7 +2212,6 @@
 	return 0;
 }
 
-
 static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
 {
 	struct rtl8139_private *np = dev->priv;
@@ -2236,33 +2220,71 @@
 	/* dev_ioctl() in ../../net/core/dev.c has already checked
 	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
 
-	if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+	if (get_user(ethcmd, (u32 *)useraddr))
 		return -EFAULT;
 
 	switch (ethcmd) {
-	case ETHTOOL_GSET:
-		{
-			struct ethtool_cmd eset = { ETHTOOL_GSET };
-			spin_lock_irq (&np->lock);
-			netdev_get_eset (dev, &eset);
-			spin_unlock_irq (&np->lock);
-			if (copy_to_user (useraddr, &eset, sizeof (eset)))
-				return -EFAULT;
-			return 0;
-		}
 
-	/* TODO: ETHTOOL_SSET */
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, np->pci_dev->slot_name);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
 
-	case ETHTOOL_GDRVINFO:
-		{
-			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
-			strcpy (info.driver, DRV_NAME);
-			strcpy (info.version, DRV_VERSION);
-			strcpy (info.bus_info, np->pci_dev->slot_name);
-			if (copy_to_user (useraddr, &info, sizeof (info)))
-				return -EFAULT;
-			return 0;
-		}
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&np->mii);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&np->mii);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
 
 	case ETHTOOL_GWOL:
 		{
@@ -2302,6 +2324,9 @@
 	int rc = 0;
 	int phy = tp->phys[0] & 0x3f;
 
+	if (!netif_running(dev))
+		return -EINVAL;
+
 	if (cmd != SIOCETHTOOL) {
 		/* With SIOCETHTOOL, this would corrupt the pointer.  */
 		data->phy_id &= 0x1f;
@@ -2336,9 +2361,9 @@
 				/* Check for autonegotiation on or reset. */
 				tp->medialock = (value & 0x9000) ? 0 : 1;
 				if (tp->medialock)
-					tp->full_duplex = (value & 0x0100) ? 1 : 0;
+					tp->mii.full_duplex = (value & 0x0100) ? 1 : 0;
 				break;
-			case 4: /* tp->advertising = value; */ break;
+			case 4: tp->mii.advertising = value; break;
 			}
 		}
 		mdio_write(dev, data->phy_id, data->reg_num, data->val_in);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/Config.in linux/drivers/net/Config.in
--- linux.orig/drivers/net/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/Config.in	Wed Feb 13 16:34:07 2002
@@ -34,11 +34,15 @@
    if [ "$CONFIG_PPC" = "y" ]; then
       dep_tristate '  MACE (Power Mac ethernet) support' CONFIG_MACE $CONFIG_ALL_PPC
       if [ "$CONFIG_MACE" != "n" ]; then
-        bool '    Use AAUI port instead of TP by default' CONFIG_MACE_AAUI_PORT
+	 bool '    Use AAUI port instead of TP by default' CONFIG_MACE_AAUI_PORT
       fi
       dep_tristate '  BMAC (G3 ethernet) support' CONFIG_BMAC $CONFIG_ALL_PPC
       dep_tristate '  GMAC (G4/iBook ethernet) support' CONFIG_GMAC $CONFIG_ALL_PPC
-      tristate '  National DP83902AV (Oak ethernet) support' CONFIG_OAKNET
+      if [ "$CONFIG_4xx" = "y" ]; then
+         if [ "$CONFIG_STB03xxx" = "y" -o "$CONFIG_403GCX" = "y" ]; then
+	    tristate '  National DP83902AV (Oak ethernet) support' CONFIG_OAKNET
+	 fi
+      fi
    fi
    if [ "$CONFIG_ZORRO" = "y" ]; then
       tristate '  Ariadne support' CONFIG_ARIADNE
@@ -73,7 +77,6 @@
    fi
    dep_tristate '  Sun BigMAC 10/100baseT support (EXPERIMENTAL)' CONFIG_SUNBMAC $CONFIG_SBUS $CONFIG_EXPERIMENTAL
    dep_tristate '  Sun QuadEthernet support' CONFIG_SUNQE $CONFIG_SBUS
-   dep_tristate '  Sun LANCE support' CONFIG_SUNLANCE $CONFIG_SBUS
    dep_tristate '  Sun GEM support' CONFIG_SUNGEM $CONFIG_PCI
    bool '  3COM cards' CONFIG_NET_VENDOR_3COM
    if [ "$CONFIG_NET_VENDOR_3COM" = "y" ]; then
@@ -171,6 +174,9 @@
       dep_tristate '    Mylex EISA LNE390A/B support (EXPERIMENTAL)' CONFIG_LNE390 $CONFIG_EISA $CONFIG_EXPERIMENTAL
       dep_tristate '    Myson MTD-8xx PCI Ethernet support' CONFIG_FEALNX $CONFIG_PCI
       dep_tristate '    National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI $CONFIG_PCI
+      if [ "$CONFIG_NATSEMI" = "y" -o "$CONFIG_NATSEMI" = "m" ]; then
+        bool       '      NatSemi workaround for high errors' CONFIG_NATSEMI_CABLE_MAGIC
+      fi
       dep_tristate '    PCI NE2000 and clones support (see help)' CONFIG_NE2K_PCI $CONFIG_PCI
       dep_tristate '    Novell/Eagle/Microdyne NE3210 EISA support (EXPERIMENTAL)' CONFIG_NE3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL
       dep_tristate '    Racal-Interlan EISA ES3210 support (EXPERIMENTAL)' CONFIG_ES3210 $CONFIG_EISA $CONFIG_EXPERIMENTAL
@@ -179,6 +185,7 @@
       dep_mbool '      Use PIO instead of MMIO' CONFIG_8139TOO_PIO $CONFIG_8139TOO
       dep_mbool '      Support for automatic channel equalization (EXPERIMENTAL)' CONFIG_8139TOO_TUNE_TWISTER $CONFIG_8139TOO $CONFIG_EXPERIMENTAL
       dep_mbool '      Support for older RTL-8129/8130 boards' CONFIG_8139TOO_8129 $CONFIG_8139TOO
+      dep_mbool '      Experiment for better RX reset (EXPERIMENTAL)' CONFIG_8139_NEW_RX_RESET $CONFIG_8139TOO $CONFIG_EXPERIMENTAL
       dep_tristate '    SiS 900/7016 PCI Fast Ethernet Adapter support' CONFIG_SIS900 $CONFIG_PCI
       dep_tristate '    SMC EtherPower II' CONFIG_EPIC100 $CONFIG_PCI
       dep_tristate '    Sundance Alta support' CONFIG_SUNDANCE $CONFIG_PCI
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/Makefile linux/drivers/net/Makefile
--- linux.orig/drivers/net/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/Makefile	Mon Jan 14 17:27:24 2002
@@ -17,7 +17,7 @@
 
 export-objs     :=	8390.o arlan.o aironet4500_core.o aironet4500_card.o \
 			ppp_async.o ppp_generic.o slhc.o pppox.o auto_irq.o \
-			net_init.o
+			net_init.o mii.o
 list-multi	:=	rcpci.o
 rcpci-objs	:=	rcpci45.o rclanmtl.o
 
@@ -63,13 +63,13 @@
 
 obj-$(CONFIG_DGRS) += dgrs.o
 obj-$(CONFIG_RCPCI) += rcpci.o
-obj-$(CONFIG_VORTEX) += 3c59x.o
+obj-$(CONFIG_VORTEX) += 3c59x.o mii.o
 obj-$(CONFIG_NE2K_PCI) += ne2k-pci.o 8390.o
-obj-$(CONFIG_PCNET32) += pcnet32.o
-obj-$(CONFIG_EEPRO100) += eepro100.o
+obj-$(CONFIG_PCNET32) += pcnet32.o mii.o
+obj-$(CONFIG_EEPRO100) += eepro100.o mii.o
 obj-$(CONFIG_TLAN) += tlan.o
-obj-$(CONFIG_EPIC100) += epic100.o
-obj-$(CONFIG_SIS900) += sis900.o
+obj-$(CONFIG_EPIC100) += epic100.o mii.o
+obj-$(CONFIG_SIS900) += sis900.o mii.o
 obj-$(CONFIG_DM9102) += dmfe.o
 obj-$(CONFIG_YELLOWFIN) += yellowfin.o
 obj-$(CONFIG_ACENIC) += acenic.o
@@ -77,7 +77,7 @@
 obj-$(CONFIG_NATSEMI) += natsemi.o
 obj-$(CONFIG_NS83820) += ns83820.o
 obj-$(CONFIG_STNIC) += stnic.o 8390.o
-obj-$(CONFIG_FEALNX) += fealnx.o
+obj-$(CONFIG_FEALNX) += fealnx.o mii.o
 
 ifeq ($(CONFIG_SK98LIN),y)
 obj-y += sk98lin/sk98lin.o
@@ -87,7 +87,7 @@
   obj-y += skfp/skfp.o
 endif
 
-obj-$(CONFIG_VIA_RHINE) += via-rhine.o
+obj-$(CONFIG_VIA_RHINE) += via-rhine.o mii.o
 obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
 
 #
@@ -100,7 +100,7 @@
 obj-$(CONFIG_AIRONET4500_PROC)	+= aironet4500_proc.o
 obj-$(CONFIG_AIRONET4500_CS)	+= aironet4500_proc.o
 
-obj-$(CONFIG_WINBOND_840) += winbond-840.o
+obj-$(CONFIG_WINBOND_840) += winbond-840.o mii.o
 obj-$(CONFIG_SUNDANCE) += sundance.o
 obj-$(CONFIG_HAMACHI) += hamachi.o
 obj-$(CONFIG_NET) += Space.o setup.o net_init.o loopback.o
@@ -164,8 +164,8 @@
 obj-$(CONFIG_3C515) += 3c515.o
 obj-$(CONFIG_EEXPRESS) += eexpress.o
 obj-$(CONFIG_EEXPRESS_PRO) += eepro.o
-obj-$(CONFIG_8139CP) += 8139cp.o
-obj-$(CONFIG_8139TOO) += 8139too.o
+obj-$(CONFIG_8139CP) += 8139cp.o mii.o
+obj-$(CONFIG_8139TOO) += 8139too.o mii.o
 obj-$(CONFIG_WAVELAN) += wavelan.o
 obj-$(CONFIG_ARLAN) += arlan.o arlan-proc.o
 obj-$(CONFIG_ZNET) += znet.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/acenic.c linux/drivers/net/acenic.c
--- linux.orig/drivers/net/acenic.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/acenic.c	Wed Jan 16 20:56:50 2002
@@ -208,6 +208,8 @@
 	(((u64)(mask) & 0xffffffff00000000) == 0 ? 0 : -EIO)
 #define pci_dma_supported(dev, mask)		\
 	(((u64)(mask) & 0xffffffff00000000) == 0 ? 1 : 0)
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
 
 #elif (LINUX_VERSION_CODE < 0x02040d)
 
@@ -231,6 +233,8 @@
 }
 #define pci_unmap_page(cookie, dma_addr, size, dir)	\
 	pci_unmap_single(cookie, dma_addr, size, dir)
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
 #endif
 
 
@@ -839,9 +843,11 @@
 			struct sk_buff *skb = ap->skb->rx_std_skbuff[i].skb;
 
 			if (skb) {
+				struct ring_info *ringp;
 				dma_addr_t mapping;
 
-				mapping = ap->skb->rx_std_skbuff[i].mapping;
+				ringp = &ap->skb->rx_std_skbuff[i];
+				mapping = pci_unmap_addr(ringp, mapping);
 				pci_unmap_page(ap->pdev, mapping,
 					       ACE_STD_BUFSIZE - (2 + 16),
 					       PCI_DMA_FROMDEVICE);
@@ -856,9 +862,11 @@
 				struct sk_buff *skb = ap->skb->rx_mini_skbuff[i].skb;
 
 				if (skb) {
+					struct ring_info *ringp;
 					dma_addr_t mapping;
 
-					mapping = ap->skb->rx_mini_skbuff[i].mapping;
+					ringp = &ap->skb->rx_mini_skbuff[i];
+					mapping = pci_unmap_addr(ringp,mapping);
 					pci_unmap_page(ap->pdev, mapping,
 						       ACE_MINI_BUFSIZE - (2 + 16),
 						       PCI_DMA_FROMDEVICE);
@@ -872,9 +880,11 @@
 		for (i = 0; i < RX_JUMBO_RING_ENTRIES; i++) {
 			struct sk_buff *skb = ap->skb->rx_jumbo_skbuff[i].skb;
 			if (skb) {
+				struct ring_info *ringp;
 				dma_addr_t mapping;
 
-				mapping = ap->skb->rx_jumbo_skbuff[i].mapping;
+				ringp = &ap->skb->rx_jumbo_skbuff[i];
+				mapping = pci_unmap_addr(ringp, mapping);
 				pci_unmap_page(ap->pdev, mapping,
 					       ACE_JUMBO_BUFSIZE - (2 + 16),
 					       PCI_DMA_FROMDEVICE);
@@ -1832,7 +1842,8 @@
 				       ACE_STD_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_std_skbuff[idx].skb = skb;
-		ap->skb->rx_std_skbuff[idx].mapping = mapping;
+		pci_unmap_addr_set(&ap->skb->rx_std_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_std_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1897,7 +1908,8 @@
 				       ACE_MINI_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_mini_skbuff[idx].skb = skb;
-		ap->skb->rx_mini_skbuff[idx].mapping = mapping;
+		pci_unmap_addr_set(&ap->skb->rx_mini_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_mini_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -1957,7 +1969,8 @@
 				       ACE_JUMBO_BUFSIZE - (2 + 16),
 				       PCI_DMA_FROMDEVICE);
 		ap->skb->rx_jumbo_skbuff[idx].skb = skb;
-		ap->skb->rx_jumbo_skbuff[idx].mapping = mapping;
+		pci_unmap_addr_set(&ap->skb->rx_jumbo_skbuff[idx],
+				   mapping, mapping);
 
 		rd = &ap->rx_jumbo_ring[idx];
 		set_aceaddr(&rd->addr, mapping);
@@ -2163,7 +2176,9 @@
 
 		skb = rip->skb;
 		rip->skb = NULL;
-		pci_unmap_page(ap->pdev, rip->mapping, mapsize,
+		pci_unmap_page(ap->pdev,
+			       pci_unmap_addr(rip, mapping),
+			       mapsize,
 			       PCI_DMA_FROMDEVICE);
 		skb_put(skb, retdesc->size);
 
@@ -2229,12 +2244,13 @@
 
 		info = ap->skb->tx_skbuff + idx;
 		skb = info->skb;
-		mapping = info->mapping;
+		mapping = pci_unmap_addr(info, mapping);
 
 		if (mapping) {
-			pci_unmap_page(ap->pdev, mapping, info->maplen,
+			pci_unmap_page(ap->pdev, mapping,
+				       pci_unmap_len(info, maplen),
 				       PCI_DMA_TODEVICE);
-			info->mapping = 0;
+			pci_unmap_addr_set(info, mapping, 0);
 		}
 
 		if (skb) {
@@ -2518,13 +2534,14 @@
 
 		info = ap->skb->tx_skbuff + i;
 		skb = info->skb;
-		mapping = info->mapping;
+		mapping = pci_unmap_addr(info, mapping);
 
 		if (mapping) {
 			memset(ap->tx_ring + i, 0, sizeof(struct tx_desc));
-			pci_unmap_page(ap->pdev, mapping, info->maplen,
+			pci_unmap_page(ap->pdev, mapping,
+				       pci_unmap_len(info, maplen),
 				       PCI_DMA_TODEVICE);
-			info->mapping = 0;
+			pci_unmap_addr_set(info, mapping, 0);
 		}
 		if (skb) {
 			dev_kfree_skb(skb);
@@ -2545,7 +2562,6 @@
 	return 0;
 }
 
-
 static inline dma_addr_t
 ace_map_tx_skb(struct ace_private *ap, struct sk_buff *skb,
 	       struct sk_buff *tail, u32 idx)
@@ -2559,8 +2575,8 @@
 
 	info = ap->skb->tx_skbuff + idx;
 	info->skb = tail;
-	info->mapping = mapping;
-	info->maplen = skb->len;
+	pci_unmap_addr_set(info, mapping, mapping);
+	pci_unmap_len_set(info, maplen, skb->len);
 	return mapping;
 }
 
@@ -2661,8 +2677,8 @@
 			} else {
 				info->skb = NULL;
 			}
-			info->mapping = mapping;
-			info->maplen = frag->size;
+			pci_unmap_addr_set(info, mapping, mapping);
+			pci_unmap_len_set(info, maplen, frag->size);
 			ace_load_tx_bd(desc, mapping, flagsize);
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/acenic.h linux/drivers/net/acenic.h
--- linux.orig/drivers/net/acenic.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/acenic.h	Fri Dec 21 18:25:01 2001
@@ -585,7 +585,7 @@
 
 struct ring_info {
 	struct sk_buff		*skb;
-	dma_addr_t		mapping;
+	DECLARE_PCI_UNMAP_ADDR(mapping)
 };
 
 
@@ -596,8 +596,8 @@
  */
 struct tx_ring_info {
 	struct sk_buff		*skb;
-	dma_addr_t		mapping;
-	int			maplen;
+	DECLARE_PCI_UNMAP_ADDR(mapping)
+	DECLARE_PCI_UNMAP_LEN(maplen)
 };
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/arcnet/com90io.c linux/drivers/net/arcnet/com90io.c
--- linux.orig/drivers/net/arcnet/com90io.c	Sun Nov  4 17:31:58 2001
+++ linux/drivers/net/arcnet/com90io.c	Wed Feb  6 20:47:54 2002
@@ -241,7 +241,10 @@
 		return -ENODEV;
 	}
 	/* Reserve the I/O region - guaranteed to work by check_region */
-	request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)");
+	if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) {
+		free_irq(dev->irq, dev);
+		return -EBUSY;
+	}
 
 	/* Initialize the rest of the device structure. */
 	dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/au1000_eth.c linux/drivers/net/au1000_eth.c
--- linux.orig/drivers/net/au1000_eth.c	Sun Sep  9 17:43:01 2001
+++ linux/drivers/net/au1000_eth.c	Wed Feb  6 20:27:18 2002
@@ -1150,12 +1150,19 @@
 
 	/* fixme */
 	switch(cmd) { 
-		case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
+		case SIOCGMIIPHY:		/* Get the address of the PHY in use. */
+		case SIOCDEVPRIVATE:		/* binary compat, remove in 2.5 */
 		data[0] = PHY_ADDRESS;
-		case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
+
+		case SIOCGMIIREG:		/* Read the specified MII register. */
+		case SIOCDEVPRIVATE+1:		/* binary compat, remove in 2.5 */
 		//data[3] = mdio_read(ioaddr, data[0], data[1]); 
 		return 0;
-		case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+
+		case SIOCSMIIREG:		/* Write the specified MII register */
+		case SIOCDEVPRIVATE+2:		/* binary compat, remove in 2.5 */
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		//mdio_write(ioaddr, data[0], data[1], data[2]);
 		return 0;
 		default:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/bmac.c linux/drivers/net/bmac.c
--- linux.orig/drivers/net/bmac.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/bmac.c	Wed Dec 26 16:34:02 2001
@@ -22,7 +22,8 @@
 #include <asm/io.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -155,7 +156,7 @@
 static void bmac_start_chip(struct net_device *dev);
 static void bmac_init_chip(struct net_device *dev);
 static void bmac_init_registers(struct net_device *dev);
-static void bmac_reset_chip(struct net_device *dev);
+static void bmac_enable_and_reset_chip(struct net_device *dev);
 static int bmac_set_address(struct net_device *dev, void *addr);
 static void bmac_misc_intr(int irq, void *dev_id, struct pt_regs *regs);
 static void bmac_txdma_intr(int irq, void *dev_id, struct pt_regs *regs);
@@ -229,21 +230,18 @@
 }
 
 static void
-bmac_reset_chip(struct net_device *dev)
+bmac_enable_and_reset_chip(struct net_device *dev)
 {
 	struct bmac_data *bp = (struct bmac_data *) dev->priv;
 	volatile struct dbdma_regs *rd = bp->rx_dma;
 	volatile struct dbdma_regs *td = bp->tx_dma;
 
-	dbdma_reset(rd);
-	dbdma_reset(td);
+	if (rd)
+		dbdma_reset(rd);
+	if (td)
+		dbdma_reset(td);
 
-	feature_set(bp->node, FEATURE_BMac_IO_enable);
-	udelay(10000);
-	feature_set(bp->node, FEATURE_BMac_reset);
-	udelay(10000);
-	feature_clear(bp->node, FEATURE_BMac_reset);
-	udelay(10000);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 1);
 }
 
 #define MIFDELAY	udelay(10)
@@ -522,10 +520,7 @@
 				}
 			}
 		}
-		feature_set(bp->node, FEATURE_BMac_reset);
-		mdelay(10);
-		feature_clear(bp->node, FEATURE_BMac_IO_enable);
-		mdelay(10);
+		pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
 		break;
 	case PBOOK_WAKE:
 		/* see if this is enough */
@@ -1267,7 +1262,7 @@
 	unsigned char *data;
 
 	save_flags(flags); cli();
-	bmac_reset_chip(dev);
+	bmac_enable_and_reset_chip(dev);
 	bmac_init_tx_ring(bp);
 	bmac_init_rx_ring(bp);
 	bmac_init_chip(dev);
@@ -1350,14 +1345,30 @@
 		       bmac->full_name);
 		return;
 	}
+	bp = (struct bmac_data *) dev->priv;
 	SET_MODULE_OWNER(dev);
+	bp->node = bmac;
 
+	if (!request_OF_resource(bmac, 0, " (bmac)")) {
+		printk(KERN_ERR "BMAC: can't request IO resource !\n");
+		goto err_out;
+	}
+	if (!request_OF_resource(bmac, 1, " (bmac tx dma)")) {
+		printk(KERN_ERR "BMAC: can't request TX DMA resource !\n");
+		goto err_out;
+	}
+
+	if (!request_OF_resource(bmac, 2, " (bmac rx dma)")) {
+		printk(KERN_ERR "BMAC: can't request RX DMA resource !\n");
+		goto err_out;
+	}
 	dev->base_addr = (unsigned long)
 		ioremap(bmac->addrs[0].address, bmac->addrs[0].size);
 	if (!dev->base_addr)
 		goto err_out;
 	dev->irq = bmac->intrs[0].line;
 
+	bmac_enable_and_reset_chip(dev);
 	bmwrite(dev, INTDISABLE, DisableAll);
 
 	printk(KERN_INFO "%s: BMAC%s at", dev->name, (is_bmac_plus? "+": ""));
@@ -1369,6 +1380,10 @@
 	XXDEBUG((", base_addr=%#0lx", dev->base_addr));
 	printk("\n");
 
+	/* Enable chip without interrupts for now */
+	bmac_enable_and_reset_chip(dev);
+	bmwrite(dev, INTDISABLE, DisableAll);
+
 	dev->open = bmac_open;
 	dev->stop = bmac_close;
 	dev->hard_start_xmit = bmac_output;
@@ -1380,7 +1395,6 @@
 	if (bmac_verify_checksum(dev) != 0)
 		goto err_out_iounmap;
 
-	bp = (struct bmac_data *) dev->priv;
 	bp->is_bmac_plus = is_bmac_plus;
 	bp->tx_dma = (volatile struct dbdma_regs *)
 		ioremap(bmac->addrs[1].address, bmac->addrs[1].size);
@@ -1399,7 +1413,6 @@
 	bp->queue = (struct sk_buff_head *)(bp->rx_cmds + N_RX_RING + 1);
 	skb_queue_head_init(bp->queue);
 
-	bp->node = bmac;
 	memset((char *) bp->tx_cmds, 0,
 	       (N_TX_RING + N_RX_RING + 2) * sizeof(struct dbdma_cmd));
 	/*     init_timer(&bp->tx_timeout); */
@@ -1421,6 +1434,12 @@
 		goto err_out_irq1;
 	}
 
+	/* Mask chip interrupts and disable chip, will be
+	 * re-enabled on open()
+	 */
+	disable_irq(dev->irq);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
+	
 	bp->next_bmac = bmac_devs;
 	bmac_devs = dev;
 	return;
@@ -1436,6 +1455,12 @@
 err_out_iounmap:
 	iounmap((void *)dev->base_addr);
 err_out:
+	if (bp->node) {
+		release_OF_resource(bp->node, 0);
+		release_OF_resource(bp->node, 1);
+		release_OF_resource(bp->node, 2);
+		pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
+	}
 	unregister_netdev(dev);
 	kfree(dev);
 }
@@ -1447,6 +1472,7 @@
 	/* reset the chip */
 	bp->opened = 1;
 	bmac_reset_and_enable(dev);
+	enable_irq(dev->irq);
 	dev->flags |= IFF_RUNNING;
 	return 0;
 }
@@ -1459,6 +1485,7 @@
 	unsigned short config;
 	int i;
 
+	bp->sleeping = 1;
 	dev->flags &= ~(IFF_UP | IFF_RUNNING);
 
 	/* disable rx and tx */
@@ -1492,6 +1519,8 @@
 	XXDEBUG(("bmac: all bufs freed\n"));
 
 	bp->opened = 0;
+	disable_irq(dev->irq);
+	pmac_call_feature(PMAC_FTR_BMAC_ENABLE, bp->node, 0, 0);
 
 	return 0;
 }
@@ -1561,7 +1590,7 @@
 	bmwrite(dev, TXCFG, (config & ~TxMACEnable));
 	out_le32(&td->control, DBDMA_CLEAR(RUN|PAUSE|FLUSH|WAKE|ACTIVE|DEAD));
 	printk(KERN_ERR "bmac: transmit timeout - resetting\n");
-	bmac_reset_chip(dev);
+	bmac_enable_and_reset_chip(dev);
 
 	/* restart rx dma */
 	cp = bus_to_virt(ld_le32(&rd->cmdptr));
@@ -1683,11 +1712,15 @@
 		bp = (struct bmac_data *) dev->priv;
 		bmac_devs = bp->next_bmac;
 
+		unregister_netdev(dev);
+
+		release_OF_resource(bp->node, 0);
+		release_OF_resource(bp->node, 1);
+		release_OF_resource(bp->node, 2);
 		free_irq(dev->irq, dev);
 		free_irq(bp->tx_dma_intr, dev);
 		free_irq(bp->rx_dma_intr, dev);
 
-		unregister_netdev(dev);
 		kfree(dev);
 	} while (bmac_devs != NULL);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/bonding.c linux/drivers/net/bonding.c
--- linux.orig/drivers/net/bonding.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/bonding.c	Mon Jan 14 16:24:07 2002
@@ -153,6 +153,14 @@
  *
  * 2001/10/23 - Takao Indoh <indou dot takao at jp dot fujitsu dot com>
  *     - Various memory leak fixes
+ *
+ * 2001/11/5 - Mark Huth <mark dot huth at mvista dot com>
+ *     - Don't take rtnl lock in bond_mii_monitor as it deadlocks under 
+ *       certain hotswap conditions.  
+ *       Note:  this same change may be required in bond_arp_monitor ???
+ *     - Remove possibility of calling bond_sethwaddr with NULL slave_dev ptr 
+ *     - Handle hot swap ethernet interface deregistration events to remove
+ *       kernel oops following hot swap of enslaved interface
  */
 
 #include <linux/config.h>
@@ -256,6 +264,7 @@
 static void bond_set_slave_active_flags(slave_t *slave);
 static int bond_enslave(struct net_device *master, struct net_device *slave);
 static int bond_release(struct net_device *master, struct net_device *slave);
+static int bond_release_all(struct net_device *master);
 static int bond_sethwaddr(struct net_device *master, struct net_device *slave);
 
 /*
@@ -371,10 +380,13 @@
 static u16 bond_check_mii_link(bonding_t *bond)
 {
 	int has_active_interface = 0;
+	unsigned long flags;
 
+	read_lock_irqsave(&bond->lock, flags);
 	read_lock(&bond->ptrlock);
 	has_active_interface = (bond->current_slave != NULL);
 	read_unlock(&bond->ptrlock);
+	read_unlock_irqrestore(&bond->lock, flags);
 
 	return (has_active_interface ? MII_LINK_READY : 0);
 }
@@ -406,7 +418,6 @@
 static int bond_close(struct net_device *master)
 {
 	bonding_t *bond = (struct bonding *) master->priv;
-	slave_t *slave;
 	unsigned long flags;
 
 	write_lock_irqsave(&bond->lock, flags);
@@ -417,13 +428,11 @@
 	if (arp_interval> 0) {  /* arp interval, in milliseconds. */
 		del_timer(&bond->arp_timer);
 	}
-	/* We need to unlock this because bond_release will re-lock it */
-	write_unlock_irqrestore(&bond->lock, flags);
 
 	/* Release the bonded slaves */
-	while ((slave = bond->prev) != (slave_t *)bond) {
-		bond_release(master, slave->dev);
-	}
+	bond_release_all(master);
+
+	write_unlock_irqrestore(&bond->lock, flags);
 
 	MOD_DEC_USE_COUNT;
 	return 0;
@@ -865,6 +874,49 @@
 	return -EINVAL;
 }
 
+/* 
+ * This function releases all slaves.
+ * Warning: must put write-locks around the call to this function.
+ */
+static int bond_release_all(struct net_device *master)
+{
+	bonding_t *bond;
+	slave_t *our_slave;
+	struct net_device *slave_dev;
+
+	if (master == NULL)  {
+		return -ENODEV;
+	}
+
+	if (master->flags & IFF_SLAVE) {
+		return -EINVAL;
+	}
+
+	bond = (struct bonding *) master->priv;
+	bond->current_slave = NULL;
+
+	while ((our_slave = bond->prev) != (slave_t *)bond) {
+		slave_dev = our_slave->dev;
+		bond->prev = our_slave->prev;
+
+		kfree(our_slave);
+
+		netdev_set_master(slave_dev, NULL);
+
+		/* only restore its RUNNING flag if monitoring set it down */
+		if (slave_dev->flags & IFF_UP)
+			slave_dev->flags |= IFF_RUNNING;
+
+		if (slave_dev->flags & IFF_NOARP)
+			dev_close(slave_dev);
+	}
+	bond->next = (slave_t *)bond;
+	bond->slave_cnt = 0;
+	printk (KERN_INFO "%s: releases all slaves\n", master->name);
+
+	return 0;
+}
+
 /* this function is called regularly to monitor each slave's link. */
 static void bond_mii_monitor(struct net_device *master)
 {
@@ -875,14 +927,6 @@
 
 	read_lock_irqsave(&bond->lock, flags);
 
-	if (rtnl_shlock_nowait()) {
-		goto monitor_out;
-	}
-
-	if (rtnl_exlock_nowait()) {
-		rtnl_shunlock();
-		goto monitor_out;
-	}
 	/* we will try to read the link status of each of our slaves, and
 	 * set their IFF_RUNNING flag appropriately. For each slave not
 	 * supporting MII status, we won't do anything so that a user-space
@@ -1056,9 +1100,10 @@
 		} /* end of switch */
 	} /* end of while */
 
-	/* if there's no active interface and we discovered that one
-	   of the slaves could be activated earlier, so we do it.
-	*/
+	/* 
+	 * if there's no active interface and we discovered that one
+	 * of the slaves could be activated earlier, so we do it.
+	 */
 	read_lock(&bond->ptrlock);
 	oldcurrent = bond->current_slave;
 	read_unlock(&bond->ptrlock);
@@ -1096,9 +1141,6 @@
 		}
 	}
 
-	rtnl_exunlock();
-	rtnl_shunlock();
-monitor_out:
 	read_unlock_irqrestore(&bond->lock, flags);
 	/* re-arm the timer */
 	mod_timer(&bond->mii_timer, jiffies + (miimon * HZ / 1000));
@@ -1127,17 +1169,17 @@
 
 	if (!IS_UP(master)) {
 		mod_timer(&bond->arp_timer, next_timer);
-		goto monitor_out;
+		goto arp_monitor_out;
 	}
 
 
 	if (rtnl_shlock_nowait()) {
-		goto monitor_out;
+		goto arp_monitor_out;
 	}
 
 	if (rtnl_exlock_nowait()) {
 		rtnl_shunlock();
-		goto monitor_out;
+		goto arp_monitor_out;
 	}
 
 	/* see if any of the previous devices are up now (i.e. they have seen a 
@@ -1242,7 +1284,9 @@
 	 * an arp on all of the interfaces 
 	 */
 
+	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, 
@@ -1250,11 +1294,14 @@
 				 slave->dev->dev_addr, arp_target_hw_addr); 
 		}
 	}
+	else {
+		read_unlock(&bond->ptrlock);
+	}
 
 	rtnl_exunlock();
 	rtnl_shunlock();
 
-monitor_out:
+arp_monitor_out:
 	read_unlock_irqrestore(&bond->lock, flags);
 
 	/* re-arm the timer */
@@ -1366,16 +1413,17 @@
 {
 	bonding_t *bond = (struct bonding *) master->priv;
 	slave_t *slave;
+	unsigned long flags;
 
 	info->bond_mode = mode;
 	info->num_slaves = 0;
 	info->miimon = miimon;
 
-	read_lock(&bond->ptrlock);
+	read_lock_irqsave(&bond->lock, flags);
 	for (slave = bond->prev; slave!=(slave_t *)bond; slave = slave->prev) {
 		info->num_slaves++;
 	}
-	read_unlock(&bond->ptrlock);
+	read_unlock_irqrestore(&bond->lock, flags);
 
 	return 0;
 }
@@ -1386,27 +1434,28 @@
 	bonding_t *bond = (struct bonding *) master->priv;
 	slave_t *slave;
 	int cur_ndx = 0;
+	unsigned long flags;
 
 	if (info->slave_id < 0) {
 		return -ENODEV;
 	}
 
-	read_lock(&bond->ptrlock);
+	read_lock_irqsave(&bond->lock, flags);
 	for (slave = bond->prev; 
 		 slave != (slave_t *)bond && cur_ndx < info->slave_id; 
 		 slave = slave->prev) {
 		cur_ndx++;
 	}
+	read_unlock_irqrestore(&bond->lock, flags);
+
 	if (cur_ndx == info->slave_id) {
 		strcpy(info->slave_name, slave->dev->name);
 		info->link = slave->link;
 		info->state = slave->state;
 		info->link_failure_count = slave->link_failure_count;
 	} else {
-		read_unlock(&bond->ptrlock);
 		return -ENODEV;
 	}
-	read_unlock(&bond->ptrlock);
 
 	return 0;
 }
@@ -1479,40 +1528,40 @@
 	}
 
 	slave_dev = dev_get_by_name(ifr->ifr_slave);
+
 #ifdef BONDING_DEBUG
 	printk(KERN_INFO "slave_dev=%x: \n", (unsigned int)slave_dev);
 	printk(KERN_INFO "slave_dev->name=%s: \n", slave_dev->name);
 #endif
-	switch (cmd) {
-	case BOND_ENSLAVE_OLD:
-	case SIOCBONDENSLAVE:		
-		ret = bond_enslave(master_dev, slave_dev);
-		break;
-	case BOND_RELEASE_OLD:			
-	case SIOCBONDRELEASE:	
-		ret = bond_release(master_dev, slave_dev); 
-		break;
-	case BOND_SETHWADDR_OLD:
-	case SIOCBONDSETHWADDR:	
-		ret = bond_sethwaddr(master_dev, slave_dev);
-		break;
-	case BOND_CHANGE_ACTIVE_OLD:
-	case SIOCBONDCHANGEACTIVE:
-		if (mode == BOND_MODE_ACTIVEBACKUP) {
-			ret = bond_change_active(master_dev, slave_dev);
-		}
-		else {
-			ret = -EINVAL;
-		}
-		break;
-	default:
-		ret = -EOPNOTSUPP;
-	}
 
-	if (slave_dev) {
-		/*
-		 * Clear the module reference that was added by dev_get_by_name
-		 */
+	if (slave_dev == NULL) {
+		ret = -ENODEV;
+	} else {
+		switch (cmd) {
+		case BOND_ENSLAVE_OLD:
+		case SIOCBONDENSLAVE:		
+			ret = bond_enslave(master_dev, slave_dev);
+			break;
+		case BOND_RELEASE_OLD:			
+		case SIOCBONDRELEASE:	
+			ret = bond_release(master_dev, slave_dev); 
+			break;
+		case BOND_SETHWADDR_OLD:
+		case SIOCBONDSETHWADDR:	
+			ret = bond_sethwaddr(master_dev, slave_dev);
+			break;
+		case BOND_CHANGE_ACTIVE_OLD:
+		case SIOCBONDCHANGEACTIVE:
+			if (mode == BOND_MODE_ACTIVEBACKUP) {
+				ret = bond_change_active(master_dev, slave_dev);
+			}
+			else {
+				ret = -EINVAL;
+			}
+			break;
+		default:
+			ret = -EOPNOTSUPP;
+		}
 		dev_put(slave_dev);
 	}
 	return ret;
@@ -1592,13 +1641,11 @@
 	}
 
 	read_lock_irqsave(&bond->lock, flags);
-	read_lock(&bond->ptrlock);
 	slave = bond->prev;
 
 	/* we're at the root, get the first slave */
 	if ((slave == NULL) || (slave->dev == NULL)) { 
 		/* no suitable interface, frame not sent */
-		read_unlock(&bond->ptrlock);
 		dev_kfree_skb(skb);
 		read_unlock_irqrestore(&bond->lock, flags);
 		return 0;
@@ -1606,8 +1653,6 @@
 
 	slave_no = (data->h_dest[5]^slave->dev->dev_addr[5]) % bond->slave_cnt;
 
-	read_unlock(&bond->ptrlock);
-
 	while ( (slave_no > 0) && (slave != (slave_t *)bond) ) {
 		slave = slave->prev;
 		slave_no--;
@@ -1748,6 +1793,7 @@
 	off_t begin = 0;
 	u16 link;
 	slave_t *slave = NULL;
+	unsigned long flags;
 
 	while (bond != NULL) {
 		/*
@@ -1756,17 +1802,19 @@
 		 */
 		link = bond_check_mii_link(bond);
 
-		read_lock(&bond->ptrlock);
-
 		len += sprintf(buf + len, "Bonding Mode: ");
 		len += sprintf(buf + len, "%s\n", mode ? "active-backup" : "load balancing");
 
 		if (mode == BOND_MODE_ACTIVEBACKUP) {
+			read_lock_irqsave(&bond->lock, flags);
+			read_lock(&bond->ptrlock);
 			if (bond->current_slave != NULL) {
 				len += sprintf(buf + len, 
 					"Currently Active Slave: %s\n", 
 					bond->current_slave->dev->name);
 			}
+			read_unlock(&bond->ptrlock);
+			read_unlock_irqrestore(&bond->lock, flags);
 		}
 
 		len += sprintf(buf + len, "MII Status: ");
@@ -1777,6 +1825,7 @@
 		len += sprintf(buf + len, "Up Delay (ms): %d\n", updelay);
 		len += sprintf(buf + len, "Down Delay (ms): %d\n", downdelay);
 
+		read_lock_irqsave(&bond->lock, flags);
 		for (slave = bond->prev; slave != (slave_t *)bond; 
 		     slave = slave->prev) {
 			len += sprintf(buf + len, "\nSlave Interface: %s\n", slave->dev->name);
@@ -1789,6 +1838,7 @@
 			len += sprintf(buf + len, "Link Failure Count: %d\n", 
 				slave->link_failure_count);
 		}
+		read_unlock_irqrestore(&bond->lock, flags);
 
 		/*
 		 * Figure out the calcs for the /proc/net interface
@@ -1802,7 +1852,6 @@
 			len = 0;
 		}
 
-		read_unlock(&bond->ptrlock);
 
 		bond = bond->next_bond;
 	}
@@ -1843,7 +1892,14 @@
 			default:
 				return NOTIFY_DONE;
 			}
-		} 
+		} else if (this_bond->device == event_dev->master) {
+			switch (event) {
+			case NETDEV_UNREGISTER:
+				bond_release(this_bond->device, event_dev);
+				break;
+			}
+			return NOTIFY_DONE;
+		}
 		this_bond = this_bond->next_bond;
 	}
 	return NOTIFY_DONE;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/cs89x0.c linux/drivers/net/cs89x0.c
--- linux.orig/drivers/net/cs89x0.c	Sun Sep 30 19:26:06 2001
+++ linux/drivers/net/cs89x0.c	Wed Jan 16 18:49:28 2002
@@ -537,7 +537,7 @@
                         dev->dev_addr[i*2+1] = eeprom_buff[i] >> 8;
                 }
 		if (net_debug > 1)
-			printk(KERN_DEBUG "%s: new adapter_cnf: 0%x\n",
+			printk(KERN_DEBUG "%s: new adapter_cnf: 0x%x\n",
 				dev->name, lp->adapter_cnf);
         }
 
@@ -582,8 +582,8 @@
 		i = lp->isa_config & INT_NO_MASK;
 		if (lp->chip_type == CS8900) {
 			/* Translate the IRQ using the IRQ mapping table. */
-			if (i > sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0]))
-				printk("\ncs89x0: bug: isa_config is %d\n", i);
+			if (i >= sizeof(cs8900_irq_map)/sizeof(cs8900_irq_map[0]))
+				printk("\ncs89x0: invalid ISA interrupt number %d\n", i);
 			else
 				i = cs8900_irq_map[i];
 			
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/de4x5.c linux/drivers/net/de4x5.c
--- linux.orig/drivers/net/de4x5.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/de4x5.c	Mon Jan  7 23:28:58 2002
@@ -3652,7 +3652,7 @@
     tmp = virt_to_bus(p->data);
     i = ((tmp + ALIGN) & ~ALIGN) - tmp;
     skb_reserve(p, i);
-    lp->rx_ring[index].buf = tmp + i;
+    lp->rx_ring[index].buf = cpu_to_le32(tmp + i);
 
     ret = lp->rx_skb[index];
     lp->rx_skb[index] = p;
@@ -5623,7 +5623,7 @@
 	if (!capable(CAP_NET_ADMIN)) return -EPERM;
 	omr = inl(DE4X5_OMR);
 	omr &= ~OMR_PR;
-	outb(omr, DE4X5_OMR);
+	outl(omr, DE4X5_OMR);
 	dev->flags &= ~IFF_PROMISC;
 	break;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/dl2k.c linux/drivers/net/dl2k.c
--- linux.orig/drivers/net/dl2k.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/dl2k.c	Mon Jan 21 17:13:47 2002
@@ -15,18 +15,24 @@
     0.01	2001/05/03	Created DL2000-based linux driver
     0.02	2001/05/21	Added VLAN and hardware checksum support.
     1.00	2001/06/26	Added jumbo frame support.
-    1.01	2001/08/21	Added two parameters, int_count and int_timeout.
+    1.01	2001/08/21	Added two parameters, rx_coalesce and rx_timeout.
     1.02	2001/10/08	Supported fiber media.
     				Added flow control parameters.
-    1.03	2001/10/12	Changed the default media to 1000mbps_fd for the 
-    				fiber devices.
-    1.04	2001/11/08	Fixed a bug which Tx stop when a very busy case.
-*/
+    1.03	2001/10/12	Changed the default media to 1000mbps_fd for 
+    				the fiber devices.
+    1.04	2001/11/08	Fixed Tx stopped when tx very busy.
+    1.05	2001/11/22	Fixed Tx stopped when unidirectional tx busy.
+    1.06	2001/12/13	Fixed disconnect bug at 10Mbps mode.
+    				Fixed tx_full flag incorrect.
+				Added tx_coalesce paramter.
+    1.07	2002/01/03	Fixed miscount of RX frame error.
+    1.08	2002/01/17	Fixed the multicast bug.
+ */
 
 #include "dl2k.h"
 
 static char version[] __devinitdata =
-    KERN_INFO "D-Link DL2000-based linux driver v1.04 2001/11/08\n";
+    KERN_INFO "D-Link DL2000-based linux driver v1.08 2002/01/17\n";
 
 #define MAX_UNITS 8
 static int mtu[MAX_UNITS];
@@ -36,11 +42,13 @@
 static int tx_flow[MAX_UNITS];
 static int rx_flow[MAX_UNITS];
 static int copy_thresh;
-static int int_count;		/* Rx frame count each interrupt */
-static int int_timeout;		/* Rx DMA wait time in 64ns increments */
+static int rx_coalesce = DEFAULT_RXC;
+static int rx_timeout = DEFAULT_RXT;	
+static int tx_coalesce = DEFAULT_TXC;	
 
 MODULE_AUTHOR ("Edward Peng");
 MODULE_DESCRIPTION ("D-Link DL2000-based Gigabit Ethernet Adapter");
+MODULE_LICENSE("GPL");
 MODULE_PARM (mtu, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (media, "1-" __MODULE_STRING (MAX_UNITS) "s");
 MODULE_PARM (vlan, "1-" __MODULE_STRING (MAX_UNITS) "i");
@@ -48,13 +56,16 @@
 MODULE_PARM (tx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (rx_flow, "1-" __MODULE_STRING (MAX_UNITS) "i");
 MODULE_PARM (copy_thresh, "i");
-MODULE_PARM (int_count, "i");
-MODULE_PARM (int_timeout, "i");
+MODULE_PARM (rx_coalesce, "i");	/* Rx frame count each interrupt */
+MODULE_PARM (rx_timeout, "i");	/* Rx DMA wait time in 64ns increments */
+MODULE_PARM (tx_coalesce, "i"); /* HW xmit count each TxComplete [1-8] */
+
 
 /* Enable the default interrupts */
+#define DEFAULT_INTR (RxDMAComplete | HostError | IntRequested | TxComplete| \
+       UpdateStats | LinkEvent)
 #define EnableInt() \
-writew(RxDMAComplete | HostError | IntRequested | TxComplete| \
-       UpdateStats | LinkEvent, ioaddr + IntEnable)
+writew(DEFAULT_INTR, ioaddr + IntEnable)
 
 static int max_intrloop = 50;
 static int multicast_filter_limit = 0x40;
@@ -163,11 +174,11 @@
 				np->speed = 10;
 				np->full_duplex = 0;
 			} else if (strcmp (media[card_idx], "1000mbps_fd") == 0 ||
-				 strcmp (media[card_idx], "5") == 0) {
+				 strcmp (media[card_idx], "6") == 0) {
 				np->speed=1000;
 				np->full_duplex=1;
 			} else if (strcmp (media[card_idx], "1000mbps_hd") == 0 ||
-				 strcmp (media[card_idx], "6") == 0) {
+				 strcmp (media[card_idx], "5") == 0) {
 				np->speed = 1000;
 				np->full_duplex = 0;
 			} else {
@@ -176,7 +187,7 @@
 		}
 		if (jumbo[card_idx] != 0) {
 			np->jumbo = 1;
-			dev->mtu = 9000;
+			dev->mtu = MAX_JUMBO;
 		} else {
 			np->jumbo = 0;
 			if (mtu[card_idx] > 0 && mtu[card_idx] < PACKET_SIZE)
@@ -184,14 +195,17 @@
 		}
 		np->vlan = (vlan[card_idx] > 0 && vlan[card_idx] < 4096) ?
 		    vlan[card_idx] : 0;
-		if (int_count != 0 && int_timeout != 0) {
-			np->int_count = int_count;
-			np->int_timeout = int_timeout;
+		if (rx_coalesce != 0 && rx_timeout != 0) {
+			np->rx_coalesce = rx_coalesce;
+			np->rx_timeout = rx_timeout;
 			np->coalesce = 1;
 		}
 		np->tx_flow = (tx_flow[card_idx]) ? 1 : 0;
 		np->rx_flow = (rx_flow[card_idx]) ? 1 : 0;
-		
+		if (tx_coalesce < 1)
+			tx_coalesce = 1;
+		if (tx_coalesce > 8)
+			tx_coalesce = 8;
 	}
 	dev->open = &rio_open;
 	dev->hard_start_xmit = &start_xmit;
@@ -202,8 +216,8 @@
 	dev->tx_timeout = &tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 	dev->change_mtu = &change_mtu;
-#ifdef TX_HW_CHECKSUM
-	dev->features = NETIF_F_SG | NETIF_F_HW_CSUM;
+#if 0
+	dev->features = NETIF_F_IP_CSUM;
 #endif
 	pci_set_drvdata (pdev, dev);
 
@@ -389,13 +403,12 @@
 	i = request_irq (dev->irq, &rio_interrupt, SA_SHIRQ, dev->name, dev);
 	if (i)
 		return i;
-
 	/* DebugCtrl bit 4, 5, 9 must set */
 	writel (readl (ioaddr + DebugCtrl) | 0x0230, ioaddr + DebugCtrl);
 
 	/* Jumbo frame */
 	if (np->jumbo != 0)
-		writew (9014, ioaddr + MaxFrameSize);
+		writew (MAX_JUMBO+14, ioaddr + MaxFrameSize);
 
 	alloc_list (dev);
 
@@ -405,7 +418,7 @@
 
 	set_multicast (dev);
 	if (np->coalesce) {
-		writel (np->int_count | np->int_timeout << 16,
+		writel (np->rx_coalesce | np->rx_timeout << 16,
 			ioaddr + RxDMAIntCtrl);
 	}
 	/* Set RIO to poll every N*320nsec. */
@@ -442,13 +455,31 @@
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
 
-	printk (KERN_WARNING "%s: Transmit timed out, TxStatus %4.4x.\n",
+	printk (KERN_INFO "%s: Tx timed out (%4.4x), is buffer full?\n",
 		dev->name, readl (ioaddr + TxStatus));
+	/* Free used tx skbuffs */
+	for (; np->cur_tx - np->old_tx > 0; np->old_tx++) {
+		int entry = np->old_tx % TX_RING_SIZE;
+		struct sk_buff *skb;
+
+		if (!(np->tx_ring[entry].status & TFDDone))
+			break;
+		skb = np->tx_skbuff[entry];
+		pci_unmap_single (np->pdev,
+				  np->tx_ring[entry].fraginfo,
+				  skb->len, PCI_DMA_TODEVICE);
+		dev_kfree_skb_irq (skb);
+		np->tx_skbuff[entry] = 0;
+	}
 	dev->if_port = 0;
 	dev->trans_start = jiffies;
 	np->stats.tx_errors++;
-	if (!np->tx_full)
+	/* If the ring is no longer full, clear tx_full and 
+	   call netif_wake_queue() */
+	if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) {
+		np->tx_full = 0;
 		netif_wake_queue (dev);
+	}
 }
 
  /* allocate and initialize Tx and Rx descriptors */
@@ -466,16 +497,19 @@
 	/* Initialize Tx descriptors, TFDListPtr leaves in start_xmit(). */
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		np->tx_skbuff[i] = 0;
-		np->tx_ring[i].status = 0;
+		np->tx_ring[i].status = cpu_to_le64 (TFDDone);
+		np->tx_ring[i].next_desc = cpu_to_le64 (np->tx_ring_dma +
+					      ((i+1)%TX_RING_SIZE) *
+					      sizeof (struct
+					      netdev_desc));
 	}
 
 	/* Initialize Rx descriptors */
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		np->rx_ring[i].next_desc = cpu_to_le64 (np->rx_ring_dma +
-							((i +
-							  1) % RX_RING_SIZE) *
-							sizeof (struct
-								netdev_desc));
+						((i + 1) % RX_RING_SIZE) *
+						sizeof (struct
+						netdev_desc));
 		np->rx_ring[i].status = 0;
 		np->rx_ring[i].fraginfo = 0;
 		np->rx_skbuff[i] = 0;
@@ -523,13 +557,12 @@
 	entry = np->cur_tx % TX_RING_SIZE;
 	np->tx_skbuff[entry] = skb;
 	txdesc = &np->tx_ring[entry];
-	txdesc->next_desc = 0;
 
 	/* Set TFDDone to avoid TxDMA gather this descriptor */
 	txdesc->status = cpu_to_le64 (TFDDone);
 	txdesc->status |=
 	    cpu_to_le64 (entry | WordAlignDisable | (1 << FragCountShift));
-#ifdef TX_HW_CHECKSUM
+#if 0
 	if (skb->ip_summed == CHECKSUM_HW) {
 		txdesc->status |=
 		    cpu_to_le64 (TCPChecksumEnable | UDPChecksumEnable |
@@ -545,21 +578,13 @@
 
 	/* Send one packet each time at 10Mbps mode */
 	/* Tx coalescing loop do not exceed 8 */
-	if (entry % 0x08 == 0 || np->speed == 10)
+	if (entry % tx_coalesce == 0 || np->speed == 10)
 		txdesc->status |= cpu_to_le64 (TxIndicate);
 	txdesc->fraginfo = cpu_to_le64 (pci_map_single (np->pdev, skb->data,
 							skb->len,
 							PCI_DMA_TODEVICE));
 	txdesc->fraginfo |= cpu_to_le64 (skb->len) << 48;
 
-	/* Chain the last descriptor's pointer to this one */
-	if (np->last_tx)
-		np->last_tx->next_desc = cpu_to_le64 (np->tx_ring_dma +
-						      entry *
-						      sizeof (struct
-							      netdev_desc));
-	np->last_tx = txdesc;
-
 	/* Clear TFDDone, then TxDMA start to send this descriptor */
 	txdesc->status &= ~cpu_to_le64 (TFDDone);
 
@@ -571,8 +596,10 @@
 	if (np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1 && np->speed != 10) {
 		/* do nothing */
 	} else {
+		spin_lock_irqsave(&np->lock, flags);
 		np->tx_full = 1;
 		netif_stop_queue (dev);
+		spin_unlock_irqrestore (&np->lock, flags);
 	}
 
 	/* The first TFDListPtr */
@@ -581,15 +608,15 @@
 			dev->base_addr + TFDListPtr0);
 		writel (0, dev->base_addr + TFDListPtr1);
 	}
-
-	spin_lock_irqsave (&np->lock, flags);
+	
 	if (np->old_tx > TX_RING_SIZE) {
+		spin_lock_irqsave (&np->lock, flags);
 		tx_shift = TX_RING_SIZE;
 		np->old_tx -= tx_shift;
 		np->cur_tx -= tx_shift;
+		spin_unlock_irqrestore (&np->lock, flags);
 	}
-	spin_unlock_irqrestore (&np->lock, flags);
-
+	
 	/* NETDEV WATCHDOG timer */
 	dev->trans_start = jiffies;
 	return 0;
@@ -606,33 +633,24 @@
 
 	ioaddr = dev->base_addr;
 	np = dev->priv;
-	spin_lock (&np->lock);
+	spin_lock(&np->lock);
 	while (1) {
-		int_status = readw (ioaddr + IntStatus) &
-		    (HostError | TxComplete | IntRequested |
-		     UpdateStats | LinkEvent | RxDMAComplete);
-		writew (int_status & (HostError | TxComplete | RxComplete |
-				      IntRequested | UpdateStats | LinkEvent |
-				      TxDMAComplete | RxDMAComplete | RFDListEnd
-				      | RxDMAPriority), ioaddr + IntStatus);
+		int_status = readw (ioaddr + IntStatus); 
+		writew (int_status, ioaddr + IntStatus);
+		int_status &= DEFAULT_INTR;
 		if (int_status == 0)
 			break;
 		/* Processing received packets */
 		if (int_status & RxDMAComplete)
 			receive_packet (dev);
 		/* TxComplete interrupt */
-		if (int_status & TxComplete || np->tx_full) {
-			int tx_status = readl (ioaddr + TxStatus);
+		if ((int_status & TxComplete) || np->tx_full) {
+			int tx_status;
+			tx_status = readl (ioaddr + TxStatus);
 			if (tx_status & 0x01)
 				tx_error (dev, tx_status);
-			/* Send one packet each time at 10Mbps mode */
-			if (np->speed == 10) {
-				np->tx_full = 0;
-				netif_wake_queue (dev);
-			}
-
 			/* Free used tx skbuffs */
-			for (; np->cur_tx - np->old_tx > 0; np->old_tx++) {
+			for (;np->cur_tx - np->old_tx > 0; np->old_tx++) {
 				int entry = np->old_tx % TX_RING_SIZE;
 				struct sk_buff *skb;
 
@@ -649,9 +667,12 @@
 		/* If the ring is no longer full, clear tx_full and 
 		   call netif_wake_queue() */
 		if (np->tx_full && np->cur_tx - np->old_tx < TX_QUEUE_LEN - 1) {
-			np->tx_full = 0;
-			netif_wake_queue (dev);
+			if (np->speed != 10 || int_status & TxComplete) {
+				np->tx_full = 0;
+				netif_wake_queue (dev);
+			}
 		}
+
 		/* Handle uncommon events */
 		if (int_status &
 		    (IntRequested | HostError | LinkEvent | UpdateStats))
@@ -666,7 +687,7 @@
 			break;
 		}
 	}
-	spin_unlock (&np->lock);
+	spin_unlock(&np->lock);
 }
 
 static void
@@ -742,7 +763,7 @@
 		np->stats.collisions++;
 #endif
 
-	/* Restart the Tx. */
+	/* Restart the Tx */
 	writel (readw (dev->base_addr + MACCtrl) | TxEnable, ioaddr + MACCtrl);
 }
 
@@ -783,7 +804,7 @@
 			if (frame_status & 0x00300000)
 				np->stats.rx_length_errors++;
 			if (frame_status & 0x00010000)
-				np->stats.rx_fifo_errors++;
+	 			np->stats.rx_fifo_errors++;
 			if (frame_status & 0x00060000)
 				np->stats.rx_frame_errors++;
 			if (frame_status & 0x00080000)
@@ -808,7 +829,7 @@
 				skb_put (skb, pkt_len);
 			}
 			skb->protocol = eth_type_trans (skb, dev);
-#ifdef RX_HW_CHECKSUM
+#if 0
 			/* Checksum done by hw, but csum value unavailable. */
 			if (!(frame_status & (TCPError | UDPError | IPError))) {
 				skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -899,7 +920,7 @@
 	/* PCI Error, a catastronphic error related to the bus interface 
 	   occurs, set GlobalReset and HostReset to reset. */
 	if (int_status & HostError) {
-		printk (KERN_ERR "%s: PCI Error! IntStatus %4.4x.\n",
+		printk (KERN_ERR "%s: HostError! IntStatus %4.4x.\n",
 			dev->name, int_status);
 		writew (GlobalReset | HostReset, ioaddr + ASICCtrl + 2);
 		mdelay (500);
@@ -914,8 +935,8 @@
 	u16 temp1;
 	u16 temp2;
 	int i;
-	/* All statistics registers need to acknowledge,
-	   else overflow could cause some problem */
+	/* All statistics registers need to be acknowledged,
+	   else statistic overflow could cause problems */
 	np->stats.rx_packets += readl (ioaddr + FramesRcvOk);
 	np->stats.tx_packets += readl (ioaddr + FramesXmtOk);
 	np->stats.rx_bytes += readl (ioaddr + OctetRcvOk);
@@ -932,11 +953,11 @@
 	    readl (ioaddr + FramesWDeferredXmt) + temp2;
 
 	/* detailed rx_error */
-	np->stats.rx_length_errors += readw (ioaddr + InRangeLengthErrors) +
-	    readw (ioaddr + FrameTooLongErrors);
+	np->stats.rx_length_errors += readw (ioaddr + FrameTooLongErrors);
 	np->stats.rx_crc_errors += readw (ioaddr + FrameCheckSeqError);
 
 	/* Clear all other statistic register. */
+	readw (ioaddr + InRangeLengthErrors);
 	readw (ioaddr + MacControlFramesXmtd);
 	readw (ioaddr + BcstFramesXmtdOk);
 	readl (ioaddr + McstFramesXmtdOk);
@@ -961,7 +982,7 @@
 change_mtu (struct net_device *dev, int new_mtu)
 {
 	struct netdev_private *np = dev->priv;
-	int max = (np->jumbo) ? 9000 : 1536;
+	int max = (np->jumbo) ? MAX_JUMBO : 1536;
 
 	if ((new_mtu < 68) || (new_mtu > max)) {
 		return -EINVAL;
@@ -996,36 +1017,42 @@
 	u32 hash_table[2];
 	u16 rx_mode = 0;
 	int i;
+	int bit;
+	int index, crc;
 	struct dev_mc_list *mclist;
 	struct netdev_private *np = dev->priv;
-
-	/* Default: receive broadcast and unicast */
-	rx_mode = ReceiveBroadcast | ReceiveUnicast;
+	
+	hash_table[0] = hash_table[1] = 0;
+	/* RxFlowcontrol DA: 01-80-C2-00-00-01. Hash index=0x39 */
+	hash_table[1] |= 0x02000000;
 	if (dev->flags & IFF_PROMISC) {
 		/* Receive all frames promiscuously. */
-		rx_mode |= ReceiveAllFrames;
-	} else if (((dev->flags & IFF_MULTICAST)
-		    && (dev->mc_count > multicast_filter_limit))
-		   || (dev->flags & IFF_ALLMULTI)) {
+		rx_mode = ReceiveAllFrames;
+	} else if ((dev->flags & IFF_ALLMULTI) || 
+			(dev->mc_count > multicast_filter_limit)) {
 		/* Receive broadcast and multicast frames */
-		rx_mode |= ReceiveBroadcast | ReceiveMulticast | ReceiveUnicast;
-	} else if ((dev->flags & IFF_MULTICAST) & (dev->mc_count > 0)) {
-		/* Receive broadcast frames and multicast frames filtering by Hashtable */
-		rx_mode |=
+		rx_mode = ReceiveBroadcast | ReceiveMulticast | ReceiveUnicast;
+	} else if (dev->mc_count > 0) {
+		/* Receive broadcast frames and multicast frames filtering 
+		   by Hashtable */
+		rx_mode =
 		    ReceiveBroadcast | ReceiveMulticastHash | ReceiveUnicast;
+		for (i=0, mclist = dev->mc_list; mclist && i < dev->mc_count; 
+				i++, mclist=mclist->next) {
+			crc = get_crc (mclist->dmi_addr, ETH_ALEN);
+			for (index=0, bit=0; bit<6; bit++, crc<<=1) {
+				if (crc & 0x80000000) index |= 1 << bit;
+			}
+			hash_table[index / 32] |= (1 << (index % 32));
+		}
+	} else {
+		rx_mode = ReceiveBroadcast | ReceiveUnicast;
 	}
 	if (np->vlan) {
 		/* ReceiveVLANMatch field in ReceiveMode */
 		rx_mode |= ReceiveVLANMatch;
 	}
-	hash_table[0] = 0x00000000;
-	hash_table[1] = 0x00000000;
 
-	for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
-	     i++, mclist = mclist->next) {
-		set_bit (get_crc (mclist->dmi_addr, ETH_ALEN) & 0x3f,
-			 hash_table);
-	}
 	writel (hash_table[0], ioaddr + HashTable0);
 	writel (hash_table[1], ioaddr + HashTable1);
 	writew (rx_mode, ioaddr + ReceiveMode);
@@ -1058,6 +1085,8 @@
 		miidata->out_value = mii_read (dev, phy_addr, miidata->reg_num);
 		break;
 	case SIOCDEVPRIVATE + 2:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 		mii_write (dev, phy_addr, miidata->reg_num, miidata->in_value);
 		break;
 	case SIOCDEVPRIVATE + 3:
@@ -1693,8 +1722,9 @@
  
 Compile command: 
  
-gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2x.c
+gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -O2 -c dl2k.c
 
 Read Documentation/networking/dl2k.txt for details.
 
 */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/dl2k.h linux/drivers/net/dl2k.h
--- linux.orig/drivers/net/dl2k.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/dl2k.h	Mon Jan 21 16:59:10 2002
@@ -33,7 +33,7 @@
 #include <linux/spinlock.h>
 #include <linux/time.h>
 #define TX_RING_SIZE	128
-#define TX_QUEUE_LEN	96	/* Limit ring entries actually used.  */
+#define TX_QUEUE_LEN	120	/* Limit ring entries actually used.  */
 #define RX_RING_SIZE 	128
 #define TX_TOTAL_SIZE	TX_RING_SIZE*sizeof(struct netdev_desc)
 #define RX_TOTAL_SIZE	RX_RING_SIZE*sizeof(struct netdev_desc)
@@ -182,12 +182,12 @@
 
 /* Bits in the ReceiveMode register. */
 enum ReceiveMode_bits {
-	ReceiveIPMulticast = 0x0020,
-	ReceiveMulticastHash = 0x0010,
-	ReceiveAllFrames = 0x0008,
-	ReceiveBroadcast = 0x0004,
-	ReceiveMulticast = 0x0002,
 	ReceiveUnicast = 0x0001,
+	ReceiveMulticast = 0x0002,
+	ReceiveBroadcast = 0x0004,
+	ReceiveAllFrames = 0x0008,
+	ReceiveMulticastHash = 0x0010,
+	ReceiveIPMulticast = 0x0020,
 	ReceiveVLANMatch = 0x0100,
 	ReceiveVLANHash = 0x0200,
 };
@@ -649,20 +649,20 @@
 	struct pci_dev *pdev;
 	spinlock_t lock;
 	struct net_device_stats stats;
-	unsigned int rx_buf_sz;	/* Based on MTU+slack. */
-	unsigned int speed;	/* Operating speed */
-	unsigned int vlan;	/* VLAN Id */
-	unsigned int chip_id;	/* PCI table chip id */
-	unsigned int int_count; /* Maximum frames each RxDMAComplete intr */
-	unsigned int int_timeout; /* Wait time between RxDMAComplete intr */
-	unsigned int tx_full:1;	/* The Tx queue is full. */
+	unsigned int rx_buf_sz;		/* Based on MTU+slack. */
+	unsigned int speed;		/* Operating speed */
+	unsigned int vlan;		/* VLAN Id */
+	unsigned int chip_id;		/* PCI table chip id */
+	unsigned int rx_coalesce; 	/* Maximum frames each RxDMAComplete intr */
+	unsigned int rx_timeout; 	/* Wait time between RxDMAComplete intr */
+	unsigned int tx_full:1;		/* The Tx queue is full. */
 	unsigned int full_duplex:1;	/* Full-duplex operation requested. */
 	unsigned int an_enable:2;	/* Auto-Negotiated Enable */
 	unsigned int jumbo:1;		/* Jumbo frame enable */
 	unsigned int coalesce:1;	/* Rx coalescing enable */
 	unsigned int tx_flow:1;		/* Tx flow control enable */
 	unsigned int rx_flow:1;		/* Rx flow control enable */
-	unsigned int phy_media:1;	/* 1: fiber, 0: copper */ 
+	unsigned int phy_media:1;	/* 1: fiber, 0: copper */
 	struct netdev_desc *last_tx;	/* Last Tx descriptor used. */
 	unsigned long cur_rx, old_rx;	/* Producer/consumer ring indices */
 	unsigned long cur_tx, old_tx;
@@ -697,7 +697,12 @@
 MODULE_DEVICE_TABLE (pci, rio_pci_tbl);
 #define TX_TIMEOUT  (4*HZ)
 #define PACKET_SIZE		1536
+#define MAX_JUMBO		8000
 #define RIO_IO_SIZE             340
+#define DEFAULT_RXC		5
+#define DEFAULT_RXT		750
+#define DEFAULT_TXC		1
+#define MAX_TXC			8
 #ifdef RIO_DEBUG
 #define DEBUG_TFD_DUMP(x) debug_tfd_dump(x)
 #define DEBUG_RFD_DUMP(x,flag) debug_rfd_dump(x,flag)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/dmfe.c linux/drivers/net/dmfe.c
--- linux.orig/drivers/net/dmfe.c	Fri Nov  9 21:50:01 2001
+++ linux/drivers/net/dmfe.c	Thu Jan 17 19:23:50 2002
@@ -61,8 +61,8 @@
 */
 
 #define DRV_NAME	"dmfe"
-#define DRV_VERSION	"1.36.3"
-#define DRV_RELDATE	"2001-11-06"
+#define DRV_VERSION	"1.36.4"
+#define DRV_RELDATE	"2002-01-17"
 
 #include <linux/module.h>
 
@@ -98,7 +98,6 @@
 #define PCI_DM9100_ID   0x91001282      /* Davicom DM9100 ID */
 #define PCI_DM9009_ID   0x90091282      /* Davicom DM9009 ID */
 
-#define DMFE_SUCC       0
 #define DM9102_IO_SIZE  0x80
 #define DM9102A_IO_SIZE 0x100
 #define TX_MAX_SEND_CNT 0x1             /* Maximum tx packet per time */
@@ -527,7 +526,7 @@
 }
 
 
-static void __exit dmfe_remove_one (struct pci_dev *pdev)
+static void __devexit dmfe_remove_one (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct dmfe_board_info *db = dev->priv;
@@ -2059,7 +2058,7 @@
 	name:		"dmfe",
 	id_table:	dmfe_pci_tbl,
 	probe:		dmfe_init_one,
-	remove:		dmfe_remove_one,
+	remove:		__devexit_p(dmfe_remove_one),
 };
 
 MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/eepro100.c linux/drivers/net/eepro100.c
--- linux.orig/drivers/net/eepro100.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/net/eepro100.c	Wed Feb 13 17:43:43 2002
@@ -28,7 +28,7 @@
 */
 
 static const char *version =
-"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html\n"
+"eepro100.c:v1.09j-t 9/29/99 Donald Becker http://www.scyld.com/network/eepro100.html\n"
 "eepro100.c: $Revision: 1.36 $ 2000/11/17 Modified by Andrey V. Savochkin <saw@saw.sw.com.sg> and others\n";
 
 /* A few user-configurable values that apply to all boards.
@@ -168,6 +168,9 @@
 #ifndef PCI_DEVICE_ID_INTEL_ID1030
 #define PCI_DEVICE_ID_INTEL_ID1030 0x1030
 #endif
+#ifndef PCI_DEVICE_ID_INTEL_ID1031          /* support for Intel Pro/100 VE */
+#define PCI_DEVICE_ID_INTEL_ID1031 0x1031
+#endif
 
 
 static int speedo_debug = 1;
@@ -833,6 +836,10 @@
 	sp->phy[0] = eeprom[6];
 	sp->phy[1] = eeprom[7];
 	sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+	if (((pdev->device > 0x1030 && (pdev->device < 0x1039))) 
+	    || (pdev->device == 0x2449)) {
+	    	sp->chip_id = 1;
+	}
 
 	if (sp->rx_bug)
 		printk(KERN_INFO "  Receiver lock-up workaround activated.\n");
@@ -1099,9 +1106,9 @@
 			mdio_read(ioaddr, phy_num, 1);
 			/* If link beat has returned... */
 			if (mdio_read(ioaddr, phy_num, 1) & 0x0004)
-				dev->flags |= IFF_RUNNING;
+				netif_carrier_on(dev);
 			else
-				dev->flags &= ~IFF_RUNNING;
+				netif_carrier_off(dev);
 		}
 	}
 	if (speedo_debug > 3) {
@@ -1375,7 +1382,7 @@
 
 	/* workaround for hardware bug on 10 mbit half duplex */
 
-	if ((sp->partner==0) && (sp->chip_id==1)) {
+	if ((sp->partner == 0) || (sp->chip_id == 1)) {
 		wait_for_cmd_done(ioaddr + SCBCmd);
 		outb(0 , ioaddr + SCBCmd);
 	}
@@ -2270,6 +2277,8 @@
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1029,
 		PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1030,
+		PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ID1031,     /* support for Intel Pro/100 VE */
 		PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_7,
 		PCI_ANY_ID, PCI_ANY_ID, },
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/eexpress.c linux/drivers/net/eexpress.c
--- linux.orig/drivers/net/eexpress.c	Sun Sep 30 19:26:06 2001
+++ linux/drivers/net/eexpress.c	Wed Feb 13 17:43:33 2002
@@ -1674,7 +1674,6 @@
 			unregister_netdev(dev);
 			kfree(dev->priv);
 			dev->priv = NULL;
-			release_region(dev->base_addr, EEXP_IO_EXTENT);
 		}
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/epic100.c linux/drivers/net/epic100.c
--- linux.orig/drivers/net/epic100.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/epic100.c	Wed Feb  6 20:41:06 2002
@@ -53,11 +53,18 @@
 	LK1.1.10:
 	* revert MII transceiver init change (jgarzik)
 
+	LK1.1.11:
+	* implement ETHTOOL_[GS]SET, _NWAY_RST, _[GS]MSGLVL, _GLINK (jgarzik)
+	* replace some MII-related magic numbers with constants
+
+	LK1.1.12:
+	* fix power-up sequence
+
 */
 
 #define DRV_NAME	"epic100"
-#define DRV_VERSION	"1.11+LK1.1.10"
-#define DRV_RELDATE	"July 6, 2001"
+#define DRV_VERSION	"1.11+LK1.1.12"
+#define DRV_RELDATE	"Jan 18, 2002"
 
 
 /* The user-configurable values.
@@ -317,12 +324,9 @@
 	/* Ring pointers. */
 	spinlock_t lock;				/* Group with Tx control cache line. */
 	unsigned int cur_tx, dirty_tx;
-	struct descriptor  *last_tx_desc;
 
 	unsigned int cur_rx, dirty_rx;
 	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
-	struct descriptor  *last_rx_desc;
-	long last_rx_time;					/* Last Rx, in jiffies. */
 
 	struct pci_dev *pci_dev;			/* PCI bus location. */
 	int chip_id, chip_flags;
@@ -334,13 +338,9 @@
 	signed char phys[4];				/* MII device addresses. */
 	u16 advertising;					/* NWay media advertisement */
 	int mii_phy_cnt;
+	struct mii_if_info mii;
 	unsigned int tx_full:1;				/* The Tx queue is full. */
-	unsigned int full_duplex:1;			/* Current duplex setting. */
-	unsigned int duplex_lock:1;			/* Duplex forced by the user. */
 	unsigned int default_port:4;		/* Last dev->if_port value. */
-	unsigned int media2:4;				/* Secondary monitored media port. */
-	unsigned int medialock:1;			/* Don't sense media type. */
-	unsigned int mediasense:1;			/* Media sensing in progress. */
 };
 
 static int epic_open(struct net_device *dev);
@@ -419,6 +419,9 @@
 
 	pci_set_drvdata(pdev, dev);
 	ep = dev->priv;
+	ep->mii.dev = dev;
+	ep->mii.mdio_read = mdio_read;
+	ep->mii.mdio_write = mdio_write;
 
 	ring_space = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &ring_dma);
 	if (!ring_space)
@@ -480,7 +483,7 @@
 	{
 		int phy, phy_idx = 0;
 		for (phy = 1; phy < 32 && phy_idx < sizeof(ep->phys); phy++) {
-			int mii_status = mdio_read(dev, phy, 1);
+			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				ep->phys[phy_idx++] = phy;
 				printk(KERN_INFO DRV_NAME "(%s): MII transceiver #%d control "
@@ -491,16 +494,17 @@
 		ep->mii_phy_cnt = phy_idx;
 		if (phy_idx != 0) {
 			phy = ep->phys[0];
-			ep->advertising = mdio_read(dev, phy, 4);
+			ep->mii.advertising = mdio_read(dev, phy, MII_ADVERTISE);
 			printk(KERN_INFO DRV_NAME "(%s): Autonegotiation advertising %4.4x link "
 				   "partner %4.4x.\n",
-				   pdev->slot_name, ep->advertising, mdio_read(dev, phy, 5));
+				   pdev->slot_name, ep->mii.advertising, mdio_read(dev, phy, 5));
 		} else if ( ! (ep->chip_flags & NO_MII)) {
 			printk(KERN_WARNING DRV_NAME "(%s): ***WARNING***: No MII transceiver found!\n",
 			       pdev->slot_name);
 			/* Use the known PHY address of the EPII. */
 			ep->phys[0] = 3;
 		}
+		ep->mii.phy_id = ep->phys[0];
 	}
 
 	/* Turn off the MII xcvr (175 only!), leave the chip in low-power mode. */
@@ -510,13 +514,11 @@
 
 	/* The lower four bits are the media type. */
 	if (duplex) {
-		ep->duplex_lock = ep->full_duplex = 1;
+		ep->mii.duplex_lock = ep->mii.full_duplex = 1;
 		printk(KERN_INFO DRV_NAME "(%s):  Forced full duplex operation requested.\n",
 		       pdev->slot_name);
 	}
 	dev->if_port = ep->default_port = option;
-	if (ep->default_port)
-		ep->medialock = 1;
 
 	/* The Epic-specific entries in the device structure. */
 	dev->open = &epic_open;
@@ -675,9 +677,8 @@
 	   required by the details of which bits are reset and the transceiver
 	   wiring on the Ositech CardBus card.
 	*/
-#if 0
-	outl(dev->if_port == 1 ? 0x13 : 0x12, ioaddr + MIICfg);
-#endif
+
+	outl(0x12, ioaddr + MIICfg);
 	if (ep->chip_flags & MII_PWRDWN)
 		outl((inl(ioaddr + NVCTL) & ~0x003C) | 0x4800, ioaddr + NVCTL);
 
@@ -699,29 +700,29 @@
 
 	if (media2miictl[dev->if_port & 15]) {
 		if (ep->mii_phy_cnt)
-			mdio_write(dev, ep->phys[0], 0, media2miictl[dev->if_port&15]);
+			mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]);
 		if (dev->if_port == 1) {
 			if (debug > 1)
 				printk(KERN_INFO "%s: Using the 10base2 transceiver, MII "
 					   "status %4.4x.\n",
-					   dev->name, mdio_read(dev, ep->phys[0], 1));
+					   dev->name, mdio_read(dev, ep->phys[0], MII_BMSR));
 		}
 	} else {
-		int mii_reg5 = mdio_read(dev, ep->phys[0], 5);
-		if (mii_reg5 != 0xffff) {
-			if ((mii_reg5 & 0x0100) || (mii_reg5 & 0x01C0) == 0x0040)
-				ep->full_duplex = 1;
-			else if (! (mii_reg5 & 0x4000))
-				mdio_write(dev, ep->phys[0], 0, 0x1200);
+		int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA);
+		if (mii_lpa != 0xffff) {
+			if ((mii_lpa & LPA_100FULL) || (mii_lpa & 0x01C0) == LPA_10FULL)
+				ep->mii.full_duplex = 1;
+			else if (! (mii_lpa & LPA_LPACK))
+				mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
 			if (debug > 1)
 				printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d"
 					   " register read of %4.4x.\n", dev->name,
-					   ep->full_duplex ? "full" : "half",
-					   ep->phys[0], mii_reg5);
+					   ep->mii.full_duplex ? "full" : "half",
+					   ep->phys[0], mii_lpa);
 		}
 	}
 
-	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
 	outl(ep->rx_ring_dma, ioaddr + PRxCDAR);
 	outl(ep->tx_ring_dma, ioaddr + PTxCDAR);
 
@@ -741,7 +742,7 @@
 		printk(KERN_DEBUG "%s: epic_open() ioaddr %lx IRQ %d status %4.4x "
 			   "%s-duplex.\n",
 			   dev->name, ioaddr, dev->irq, (int)inl(ioaddr + GENCTL),
-			   ep->full_duplex ? "full" : "half");
+			   ep->mii.full_duplex ? "full" : "half");
 
 	/* Set the timer to switch to check for link beat and perhaps switch
 	   to an alternate media type. */
@@ -810,7 +811,7 @@
 
 	ep->tx_threshold = TX_FIFO_THRESH;
 	outl(ep->tx_threshold, ioaddr + TxThresh);
-	outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+	outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
 	outl(ep->rx_ring_dma + (ep->cur_rx%RX_RING_SIZE)*
 		sizeof(struct epic_rx_desc), ioaddr + PRxCDAR);
 	outl(ep->tx_ring_dma + (ep->dirty_tx%TX_RING_SIZE)*
@@ -836,20 +837,20 @@
 {
 	struct epic_private *ep = dev->priv;
 	long ioaddr = dev->base_addr;
-	int mii_reg5 = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], 5) : 0;
-	int negotiated = mii_reg5 & ep->advertising;
+	int mii_lpa = ep->mii_phy_cnt ? mdio_read(dev, ep->phys[0], MII_LPA) : 0;
+	int negotiated = mii_lpa & ep->mii.advertising;
 	int duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
 
-	if (ep->duplex_lock)
+	if (ep->mii.duplex_lock)
 		return;
-	if (mii_reg5 == 0xffff)		/* Bogus read */
+	if (mii_lpa == 0xffff)		/* Bogus read */
 		return;
-	if (ep->full_duplex != duplex) {
-		ep->full_duplex = duplex;
+	if (ep->mii.full_duplex != duplex) {
+		ep->mii.full_duplex = duplex;
 		printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
 			   " partner capability of %4.4x.\n", dev->name,
-			   ep->full_duplex ? "full" : "half", ep->phys[0], mii_reg5);
-		outl(ep->full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
+			   ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa);
+		outl(ep->mii.full_duplex ? 0x7F : 0x79, ioaddr + TxCtrl);
 	}
 }
 
@@ -913,7 +914,6 @@
 	ep->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
 	ep->dirty_tx = ep->cur_tx = 0;
 	ep->cur_rx = ep->dirty_rx = 0;
-	ep->last_rx_time = jiffies;
 	ep->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
 
 	/* Initialize all Rx descriptors. */
@@ -1371,17 +1371,66 @@
 		return -EFAULT;
 
 	switch (ethcmd) {
-	case ETHTOOL_GDRVINFO:
-		{
-			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
-			strcpy (info.driver, DRV_NAME);
-			strcpy (info.version, DRV_VERSION);
-			strcpy (info.bus_info, np->pci_dev->slot_name);
-			if (copy_to_user (useraddr, &info, sizeof (info)))
-				return -EFAULT;
-			return 0;
-		}
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, np->pci_dev->slot_name);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
 
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&np->mii);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&np->mii);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
 	default:
 		break;
 	}
@@ -1432,11 +1481,11 @@
 			switch (data->reg_num) {
 			case 0:
 				/* Check for autonegotiation on or reset. */
-				ep->duplex_lock = (value & 0x9000) ? 0 : 1;
-				if (ep->duplex_lock)
-					ep->full_duplex = (value & 0x0100) ? 1 : 0;
+				ep->mii.duplex_lock = (value & 0x9000) ? 0 : 1;
+				if (ep->mii.duplex_lock)
+					ep->mii.full_duplex = (value & 0x0100) ? 1 : 0;
 				break;
-			case 4: ep->advertising = value; break;
+			case 4: ep->mii.advertising = value; break;
 			}
 			/* Perhaps check_duplex(dev), depending on chip semantics. */
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/fc/Makefile linux/drivers/net/fc/Makefile
--- linux.orig/drivers/net/fc/Makefile	Fri Dec 29 22:07:22 2000
+++ linux/drivers/net/fc/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for linux/drivers/net/fc
 #
-# 9 Aug 2000, Christoph Hellwig <hch@caldera.de>
+# 9 Aug 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/fealnx.c linux/drivers/net/fealnx.c
--- linux.orig/drivers/net/fealnx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/fealnx.c	Mon Jan 14 17:27:24 2002
@@ -15,8 +15,19 @@
 
 	Support information and updates available at
 	http://www.scyld.com/network/pci-skeleton.html
+
+	Linux kernel updates:
+
+	Version 2.51, Nov 17, 2001 (jgarzik):
+	- Add ethtool support
+	- Replace some MII-related magic numbers with constants
+
 */
 
+#define DRV_NAME	"fealnx"
+#define DRV_VERSION	"2.51"
+#define DRV_RELDATE	"Nov-17-2001"
+
 static int debug;		/* 1-> print debug message */
 static int max_interrupt_work = 20;
 
@@ -72,13 +83,15 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 #include <linux/mii.h>
+#include <linux/ethtool.h>
 #include <asm/processor.h>	/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
+#include <asm/uaccess.h>
 
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
-KERN_INFO "fealnx.c:v2.50 1/17/2001\n";
+KERN_INFO DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE "\n";
 
 
 /* This driver was written to use PCI memory space, however some x86 systems
@@ -379,6 +392,8 @@
 	dma_addr_t rx_ring_dma;
 	dma_addr_t tx_ring_dma;
 
+	spinlock_t lock;
+
 	struct net_device_stats stats;
 
 	/* Media monitoring timer. */
@@ -403,19 +418,17 @@
 	unsigned int linkok;
 	unsigned int line_speed;
 	unsigned int duplexmode;
-	unsigned int full_duplex:1;	/* Full-duplex operation requested. */
-	unsigned int duplex_lock:1;
-	unsigned int medialock:1;	/* Do not sense media. */
 	unsigned int default_port:4;	/* Last dev->if_port value. */
 	unsigned int PHYType;
 
 	/* MII transceiver section. */
 	int mii_cnt;		/* MII device addresses. */
 	unsigned char phys[2];	/* MII device addresses. */
+	struct mii_if_info mii;
 };
 
 
-static unsigned int mdio_read(struct net_device *dev, int phy_id, int location);
+static int mdio_read(struct net_device *dev, int phy_id, int location);
 static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
 static int netdev_open(struct net_device *dev);
 static void getlinktype(struct net_device *dev);
@@ -539,9 +552,13 @@
 
 	/* Make certain the descriptor lists are aligned. */
 	np = dev->priv;
+	spin_lock_init(&np->lock);
 	np->pci_dev = pdev;
 	np->flags = skel_netdrv_tbl[chip_id].flags;
 	pci_set_drvdata(pdev, dev);
+	np->mii.dev = dev;
+	np->mii.mdio_read = mdio_read;
+	np->mii.mdio_write = mdio_write;
 
 	ring_space = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &ring_dma);
 	if (!ring_space) {
@@ -606,6 +623,7 @@
 		else
 			np->PHYType = OtherPHY;
 	}
+	np->mii.phy_id = np->phys[0];
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -613,17 +631,14 @@
 	/* The lower four bits are the media type. */
 	if (option > 0) {
 		if (option & 0x200)
-			np->full_duplex = 1;
+			np->mii.full_duplex = 1;
 		np->default_port = option & 15;
-
-		if (np->default_port)
-			np->medialock = 1;
 	}
 
 	if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
-		np->full_duplex = full_duplex[card_idx];
+		np->mii.full_duplex = full_duplex[card_idx];
 
-	if (np->full_duplex) {
+	if (np->mii.full_duplex) {
 		printk(KERN_INFO "%s: Media type forced to Full Duplex.\n", dev->name);
 /* 89/6/13 add, (begin) */
 //      if (np->PHYType==MarvellPHY)
@@ -636,10 +651,10 @@
 		}
 /* 89/6/13 add, (end) */
 		if (np->flags == HAS_MII_XCVR)
-			mdio_write(dev, np->phys[0], 4, 0x141);
+			mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL);
 		else
-			writel(0x141, dev->base_addr + ANARANLPAR);
-		np->duplex_lock = 1;
+			writel(ADVERTISE_FULL, dev->base_addr + ANARANLPAR);
+		np->mii.duplex_lock = 1;
 	}
 
 	/* The chip-specific entries in the device structure. */
@@ -787,7 +802,7 @@
 }
 
 
-static unsigned int mdio_read(struct net_device *dev, int phyad, int regad)
+static int mdio_read(struct net_device *dev, int phyad, int regad)
 {
 	long miiport = dev->base_addr + MANAGEMENT;
 	ulong miir;
@@ -821,7 +836,7 @@
 	miir &= ~MASK_MIIR_MII_MDC;
 	writel(miir, miiport);
 
-	return data;
+	return data & 0xffff;
 }
 
 
@@ -941,7 +956,7 @@
 // 89/9/1 modify,
 //   np->crvalue = 0x00e40001;    /* tx store and forward, tx/rx enable */
 	np->crvalue |= 0x00e40001;	/* tx store and forward, tx/rx enable */
-	np->full_duplex = np->duplex_lock;
+	np->mii.full_duplex = np->mii.duplex_lock;
 	getlinkstatus(dev);
 	if (np->linkok)
 		getlinktype(dev);
@@ -990,7 +1005,7 @@
 		}
 	} else {
 		for (i = 0; i < DelayTime; ++i) {
-			if (mdio_read(dev, np->phys[0], 1) & 0x4) {
+			if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) {
 				np->linkok = 1;
 				return;
 			}
@@ -1475,7 +1490,7 @@
 						np->stats.tx_window_errors++;
 					if (tx_status & UDF)
 						np->stats.tx_fifo_errors++;
-					if ((tx_status & HF) && np->full_duplex == 0)
+					if ((tx_status & HF) && np->mii.full_duplex == 0)
 						np->stats.tx_heartbeat_errors++;
 
 #ifdef ETHER_STATS
@@ -1771,12 +1786,91 @@
 	writel(np->crvalue, ioaddr + TCRRCR);
 }
 
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	struct netdev_private *np = dev->priv;
+	u32 ethcmd;
+
+	if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+		return -EFAULT;
+
+	switch (ethcmd) {
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, np->pci_dev->slot_name);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&np->mii, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&np->mii);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&np->mii);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
 
 static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
 
 	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
 	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
 	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
 		data->phy_id = ((struct netdev_private *) dev->priv)->phys[0] & 0x1f;
@@ -1789,7 +1883,7 @@
 
 	case SIOCSMIIREG:		/* Write MII PHY register. */
 	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
-		if (!suser())
+		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
 		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, data->val_in);
 		return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/gmac.c linux/drivers/net/gmac.c
--- linux.orig/drivers/net/gmac.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/gmac.c	Wed Dec 26 16:34:02 2001
@@ -36,7 +36,8 @@
 #include <asm/prom.h>
 #include <asm/io.h>
 #include <asm/pgtable.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/keylargo.h>
 #include <asm/pci-bridge.h>
 #ifdef CONFIG_PMAC_PBOOK
@@ -296,6 +297,7 @@
 	}
 }
 
+#ifdef CONFIG_PMAC_PBOOK
 /* Power management: stop PHY chip for suspend mode
  * 
  * TODO: This will have to be modified is WOL is to be supported.
@@ -441,6 +443,7 @@
 		GM_OUT(GM_RX_CONF, 0);
 	}
 }
+#endif
 
 static int
 mii_do_reset_phy(struct gmac *gm, int phy_addr)
@@ -585,8 +588,8 @@
 	gm->phy_type = PHY_UNKNOWN;
 
 	/* Hard reset the PHY */
-	feature_gmac_phy_reset(gm->of_node);
-	
+	pmac_call_feature(PMAC_FTR_GMAC_PHY_RESET, gm->of_node, 0, 0);
+		
 	/* Find the PHY */
 	for(i=0; i<=31; i++) {
 		mii_control = mii_read(gm, i, MII_CR);
@@ -683,7 +686,7 @@
 gmac_set_power(struct gmac *gm, int power_up)
 {
 	if (power_up) {
-		feature_set_gmac_power(gm->of_node, 1);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gm->of_node, 0, 1);
 		if (gm->pci_devfn != 0xff) {
 			u16 cmd;
 			
@@ -708,7 +711,7 @@
 	    			PCI_CACHE_LINE_SIZE, 8);
 		}
 	} else {
-		feature_set_gmac_power(gm->of_node, 0);
+		pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gm->of_node, 0, 0);
 	}
 }
 
@@ -1617,6 +1620,11 @@
 	SET_MODULE_OWNER(dev);
 
 	gm = dev->priv;
+	gm->of_node = gmac;
+	if (!request_OF_resource(gmac, 0, " (gmac)")) {
+		printk(KERN_ERR "GMAC: can't request IO resource !\n");
+		goto out_unreg;
+	}
 	dev->base_addr = gmac->addrs[0].address;
 	gm->regs = (volatile unsigned int *)
 		ioremap(gmac->addrs[0].address, 0x10000);
@@ -1626,7 +1634,6 @@
 	}
 	dev->irq = gmac->intrs[0].line;
 	gm->dev = dev;
-	gm->of_node = gmac;
 
 	spin_lock_init(&gm->lock);
 	
@@ -1667,6 +1674,8 @@
 
 out_unreg:
 	unregister_netdev(dev);
+	if (gm->of_node)
+		release_OF_resource(gm->of_node, 0);
 	kfree(dev);
 out_rxdesc:
 	free_page(rx_descpage);
@@ -1677,6 +1686,7 @@
 MODULE_AUTHOR("Paul Mackerras/Ben Herrenschmidt");
 MODULE_DESCRIPTION("PowerMac GMAC driver.");
 MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
 
 static void __exit gmac_cleanup_module(void)
 {
@@ -1694,6 +1704,7 @@
 		iounmap((void *) gm->regs);
 		free_page(gm->tx_desc_page);
 		free_page(gm->rx_desc_page);
+		release_OF_resource(gm->of_node, 0);
 		gmacs = gm->next_gmac;
 		kfree(dev);
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamachi.c linux/drivers/net/hamachi.c
--- linux.orig/drivers/net/hamachi.c	Tue Oct  9 22:13:03 2001
+++ linux/drivers/net/hamachi.c	Wed Feb 13 17:37:40 2002
@@ -1981,7 +1981,7 @@
 }
 
 
-static void __exit hamachi_remove_one (struct pci_dev *pdev)
+static void __devexit hamachi_remove_one (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 
@@ -2011,7 +2011,7 @@
 	name:		DRV_NAME,
 	id_table:	hamachi_pci_tbl,
 	probe:		hamachi_init_one,
-	remove:		hamachi_remove_one,
+	remove:		__devexit_p(hamachi_remove_one),
 };
 
 static int __init hamachi_init (void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/6pack.c linux/drivers/net/hamradio/6pack.c
--- linux.orig/drivers/net/hamradio/6pack.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/hamradio/6pack.c	Wed Feb 13 17:43:54 2002
@@ -700,7 +700,6 @@
 /* Initialize 6pack control device -- register 6pack line discipline */
 
 static char msg_banner[]  __initdata = KERN_INFO "AX.25: 6pack driver, " SIXPACK_VERSION " (dynamic channels, max=%d)\n";
-static char msg_invparm[] __initdata = KERN_ERR  "6pack: sixpack_maxdev parameter too large.\n";
 static char msg_nomem[]   __initdata = KERN_ERR  "6pack: can't allocate sixpack_ctrls[] array! No 6pack available.\n";
 static char msg_regfail[] __initdata = KERN_ERR  "6pack: can't register line discipline (err = %d)\n";
 
@@ -1063,6 +1062,7 @@
 
 MODULE_AUTHOR("Andreas Könsgen <ajk@ccac.rwth-aachen.de>");
 MODULE_DESCRIPTION("6pack driver for AX.25");
+MODULE_LICENSE("GPL");
 
 module_init(sixpack_init_driver);
 module_exit(sixpack_exit_driver);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/Makefile linux/drivers/net/hamradio/Makefile
--- linux.orig/drivers/net/hamradio/Makefile	Thu Jan  4 20:50:12 2001
+++ linux/drivers/net/hamradio/Makefile	Mon Feb  4 17:38:23 2002
@@ -7,7 +7,7 @@
 #               Joerg Reuter DL1BKE <jreuter@yaina.de>
 #
 # 20000806	Rewritten to use lists instead of if-statements.
-#		Christoph Hellwig <hch@caldera.de>
+#		Christoph Hellwig <hch@infradead.org>
 #
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/baycom_epp.c linux/drivers/net/hamradio/baycom_epp.c
--- linux.orig/drivers/net/hamradio/baycom_epp.c	Mon Sep 10 16:04:53 2001
+++ linux/drivers/net/hamradio/baycom_epp.c	Wed Feb 13 17:43:54 2002
@@ -1414,6 +1414,7 @@
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("Baycom epp amateur radio modem driver");
+MODULE_LICENSE("GPL");
 
 /* --------------------------------------------------------------------- */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/baycom_par.c linux/drivers/net/hamradio/baycom_par.c
--- linux.orig/drivers/net/hamradio/baycom_par.c	Wed Aug 15 08:22:15 2001
+++ linux/drivers/net/hamradio/baycom_par.c	Wed Feb 13 17:43:54 2002
@@ -493,6 +493,7 @@
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("Baycom par96 and picpar amateur radio modem driver");
+MODULE_LICENSE("GPL");
 
 /* --------------------------------------------------------------------- */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/baycom_ser_fdx.c linux/drivers/net/hamradio/baycom_ser_fdx.c
--- linux.orig/drivers/net/hamradio/baycom_ser_fdx.c	Wed Aug 15 08:22:15 2001
+++ linux/drivers/net/hamradio/baycom_ser_fdx.c	Wed Feb 13 17:43:54 2002
@@ -609,6 +609,7 @@
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("Baycom ser12 full duplex amateur radio modem driver");
+MODULE_LICENSE("GPL");
 
 /* --------------------------------------------------------------------- */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/baycom_ser_hdx.c linux/drivers/net/hamradio/baycom_ser_hdx.c
--- linux.orig/drivers/net/hamradio/baycom_ser_hdx.c	Wed Aug 15 08:22:15 2001
+++ linux/drivers/net/hamradio/baycom_ser_hdx.c	Wed Feb 13 17:43:54 2002
@@ -649,6 +649,7 @@
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("Baycom ser12 half duplex amateur radio modem driver");
+MODULE_LICENSE("GPL");
 
 /* --------------------------------------------------------------------- */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/bpqether.c linux/drivers/net/hamradio/bpqether.c
--- linux.orig/drivers/net/hamradio/bpqether.c	Thu Sep 13 23:04:43 2001
+++ linux/drivers/net/hamradio/bpqether.c	Wed Feb 13 17:43:54 2002
@@ -645,5 +645,6 @@
 
 MODULE_AUTHOR("Joerg Reuter DL1BKE <jreuter@yaina.de>");
 MODULE_DESCRIPTION("Transmit and receive AX.25 packets over Ethernet");
+MODULE_LICENSE("GPL");
 module_init(bpq_init_driver);
 module_exit(bpq_cleanup_driver);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/dmascc.c linux/drivers/net/hamradio/dmascc.c
--- linux.orig/drivers/net/hamradio/dmascc.c	Wed Apr 18 21:40:06 2001
+++ linux/drivers/net/hamradio/dmascc.c	Wed Feb 13 17:43:54 2002
@@ -306,6 +306,7 @@
 MODULE_AUTHOR("Klaus Kudielka");
 MODULE_DESCRIPTION("Driver for high-speed SCC boards");
 MODULE_PARM(io, "1-" __MODULE_STRING(MAX_NUM_DEVS) "i");
+MODULE_LICENSE("GPL");
 
 
 int init_module(void) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/hdlcdrv.c linux/drivers/net/hamradio/hdlcdrv.c
--- linux.orig/drivers/net/hamradio/hdlcdrv.c	Wed Aug 15 08:22:15 2001
+++ linux/drivers/net/hamradio/hdlcdrv.c	Wed Feb 13 17:43:54 2002
@@ -901,6 +901,7 @@
 
 MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
 MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+MODULE_LICENSE("GPL");
 module_init(hdlcdrv_init_driver);
 module_exit(hdlcdrv_cleanup_driver);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/mkiss.c linux/drivers/net/hamradio/mkiss.c
--- linux.orig/drivers/net/hamradio/mkiss.c	Thu Sep 13 23:04:43 2001
+++ linux/drivers/net/hamradio/mkiss.c	Wed Feb 13 17:43:54 2002
@@ -1008,6 +1008,7 @@
 MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
 MODULE_PARM(ax25_maxdev, "i");
 MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
+MODULE_LICENSE("GPL");
 
 module_init(mkiss_init_driver);
 module_exit(mkiss_exit_driver);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/scc.c linux/drivers/net/hamradio/scc.c
--- linux.orig/drivers/net/hamradio/scc.c	Thu Oct 25 20:58:15 2001
+++ linux/drivers/net/hamradio/scc.c	Wed Feb 13 17:43:54 2002
@@ -2179,5 +2179,6 @@
 MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
 MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");
 MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio");
+MODULE_LICENSE("GPL");
 module_init(scc_init_driver);
 module_exit(scc_cleanup_driver);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/hamradio/yam.c linux/drivers/net/hamradio/yam.c
--- linux.orig/drivers/net/hamradio/yam.c	Sun Sep 30 19:26:08 2001
+++ linux/drivers/net/hamradio/yam.c	Wed Feb 13 17:43:54 2002
@@ -308,7 +308,8 @@
 static void delay(int ms)
 {
 	unsigned long timeout = jiffies + ((ms * HZ) / 1000);
-	while (jiffies < timeout);
+	while (time_before(jiffies, timeout))
+		cpu_relax();
 }
 
 /*
@@ -1177,6 +1178,7 @@
 
 MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");
 MODULE_DESCRIPTION("Yam amateur radio modem driver");
+MODULE_LICENSE("GPL");
 
 module_init(yam_init_driver);
 module_exit(yam_cleanup_driver);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/Makefile linux/drivers/net/irda/Makefile
--- linux.orig/drivers/net/irda/Makefile	Thu Oct 25 20:53:47 2001
+++ linux/drivers/net/irda/Makefile	Mon Feb  4 17:38:23 2002
@@ -2,7 +2,7 @@
 #
 # Makefile for the Linux IrDA infrared port device drivers.
 #
-# 9 Aug 2000, Christoph Hellwig <hch@caldera.de>
+# 9 Aug 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/ali-ircc.c linux/drivers/net/irda/ali-ircc.c
--- linux.orig/drivers/net/irda/ali-ircc.c	Sun Sep 30 19:26:06 2001
+++ linux/drivers/net/irda/ali-ircc.c	Mon Feb  4 16:55:14 2002
@@ -1496,7 +1496,7 @@
 		if (mtt) 
 		{
 			/* Check how much time we have used already */
-			get_fast_time(&self->now);
+			do_gettimeofday(&self->now);
 			
 			diff = self->now.tv_usec - self->stamp.tv_usec;
 			/* self->stamp is set from ali_ircc_dma_receive_complete() */
@@ -1913,7 +1913,7 @@
 			 * reduce the min turn time a bit since we will know
 			 * how much time we have used for protocol processing
 			 */
-			get_fast_time(&self->stamp);
+			do_gettimeofday(&self->stamp);
 
 			skb = dev_alloc_skb(len+1);
 			if (skb == NULL)  
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/irda-usb.c linux/drivers/net/irda/irda-usb.c
--- linux.orig/drivers/net/irda/irda-usb.c	Fri Nov  9 22:22:17 2001
+++ linux/drivers/net/irda/irda-usb.c	Mon Feb  4 16:55:14 2002
@@ -427,7 +427,7 @@
 		mtt = irda_get_mtt(skb);
 		if (mtt) {
 			int diff;
-			get_fast_time(&self->now);
+			do_gettimeofday(&self->now);
 			diff = self->now.tv_usec - self->stamp.tv_usec;
 #ifdef IU_USB_MIN_RTT
 			/* Factor in USB delays -> Get rid of udelay() that
@@ -798,7 +798,7 @@
 	 * reduce the min turn time a bit since we will know
 	 * how much time we have used for protocol processing
 	 */
-        get_fast_time(&self->stamp);
+        do_gettimeofday(&self->stamp);
 
 	/* Fix skb, and remove USB-IrDA header */
 	skb_put(skb, purb->actual_length);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/irport.c linux/drivers/net/irda/irport.c
--- linux.orig/drivers/net/irda/irport.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/irda/irport.c	Thu Jan 10 18:12:40 2002
@@ -140,7 +140,7 @@
 {
 	struct net_device *dev;
 	struct irport_cb *self;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(0, __FUNCTION__ "()\n");
@@ -169,13 +169,12 @@
         self->io.fifo_size = 16;
 
 	/* Lock the port that we need */
-	ret = check_region(self->io.sir_base, self->io.sir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.sir_base, self->io.sir_ext, driver_name);
+	if (!ret) { 
 		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
 			   self->io.sir_base);
 		return NULL;
 	}
-	request_region(self->io.sir_base, self->io.sir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/nsc-ircc.c linux/drivers/net/irda/nsc-ircc.c
--- linux.orig/drivers/net/irda/nsc-ircc.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/irda/nsc-ircc.c	Mon Feb  4 16:55:14 2002
@@ -246,7 +246,7 @@
 	struct net_device *dev;
 	struct nsc_ircc_cb *self;
         struct pm_dev *pmdev;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(2, __FUNCTION__ "()\n");
@@ -282,15 +282,14 @@
         self->io.fifo_size = 32;
 	
 	/* Reserve the ioports that we need */
-	ret = check_region(self->io.fir_base, self->io.fir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+	if (!ret) {
 		WARNING(__FUNCTION__ "(), can't get iobase of 0x%03x\n",
 			self->io.fir_base);
 		dev_self[i] = NULL;
 		kfree(self);
 		return -ENODEV;
 	}
-	request_region(self->io.fir_base, self->io.fir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
@@ -1164,7 +1163,7 @@
 		mtt = irda_get_mtt(skb);
 		if (mtt) {
 			/* Check how much time we have used already */
-			get_fast_time(&self->now);
+			do_gettimeofday(&self->now);
 			diff = self->now.tv_usec - self->stamp.tv_usec;
 			if (diff < 0) 
 				diff += 1000000;
@@ -1536,7 +1535,7 @@
 			 * reduce the min turn time a bit since we will know
 			 * how much time we have used for protocol processing
 			 */
-			get_fast_time(&self->stamp);
+			do_gettimeofday(&self->stamp);
 
 			skb = dev_alloc_skb(len+1);
 			if (skb == NULL)  {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/irda/w83977af_ir.c linux/drivers/net/irda/w83977af_ir.c
--- linux.orig/drivers/net/irda/w83977af_ir.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/irda/w83977af_ir.c	Thu Jan 10 18:12:40 2002
@@ -160,7 +160,7 @@
 {
 	struct net_device *dev;
         struct w83977af_ir *self;
-	int ret;
+	void *ret;
 	int err;
 
 	IRDA_DEBUG(0, __FUNCTION__ "()\n");
@@ -190,14 +190,13 @@
         self->io.fifo_size = 32;
 
 	/* Lock the port that we need */
-	ret = check_region(self->io.fir_base, self->io.fir_ext);
-	if (ret < 0) { 
+	ret = request_region(self->io.fir_base, self->io.fir_ext, driver_name);
+	if (!ret) { 
 		IRDA_DEBUG(0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
 		      self->io.fir_base);
 		/* w83977af_cleanup( self);  */
 		return -ENODEV;
 	}
-	request_region(self->io.fir_base, self->io.fir_ext, driver_name);
 
 	/* Initialize QoS for this device */
 	irda_init_max_qos_capabilies(&self->qos);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/mace.c linux/drivers/net/mace.c
--- linux.orig/drivers/net/mace.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/mace.c	Tue Jan  8 18:08:44 2002
@@ -24,18 +24,18 @@
 static struct net_device *mace_devs;
 static int port_aaui = -1;
 
-MODULE_PARM(port_aaui, "i");
-MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
+#define N_RX_RING		8
+#define N_TX_RING		6
+#define MAX_TX_ACTIVE		1
+#define NCMDS_TX		1	/* dma commands per element in tx ring */
+#define RX_BUFLEN		(ETH_FRAME_LEN + 8)
+#define TX_TIMEOUT		HZ	/* 1 second */
 
-#define N_RX_RING	8
-#define N_TX_RING	6
-#define MAX_TX_ACTIVE	1
-#define NCMDS_TX	1	/* dma commands per element in tx ring */
-#define RX_BUFLEN	(ETH_FRAME_LEN + 8)
-#define TX_TIMEOUT	HZ	/* 1 second */
+/* Chip rev needs workaround on HW & multicast addr change */
+#define BROKEN_ADDRCHG_REV	0x0941
 
 /* Bits in transmit DMA status */
-#define TX_DMA_ERR	0x80
+#define TX_DMA_ERR		0x80
 
 struct mace_data {
     volatile struct mace *mace;
@@ -59,6 +59,8 @@
     struct timer_list tx_timeout;
     int timeout_active;
     int port_aaui;
+    int chipid;
+    struct device_node* of_node;
     struct net_device *next_mace;
 };
 
@@ -152,9 +154,25 @@
 	SET_MODULE_OWNER(dev);
 
 	mp = dev->priv;
+	mp->of_node = mace;
+	
+	if (!request_OF_resource(mace, 0, " (mace)")) {
+		printk(KERN_ERR "MACE: can't request IO resource !\n");
+		goto err_out;
+	}
+	if (!request_OF_resource(mace, 1, " (mace tx dma)")) {
+		printk(KERN_ERR "MACE: can't request TX DMA resource !\n");
+		goto err_out;
+	}
+
+	if (!request_OF_resource(mace, 2, " (mace tx dma)")) {
+		printk(KERN_ERR "MACE: can't request RX DMA resource !\n");
+		goto err_out;
+	}
+
 	dev->base_addr = mace->addrs[0].address;
 	mp->mace = (volatile struct mace *)
-		ioremap(mace->addrs[0].address, 0x1000);
+				ioremap(mace->addrs[0].address, 0x1000);
 	dev->irq = mace->intrs[0].line;
 
 	printk(KERN_INFO "%s: MACE at", dev->name);
@@ -163,8 +181,10 @@
 		dev->dev_addr[j] = rev? bitrev(addr[j]): addr[j];
 		printk("%c%.2x", (j? ':': ' '), dev->dev_addr[j]);
 	}
-	printk(", chip revision %d.%d\n",
-		in_8(&mp->mace->chipid_hi), in_8(&mp->mace->chipid_lo));
+	mp->chipid = (in_8(&mp->mace->chipid_hi) << 8) |
+			in_8(&mp->mace->chipid_lo);
+	printk(", chip revision %d.%d\n", mp->chipid >> 8, mp->chipid & 0xff);
+		
 
 	mp = (struct mace_data *) dev->priv;
 	mp->maccc = ENXMT | ENRCV;
@@ -221,6 +241,16 @@
 
 	mp->next_mace = mace_devs;
 	mace_devs = dev;
+	return;
+	
+err_out:
+	unregister_netdev(dev);
+	if (mp->of_node) {
+		release_OF_resource(mp->of_node, 0);
+		release_OF_resource(mp->of_node, 1);
+		release_OF_resource(mp->of_node, 2);
+	}
+	kfree(dev);
 }
 
 static void dbdma_reset(volatile struct dbdma_regs *dma)
@@ -273,14 +303,19 @@
     __mace_set_address(dev, dev->dev_addr);
 
     /* clear the multicast filter */
-    out_8(&mb->iac, ADDRCHG | LOGADDR);
-    while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	;
-    for (i = 0; i < 8; ++i) {
-	out_8(&mb->ladrf, 0);
+    if (mp->chipid == BROKEN_ADDRCHG_REV)
+	out_8(&mb->iac, LOGADDR);
+    else {
+	out_8(&mb->iac, ADDRCHG | LOGADDR);
+	while ((in_8(&mb->iac) & ADDRCHG) != 0)
+		;
     }
+    for (i = 0; i < 8; ++i)
+	out_8(&mb->ladrf, 0);
+
     /* done changing address */
-    out_8(&mb->iac, 0);
+    if (mp->chipid != BROKEN_ADDRCHG_REV)
+	out_8(&mb->iac, 0);
 
     if (mp->port_aaui)
     	out_8(&mb->plscc, PORTSEL_AUI + ENPLSIO);
@@ -290,16 +325,23 @@
 
 static void __mace_set_address(struct net_device *dev, void *addr)
 {
-    volatile struct mace *mb = ((struct mace_data *) dev->priv)->mace;
+    struct mace_data *mp = (struct mace_data *) dev->priv;
+    volatile struct mace *mb = mp->mace;
     unsigned char *p = addr;
     int i;
 
     /* load up the hardware address */
-    out_8(&mb->iac, ADDRCHG | PHYADDR);
-    while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	;
+    if (mp->chipid == BROKEN_ADDRCHG_REV)
+    	out_8(&mb->iac, PHYADDR);
+    else {
+    	out_8(&mb->iac, ADDRCHG | PHYADDR);
+	while ((in_8(&mb->iac) & ADDRCHG) != 0)
+	    ;
+    }
     for (i = 0; i < 6; ++i)
 	out_8(&mb->padr, dev->dev_addr[i] = p[i]);
+    if (mp->chipid != BROKEN_ADDRCHG_REV)
+        out_8(&mb->iac, 0);
 }
 
 static int mace_set_address(struct net_device *dev, void *addr)
@@ -312,7 +354,6 @@
 
     __mace_set_address(dev, addr);
 
-    out_8(&mb->iac, 0);
     /* note: setting ADDRCHG clears ENRCV */
     out_8(&mb->maccc, mp->maccc);
 
@@ -557,12 +598,17 @@
 	printk("\n");
 #endif
 
-	out_8(&mb->iac, ADDRCHG | LOGADDR);
-	while ((in_8(&mb->iac) & ADDRCHG) != 0)
-	    ;
-	for (i = 0; i < 8; ++i) {
-	    out_8(&mb->ladrf, multicast_filter[i]);
+	if (mp->chipid == BROKEN_ADDRCHG_REV)
+	    out_8(&mb->iac, LOGADDR);
+	else {
+	    out_8(&mb->iac, ADDRCHG | LOGADDR);
+	    while ((in_8(&mb->iac) & ADDRCHG) != 0)
+		;
 	}
+	for (i = 0; i < 8; ++i)
+	    out_8(&mb->ladrf, multicast_filter[i]);
+	if (mp->chipid != BROKEN_ADDRCHG_REV)
+	    out_8(&mb->iac, 0);
     }
     /* reset maccc */
     out_8(&mb->maccc, mp->maccc);
@@ -913,7 +959,10 @@
 
 MODULE_AUTHOR("Paul Mackerras");
 MODULE_DESCRIPTION("PowerMac MACE driver.");
+MODULE_PARM(port_aaui, "i");
+MODULE_PARM_DESC(port_aaui, "MACE uses AAUI port (0-1)");
 MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
 
 static void __exit mace_cleanup (void)
 {
@@ -921,19 +970,23 @@
     struct mace_data *mp;
 
     while ((dev = mace_devs) != 0) {
-	mp = (struct mace_data *) mace_devs->priv;
-	mace_devs = mp->next_mace;
+		mp = (struct mace_data *) mace_devs->priv;
+		mace_devs = mp->next_mace;
 
-	free_irq(dev->irq, dev);
-	free_irq(mp->tx_dma_intr, dev);
-	free_irq(mp->rx_dma_intr, dev);
+		unregister_netdev(dev);
+		free_irq(dev->irq, dev);
+		free_irq(mp->tx_dma_intr, dev);
+		free_irq(mp->rx_dma_intr, dev);
+
+		release_OF_resource(mp->of_node, 0);
+		release_OF_resource(mp->of_node, 1);
+		release_OF_resource(mp->of_node, 2);
 
-	unregister_netdev(dev);
-	kfree(dev);
+		kfree(dev);
     }
     if (dummy_buf != NULL) {
-	kfree(dummy_buf);
-	dummy_buf = NULL;
+		kfree(dummy_buf);
+		dummy_buf = NULL;
     }
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/mii.c linux/drivers/net/mii.c
--- linux.orig/drivers/net/mii.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/net/mii.c	Wed Feb  6 20:42:02 2002
@@ -0,0 +1,178 @@
+/*
+
+	mii.c: MII interface library
+
+	Maintained by Jeff Garzik <jgarzik@mandrakesoft.com>
+	Copyright 2001 Jeff Garzik
+
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
+{
+	struct net_device *dev = mii->dev;
+	u32 advert, bmcr, lpa, nego;
+
+	ecmd->supported =
+	    (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+	     SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+	     SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
+
+	/* only supports twisted-pair */
+	ecmd->port = PORT_MII;
+
+	/* only supports internal transceiver */
+	ecmd->transceiver = XCVR_INTERNAL;
+
+	/* this isn't fully supported at higher layers */
+	ecmd->phy_address = mii->phy_id;
+
+	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+	advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
+	if (advert & ADVERTISE_10HALF)
+		ecmd->advertising |= ADVERTISED_10baseT_Half;
+	if (advert & ADVERTISE_10FULL)
+		ecmd->advertising |= ADVERTISED_10baseT_Full;
+	if (advert & ADVERTISE_100HALF)
+		ecmd->advertising |= ADVERTISED_100baseT_Half;
+	if (advert & ADVERTISE_100FULL)
+		ecmd->advertising |= ADVERTISED_100baseT_Full;
+
+	bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
+	lpa = mii->mdio_read(dev, mii->phy_id, MII_LPA);
+	if (bmcr & BMCR_ANENABLE) {
+		ecmd->advertising |= ADVERTISED_Autoneg;
+		ecmd->autoneg = AUTONEG_ENABLE;
+		
+		nego = mii_nway_result(advert & lpa);
+		if (nego == LPA_100FULL || nego == LPA_100HALF)
+			ecmd->speed = SPEED_100;
+		else
+			ecmd->speed = SPEED_10;
+		if (nego == LPA_100FULL || nego == LPA_10FULL) {
+			ecmd->duplex = DUPLEX_FULL;
+			mii->full_duplex = 1;
+		} else {
+			ecmd->duplex = DUPLEX_HALF;
+			mii->full_duplex = 0;
+		}
+	} else {
+		ecmd->autoneg = AUTONEG_DISABLE;
+
+		ecmd->speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10;
+		ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+	}
+
+	/* ignore maxtxpkt, maxrxpkt for now */
+
+	return 0;
+}
+
+int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
+{
+	struct net_device *dev = mii->dev;
+
+	if (ecmd->speed != SPEED_10 && ecmd->speed != SPEED_100)
+		return -EINVAL;
+	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+		return -EINVAL;
+	if (ecmd->port != PORT_MII)
+		return -EINVAL;
+	if (ecmd->transceiver != XCVR_INTERNAL)
+		return -EINVAL;
+	if (ecmd->phy_address != mii->phy_id)
+		return -EINVAL;
+	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
+		return -EINVAL;
+				  
+	/* ignore supported, maxtxpkt, maxrxpkt */
+	
+	if (ecmd->autoneg == AUTONEG_ENABLE) {
+		u32 bmcr, advert, tmp;
+
+		if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
+					  ADVERTISED_10baseT_Full |
+					  ADVERTISED_100baseT_Half |
+					  ADVERTISED_100baseT_Full)) == 0)
+			return -EINVAL;
+
+		/* 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)
+			tmp |= ADVERTISE_10HALF;
+		if (ADVERTISED_10baseT_Full)
+			tmp |= ADVERTISE_10FULL;
+		if (ADVERTISED_100baseT_Half)
+			tmp |= ADVERTISE_100HALF;
+		if (ADVERTISED_100baseT_Full)
+			tmp |= ADVERTISE_100FULL;
+		if (advert != tmp) {
+			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
+			mii->advertising = tmp;
+		}
+		
+		/* turn on autonegotiation, and force a renegotiate */
+		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
+		bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
+		mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
+
+		mii->duplex_lock = 0;
+	} else {
+		u32 bmcr, tmp;
+
+		/* turn off auto negotiation, set speed and duplexity */
+		bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
+		tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
+		if (ecmd->speed == SPEED_100)
+			bmcr |= BMCR_SPEED100;
+		if (ecmd->duplex == DUPLEX_FULL) {
+			bmcr |= BMCR_FULLDPLX;
+			mii->full_duplex = 1;
+		} else
+			mii->full_duplex = 0;
+		if (bmcr != tmp)
+			mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
+
+		mii->duplex_lock = 1;
+	}
+	return 0;
+}
+
+int mii_link_ok (struct mii_if_info *mii)
+{
+	if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
+		return 1;
+	return 0;
+}
+
+int mii_nway_restart (struct mii_if_info *mii)
+{
+	int bmcr;
+	int r = -EINVAL;
+
+	/* if autoneg is off, it's an error */
+	bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
+
+	if (bmcr & BMCR_ANENABLE) {
+		bmcr |= BMCR_ANRESTART;
+		mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
+		r = 0;
+	}
+
+	return r;
+}
+
+MODULE_AUTHOR ("Jeff Garzik <jgarzik@mandrakesoft.com>");
+MODULE_DESCRIPTION ("MII hardware support library");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(mii_link_ok);
+EXPORT_SYMBOL(mii_nway_restart);
+EXPORT_SYMBOL(mii_ethtool_gset);
+EXPORT_SYMBOL(mii_ethtool_sset);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/myri_code.h linux/drivers/net/myri_code.h
--- linux.orig/drivers/net/myri_code.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/myri_code.h	Tue Jan 15 19:08:06 2002
@@ -6284,4 +6284,4 @@
 #define MYRI_NetReceiveBadCrcs       0xB8D4
 #define MYRI_NetReceiveBytes         0xB8DC
 
-#endif SYMBOL_DEFINES_COMPILED
+#endif /* SYMBOL_DEFINES_COMPILED */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/natsemi.c linux/drivers/net/natsemi.c
--- linux.orig/drivers/net/natsemi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/natsemi.c	Wed Feb 13 16:34:07 2002
@@ -100,7 +100,10 @@
 		* ETHTOOL_* further support (Tim Hockin)
 
 	version 1.0.13:
-		* ETHTOOL_[GS]EEPROM support (Tim Hockin)
+		* ETHTOOL_GEEPROM support (Tim Hockin)
+
+	version 1.0.14:
+		* Cleanup some messages and autoneg in ethtool (Tim Hockin)
 
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
@@ -108,9 +111,40 @@
 	* flow control
 */
 
+#if !defined(__OPTIMIZE__)
+#warning  You must compile this file with the correct options!
+#warning  See the last lines of the source file.
+#error You must compile this driver with "-O".
+#endif
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/rtnetlink.h>
+#include <linux/mii.h>
+#include <asm/processor.h>	/* Processor type for cache alignment. */
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.13"
-#define DRV_RELDATE	"Oct 19, 2001"
+#define DRV_VERSION	"1.07+LK1.0.14"
+#define DRV_RELDATE	"Nov 27, 2001"
 
 /* Updated to recommendations in pci-skeleton v2.03. */
 
@@ -129,7 +163,12 @@
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
-static int debug = 1; /* 1 normal messages, 0 quiet .. 7 verbose. */
+#define NATSEMI_DEF_MSG		(NETIF_MSG_DRV		| \
+				 NETIF_MSG_LINK		| \
+				 NETIF_MSG_WOL		| \
+				 NETIF_MSG_RX_ERR	| \
+				 NETIF_MSG_TX_ERR)
+static int debug = NATSEMI_DEF_MSG;
 
 /* Maximum events (Rx packets, etc.) to handle at each interrupt. */
 static int max_interrupt_work = 20;
@@ -180,37 +219,6 @@
 
 #define PKT_BUF_SZ		1536 /* Size of each temporary Rx buffer. */
 
-#if !defined(__OPTIMIZE__)
-#warning  You must compile this file with the correct options!
-#warning  See the last lines of the source file.
-#error You must compile this driver with "-O".
-#endif
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/errno.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/rtnetlink.h>
-#include <linux/mii.h>
-#include <asm/processor.h>	/* Processor type for cache alignment. */
-#include <asm/bitops.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
 KERN_INFO DRV_NAME ".c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
@@ -229,7 +237,7 @@
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM_DESC(max_interrupt_work, "DP8381x maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
-MODULE_PARM_DESC(debug, "DP8381x debug level (0-5)");
+MODULE_PARM_DESC(debug, "DP8381x debug bitmask");
 MODULE_PARM_DESC(rx_copybreak, "DP8381x copy breakpoint for copy-only-tiny-frames");
 MODULE_PARM_DESC(options, "DP8381x: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
@@ -391,7 +399,11 @@
 	SDCFG			= 0xF8
 };
 /* the values for the 'magic' registers above (PGSEL=1) */
+#ifdef CONFIG_NATSEMI_CABLE_MAGIC
+#define PMDCSR_VAL	0x1898
+#else
 #define PMDCSR_VAL	0x189C
+#endif
 #define TSTDAT_VAL	0x0
 #define DSPCFG_VAL	0x5040
 #define SDCFG_VAL	0x008c
@@ -414,6 +426,7 @@
 enum ChipConfig_bits {
 	CfgPhyDis		= 0x200,
 	CfgPhyRst		= 0x400,
+	CfgExtPhy		= 0x1000,
 	CfgAnegEnable		= 0x2000,
 	CfgAneg100		= 0x4000,
 	CfgAnegFull		= 0x8000,
@@ -627,6 +640,7 @@
 	u16 advertising; /* NWay media advertisement */
 	unsigned int iosize;
 	spinlock_t lock;
+	u32 msg_enable;
 };
 
 static int eeprom_read(long ioaddr, int location);
@@ -746,6 +760,7 @@
 	pci_set_drvdata(pdev, dev);
 	np->iosize = iosize;
 	spin_lock_init(&np->lock);
+	np->msg_enable = debug;
 
 	/* Reset the chip to erase previous misconfiguration. */
 	natsemi_reload_eeprom(dev);
@@ -760,7 +775,8 @@
 		if (option & 0x200)
 			np->full_duplex = 1;
 		if (option & 15)
-			printk(KERN_INFO "%s: ignoring user supplied media type %d",
+			printk(KERN_INFO 
+				"%s: ignoring user supplied media type %d",
 				dev->name, option & 15);
 	}
 	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
@@ -789,14 +805,17 @@
 	}
 	netif_carrier_off(dev);
 
-	printk(KERN_INFO "%s: %s at 0x%lx, ",
-		   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
-	for (i = 0; i < ETH_ALEN-1; i++)
-			printk("%2.2x:", dev->dev_addr[i]);
-	printk("%2.2x, IRQ %d.\n", dev->dev_addr[i], irq);
+	if (netif_msg_drv(np)) {
+		printk(KERN_INFO "%s: %s at %#08lx, ",
+			   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
+		for (i = 0; i < ETH_ALEN-1; i++)
+				printk("%02x:", dev->dev_addr[i]);
+		printk("%02x, IRQ %d.\n", dev->dev_addr[i], irq);
+	}
 
 	np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
-	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000) {
+	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000 
+	 && netif_msg_probe(np)) {
 		u32 chip_config = readl(ioaddr + ChipConfig);
 		printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
 			   "10%s %s duplex.\n",
@@ -805,12 +824,18 @@
 			   chip_config & CfgAneg100 ? "0" : "",
 			   chip_config & CfgAnegFull ? "full" : "half");
 	}
-	printk(KERN_INFO "%s: Transceiver status 0x%4.4x advertising %4.4x.\n",
-		   dev->name, mdio_read(dev, 1, MII_BMSR), 
-		   np->advertising);
+	if (netif_msg_probe(np))
+		printk(KERN_INFO 
+			"%s: Transceiver status %#04x advertising %#04x.\n",
+			dev->name, mdio_read(dev, 1, MII_BMSR), 
+			np->advertising);
 
 	/* save the silicon revision for later querying */
 	np->srr = readl(ioaddr + SiliconRev);
+	if (netif_msg_hw(np))
+		printk(KERN_INFO "%s: silicon revision %#04x.\n",
+				dev->name, np->srr);
+
 
 	return 0;
 }
@@ -907,6 +932,7 @@
 	u32 rfcr;
 	u16 pmatch[3];
 	u16 sopass[3];
+	struct netdev_private *np = dev->priv;
 
 	/* 
 	 * Resetting the chip causes some registers to be lost.
@@ -940,10 +966,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: reset did not complete in %d usec.\n",
 		   dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
 		   dev->name, i*5);
 	}
@@ -972,6 +998,7 @@
 
 static void natsemi_reload_eeprom(struct net_device *dev)
 {
+	struct netdev_private *np = dev->priv;
 	int i;
 
 	writel(EepromReload, dev->base_addr + PCIBusCfg);
@@ -980,10 +1007,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: EEPROM did not reload in %d usec.\n",
 		   dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n",
 		   dev->name, i*5);
 	}
@@ -992,6 +1019,7 @@
 static void natsemi_stop_rxtx(struct net_device *dev)
 {
 	long ioaddr = dev->base_addr;
+	struct netdev_private *np = dev->priv;
 	int i;
 
 	writel(RxOff | TxOff, ioaddr + ChipCmd);
@@ -1000,10 +1028,10 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
 		printk(KERN_INFO "%s: Tx/Rx process did not stop in %d usec.\n",
 				dev->name, i*5);
-	} else if (debug > 2) {
+	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
 				dev->name, i*5);
 	}
@@ -1021,7 +1049,7 @@
 	i = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev);
 	if (i) return i;
 
-	if (debug > 1)
+	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
 			   dev->name, dev->irq);
 	i = alloc_ring(dev);
@@ -1036,8 +1064,8 @@
 
 	netif_start_queue(dev);
 
-	if (debug > 2)
-		printk(KERN_DEBUG "%s: Done netdev_open(), status: %x.\n",
+	if (netif_msg_ifup(np))
+		printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
 			   dev->name, (int)readl(ioaddr + ChipCmd));
 
 	/* Set the timer to check for link beat. */
@@ -1057,19 +1085,18 @@
 	int duplex;
 	int chipcfg = readl(ioaddr + ChipConfig);
 
-	if(!(chipcfg & CfgLink)) {
+	if (!(chipcfg & CfgLink)) {
 		if (netif_carrier_ok(dev)) {
-			if (debug)
-				printk(KERN_INFO "%s: no link. Disabling watchdog.\n",
+			if (netif_msg_link(np))
+				printk(KERN_NOTICE "%s: link down.\n", 
 					dev->name);
 			netif_carrier_off(dev);
 		}
 		return;
 	}
 	if (!netif_carrier_ok(dev)) {
-		if (debug)
-			printk(KERN_INFO "%s: link is back. Enabling watchdog.\n",
-					dev->name);
+		if (netif_msg_link(np))
+			printk(KERN_NOTICE "%s: link up.\n", dev->name);
 		netif_carrier_on(dev);
 	}
 
@@ -1077,10 +1104,11 @@
 
 	/* if duplex is set then bit 28 must be set, too */
 	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
-		if (debug)
-			printk(KERN_INFO "%s: Setting %s-duplex based on negotiated link"
-				   " capability.\n", dev->name,
-				   duplex ? "full" : "half");
+		if (netif_msg_link(np))
+			printk(KERN_INFO 
+				"%s: Setting %s-duplex based on negotiated "
+				"link capability.\n", dev->name,
+				duplex ? "full" : "half");
 		if (duplex) {
 			np->rx_config |= RxAcceptTx;
 			np->tx_config |= TxCarrierIgn | TxHeartIgn;
@@ -1099,17 +1127,12 @@
 	long ioaddr = dev->base_addr;
 	int i;
 
-	/* save the silicon revision for later */
-	if (debug > 4)
-		printk(KERN_DEBUG "%s: found silicon revision %xh.\n",
-				dev->name, np->srr);
-
 	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
 		if (readl(dev->base_addr + ChipConfig) & CfgAnegDone)
 			break;
 		udelay(10);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && debug) {
+	if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
 		printk(KERN_INFO 
 			"%s: autonegotiation did not complete in %d usec.\n",
 			dev->name, i*10);
@@ -1174,8 +1197,8 @@
 	 * nothing will be written to memory. */
 	np->SavedClkRun = readl(ioaddr + ClkRun);
 	writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
-	if (np->SavedClkRun & PMEStatus) {
-		printk(KERN_NOTICE "%s: Wake-up event %8.8x\n", 
+	if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
+		printk(KERN_NOTICE "%s: Wake-up event %#08x\n", 
 			dev->name, readl(ioaddr + WOLCmd));
 	}
 
@@ -1205,7 +1228,7 @@
 	long ioaddr = dev->base_addr;
 	u16 dspcfg;
 
-	if (debug > 3) {
+	if (netif_msg_timer(np)) {
 		/* DO NOT read the IntrStatus register, 
 		 * a read clears any pending interrupts.
 		 */
@@ -1219,9 +1242,9 @@
 	writew(0, ioaddr+PGSEL);
 	if (dspcfg != DSPCFG_VAL) {
 		if (!netif_queue_stopped(dev)) {
-			printk(KERN_INFO 
-				"%s: possible phy reset: re-initializing\n",
-				dev->name);
+			if (netif_msg_hw(np))
+				printk(KERN_NOTICE "%s: possible phy reset: "
+					"re-initializing\n", dev->name);
 			disable_irq(dev->irq);
 			spin_lock_irq(&np->lock);
 			init_registers(dev);
@@ -1244,18 +1267,18 @@
 {
 	struct netdev_private *np = dev->priv;
 
-	if (debug > 2) {
+	if (netif_msg_pktdata(np)) {
 		int i;
 		printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
 		for (i = 0; i < TX_RING_SIZE; i++) {
-			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
+			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
 				   i, np->tx_ring[i].next_desc,
 				   np->tx_ring[i].cmd_status, 
 				   np->tx_ring[i].addr);
 		}
 		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++) {
-			printk(KERN_DEBUG " #%d desc. %8.8x %8.8x %8.8x.\n",
+			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
 				   i, np->rx_ring[i].next_desc,
 				   np->rx_ring[i].cmd_status, 
 				   np->rx_ring[i].addr);
@@ -1271,9 +1294,11 @@
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
 	if (netif_device_present(dev)) {
-		printk(KERN_WARNING "%s: Transmit timed out, status %8.8x,"
-			" resetting...\n", 
-			dev->name, readl(ioaddr + IntrStatus));
+		if (netif_msg_tx_err(np))
+			printk(KERN_WARNING 
+				"%s: Transmit timed out, status %#08x,"
+				" resetting...\n", 
+				dev->name, readl(ioaddr + IntrStatus));
 		dump_ring(dev);
 
 		natsemi_reset(dev);
@@ -1431,7 +1456,7 @@
 
 	dev->trans_start = jiffies;
 
-	if (debug > 4) {
+	if (netif_msg_tx_queued(np)) {
 		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
 			   dev->name, np->cur_tx, entry);
 	}
@@ -1444,14 +1469,11 @@
 
 	for (; np->cur_tx - np->dirty_tx > 0; np->dirty_tx++) {
 		int entry = np->dirty_tx % TX_RING_SIZE;
-		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn)) {
-			if (debug > 4)
-				printk(KERN_DEBUG "%s: tx frame #%d is busy.\n",
-						dev->name, np->dirty_tx);
+		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn))
 			break;
-		}
-		if (debug > 4)
-			printk(KERN_DEBUG "%s: tx frame #%d finished with status %8.8xh.\n",
+		if (netif_msg_tx_done(np))
+			printk(KERN_DEBUG 
+				"%s: tx frame #%d finished, status %#08x.\n",
 					dev->name, np->dirty_tx,
 					le32_to_cpu(np->tx_ring[entry].cmd_status));
 		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescPktOK)) {
@@ -1488,21 +1510,18 @@
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
 {
 	struct net_device *dev = dev_instance;
-	struct netdev_private *np;
-	long ioaddr;
+	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
 	int boguscnt = max_interrupt_work;
 
-	ioaddr = dev->base_addr;
-	np = dev->priv;
-	
 	if (!netif_device_present(dev))
 		return;
 	do {
 		/* Reading automatically acknowledges all int sources. */
 		u32 intr_status = readl(ioaddr + IntrStatus);
 
-		if (debug > 4)
-			printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n",
+		if (netif_msg_intr(np))
+			printk(KERN_DEBUG "%s: Interrupt, status %#08x.\n",
 				   dev->name, intr_status);
 
 		if (intr_status == 0)
@@ -1523,13 +1542,13 @@
 
 		if (--boguscnt < 0) {
 			printk(KERN_WARNING "%s: Too much work at interrupt, "
-				   "status=0x%4.4x.\n",
+				   "status=%#08x.\n",
 				   dev->name, intr_status);
 			break;
 		}
 	} while (1);
 
-	if (debug > 4)
+	if (netif_msg_intr(np))
 		printk(KERN_DEBUG "%s: exiting interrupt.\n",
 			   dev->name);
 }
@@ -1545,22 +1564,24 @@
 
 	/* If the driver owns the next entry it's a new packet. Send it up. */
 	while (desc_status < 0) {        /* e.g. & DescOwn */
-		if (debug > 4)
-			printk(KERN_DEBUG "  In netdev_rx() entry %d status was %8.8x.\n",
-				   entry, desc_status);
+		if (netif_msg_rx_status(np))
+			printk(KERN_DEBUG 
+				"  netdev_rx() entry %d status was %#08x.\n",
+				entry, desc_status);
 		if (--boguscnt < 0)
 			break;
-		if ((desc_status & (DescMore|DescPktOK|DescRxLong)) != DescPktOK) {
+		if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
 			if (desc_status & DescMore) {
-				printk(KERN_WARNING "%s: Oversized(?) Ethernet frame spanned "
-					   "multiple buffers, entry %#x status %x.\n",
-					   dev->name, np->cur_rx, desc_status);
+				if (netif_msg_rx_err(np))
+					printk(KERN_WARNING 
+						"%s: Oversized(?) Ethernet "
+						"frame spanned multiple "
+						"buffers, entry %#08x "
+						"status %#08x.\n", dev->name, 
+						np->cur_rx, desc_status);
 				np->stats.rx_length_errors++;
 			} else {
-				/* There was a error. */
-				if (debug > 2)
-					printk(KERN_DEBUG "  netdev_rx() Rx error was %8.8x.\n",
-						   desc_status);
+				/* There was an error. */
 				np->stats.rx_errors++;
 				if (desc_status & (DescRxAbort|DescRxOver)) 
 					np->stats.rx_over_errors++;
@@ -1575,8 +1596,8 @@
 			struct sk_buff *skb;
 			/* Omit CRC size. */
 			int pkt_len = (desc_status & DescSizeMask) - 4;
-			/* Check if the packet is long enough to accept without copying
-			   to a minimally-sized skbuff. */
+			/* Check if the packet is long enough to accept 
+			 * without copying to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
@@ -1639,11 +1660,16 @@
 
 	spin_lock(&np->lock);
 	if (intr_status & LinkChange) {
-		printk(KERN_NOTICE 
-			"%s: Link changed: Autonegotiation advertising"
-			" %4.4x  partner %4.4x.\n", dev->name,
-			(int)mdio_read(dev, 1, MII_ADVERTISE), 
-			(int)mdio_read(dev, 1, MII_LPA));
+		u16 adv = mdio_read(dev, 1, MII_ADVERTISE);
+		u16 lpa = mdio_read(dev, 1, MII_LPA);
+		if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE 
+		 && netif_msg_link(np)) {
+			printk(KERN_INFO 
+				"%s: Autonegotiation advertising"
+				" %#04x  partner %#04x.\n", dev->name,
+				adv, lpa);
+		}
+
 		/* read MII int status to clear the flag */
 		readw(ioaddr + MIntrStatus);
 		check_link(dev);
@@ -1654,18 +1680,19 @@
 	if (intr_status & IntrTxUnderrun) {
 		if ((np->tx_config & TxDrthMask) < 62)
 			np->tx_config += 2;
-		if (debug > 2)
-			printk(KERN_NOTICE "%s: increasing Tx theshold, new tx cfg %8.8xh.\n",
-					dev->name, np->tx_config);
+		if (netif_msg_tx_err(np))
+			printk(KERN_NOTICE 
+				"%s: increased Tx theshold, txcfg %#08x.\n",
+				dev->name, np->tx_config);
 		writel(np->tx_config, ioaddr + TxConfig);
 	}
-	if (intr_status & WOLPkt) {
+	if (intr_status & WOLPkt && netif_msg_wol(np)) {
 		int wol_status = readl(ioaddr + WOLCmd);
-		printk(KERN_NOTICE "%s: Link wake-up event %8.8x\n",
+		printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
 			   dev->name, wol_status);
 	}
 	if (intr_status & RxStatusFIFOOver) {
-		if (debug >= 2) {
+		if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
 			printk(KERN_NOTICE "%s: Rx status FIFO overrun\n", 
 				dev->name);
 		}
@@ -1673,10 +1700,8 @@
 	}
 	/* Hmmmmm, it's not clear how to recover from PCI faults. */
 	if (intr_status & IntrPCIErr) {
-		if (debug) {
-			printk(KERN_NOTICE "%s: PCI error %08x\n", dev->name,
-				intr_status & IntrPCIErr);
-		}
+		printk(KERN_NOTICE "%s: PCI error %#08x\n", dev->name,
+			intr_status & IntrPCIErr);
 		np->stats.tx_fifo_errors++;
 		np->stats.rx_fifo_errors++;
 	}
@@ -1775,7 +1800,8 @@
 
 	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
 		/* Unconditionally log net taps. */
-		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", 
+			dev->name);
 		rx_mode = RxFilterEnable | AcceptBroadcast 
 			| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
 	} else if ((dev->mc_count > multicast_filter_limit)
@@ -1910,7 +1936,7 @@
 	/* get message-level */
 	case ETHTOOL_GMSGLVL: {
 		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
-		edata.data = debug;
+		edata.data = np->msg_enable;
 		if (copy_to_user(useraddr, &edata, sizeof(edata)))
 			return -EFAULT;
 		return 0;
@@ -1920,7 +1946,7 @@
 		struct ethtool_value edata;
 		if (copy_from_user(&edata, useraddr, sizeof(edata)))
 			return -EFAULT;
-		debug = edata.data;
+		np->msg_enable = edata.data;
 		return 0;
 	}
 	/* restart autonegotiation */
@@ -2108,18 +2134,22 @@
 	ecmd->supported = 
 		(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
-		SUPPORTED_Autoneg | SUPPORTED_TP);
+		SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
 	
-	/* only supports twisted-pair */
-	ecmd->port = PORT_TP;
+	/* only supports twisted-pair or MII */
+	tmp = readl(dev->base_addr + ChipConfig);
+	if (tmp & CfgExtPhy)
+		ecmd->port = PORT_MII;
+	else
+		ecmd->port = PORT_TP;
 
 	/* only supports internal transceiver */
 	ecmd->transceiver = XCVR_INTERNAL;
 
-	/* this isn't fully supported at higher layers */
+	/* not sure what this is for */
 	ecmd->phy_address = readw(dev->base_addr + PhyCtrl) & PhyAddrMask;
 
-	ecmd->advertising = ADVERTISED_TP;
+	ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
 	tmp = mdio_read(dev, 1, MII_ADVERTISE);
 	if (tmp & ADVERTISE_10HALF)
 		ecmd->advertising |= ADVERTISED_10baseT_Half;
@@ -2130,20 +2160,21 @@
 	if (tmp & ADVERTISE_100FULL)
 		ecmd->advertising |= ADVERTISED_100baseT_Full;
 
-	tmp = readl(dev->base_addr + ChipConfig);
-	if (tmp & CfgAnegEnable) {
+	tmp = mdio_read(dev, 1, MII_BMCR);
+	if (tmp & BMCR_ANENABLE) {
 		ecmd->advertising |= ADVERTISED_Autoneg;
 		ecmd->autoneg = AUTONEG_ENABLE;
 	} else {
 		ecmd->autoneg = AUTONEG_DISABLE;
 	}
 
+	tmp = readl(dev->base_addr + ChipConfig);
 	if (tmp & CfgSpeed100) {
 		ecmd->speed = SPEED_100;
 	} else {
 		ecmd->speed = SPEED_10;
 	}
-
+		
 	if (tmp & CfgFullDuplex) {
 		ecmd->duplex = DUPLEX_FULL;
 	} else {
@@ -2164,7 +2195,7 @@
 		return -EINVAL;
 	if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
 		return -EINVAL;
-	if (ecmd->port != PORT_TP)
+	if (ecmd->port != PORT_TP && ecmd->port != PORT_MII)
 		return -EINVAL;
 	if (ecmd->transceiver != XCVR_INTERNAL)
 		return -EINVAL;
@@ -2174,39 +2205,22 @@
 	
 	/* WHEW! now lets bang some bits */
 	
+	tmp = mdio_read(dev, 1, MII_BMCR);
 	if (ecmd->autoneg == AUTONEG_ENABLE) {
-		/* advertise only what has been requested */
-		tmp = readl(dev->base_addr + ChipConfig);
-		tmp &= ~(CfgAneg100 | CfgAnegFull);
-		tmp |= CfgAnegEnable;
-		if (ecmd->advertising & ADVERTISED_100baseT_Half 
-		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
-			tmp |= CfgAneg100;
-		}
-		if (ecmd->advertising & ADVERTISED_10baseT_Full 
-		 || ecmd->advertising & ADVERTISED_100baseT_Full) {
-			tmp |= CfgAnegFull;
-		}
-		writel(tmp, dev->base_addr + ChipConfig);
-		/* turn on autonegotiation, and force a renegotiate */
-		tmp = mdio_read(dev, 1, MII_BMCR);
-		tmp |= (BMCR_ANENABLE | BMCR_ANRESTART);
-		mdio_write(dev, 1, MII_BMCR, tmp);
+		/* turn on autonegotiation */
+		tmp |= BMCR_ANENABLE;
 		np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
 	} else {
 		/* turn off auto negotiation, set speed and duplexity */
-		tmp = mdio_read(dev, 1, MII_BMCR);
 		tmp &= ~(BMCR_ANENABLE | BMCR_SPEED100 | BMCR_FULLDPLX);
-		if (ecmd->speed == SPEED_100) {
+		if (ecmd->speed == SPEED_100)
 			tmp |= BMCR_SPEED100;
-		}
-		if (ecmd->duplex == DUPLEX_FULL) {
+		if (ecmd->duplex == DUPLEX_FULL)
 			tmp |= BMCR_FULLDPLX;
-		} else {
+		else
 			np->full_duplex = 0;
-		}
-		mdio_write(dev, 1, MII_BMCR, tmp);
 	}
+	mdio_write(dev, 1, MII_BMCR, tmp);
 	return 0;
 }
 
@@ -2241,7 +2255,7 @@
 	/* the interrupt status is clear-on-read - see if we missed any */
 	if (rbuf[4] & rbuf[5]) {
 		printk(KERN_WARNING 
-			"%s: shoot, we dropped an interrupt (0x%x)\n", 
+			"%s: shoot, we dropped an interrupt (%#08x)\n", 
 			dev->name, rbuf[4] & rbuf[5]);
 	}
 
@@ -2308,7 +2322,7 @@
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
 
-	if (debug > 1)
+	if (netif_msg_wol(np))
 		printk(KERN_INFO "%s: remaining active for wake-on-lan\n", 
 			dev->name);
 
@@ -2343,12 +2357,15 @@
 	netif_stop_queue(dev);
 	netif_carrier_off(dev);
 
-	if (debug > 1) {
-		printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
-			   dev->name, (int)readl(ioaddr + ChipCmd));
-		printk(KERN_DEBUG "%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
-			   dev->name, np->cur_tx, np->dirty_tx, np->cur_rx, np->dirty_rx);
-	}
+	if (netif_msg_ifdown(np))
+		printk(KERN_DEBUG 
+			"%s: Shutting down ethercard, status was %#04x.\n",
+			dev->name, (int)readl(ioaddr + ChipCmd));
+	if (netif_msg_pktdata(np))
+		printk(KERN_DEBUG 
+			"%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
+			dev->name, np->cur_tx, np->dirty_tx, 
+			np->cur_rx, np->dirty_rx);
 
 	del_timer_sync(&np->timer);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/ns83820.c linux/drivers/net/ns83820.c
--- linux.orig/drivers/net/ns83820.c	Fri Dec 21 17:41:54 2001
+++ linux/drivers/net/ns83820.c	Thu Jan 17 19:21:01 2002
@@ -848,7 +848,7 @@
 			skb->protocol = eth_type_trans(skb, &dev->net_dev);
 			if (NET_RX_DROP == netif_rx(skb))
 				dev->stats.rx_dropped ++;
-#ifndef __i386__
+#if 0 //ndef __i386__
 		done:;
 #endif
 		} else {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/3c589_cs.c linux/drivers/net/pcmcia/3c589_cs.c
--- linux.orig/drivers/net/pcmcia/3c589_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcmcia/3c589_cs.c	Fri Dec 21 18:50:47 2001
@@ -17,6 +17,9 @@
 
 ======================================================================*/
 
+#define DRV_NAME	"3c589_cs"
+#define DRV_VERSION	"1.162"
+
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -28,6 +31,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -134,7 +140,7 @@
 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"3c589_cs.c 1.162 2001/10/13 00:08:50 (David Hinds)";
+DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -159,6 +165,7 @@
 static int el3_close(struct net_device *dev);
 static void el3_tx_timeout(struct net_device *dev);
 static void set_multicast_list(struct net_device *dev);
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
 
 static dev_info_t dev_info = "3c589_cs";
 
@@ -249,7 +256,8 @@
     dev->tx_timeout = el3_tx_timeout;
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
-    
+    dev->do_ioctl = netdev_ioctl;
+
     /* Register with Card Services */
     link->next = dev_list;
     dev_list = link;
@@ -638,6 +646,71 @@
 	 ioaddr + EL3_CMD);
     outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
 	 | AdapterFailure, ioaddr + EL3_CMD);
+}
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int netdev_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	int rc;
+
+	switch (cmd) {
+	case SIOCETHTOOL:
+		rc = netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+		break;
+
+	default:
+		rc = -EOPNOTSUPP;
+		break;
+	}
+
+	return rc;
 }
 
 static int el3_config(struct net_device *dev, struct ifmap *map)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/aironet4500_cs.c linux/drivers/net/pcmcia/aironet4500_cs.c
--- linux.orig/drivers/net/pcmcia/aironet4500_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcmcia/aironet4500_cs.c	Fri Dec 21 18:50:47 2001
@@ -10,8 +10,11 @@
  *
  */
 
+#define DRV_NAME	"aironet4500_cs"
+#define DRV_VERSION	"0.1"
+
 static const char *awc_version =
-"aironet4500_cs.c v0.1 1/1/99 Elmer Joandi, elmer@ylenurme.ee.\n";
+DRV_NAME ".c v" DRV_VERSION " 1/1/99 Elmer Joandi, elmer@ylenurme.ee.\n";
 
 
 #include <linux/module.h>
@@ -24,6 +27,9 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -159,6 +165,66 @@
 	return ret;
 }
 
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int awc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
+
 /*
 	awc_attach() creates an "instance" of the driver, allocating
 	local data structures for one device.  The device is registered
@@ -230,7 +296,8 @@
 //	dev->set_config = 		&awc_config_misiganes,aga mitte awc_config;
 	dev->get_stats = 		&awc_get_stats;
 //	dev->set_multicast_list = 	&awc_set_multicast_list;
-
+	dev->do_ioctl =			&awc_ioctl;
+	
 	strcpy(dev->name, ((struct awc_private *)dev->priv)->node.dev_name);
 
 	ether_setup(dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/fmvj18x_cs.c linux/drivers/net/pcmcia/fmvj18x_cs.c
--- linux.orig/drivers/net/pcmcia/fmvj18x_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcmcia/fmvj18x_cs.c	Fri Dec 21 18:50:47 2001
@@ -28,6 +28,9 @@
    
 ======================================================================*/
 
+#define DRV_NAME	"fmvj18x_cs"
+#define DRV_VERSION	"2.6"
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -39,6 +42,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 
@@ -73,7 +79,7 @@
 #ifdef PCMCIA_DEBUG
 INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version = "fmvj18x_cs.c 2.6 2001/09/17";
+static char *version = DRV_NAME ".c " DRV_VERSION " 2001/09/17";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -103,6 +109,7 @@
 static struct net_device_stats *fjn_get_stats(struct net_device *dev);
 static void set_rx_mode(struct net_device *dev);
 static void fjn_tx_timeout(struct net_device *dev);
+static int fjn_ioctl(struct net_device *, struct ifreq *, int);
 
 static dev_info_t dev_info = "fmvj18x_cs";
 static dev_link_t *dev_list;
@@ -315,6 +322,7 @@
     dev->tx_timeout = fjn_tx_timeout;
     dev->watchdog_timeo = TX_TIMEOUT;
 #endif
+    dev->do_ioctl = fjn_ioctl;
     
     /* Register with Card Services */
     link->next = dev_list;
@@ -1101,6 +1109,65 @@
 } /* fjn_rx */
 
 /*====================================================================*/
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int fjn_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+}
 
 static int fjn_config(struct net_device *dev, struct ifmap *map){
     return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/netwave_cs.c linux/drivers/net/pcmcia/netwave_cs.c
--- linux.orig/drivers/net/pcmcia/netwave_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcmcia/netwave_cs.c	Fri Dec 21 18:50:47 2001
@@ -37,6 +37,9 @@
 /* To have statistics (just packets sent) define this */
 #undef NETWAVE_STATS
 
+#define DRV_NAME	"netwave_cs"
+#define DRV_VERSION	"0.3.0"
+
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
@@ -51,6 +54,9 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/timer.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
@@ -163,7 +169,7 @@
 MODULE_PARM(pc_debug, "i");
 #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
 static char *version =
-"netwave_cs.c 0.3.0 Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
+DRV_NAME ".c " DRV_VERSION " Thu Jul 17 14:36:02 1997 (John Markus Bjørndalen)\n";
 #else
 #define DEBUG(n, args...)
 #endif
@@ -597,6 +603,53 @@
     }
 } /* netwave_flush_stale_links */
 
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
 /*
  * Function netwave_ioctl (dev, rq, cmd)
  *
@@ -619,6 +672,9 @@
 	
     DEBUG(0, "%s: ->netwave_ioctl(cmd=0x%X)\n", dev->name, cmd);
 	
+    if (cmd == SIOCETHTOOL)
+    	return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
     /* Disable interrupts & save flags */
     save_flags(flags);
     cli();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcmcia/nmclan_cs.c linux/drivers/net/pcmcia/nmclan_cs.c
--- linux.orig/drivers/net/pcmcia/nmclan_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcmcia/nmclan_cs.c	Fri Dec 21 18:50:47 2001
@@ -106,6 +106,10 @@
 
 ---------------------------------------------------------------------------- */
 
+#define DRV_NAME	"nmclan_cs"
+#define DRV_VERSION	"0.16"
+
+
 /* ----------------------------------------------------------------------------
 Conditional Compilation Options
 ---------------------------------------------------------------------------- */
@@ -130,6 +134,9 @@
 #include <linux/interrupt.h>
 #include <linux/in.h>
 #include <linux/delay.h>
+#include <linux/ethtool.h>
+
+#include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -375,7 +382,7 @@
 static char rcsid[] =
 "nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
 static char *version =
-"nmclan_cs 0.16 (Roger C. Pao)";
+DRV_NAME " " DRV_VERSION " (Roger C. Pao)";
 #endif
 
 static dev_info_t dev_info="nmclan_cs";
@@ -1001,6 +1008,66 @@
 
   return 0;
 } /* mace_close */
+
+static int netdev_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	u32 ethcmd;
+
+	/* dev_ioctl() in ../../net/core/dev.c has already checked
+	   capable(CAP_NET_ADMIN), so don't bother with that here.  */
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		sprintf(info.bus_info, "PCMCIA 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+#ifdef PCMCIA_DEBUG
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pc_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pc_debug = edata.data;
+		return 0;
+	}
+#endif
+
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int smc91c92_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	switch (cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
+	default:
+		return -EOPNOTSUPP;
+	}
+	return 0;
+}
 
 /* ----------------------------------------------------------------------------
 mace_start_xmit
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/pcnet32.c linux/drivers/net/pcnet32.c
--- linux.orig/drivers/net/pcnet32.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/pcnet32.c	Mon Jan 14 17:27:24 2002
@@ -21,7 +21,12 @@
  *
  *************************************************************************/
 
-static const char *version = "pcnet32.c:v1.25kf 26.9.1999 tsbogend@alpha.franken.de\n";
+#define DRV_NAME	"pcnet32"
+#define DRV_VERSION	"1.25kf"
+#define DRV_RELDATE	"17.11.2001"
+
+static const char *version =
+DRV_NAME ".c:v" DRV_VERSION " " DRV_RELDATE " tsbogend@alpha.franken.de\n";
 
 #include <linux/module.h>
 
@@ -36,9 +41,12 @@
 #include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
+#include <asm/uaccess.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -284,11 +292,11 @@
     int	 shared_irq:1,			/* shared irq possible */
 	ltint:1,
 #ifdef DO_DXSUFLO
-	      dxsuflo:1,						    /* disable transmit stop on uflo */
+      dxsuflo:1,			    /* disable transmit stop on uflo */
 #endif
-	full_duplex:1,				/* full duplex possible */
 	mii:1;					/* mii port available */
     struct net_device *next;
+    struct mii_if_info mii_if;
 };
 
 static int  pcnet32_probe_vlbus(int cards_found);
@@ -303,9 +311,9 @@
 static int  pcnet32_close(struct net_device *);
 static struct net_device_stats *pcnet32_get_stats(struct net_device *);
 static void pcnet32_set_multicast_list(struct net_device *);
-#ifdef HAVE_PRIVATE_IOCTL
-static int  pcnet32_mii_ioctl(struct net_device *, struct ifreq *, int);
-#endif
+static int  pcnet32_ioctl(struct net_device *, struct ifreq *, int);
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num);
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val);
 
 enum pci_flags_bit {
     PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
@@ -648,6 +656,13 @@
 #if defined(__i386__)
 	    printk(KERN_WARNING "%s: Probably a Compaq, using the PROM address of", dev->name);
 	    memcpy(dev->dev_addr, promaddr, 6);
+#elif defined(__powerpc__)
+	    if (!is_valid_ether_addr(dev->dev_addr)
+		&& is_valid_ether_addr(promaddr)) {
+		    printk("\n" KERN_WARNING "%s: using PROM address:",
+			   dev->name);
+		    memcpy(dev->dev_addr, promaddr, 6);
+	    }
 #endif
 	}	    	    
     }
@@ -703,7 +718,7 @@
     dev->priv = lp;
     lp->name = chipname;
     lp->shared_irq = shared;
-    lp->full_duplex = fdx;
+    lp->mii_if.full_duplex = fdx;
 #ifdef DO_DXSUFLO
     lp->dxsuflo = dxsuflo;
 #endif
@@ -713,6 +728,9 @@
 	lp->options = PORT_ASEL;
     else
 	lp->options = options_mapping[options[card_idx]];
+    lp->mii_if.dev = dev;
+    lp->mii_if.mdio_read = mdio_read;
+    lp->mii_if.mdio_write = mdio_write;
     
     if (fdx && !(lp->options & PORT_ASEL) && full_duplex[card_idx])
 	lp->options |= PORT_FD;
@@ -782,9 +800,7 @@
     dev->stop = &pcnet32_close;
     dev->get_stats = &pcnet32_get_stats;
     dev->set_multicast_list = &pcnet32_set_multicast_list;
-#ifdef HAVE_PRIVATE_IOCTL
-    dev->do_ioctl = &pcnet32_mii_ioctl;
-#endif
+    dev->do_ioctl = &pcnet32_ioctl;
     dev->tx_timeout = pcnet32_tx_timeout;
     dev->watchdog_timeo = (HZ >> 1);
 
@@ -835,7 +851,7 @@
     lp->a.write_bcr (ioaddr, 2, val);
     
     /* handle full duplex setting */
-    if (lp->full_duplex) {
+    if (lp->mii_if.full_duplex) {
 	val = lp->a.read_bcr (ioaddr, 9) & ~3;
 	if (lp->options & PORT_FD) {
 	    val |= 1;
@@ -1489,29 +1505,157 @@
     pcnet32_restart(dev, 0x0042); /*  Resume normal operation */
 }
 
-#ifdef HAVE_PRIVATE_IOCTL
-static int pcnet32_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
+{
+	struct pcnet32_private *lp = dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	u16 val_out;
+	int phyaddr;
+
+	if (!lp->mii)
+		return 0;
+		
+	phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+	lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+	val_out = lp->a.read_bcr(ioaddr, 34);
+	lp->a.write_bcr(ioaddr, 33, phyaddr);
+	
+	return val_out;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
+{
+	struct pcnet32_private *lp = dev->priv;
+	unsigned long ioaddr = dev->base_addr;
+	int phyaddr;
+
+	if (!lp->mii)
+		return;
+		
+	phyaddr = lp->a.read_bcr(ioaddr, 33);
+
+	lp->a.write_bcr(ioaddr, 33, ((phy_id & 0x1f) << 5) | (reg_num & 0x1f));
+	lp->a.write_bcr(ioaddr, 34, val);
+	lp->a.write_bcr(ioaddr, 33, phyaddr);
+}
+
+static int pcnet32_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	struct pcnet32_private *lp = dev->priv;
+	u32 ethcmd;
+	int phyaddr = 0;
+	int phy_id = 0;
+	unsigned long ioaddr = dev->base_addr;
+
+	if (lp->mii) {
+		phyaddr = lp->a.read_bcr (ioaddr, 33);
+		phy_id = (phyaddr >> 5) & 0x1f;
+		lp->mii_if.phy_id = phy_id;
+	}
+
+	if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+		return -EFAULT;
+
+	switch (ethcmd) {
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		if (lp->pci_dev)
+			strcpy (info.bus_info, lp->pci_dev->slot_name);
+		else
+			sprintf(info.bus_info, "VLB 0x%lx", dev->base_addr);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&lp->lock);
+		mii_ethtool_gset(&lp->mii_if, &ecmd);
+		spin_unlock_irq(&lp->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&lp->lock);
+		r = mii_ethtool_sset(&lp->mii_if, &ecmd);
+		spin_unlock_irq(&lp->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&lp->mii_if);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&lp->mii_if);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = pcnet32_debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		pcnet32_debug = edata.data;
+		return 0;
+	}
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
     unsigned long ioaddr = dev->base_addr;
     struct pcnet32_private *lp = dev->priv;	 
-    u16 *data = (u16 *)&rq->ifr_data;
+    struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
     int phyaddr = lp->a.read_bcr (ioaddr, 33);
 
+    if (cmd == SIOCETHTOOL)
+	return pcnet32_ethtool_ioctl(dev, (void *) rq->ifr_data);
+
     if (lp->mii) {
 	switch(cmd) {
-	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
-	    data[0] = (phyaddr >> 5) & 0x1f;
+	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
+	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
+	    data->phy_id = (phyaddr >> 5) & 0x1f;
 	    /* Fall Through */
-	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
-	    lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-	    data[3] = lp->a.read_bcr (ioaddr, 34);
+	case SIOCGMIIREG:		/* Read MII PHY register. */
+	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
+	    lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+	    data->val_out = lp->a.read_bcr (ioaddr, 34);
 	    lp->a.write_bcr (ioaddr, 33, phyaddr);
 	    return 0;
-	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
+	case SIOCSMIIREG:		/* Write MII PHY register. */
+	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
 	    if (!capable(CAP_NET_ADMIN))
 		return -EPERM;
-	    lp->a.write_bcr (ioaddr, 33, ((data[0] & 0x1f) << 5) | (data[1] & 0x1f));
-	    lp->a.write_bcr (ioaddr, 34, data[2]);
+	    lp->a.write_bcr (ioaddr, 33, ((data->phy_id & 0x1f) << 5) | (data->reg_num & 0x1f));
+	    lp->a.write_bcr (ioaddr, 34, data->val_in);
 	    lp->a.write_bcr (ioaddr, 33, phyaddr);
 	    return 0;
 	default:
@@ -1520,13 +1664,12 @@
     }
     return -EOPNOTSUPP;
 }
-#endif	/* HAVE_PRIVATE_IOCTL */
 					    
 static struct pci_driver pcnet32_driver = {
-    name:  "pcnet32",
-    probe: pcnet32_probe_pci,
-    remove: NULL,
-    id_table: pcnet32_pci_tbl,
+	name:		DRV_NAME,
+	probe:		pcnet32_probe_pci,
+	remove:		NULL,
+	id_table:	pcnet32_pci_tbl,
 };
 
 MODULE_PARM(debug, "i");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/ppp_generic.c linux/drivers/net/ppp_generic.c
--- linux.orig/drivers/net/ppp_generic.c	Thu Oct 11 16:18:31 2001
+++ linux/drivers/net/ppp_generic.c	Thu Feb  7 17:06:25 2002
@@ -166,11 +166,11 @@
  */
 
 /*
- * all_ppp_lock protects the all_ppp_units.
+ * all_ppp_sem protects the all_ppp_units.
  * It also ensures that finding a ppp unit in the all_ppp_units list
  * and updating its file.refcnt field is atomic.
  */
-static spinlock_t all_ppp_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(all_ppp_sem);
 static LIST_HEAD(all_ppp_units);
 
 /*
@@ -706,11 +706,11 @@
 		/* Attach to an existing ppp unit */
 		if (get_user(unit, (int *) arg))
 			break;
-		spin_lock(&all_ppp_lock);
+		down(&all_ppp_sem);
 		ppp = ppp_find_unit(unit);
 		if (ppp != 0)
 			atomic_inc(&ppp->file.refcnt);
-		spin_unlock(&all_ppp_lock);
+		up(&all_ppp_sem);
 		err = -ENXIO;
 		if (ppp == 0)
 			break;
@@ -1528,6 +1528,7 @@
 			   error indication. */
 			if (len == DECOMP_FATALERROR)
 				ppp->rstate |= SC_DC_FERROR;
+			kfree_skb(ns);
 			goto err;
 		}
 
@@ -2199,7 +2200,7 @@
 	int ret = -EEXIST;
 	int i;
 
-	spin_lock(&all_ppp_lock);
+	down(&all_ppp_sem);
 	list = &all_ppp_units;
 	while ((list = list->next) != &all_ppp_units) {
 		ppp = list_entry(list, struct ppp, file.list);
@@ -2215,11 +2216,11 @@
 
 	/* Create a new ppp structure and link it before `list'. */
 	ret = -ENOMEM;
-	ppp = kmalloc(sizeof(struct ppp), GFP_ATOMIC);
+	ppp = kmalloc(sizeof(struct ppp), GFP_KERNEL);
 	if (ppp == 0)
 		goto out;
 	memset(ppp, 0, sizeof(struct ppp));
-	dev = kmalloc(sizeof(struct net_device), GFP_ATOMIC);
+	dev = kmalloc(sizeof(struct net_device), GFP_KERNEL);
 	if (dev == 0) {
 		kfree(ppp);
 		goto out;
@@ -2258,7 +2259,7 @@
 
 	list_add(&ppp->file.list, list->prev);
  out:
-	spin_unlock(&all_ppp_lock);
+ 	up(&all_ppp_sem);
 	*retp = ret;
 	if (ret != 0)
 		ppp = 0;
@@ -2286,7 +2287,7 @@
 	struct net_device *dev;
 	int n_channels ;
 
-	spin_lock(&all_ppp_lock);
+	down(&all_ppp_sem);
 	list_del(&ppp->file.list);
 
 	/* Last fd open to this ppp unit is being closed or detached:
@@ -2326,19 +2327,19 @@
 
 	/*
 	 * We can't acquire any new channels (since we have the
-	 * all_ppp_lock) so if n_channels is 0, we can free the
+	 * all_ppp_sem) so if n_channels is 0, we can free the
 	 * ppp structure.  Otherwise we leave it around until the
 	 * last channel disconnects from it.
 	 */
 	if (n_channels == 0) 
 		kfree(ppp);
 
-	spin_unlock(&all_ppp_lock);
+	up(&all_ppp_sem);
 }
 
 /*
  * Locate an existing ppp unit.
- * The caller should have locked the all_ppp_lock.
+ * The caller must have locked the all_ppp_sem.
  */
 static struct ppp *
 ppp_find_unit(int unit)
@@ -2384,7 +2385,7 @@
 	int ret = -ENXIO;
 	int hdrlen;
 
-	spin_lock(&all_ppp_lock);
+	down(&all_ppp_sem);
 	ppp = ppp_find_unit(unit);
 	if (ppp == 0)
 		goto out;
@@ -2413,7 +2414,7 @@
  outwl:
 	write_unlock_bh(&pch->upl);
  out:
-	spin_unlock(&all_ppp_lock);
+ 	up(&all_ppp_sem);
 	return ret;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/rclanmtl.c linux/drivers/net/rclanmtl.c
--- linux.orig/drivers/net/rclanmtl.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/rclanmtl.c	Mon Jan 14 18:53:53 2002
@@ -299,21 +299,21 @@
 {
 	int result;
 	PPAB pPab;
-	U32 pciBaseAddr = dev->base_addr;
 	PDPA pDpa = dev->priv;
+	U32 pciBaseAddr = dev->base_addr;
 	PU8 p_msgbuf = pDpa->PLanApiPA;
 	PU8 p_phymsgbuf = (PU8) virt_to_bus ((void *) p_msgbuf);
 
 	dprintk
-	    ("InitI2O: Adapter:0x%04ux ATU:0x%08ulx msgbuf:0x%08ulx phymsgbuf:0x%08ulx\n"
-	     "TransmitCallbackFunction:0x%08ulx  ReceiveCallbackFunction:0x%08ulx\n",
+	    ("InitI2O: Adapter:0x%x ATU:0x%x msgbuf:0x%x phymsgbuf:0x%x\n"
+	     "TransmitCallbackFunction:0x%x  ReceiveCallbackFunction:0x%x\n",
 	     pDpa->id, pciBaseAddr, (u32) p_msgbuf, (u32) p_phymsgbuf,
 	     (u32) TransmitCallbackFunction, (u32) ReceiveCallbackFunction);
 
 	/* Check if this interface already initialized - if so, shut it down */
 	if (pDpa->pPab != NULL) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) pDpa->pPab [%d] != NULL\n",
+		dprintk (KERN_WARNING
+			"pDpa->pPab [%d] != NULL\n",
 			pDpa->id);
 /*          RCResetLANCard(pDpa->id, 0, (PU32)NULL, (PFNCALLBACK)NULL); */
 		pDpa->pPab = NULL;
@@ -324,8 +324,8 @@
 
 	pPab = kmalloc (sizeof (*pPab), GFP_KERNEL);
 	if (!pPab) {
-		printk (KERN_ERR
-			"(rcpci45 driver:) RCInitI2OMsgLayer: Could not allocate memory for PAB struct!\n");
+		dprintk (KERN_ERR 
+				"RCInitI2OMsgLayer: Could not allocate memory for PAB struct!\n");
 		result = RC_RTN_MALLOC_ERROR;
 		goto err_out;
 	}
@@ -354,8 +354,7 @@
 		goto err_out_dealloc;
 
 	if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL) {
-		printk (KERN_INFO
-			"(rcpci45 driver:) pPab->IOPState == op: resetting adapter\n");
+		dprintk (KERN_INFO "pPab->IOPState == op: resetting adapter\n");
 		RCResetLANCard (dev, 0, (PU32) NULL, (PFNCALLBACK) NULL);
 	}
 
@@ -445,7 +444,7 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
 	size = FillI2OMsgSGLFromTCB (pMsg + 4, pTransCtrlBlock);
@@ -502,7 +501,7 @@
 		dprintk ("RCPostRecvBuffers(): Inbound Free Q empty!\n");
 		return RC_RTN_FREE_Q_EMPTY;
 	}
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
 	size = FillI2OMsgSGLFromTCB (pMsg + 4, pTransCtrlBlock);
@@ -570,13 +569,13 @@
 						     dev);
 		} else if (I2O_LAN_RECEIVE_POST == p8Msg[7]) {	/* Receive Packet Reply Msg */
 			dprintk
-			    ("I2O_RECV_REPLY pPab:0x%08ulx p8Msg:0x%08ulx p32:0x%08ulx\n",
+			    ("I2O_RECV_REPLY pPab:0x%x p8Msg:0x%x p32:0x%x\n",
 			     (u32) pPab, (u32) p8Msg, (u32) p32);
-			dprintk ("msg: 0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
+			dprintk ("msg: 0x%x:0x%x:0x%x:0x%x\n",
 				 p32[0], p32[1], p32[2], p32[3]);
-			dprintk ("     0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
+			dprintk ("     0x%x:0x%x:0x%x:0x%x\n",
 				 p32[4], p32[5], p32[6], p32[7]);
-			dprintk ("     0x%08ulx:0X%08ulx:0x%08ulx:0x%08ulx\n",
+			dprintk ("     0x%x:0X%x:0x%x:0x%x\n",
 				 p32[8], p32[9], p32[10], p32[11]);
 			/*  status, count, buckets remaining, packetParmBlock, adapter */
 			(*pPab->pRecvCallbackFunc) (p8Msg[19], p8Msg[12],
@@ -623,8 +622,8 @@
 								      dev);
 				break;
 			default:
-				printk (KERN_WARNING
-					"(rcpci45 driver:) Unknown private I2O msg received: 0x%x\n",
+				dprintk (KERN_WARNING
+					"Unknown private I2O msg received: 0x%x\n",
 					p32[5]);
 				break;
 			}
@@ -658,14 +657,13 @@
 		     PFNWAITCALLBACK WaitCallback)
 {
 	U32 msgOffset;
-	volatile U32 timeout;
 	volatile PU32 pMsg;
 	volatile PU32 p32, pReturnAddr;
 	P_NICSTAT pStats;
 	int i;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
 
-/*dprintk("Get82558Stats() StatsReturnAddr:0x%08ulx\n", StatsReturnAddr); */
+/*dprintk("Get82558Stats() StatsReturnAddr:0x%x\n", StatsReturnAddr); */
 
 	if (pPab == NULL)
 		return RC_RTN_ADPTR_NOT_REGISTERED;
@@ -677,10 +675,10 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
-/*dprintk("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintk("Get82558Stats - pMsg = 0x%x, InQ msgOffset = 0x%x\n", pMsg, msgOffset);*/
 /*dprintk("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
 
 	pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -690,30 +688,22 @@
 	pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS;
 	pMsg[5] = pPab->outMsgBlockPhyAddr;
 
-	p32 = (PU32) pPab->outMsgBlockPhyAddr;
+//	p32 = (PU32) pPab->outMsgBlockPhyAddr;
+	p32 = (PU32)pPab->pLinOutMsgBlock;
 	pStats = (P_NICSTAT) pPab->pLinOutMsgBlock;
 	pStats->dump_status = 0xFFFFFFFF;
 
 	/* post to Inbound Post Q */
 	pPab->p_atu->InQueue = msgOffset;
 
-	timeout = 100000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);
-
-		if (pStats->dump_status != 0xFFFFFFFF)
-			break;
-
-		if (!timeout--) {
-			dprintk
-			    ("RCGet82558Stats() Timeout waiting for NIC statistics\n");
+	i = 0;
+	while (pStats->dump_status == 0xFFFFFFFF) {
+		if (i++ > 0xff) {
+			dprintk ("Timeout waiting for NIC statistics\n");
 			return RC_RTN_MSG_REPLY_TIMEOUT;
 		}
+		udelay(50);
 	}
-
 	pReturnAddr = (PU32) StatsReturnAddr;
 
 	/* copy Nic stats to user's structure */
@@ -732,13 +722,13 @@
 RCGetLinkStatus (struct net_device * dev, PU32 ReturnAddr,
 		 PFNWAITCALLBACK WaitCallback)
 {
+	int i;
 	U32 msgOffset;
-	volatile U32 timeout;
 	volatile PU32 pMsg;
 	volatile PU32 p32;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
 
-	dprintk ("Get82558LinkStatus() ReturnPhysAddr:0x%08ulx\n",
+	dprintk ("Get82558LinkStatus() ReturnPhysAddr:0x%x\n",
 		 (u32) ReturnAddr);
 
 	if (pPab == NULL)
@@ -751,9 +741,9 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
-/*dprintk("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
+/*dprintk("Get82558LinkStatus - pMsg = 0x%x, InQ msgOffset = 0x%x\n", pMsg, msgOffset);*/
 /*dprintk("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/
 
 	pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -769,20 +759,13 @@
 	/* post to Inbound Post Q */
 	pPab->p_atu->InQueue = msgOffset;
 
-	timeout = 100000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);
-
-		if (*p32 != 0xFFFFFFFF)
-			break;
-
-		if (!timeout--) {
+	i = 0;
+	while (*p32 == 0xFFFFFFFF) {
+		if (i++ > 0xff) {
 			dprintk ("Timeout waiting for link status\n");
 			return RC_RTN_MSG_REPLY_TIMEOUT;
 		}
+		udelay(50);
 	}
 
 	*ReturnAddr = *p32;	/* 1 = up 0 = down */
@@ -802,8 +785,7 @@
 RC_RETURN
 RCGetMAC (struct net_device * dev, PFNWAITCALLBACK WaitCallback)
 {
-	unsigned timeout;
-	U32 off;
+	U32 off, i;
 	PU8 mac = dev->dev_addr;
 	PU32 p;
 	U32 temp[2];
@@ -839,17 +821,13 @@
 		 (uint) p_atu, (uint) off, (uint) p);
 
 	/* wait for the rcpci45 board to update the info */
-	timeout = 1000000;
+	i = 0;
 	while (0 == p_atu->EtherMacLow) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);
-
-		if (!timeout--) {
-			printk ("rc_getmac: Timeout\n");
+		if (i++ > 0xff) {
+			dprintk ("rc_getmac: Timeout\n");
 			return RC_RTN_MSG_REPLY_TIMEOUT;
-		}
+		}	
+		udelay(50);
 	}
 
 	/* read the mac address  */
@@ -1001,16 +979,16 @@
 RCGetPromiscuousMode (struct net_device * dev, PU32 pMode,
 		      PFNWAITCALLBACK WaitCallback)
 {
-	U32 msgOffset, timeout;
 	PU32 pMsg;
 	volatile PU32 p32;
+	U32 msgOffset, i;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
 
 	msgOffset = pPab->p_atu->InQueue;
 
 	if (msgOffset == 0xFFFFFFFF) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n");
+		dprintk (KERN_WARNING
+			"RCGetLinkSpeed(): Inbound Free Q empty!\n");
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
@@ -1034,23 +1012,15 @@
 
 	pPab->p_atu->InQueue = msgOffset;
 
-	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] != 0xff)
-			break;
+	i = 0;
 
-		if (!timeout--) {
-			dprintk
-			    ("Timeout waiting for promiscuous mode from adapter\n");
-			dprintk ("0x%8x\n", p32[0]);
+	/* wait for response */
+	while (p32[0] == 0xff) {
+		if (i++ > 0xff) {
+			dprintk ("Timeout waiting for promiscuous mode\n");
 			return RC_RTN_NO_LINK_SPEED;
 		}
+		udelay(50);
 	}
 
 	/* get mode */
@@ -1115,7 +1085,7 @@
 RCGetBroadcastMode (struct net_device * dev, PU32 pMode,
 		    PFNWAITCALLBACK WaitCallback)
 {
-	U32 msgOffset, timeout;
+	U32 msgOffset;
 	PU32 pMsg;
 	volatile PU32 p32;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
@@ -1123,8 +1093,8 @@
 	msgOffset = pPab->p_atu->InQueue;
 
 	if (msgOffset == 0xFFFFFFFF) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n");
+		dprintk (KERN_WARNING
+			"RCGetLinkSpeed(): Inbound Free Q empty!\n");
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
@@ -1149,23 +1119,10 @@
 	pPab->p_atu->InQueue = msgOffset;
 
 	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] != 0xff)
-			break;
-
-		if (!timeout--) {
-			printk (KERN_WARNING
-				"(rcpci45 driver:) Timeout waiting for promiscuous mode from adapter\n");
-			printk (KERN_WARNING "(rcpci45 driver:) 0x%8x\n",
-				p32[0]);
-			return RC_RTN_NO_LINK_SPEED;
-		}
+	if (p32[0] == 0xff) {
+		dprintk (KERN_WARNING
+			"Timeout waiting for promiscuous mode\n");
+		return RC_RTN_NO_LINK_SPEED;
 	}
 
 	/* get mode */
@@ -1192,7 +1149,7 @@
 RCGetLinkSpeed (struct net_device * dev, PU32 pLinkSpeedCode,
 		PFNWAITCALLBACK WaitCallback)
 {
-	U32 msgOffset, timeout;
+	U32 msgOffset, i;
 	PU32 pMsg;
 	volatile PU32 p32;
 	U8 IOPLinkSpeed;
@@ -1201,8 +1158,8 @@
 	msgOffset = pPab->p_atu->InQueue;
 
 	if (msgOffset == 0xFFFFFFFF) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) RCGetLinkSpeed(): Inbound Free Q empty!\n");
+		dprintk (KERN_WARNING
+			"RCGetLinkSpeed(): Inbound Free Q empty!\n");
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
@@ -1227,26 +1184,16 @@
 	pPab->p_atu->InQueue = msgOffset;
 
 	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] != 0xff)
-			break;
-
-		if (!timeout--) {
-			dprintk ("Timeout waiting for link speed from IOP\n");
-			dprintk ("0x%8x\n", p32[0]);
+	i = 0;
+	while (p32[0] == 0xff) {
+		if (i++ > 0xff) {
+			dprintk ("Timeout waiting for link speed\n");
 			return RC_RTN_NO_LINK_SPEED;
 		}
+		udelay(50);
 	}
-
 	/* get Link speed */
 	IOPLinkSpeed = (U8) ((volatile PU8) p32)[0] & 0x0f;
-
 	*pLinkSpeedCode = IOPLinkSpeed;
 
 	return RC_RTN_NO_ERROR;
@@ -1304,7 +1251,7 @@
 RCGetFirmwareVer (struct net_device * dev, PU8 pFirmString,
 		  PFNWAITCALLBACK WaitCallback)
 {
-	U32 msgOffset, timeout;
+	U32 msgOffset, i;
 	PU32 pMsg;
 	volatile PU32 p32;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
@@ -1336,22 +1283,14 @@
 	pPab->p_atu->InQueue = msgOffset;
 
 	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] != 0xff)
-			break;
-
-		if (!timeout--) {
-			dprintk ("Timeout waiting for link speed from IOP\n");
+	i = 0;
+	while (p32[0] == 0xff) {
+		if (i++ > 0xff) {
+			dprintk ("Timeout waiting for link speed\n");
 			return RC_RTN_NO_FIRM_VER;
 		}
+		udelay(50);
 	}
-
 	strcpy (pFirmString, (PU8) p32);
 	return RC_RTN_NO_ERROR;
 }
@@ -1403,9 +1342,9 @@
 		   or until timer goes off */
 		while (pPab->pCallbackFunc == (PFNCALLBACK) NULL) {
 			RCProcI2OMsgQ (dev);
-			udelay (1000);	/* please don't hog the bus!!! */
+			mdelay (1);
 			timeout++;
-			if (timeout > 10000) {
+			if (timeout > 200) {
 				break;
 			}
 		}
@@ -1427,7 +1366,7 @@
 RC_RETURN
 RCResetIOP (struct net_device * dev)
 {
-	U32 msgOffset, timeout;
+	U32 msgOffset, i;
 	PU32 pMsg;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
 	volatile PU32 p32;
@@ -1452,7 +1391,7 @@
 	pMsg[7] = 0;
 	pMsg[8] = 1;		/*  return 1 byte */
 
-	/* virual pointer to return buffer - clear first two dwords */
+	/* virtual pointer to return buffer - clear first two dwords */
 	p32 = (volatile PU32) pPab->pLinOutMsgBlock;
 	p32[0] = 0;
 	p32[1] = 0;
@@ -1462,17 +1401,13 @@
 	pPab->p_atu->InQueue = msgOffset;
 
 	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] || p32[1])
-			break;
-
-		if (!timeout--) {
+	i = 0;
+	while (!p32[0] && !p32[1]) {
+		if (i++ > 0xff) {
 			dprintk ("RCResetIOP timeout\n");
 			return RC_RTN_MSG_REPLY_TIMEOUT;
 		}
+		udelay(100);
 	}
 	return RC_RTN_NO_ERROR;
 }
@@ -1525,11 +1460,11 @@
 		   or until timer goes off */
 		while (pPab->pCallbackFunc == (PFNCALLBACK) NULL) {
 			RCProcI2OMsgQ (dev);
-			udelay (1000);	/* please don't hog the bus!!! */
+			mdelay (1);
 			timeout++;
-			if (timeout > 10000) {
-				printk (KERN_WARNING
-					"(rcpci45 driver:) RCShutdownLANCard(): timeout\n");
+			if (timeout > 200) {
+				dprintk (KERN_WARNING
+					"RCShutdownLANCard(): timeout\n");
 				break;
 			}
 		}
@@ -1594,14 +1529,13 @@
 RCGetRavlinIPandMask (struct net_device * dev, PU32 pIpAddr, PU32 pNetMask,
 		      PFNWAITCALLBACK WaitCallback)
 {
-	unsigned timeout;
-	U32 off;
+	U32 off, i;
 	PU32 pMsg, p32;
 	PPAB pPab = ((PDPA) dev->priv)->pPab;
 	PATU p_atu;
 
 	dprintk
-	    ("RCGetRavlinIPandMask: pIpAddr is 0x%08ulx, *IpAddr is 0x%08ulx\n",
+	    ("RCGetRavlinIPandMask: pIpAddr is 0x%x, *IpAddr is 0x%x\n",
 	     (u32) pIpAddr, *pIpAddr);
 
 	if (pPab == NULL)
@@ -1619,7 +1553,7 @@
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + off);
 
 	dprintk
-	    ("RCGetRavlinIPandMask: p_atu 0x%08ulx, off 0x%08ulx, p32 0x%08ulx\n",
+	    ("RCGetRavlinIPandMask: p_atu 0x%x, off 0x%x, p32 0x%x\n",
 	     (u32) p_atu, off, (u32) p32);
 	/* setup private message */
 	pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1631,25 +1565,21 @@
 
 	p_atu->InQueue = off;	/* send it to the I2O device */
 	dprintk
-	    ("RCGetRavlinIPandMask: p_atu 0x%08ulx, off 0x%08ulx, p32 0x%08ulx\n",
+	    ("RCGetRavlinIPandMask: p_atu 0x%x, off 0x%x, p32 0x%x\n",
 	     (u32) p_atu, off, (u32) p32);
 
 	/* wait for the rcpci45 board to update the info */
-	timeout = 100000;
+	i = 0;
 	while (0xffffffff == *p32) {
-		if (WaitCallback)
-			(*WaitCallback) ();
-
-		udelay (10);
-
-		if (!timeout--) {
+		if (i++ > 0xff) {
 			dprintk ("RCGetRavlinIPandMask: Timeout\n");
 			return RC_RTN_MSG_REPLY_TIMEOUT;
 		}
+		udelay(50);
 	}
 
 	dprintk
-	    ("RCGetRavlinIPandMask: after time out\np32[0] (IpAddr) 0x%08ulx, p32[1] (IPmask) 0x%08ulx\n",
+	    ("RCGetRavlinIPandMask: after time out\np32[0] (IpAddr) 0x%x, p32[1] (IPmask) 0x%x\n",
 	     p32[0], p32[1]);
 
 	/* send IP and mask to user's space  */
@@ -1657,7 +1587,7 @@
 	*pNetMask = p32[1];
 
 	dprintk
-	    ("RCGetRavlinIPandMask: pIpAddr is 0x%08ulx, *IpAddr is 0x%08ulx\n",
+	    ("RCGetRavlinIPandMask: pIpAddr is 0x%x, *IpAddr is 0x%x\n",
 	     (u32) pIpAddr, *pIpAddr);
 
 	return RC_RTN_NO_ERROR;
@@ -1682,7 +1612,7 @@
 static int
 SendI2OOutboundQInitMsg (PPAB pPab)
 {
-	U32 msgOffset, timeout, phyOutQFrames, i;
+	U32 msgOffset, phyOutQFrames, i;
 	volatile PU32 pMsg;
 	volatile PU32 p32;
 
@@ -1693,11 +1623,11 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
 	dprintk
-	    ("SendI2OOutboundQInitMsg - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n",
+	    ("SendI2OOutboundQInitMsg - pMsg = 0x%x, InQ msgOffset = 0x%x\n",
 	     (u32) pMsg, msgOffset);
 
 	pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
@@ -1711,7 +1641,7 @@
 	/* phys address to return status - area right after PAB */
 	pMsg[7] = pPab->outMsgBlockPhyAddr;
 
-	/* virual pointer to return buffer - clear first two dwords */
+	/* virtual pointer to return buffer - clear first two dwords */
 	p32 = (PU32) pPab->pLinOutMsgBlock;
 	p32[0] = 0;
 
@@ -1719,34 +1649,19 @@
 	pPab->p_atu->InQueue = msgOffset;
 
 	/* wait for response */
-	timeout = 100000;
-	while (1) {
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0])
-			break;
-
-		if (!timeout--) {
-			dprintk
-			    ("Timeout wait for InitOutQ InPrgress status from IOP\n");
+	i = 0;
+	while (!p32[0]) {
+		if (i++ > 0xff) {
+			printk("rc: InitOutQ timeout\n");
 			return RC_RTN_NO_I2O_STATUS;
 		}
+		udelay(50);
 	}
-
-	timeout = 100000;
-	while (1) {
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] == I2O_EXEC_OUTBOUND_INIT_COMPLETE)
-			break;
-
-		if (!timeout--) {
-			dprintk
-			    ("Timeout wait for InitOutQ Complete status from IOP\n");
-			return RC_RTN_NO_I2O_STATUS;
-		}
+	if (p32[0] != I2O_EXEC_OUTBOUND_INIT_COMPLETE) {
+		printk("rc: exec outbound init failed (%x)\n",
+				p32[0]);
+		return RC_RTN_NO_I2O_STATUS;
 	}
-
 	/* load PCI outbound free Q with MF physical addresses */
 	phyOutQFrames = pPab->outMsgBlockPhyAddr;
 
@@ -1768,7 +1683,7 @@
 static int
 GetI2OStatus (PPAB pPab)
 {
-	U32 msgOffset, timeout;
+	U32 msgOffset, i;
 	PU32 pMsg;
 	volatile PU32 p32;
 
@@ -1779,7 +1694,7 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
 	pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1793,51 +1708,41 @@
 	pMsg[7] = 0;
 	pMsg[8] = 88;		/*  return 88 bytes */
 
-	/* virual pointer to return buffer - clear first two dwords */
+	/* virtual pointer to return buffer - clear first two dwords */
 	p32 = (volatile PU32) pPab->pLinOutMsgBlock;
 	p32[0] = 0;
 	p32[1] = 0;
 
 	dprintk
-	    ("GetI2OStatus - pMsg:0x%08ulx, msgOffset:0x%08ulx, [1]:0x%08ulx, [6]:0x%08ulx\n",
+	    ("GetI2OStatus - pMsg:0x%x, msgOffset:0x%x, [1]:0x%x, [6]:0x%x\n",
 	     (u32) pMsg, msgOffset, pMsg[1], pMsg[6]);
 
 	/* post to Inbound Post Q */
 	pPab->p_atu->InQueue = msgOffset;
 
-	dprintk ("Return status to p32 = 0x%08ulx\n", (u32) p32);
+	dprintk ("Return status to p32 = 0x%x\n", (u32) p32);
 
 	/* wait for response */
-	timeout = 1000000;
-	while (1) {
-		udelay (10);	/* please don't hog the bus!!! */
-
-		if (p32[0] && p32[1])
-			break;
-
-		if (!timeout--) {
+	i = 0;
+	while (!p32[0] || !p32[1]) {
+		if (i++ > 0xff) {
 			dprintk ("Timeout waiting for status from IOP\n");
-			dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
-				 p32[0], p32[1], p32[2], p32[3]);
-			dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
-				 p32[4], p32[5], p32[6], p32[7]);
-			dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
-				 p32[8], p32[9], p32[10], p32[11]);
 			return RC_RTN_NO_I2O_STATUS;
 		}
+		udelay(50);
 	}
 
-	dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[0], p32[1],
+	dprintk ("0x%x:0x%x:0x%x:0x%x\n", p32[0], p32[1],
 		 p32[2], p32[3]);
-	dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[4], p32[5],
+	dprintk ("0x%x:0x%x:0x%x:0x%x\n", p32[4], p32[5],
 		 p32[6], p32[7]);
-	dprintk ("0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[8], p32[9],
+	dprintk ("0x%x:0x%x:0x%x:0x%x\n", p32[8], p32[9],
 		 p32[10], p32[11]);
 	/* get IOP state */
 	pPab->IOPState = ((volatile PU8) p32)[10];
 	pPab->InboundMFrameSize = ((volatile PU16) p32)[6];
 
-	dprintk ("IOP state 0x%02x InFrameSize = 0x%04x\n",
+	dprintk ("IOP state 0x%x InFrameSize = 0x%x\n",
 		 pPab->IOPState, pPab->InboundMFrameSize);
 	return RC_RTN_NO_ERROR;
 }
@@ -1862,11 +1767,11 @@
 		return RC_RTN_FREE_Q_EMPTY;
 	}
 
-	/* calc virual address of msg - virual already mapped to physical */
+	/* calc virtual address of msg - virtual already mapped to physical */
 	pMsg = (PU32) (pPab->pPci45LinBaseAddr + msgOffset);
 
 	dprintk
-	    ("SendEnableSysMsg - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n",
+	    ("SendEnableSysMsg - pMsg = 0x%x, InQ msgOffset = 0x%x\n",
 	     (u32) pMsg, msgOffset);
 
 	pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
@@ -1885,7 +1790,7 @@
 ** =========================================================================
 ** FillI2OMsgFromTCB()
 **
-** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
+** inputs   pMsgU32 - virtual pointer (mapped to physical) of message frame
 **          pXmitCntrlBlock - pointer to caller buffer control block.
 **
 ** fills in LAN SGL after Transaction Control Word or Bucket Count.
@@ -1908,9 +1813,9 @@
 	nmbrDwords = 0;
 
 	dprintk ("FillI2OMsgSGLFromTCBX\n");
-	dprintk ("TCB  0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n",
+	dprintk ("TCB  0x%x:0x%x:0x%x:0x%x:0x%x\n",
 		 pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
-	dprintk ("pTCB 0x%08ulx, pMsg 0x%08ulx\n", (u32) pTCB, (u32) pMsg);
+	dprintk ("pTCB 0x%x, pMsg 0x%x\n", (u32) pTCB, (u32) pMsg);
 
 	nmbrBuffers = *pTCB++;
 
@@ -1987,11 +1892,11 @@
 	p32 = (PU32) p8Msg;
 
 	dprintk
-	    ("VXD: ProcessOutboundI2OMsg - pPab 0x%08ulx, phyAdr 0x%08ulx, linAdr 0x%08ulx\n",
+	    ("VXD: ProcessOutboundI2OMsg - pPab 0x%x, phyAdr 0x%x, linAdr 0x%x\n",
 	     (u32) pPab, phyAddrMsg, (u32) p8Msg);
-	dprintk ("msg :0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[0], p32[1],
+	dprintk ("msg :0x%x:0x%x:0x%x:0x%x\n", p32[0], p32[1],
 		 p32[2], p32[3]);
-	dprintk ("msg :0x%08ulx:0x%08ulx:0x%08ulx:0x%08ulx\n", p32[4], p32[5],
+	dprintk ("msg :0x%x:0x%x:0x%x:0x%x\n", p32[4], p32[5],
 		 p32[6], p32[7]);
 
 	if (p32[4] >> 24 != I2O_REPLY_STATUS_SUCCESS) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/rclanmtl.h linux/drivers/net/rclanmtl.h
--- linux.orig/drivers/net/rclanmtl.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/rclanmtl.h	Mon Jan 14 18:53:53 2002
@@ -54,10 +54,10 @@
 #include <asm/io.h>
 
 /* Debug stuff. Define for debug output */
-#define RCDEBUG
+#undef RCDEBUG
 
 #ifdef RCDEBUG
-#define dprintk(args...) printk(KERN_DEBUG "(rcpci45 driver:) " args)
+#define dprintk(args...) printk("rc: " args)
 #else
 #define dprintk(args...) { }
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/rcpci45.c linux/drivers/net/rcpci45.c
--- linux.orig/drivers/net/rcpci45.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/rcpci45.c	Wed Feb 13 17:37:40 2002
@@ -29,6 +29,9 @@
 **  along with this program; if not, write to the Free Software
 **  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 **
+**  Pete Popov, Oct 2001: Fixed a few bugs to make the driver functional
+**  again. Note that this card is not supported or manufactured by 
+**  RedCreek anymore.
 **   
 **  Rasmus Andersen, December 2000: Converted to new PCI API and general
 **  cleanup.
@@ -62,7 +65,7 @@
 #include <asm/uaccess.h>
 
 static char version[] __initdata =
-    "RedCreek Communications PCI linux driver version 2.03\n";
+    "RedCreek Communications PCI linux driver version 2.20\n";
 
 #define RC_LINUX_MODULE
 #include "rclanmtl.h"
@@ -87,6 +90,14 @@
 /* RedCreek's OSM default LAN receive Initiator */
 #define DEFAULT_RECV_INIT_CONTEXT  0xA17
 
+/* minimum msg buffer size needed by the card 
+ * Note that the size of this buffer is hard code in the
+ * ipsec card's firmware. Thus, the size MUST be a minimum
+ * of 16K. Otherwise the card will end up using memory
+ * that does not belong to it.
+ */
+#define MSG_BUF_SIZE  16384
+
 static U32 DriverControlWord;
 
 static void rc_timer (unsigned long);
@@ -113,46 +124,32 @@
 MODULE_DEVICE_TABLE (pci, rcpci45_pci_table);
 MODULE_LICENSE("GPL");
 
-static void __exit
+static void __devexit
 rcpci45_remove_one (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata (pdev);
 	PDPA pDpa = dev->priv;
 
 	if (!dev) {
-		printk (KERN_ERR
-			"(rcpci45 driver:) remove non-existent device\n");
+		printk (KERN_ERR "%s: remove non-existent device\n",
+				dev->name);
 		return;
 	}
 
-	dprintk ("remove_one: IOP reset: 0x%x\n", RCResetIOP (dev));
-
-	/* RAA Inspired by starfire.c and yellowfin.c we keep these
-	 * here. */
+	RCResetIOP (dev);
 	unregister_netdev (dev);
 	free_irq (dev->irq, dev);
 	iounmap ((void *) dev->base_addr);
 	pci_release_regions (pdev);
-	kfree (pDpa->PLanApiPA);
-	kfree (pDpa->pPab);
-	kfree (pDpa);
+	if (pDpa->msgbuf)
+		kfree (pDpa->msgbuf);
+	if (pDpa->pPab)
+		kfree (pDpa->pPab);
 	kfree (dev);
 	pci_set_drvdata (pdev, NULL);
 }
 
 static int
-RCinit (struct net_device *dev)
-{
-	dev->open = &RCopen;
-	dev->hard_start_xmit = &RC_xmit_packet;
-	dev->stop = &RCclose;
-	dev->get_stats = &RCget_stats;
-	dev->do_ioctl = &RCioctl;
-	dev->set_config = &RCconfig;
-	return 0;
-}
-
-static int
 rcpci45_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
 {
 	unsigned long *vaddr;
@@ -166,17 +163,17 @@
 
 	/* 
 	 * Allocate and fill new device structure. 
-	 * We need enough for struct net_device plus DPA plus the LAN API private
-	 * area, which requires a minimum of 16KB.  The top of the allocated
-	 * area will be assigned to struct net_device; the next chunk will be
-	 * assigned to DPA; and finally, the rest will be assigned to the
-	 * the LAN API layer.
+	 * We need enough for struct net_device plus DPA plus the LAN 
+	 * API private area, which requires a minimum of 16KB.  The top 
+	 * of the allocated area will be assigned to struct net_device; 
+	 * the next chunk will be assigned to DPA; and finally, the rest 
+	 * will be assigned to the the LAN API layer.
 	 */
 
 	dev = init_etherdev (NULL, sizeof (*pDpa));
 	if (!dev) {
 		printk (KERN_ERR
-			"(rcpci45 driver:) unable to allocate in init_etherdev\n");
+			"(rcpci45 driver:) init_etherdev alloc failed\n");
 		error = -ENOMEM;
 		goto err_out;
 	}
@@ -184,13 +181,14 @@
 	error = pci_enable_device (pdev);
 	if (error) {
 		printk (KERN_ERR
-			"(rcpci45 driver:) %d: unable to enable pci device, aborting\n",
+			"(rcpci45 driver:) %d: pci enable device error\n",
 			card_idx);
 		goto err_out;
 	}
 	error = -ENOMEM;
 	pci_start = pci_resource_start (pdev, 0);
 	pci_len = pci_resource_len (pdev, 0);
+	printk("pci_start %x pci_len %x\n", pci_start, pci_len);
 
 	pci_set_drvdata (pdev, dev);
 
@@ -200,29 +198,30 @@
 
 	if (!pci_start || !(pci_resource_flags (pdev, 0) & IORESOURCE_MEM)) {
 		printk (KERN_ERR
-			"(rcpci45 driver:) No PCI memory resources! Aborting.\n");
+			"(rcpci45 driver:) No PCI mem resources! Aborting\n");
 		error = -EBUSY;
 		goto err_out_free_dev;
 	}
 
 	/*
-	 * Save the starting address of the LAN API private area.  We'll
-	 * pass that to RCInitI2OMsgLayer().
+	 * pDpa->msgbuf is where the card will dma the I2O 
+	 * messages. Thus, we need contiguous physical pages of
+	 * memory.
 	 */
-	/* RAA FIXME: This size should be a #define somewhere after I
-	 * clear up some questions: What flags are neeeded in the alloc below
-	 * and what needs to be done before the memarea is long word aligned?
-	 * (Look in old code for an approach.) (Also note that the 16K below
-	 * is substantially less than the 32K allocated before (even though
-	 * some of the spacce was used for data structures.) */
-	pDpa->msgbuf = kmalloc (16384, GFP_KERNEL);
+	pDpa->msgbuf = kmalloc (MSG_BUF_SIZE, GFP_DMA|GFP_ATOMIC|GFP_KERNEL);
 	if (!pDpa->msgbuf) {
-		printk (KERN_ERR "(rcpci45 driver:) Could not allocate %d byte memory for the private msgbuf!\n", 16384);	/* RAA FIXME not hardcoded! */
+		printk (KERN_ERR "(rcpci45 driver:) \
+			Could not allocate %d byte memory for the \
+				private msgbuf!\n", MSG_BUF_SIZE);
 		goto err_out_free_dev;
 	}
-	pDpa->PLanApiPA = (void *) (((long) pDpa->msgbuf + 0xff) & ~0xff);
 
-	dprintk ("pDpa->PLanApiPA = 0x%x\n", (uint) pDpa->PLanApiPA);
+	/*
+	 * Save the starting address of the LAN API private area.  We'll
+	 * pass that to RCInitI2OMsgLayer().
+	 *
+	 */
+	pDpa->PLanApiPA = (void *) (((long) pDpa->msgbuf + 0xff) & ~0xff);
 
 	/* The adapter is accessible through memory-access read/write, not
 	 * I/O read/write.  Thus, we need to map it to some virtual address
@@ -235,17 +234,20 @@
 	vaddr = (ulong *) ioremap (pci_start, pci_len);
 	if (!vaddr) {
 		printk (KERN_ERR
-			"(rcpci45 driver:) Unable to remap address range from %lu to %lu\n",
+			"(rcpci45 driver:) \
+			Unable to remap address range from %lu to %lu\n",
 			pci_start, pci_start + pci_len);
 		goto err_out_free_region;
 	}
 
-	dprintk ("rcpci45_init_one: 0x%x, priv = 0x%x, vaddr = 0x%x\n",
-		 (uint) dev, (uint) dev->priv, (uint) vaddr);
 	dev->base_addr = (unsigned long) vaddr;
 	dev->irq = pdev->irq;
-
-	dev->init = &RCinit;
+	dev->open = &RCopen;
+	dev->hard_start_xmit = &RC_xmit_packet;
+	dev->stop = &RCclose;
+	dev->get_stats = &RCget_stats;
+	dev->do_ioctl = &RCioctl;
+	dev->set_config = &RCconfig;
 
 	return 0;		/* success */
 
@@ -258,23 +260,22 @@
 	kfree (dev);
 err_out:
 	card_idx--;
-	return error;
+	return -ENODEV;
 }
 
 static struct pci_driver rcpci45_driver = {
 	name:		"rcpci45",
 	id_table:	rcpci45_pci_table,
 	probe:		rcpci45_init_one,
-	remove:		rcpci45_remove_one,
+	remove:		__devexit_p(rcpci45_remove_one),
 };
 
 static int __init
 rcpci_init_module (void)
 {
 	int rc = pci_module_init (&rcpci45_driver);
-
 	if (!rc)
-		printk (KERN_INFO "%s", version);
+		printk (KERN_ERR "%s", version);
 	return rc;
 }
 
@@ -287,56 +288,56 @@
 	int requested = 0;
 	int error;
 
-	dprintk ("(rcpci45 driver:) RCopen\n");
+	MOD_INC_USE_COUNT;
+	if (pDpa->nexus) {
+		/* This is not the first time RCopen is called.  Thus,
+		 * the interface was previously opened and later closed
+		 * by RCclose().  RCclose() does a Shutdown; to wake up
+		 * the adapter, a reset is mandatory before we can post
+		 * receive buffers.  However, if the adapter initiated 
+		 * a reboot while the interface was closed -- and interrupts
+		 * were turned off -- we need will need to reinitialize
+		 * the adapter, rather than simply waking it up.  
+		 */
+		printk (KERN_INFO "Waking up adapter...\n");
+		RCResetLANCard (dev, 0, 0, 0);
+	} else {
+		pDpa->nexus = 1;
+		/* 
+		 * RCInitI2OMsgLayer is done only once, unless the
+		 * adapter was sent a warm reboot
+		 */
+		error = RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback,
+					   (PFNRXCALLBACK) RCrecv_callback,
+					   (PFNCALLBACK) RCreboot_callback);
+		if (error) {
+			printk (KERN_ERR "%s: Unable to init msg layer (%x)\n",
+					dev->name, error);
+			goto err_out;
+		}
+		if ((error = RCGetMAC (dev, NULL))) {
+			printk (KERN_ERR "%s: Unable to get adapter MAC\n",
+					dev->name);
+			goto err_out;
+		}
+	}
 
 	/* Request a shared interrupt line. */
 	error = request_irq (dev->irq, RCinterrupt, SA_SHIRQ, dev->name, dev);
 	if (error) {
-		printk (KERN_ERR "(rcpci45 driver:) %s: unable to get IRQ %d\n",
-			dev->name, dev->irq);
+		printk (KERN_ERR "%s: unable to get IRQ %d\n", 
+				dev->name, dev->irq);
 		goto err_out;
 	}
 
-	error = RCInitI2OMsgLayer (dev, (PFNTXCALLBACK) RCxmit_callback,
-				   (PFNRXCALLBACK) RCrecv_callback,
-				   (PFNCALLBACK) RCreboot_callback);
-	if (error) {
-		printk (KERN_ERR
-			"(rcpci45 driver:) Unable to initialize msg layer\n");
-		goto err_out_free_irq;
-	}
-	if ((error = RCGetMAC (dev, NULL))) {
-		printk (KERN_ERR
-			"(rcpci45 driver:) Unable to get adapter MAC\n");
-		goto err_out_free_irq;
-	}
-
 	DriverControlWord |= WARM_REBOOT_CAPABLE;
 	RCReportDriverCapability (dev, DriverControlWord);
 
 	printk (KERN_INFO "%s: RedCreek Communications IPSEC VPN adapter\n",
 		dev->name);
 
-	/* RAA: Old RCopen starts here */
 	RCEnableI2OInterrupts (dev);
 
-	/* RAA Hmm, how does the comment below jibe with the newly imported
-	 * code above? A FIXME!!*/
-	if (pDpa->nexus) {
-		/* This is not the first time RCopen is called.  Thus,
-		 * the interface was previously opened and later closed
-		 * by RCclose().  RCclose() does a Shutdown; to wake up
-		 * the adapter, a reset is mandatory before we can post
-		 * receive buffers.  However, if the adapter initiated 
-		 * a reboot while the interface was closed -- and interrupts
-		 * were turned off -- we need will need to reinitialize
-		 * the adapter, rather than simply waking it up.  
-		 */
-		dprintk (KERN_INFO "Waking up adapter...\n");
-		RCResetLANCard (dev, 0, 0, 0);
-	} else
-		pDpa->nexus = 1;
-
 	while (post_buffers) {
 		if (post_buffers > MAX_NMBR_POST_BUFFERS_PER_MSG)
 			requested = MAX_NMBR_POST_BUFFERS_PER_MSG;
@@ -346,29 +347,30 @@
 
 		if (count < requested) {
 			/*
-			 * Check to see if we were able to post any buffers at all.
+			 * Check to see if we were able to post 
+			 * any buffers at all.
 			 */
 			if (post_buffers == MAX_NMBR_RCV_BUFFERS) {
-				printk (KERN_ERR
-					"(rcpci45 driver:) Error RCopen: not able to allocate any buffers\r\n");
-				return (-ENOMEM);
+				printk (KERN_ERR "%s: \
+					unable to allocate any buffers\n", 
+						dev->name);
+				goto err_out_free_irq;
 			}
-			printk (KERN_WARNING
-				"(rcpci45 driver:) Warning RCopen: not able to allocate all requested buffers\r\n");
+			printk (KERN_WARNING "%s: \
+			unable to allocate all requested buffers\n", dev->name);
 			break;	/* we'll try to post more buffers later */
 		} else
 			post_buffers -= count;
 	}
 	pDpa->numOutRcvBuffers = MAX_NMBR_RCV_BUFFERS - post_buffers;
 	pDpa->shutdown = 0;	/* just in case */
-	dprintk ("RCopen: posted %d buffers\n", (uint) pDpa->numOutRcvBuffers);
-	MOD_INC_USE_COUNT;
 	netif_start_queue (dev);
 	return 0;
 
 err_out_free_irq:
 	free_irq (dev->irq, dev);
 err_out:
+	MOD_DEC_USE_COUNT;
 	return error;
 }
 
@@ -384,15 +386,16 @@
 	netif_stop_queue (dev);
 
 	if (pDpa->shutdown || pDpa->reboot) {
-		dprintk ("RC_xmit_packet: tbusy!\n");
+		printk ("RC_xmit_packet: tbusy!\n");
 		return 1;
 	}
 
 	/*
-	 * The user is free to reuse the TCB after RCI2OSendPacket() returns, since
-	 * the function copies the necessary info into its own private space.  Thus,
-	 * our TCB can be a local structure.  The skb, on the other hand, will be
-	 * freed up in our interrupt handler.
+	 * The user is free to reuse the TCB after RCI2OSendPacket() 
+	 * returns, since the function copies the necessary info into its 
+	 * own private space.  Thus, our TCB can be a local structure.  
+	 * The skb, on the other hand, will be freed up in our interrupt 
+	 * handler.
 	 */
 
 	ptcb->bcount = 1;
@@ -406,11 +409,9 @@
 	ptcb->b.size = skb->len;
 	ptcb->b.addr = virt_to_bus ((void *) skb->data);
 
-	dprintk ("RC xmit: skb = 0x%x, pDpa = 0x%x, id = %d, ptcb = 0x%x\n",
-		 (uint) skb, (uint) pDpa, (uint) pDpa->id, (uint) ptcb);
 	if ((status = RCI2OSendPacket (dev, (U32) NULL, (PRCTCB) ptcb))
 	    != RC_RTN_NO_ERROR) {
-		dprintk ("RC send error 0x%x\n", (uint) status);
+		printk ("%s: send error 0x%x\n", dev->name, (uint) status);
 		return 1;
 	} else {
 		dev->trans_start = jiffies;
@@ -440,23 +441,20 @@
 	PDPA pDpa = dev->priv;
 
 	if (!pDpa) {
-		printk (KERN_ERR
-			"(rcpci45 driver:) Fatal error: xmit callback, !pDpa\n");
+		printk (KERN_ERR "%s: Fatal Error in xmit callback, !pDpa\n",
+				dev->name);
 		return;
 	}
 
-/*      dprintk("xmit_callback: Status = 0x%x\n", (uint)Status); */
 	if (Status != I2O_REPLY_STATUS_SUCCESS)
-		dprintk ("xmit_callback: Status = 0x%x\n", (uint) Status);
+		printk (KERN_INFO "%s: xmit_callback: Status = 0x%x\n", 
+				dev->name, (uint) Status);
 	if (pDpa->shutdown || pDpa->reboot)
-		dprintk ("xmit callback: shutdown||reboot\n");
-
-	dprintk ("xmit_callback: PcktCount = %d, BC = 0x%x\n",
-		 (uint) PcktCount, (uint) BufferContext);
+		printk (KERN_INFO "%s: xmit callback: shutdown||reboot\n",
+				dev->name);
 
 	while (PcktCount--) {
 		skb = (struct sk_buff *) (BufferContext[0]);
-		dprintk ("skb = 0x%x\n", (uint) skb);
 		BufferContext++;
 		dev_kfree_skb_irq (skb);
 	}
@@ -468,19 +466,18 @@
 {
 	PDPA pDpa = dev->priv;
 
-	dprintk ("RCreset_callback Status 0x%x\n", (uint) Status);
+	printk ("RCreset_callback Status 0x%x\n", (uint) Status);
 	/*
 	 * Check to see why we were called.
 	 */
 	if (pDpa->shutdown) {
-		printk (KERN_INFO
-			"(rcpci45 driver:) Shutting down interface\n");
+		printk (KERN_INFO "%s: shutting down interface\n",
+				dev->name);
 		pDpa->shutdown = 0;
 		pDpa->reboot = 0;
-		MOD_DEC_USE_COUNT;
 	} else if (pDpa->reboot) {
-		printk (KERN_INFO
-			"(rcpci45 driver:) reboot, shutdown adapter\n");
+		printk (KERN_INFO "%s: reboot, shutdown adapter\n",
+				dev->name);
 		/*
 		 * We don't set any of the flags in RCShutdownLANCard()
 		 * and we don't pass a callback routine to it.
@@ -489,7 +486,7 @@
 		 */
 		RCDisableI2OInterrupts (dev);
 		RCShutdownLANCard (dev, 0, 0, 0);
-		printk (KERN_INFO "(rcpci45 driver:) scheduling timer...\n");
+		printk (KERN_INFO "%s: scheduling timer...\n", dev->name);
 		init_timer (&pDpa->timer);
 		pDpa->timer.expires = RUN_AT ((40 * HZ) / 10);	/* 4 sec. */
 		pDpa->timer.data = (unsigned long) dev;
@@ -503,12 +500,12 @@
 {
 	PDPA pDpa = dev->priv;
 
-	dprintk ("RCreboot: rcv buffers outstanding = %d\n",
-		 (uint) pDpa->numOutRcvBuffers);
+	printk (KERN_INFO "%s: reboot: rcv buffers outstanding = %d\n",
+		 dev->name, (uint) pDpa->numOutRcvBuffers);
 
 	if (pDpa->shutdown) {
-		printk (KERN_INFO
-			"(rcpci45 driver:) skipping reboot sequence -- shutdown already initiated\n");
+		printk (KERN_INFO "%s: skip reboot, shutdown initiated\n",
+				dev->name);
 		return;
 	}
 	pDpa->reboot = 1;
@@ -560,12 +557,9 @@
 
 	ptcb->bcount = 1;
 
-	dprintk ("RCrecv_callback: 0x%x, 0x%x, 0x%x\n",
-		 (uint) PktCount, (uint) BucketsRemain, (uint) PacketDescBlock);
-
 	if ((pDpa->shutdown || pDpa->reboot) && !Status)
-		dprintk ("shutdown||reboot && !Status: PktCount = %d\n",
-			 PktCount);
+		printk (KERN_INFO "%s: shutdown||reboot && !Status (%d)\n",
+				dev->name, PktCount);
 
 	if ((Status != I2O_REPLY_STATUS_SUCCESS) || pDpa->shutdown) {
 		/*
@@ -574,76 +568,38 @@
 		 */
 
 		if (!pDpa->shutdown && !pDpa->reboot)
-			printk (KERN_INFO
-				"(rcpci45 driver:) RCrecv error: status = 0x%x\n",
-				(uint) Status);
+			printk (KERN_INFO "%s: recv error status = 0x%x\n",
+					dev->name, (uint) Status);
 		else
-			dprintk ("Returning %d buffers, status = 0x%x\n",
-				 PktCount, (uint) Status);
+			printk (KERN_DEBUG "%s: Returning %d buffs stat 0x%x\n",
+					dev->name, PktCount, (uint) Status);
 		/*
-		 * TO DO: check the nature of the failure and put the adapter in
-		 * failed mode if it's a hard failure.  Send a reset to the adapter
-		 * and free all outstanding memory.
+		 * TO DO: check the nature of the failure and put the 
+		 * adapter in failed mode if it's a hard failure.  
+		 * Send a reset to the adapter and free all outstanding memory.
 		 */
-		if (Status == I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER)
-			dprintk ("RCrecv status ABORT NO DATA TRANSFER\n");
-
-		/* check for reset status: I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER */
 		if (PacketDescBlock) {
 			while (PktCount--) {
 				skb = (struct sk_buff *) PacketDescBlock[0];
-				dprintk ("free skb 0x%p\n", skb);
 				dev_kfree_skb (skb);
 				pDpa->numOutRcvBuffers--;
-				PacketDescBlock += BD_SIZE;	/* point to next context field */
+				/* point to next context field */
+				PacketDescBlock += BD_SIZE;
 			}
 		}
 		return;
 	} else {
 		while (PktCount--) {
 			skb = (struct sk_buff *) PacketDescBlock[0];
-			if (pDpa->shutdown)
-				dprintk ("shutdown: skb=0x%x\n", (uint) skb);
-
-			dprintk ("skb = 0x%x: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
-				 (uint) skb, (uint) skb->data[0],
-				 (uint) skb->data[1], (uint) skb->data[2],
-				 (uint) skb->data[3], (uint) skb->data[4],
-				 (uint) skb->data[5]);
-
-#ifdef PROMISCUOUS_BY_DEFAULT	/* early 2.x firmware */
-			if ((memcmp (dev->dev_addr, skb->data, 6)) &&
-			    (!broadcast_packet (skb->data))) {
-				/*
-				 * Re-post the buffer to the adapter.  Since the adapter usually
-				 * return 1 to 2 receive buffers at a time, it's not too inefficient
-				 * post one buffer at a time but ... may be that should be 
-				 * optimized at some point.
-				 */
-				ptcb->b.context = (U32) skb;
-				ptcb->b.scount = 1;
-				ptcb->b.size = MAX_ETHER_SIZE;
-				ptcb->b.addr = virt_to_bus ((void *) skb->data);
-
-				if (RCPostRecvBuffers (dev, (PRCTCB) ptcb) !=
-				    RC_RTN_NO_ERROR) {
-					printk (KERN_WARNING
-						"(rcpci45 driver:) RCrecv_callback: post buffer failed!\n");
-					dev_kfree_skb (skb);
-				} else
-					pDpa->numOutRcvBuffers++;
-			} else
-#endif				/* PROMISCUOUS_BY_DEFAULT */
-			{
-				len = PacketDescBlock[2];
-				skb->dev = dev;
-				skb_put (skb, len);	/* adjust length and tail */
-				skb->protocol = eth_type_trans (skb, dev);
-				netif_rx (skb);	/* send the packet to the kernel */
-				dev->last_rx = jiffies;
-			}
-			pDpa->numOutRcvBuffers--;
-			PacketDescBlock += BD_SIZE;	/* point to next context field */
+			len = PacketDescBlock[2];
+			skb->dev = dev;
+			skb_put (skb, len);	/* adjust length and tail */
+			skb->protocol = eth_type_trans (skb, dev);
+			netif_rx (skb);	/* send the packet to the kernel */
+			dev->last_rx = jiffies;
+			pDpa->numOutRcvBuffers--;	
+			/* point to next context field */
+			PacketDescBlock += BD_SIZE;
 		}
 	}
 
@@ -680,11 +636,8 @@
 	pDpa = dev->priv;
 
 	if (pDpa->shutdown)
-		dprintk ("shutdown: service irq\n");
-
-	dprintk ("RC irq: pDpa = 0x%x, dev = 0x%x, id = %d\n",
-		 (uint) pDpa, (uint) dev, (uint) pDpa->id);
-	dprintk ("dev = 0x%x\n", (uint) dev);
+		printk (KERN_DEBUG "%s: shutdown, service irq\n",
+				dev->name);
 
 	RCProcI2OMsgQ (dev);
 }
@@ -715,62 +668,60 @@
 			RCReportDriverCapability (dev, DriverControlWord);
 			RCEnableI2OInterrupts (dev);
 
-			if (dev->flags & IFF_UP) {
-				while (post_buffers) {
-					if (post_buffers >
-					    MAX_NMBR_POST_BUFFERS_PER_MSG)
-						requested =
-						    MAX_NMBR_POST_BUFFERS_PER_MSG;
-					else
-						requested = post_buffers;
-					count =
-					    RC_allocate_and_post_buffers (dev,
-									  requested);
-					post_buffers -= count;
-					if (count < requested)
-						break;
-				}
-				pDpa->numOutRcvBuffers =
-				    MAX_NMBR_RCV_BUFFERS - post_buffers;
-				dprintk ("rc: posted %d buffers \r\n",
-					 (uint) pDpa->numOutRcvBuffers);
+
+			if (!(dev->flags & IFF_UP)) {
+				retry = 0;
+				return;
+			}
+			while (post_buffers) {
+				if (post_buffers > 
+						MAX_NMBR_POST_BUFFERS_PER_MSG)
+					requested = 
+						MAX_NMBR_POST_BUFFERS_PER_MSG;
+				else
+					requested = post_buffers;
+				count =
+				    RC_allocate_and_post_buffers (dev,
+								  requested);
+				post_buffers -= count;
+				if (count < requested)
+					break;
 			}
-			dprintk ("Initialization done.\n");
+			pDpa->numOutRcvBuffers =
+			    MAX_NMBR_RCV_BUFFERS - post_buffers;
+			printk ("Initialization done.\n");
 			netif_wake_queue (dev);
 			retry = 0;
 			return;
 		case RC_RTN_FREE_Q_EMPTY:
 			retry++;
-			printk (KERN_WARNING
-				"(rcpci45 driver:) inbound free q empty\n");
+			printk (KERN_WARNING "%s inbound free q empty\n",
+					dev->name);
 			break;
 		default:
 			retry++;
-			printk (KERN_WARNING
-				"(rcpci45 driver:) bad status after reboot: %d\n",
-				init_status);
+			printk (KERN_WARNING "%s bad stat after reboot: %d\n",
+					dev->name, init_status);
 			break;
 		}
 
 		if (retry > REBOOT_REINIT_RETRY_LIMIT) {
-			printk (KERN_WARNING
-				"(rcpci45 driver:) unable to reinitialize adapter after reboot\n");
-			printk (KERN_WARNING
-				"(rcpci45 driver:) decrementing driver and closing interface\n");
+			printk (KERN_WARNING "%s unable to reinitialize adapter after reboot\n", dev->name);
+			printk (KERN_WARNING "%s decrementing driver and closing interface\n", dev->name);
 			RCDisableI2OInterrupts (dev);
 			dev->flags &= ~IFF_UP;
 			MOD_DEC_USE_COUNT;
 		} else {
-			printk (KERN_INFO
-				"(rcpci45 driver:) rescheduling timer...\n");
+			printk (KERN_INFO "%s: rescheduling timer...\n",
+					dev->name);
 			init_timer (&pDpa->timer);
-			pDpa->timer.expires = RUN_AT ((40 * HZ) / 10);	/* 3 sec. */
+			pDpa->timer.expires = RUN_AT ((40 * HZ) / 10);
 			pDpa->timer.data = (unsigned long) dev;
-			pDpa->timer.function = &rc_timer;	/* timer handler */
+			pDpa->timer.function = &rc_timer;
 			add_timer (&pDpa->timer);
 		}
 	} else
-		printk (KERN_WARNING "(rcpci45 driver:) timer??\n");
+		printk (KERN_WARNING "%s: unexpected timer irq\n", dev->name);
 }
 
 static int
@@ -778,19 +729,16 @@
 {
 	PDPA pDpa = dev->priv;
 
+	printk("RCclose\n");
 	netif_stop_queue (dev);
 
-	dprintk ("RCclose\r\n");
-
 	if (pDpa->reboot) {
-		printk (KERN_INFO
-			"(rcpci45 driver:) skipping reset -- adapter already in reboot mode\n");
+		printk (KERN_INFO "%s skipping reset -- adapter already in reboot mode\n", dev->name);
 		dev->flags &= ~IFF_UP;
 		pDpa->shutdown = 1;
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
-	dprintk ("receive buffers outstanding: %d\n",
-		 (uint) pDpa->numOutRcvBuffers);
 
 	pDpa->shutdown = 1;
 
@@ -806,6 +754,7 @@
 			   (PFNCALLBACK) RCreset_callback);
 
 	dev->flags &= ~IFF_UP;
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -817,56 +766,42 @@
 	PDPA pDpa = dev->priv;
 
 	if (!pDpa) {
-		dprintk ("RCget_stats: !pDpa\n");
 		return 0;
 	} else if (!(dev->flags & IFF_UP)) {
-		dprintk ("RCget_stats: device down\n");
 		return 0;
 	}
 
 	memset (&RCstats, 0, sizeof (RCLINKSTATS));
 	if ((RCGetLinkStatistics (dev, &RCstats, (void *) 0)) ==
 	    RC_RTN_NO_ERROR) {
-		dprintk ("TX_good 0x%x\n", (uint) RCstats.TX_good);
-		dprintk ("TX_maxcol 0x%x\n", (uint) RCstats.TX_maxcol);
-		dprintk ("TX_latecol 0x%x\n", (uint) RCstats.TX_latecol);
-		dprintk ("TX_urun 0x%x\n", (uint) RCstats.TX_urun);
-		dprintk ("TX_crs 0x%x\n", (uint) RCstats.TX_crs);
-		dprintk ("TX_def 0x%x\n", (uint) RCstats.TX_def);
-		dprintk ("TX_singlecol 0x%x\n", (uint) RCstats.TX_singlecol);
-		dprintk ("TX_multcol 0x%x\n", (uint) RCstats.TX_multcol);
-		dprintk ("TX_totcol 0x%x\n", (uint) RCstats.TX_totcol);
-
-		dprintk ("Rcv_good 0x%x\n", (uint) RCstats.Rcv_good);
-		dprintk ("Rcv_CRCerr 0x%x\n", (uint) RCstats.Rcv_CRCerr);
-		dprintk ("Rcv_alignerr 0x%x\n", (uint) RCstats.Rcv_alignerr);
-		dprintk ("Rcv_reserr 0x%x\n", (uint) RCstats.Rcv_reserr);
-		dprintk ("Rcv_orun 0x%x\n", (uint) RCstats.Rcv_orun);
-		dprintk ("Rcv_cdt 0x%x\n", (uint) RCstats.Rcv_cdt);
-		dprintk ("Rcv_runt 0x%x\n", (uint) RCstats.Rcv_runt);
-
-		pDpa->stats.rx_packets = RCstats.Rcv_good;	/* total packets received    */
-		pDpa->stats.tx_packets = RCstats.TX_good;	/* total packets transmitted    */
 
-		pDpa->stats.rx_errors = RCstats.Rcv_CRCerr + RCstats.Rcv_alignerr + RCstats.Rcv_reserr + RCstats.Rcv_orun + RCstats.Rcv_cdt + RCstats.Rcv_runt;	/* bad packets received        */
+		/* total packets received    */
+		pDpa->stats.rx_packets = RCstats.Rcv_good
+		/* total packets transmitted    */;
+		pDpa->stats.tx_packets = RCstats.TX_good;
+
+		pDpa->stats.rx_errors = RCstats.Rcv_CRCerr + 
+			RCstats.Rcv_alignerr + RCstats.Rcv_reserr + 
+			RCstats.Rcv_orun + RCstats.Rcv_cdt + RCstats.Rcv_runt;
 
-		pDpa->stats.tx_errors = RCstats.TX_urun + RCstats.TX_crs + RCstats.TX_def + RCstats.TX_totcol;	/* packet transmit problems    */
+		pDpa->stats.tx_errors = RCstats.TX_urun + RCstats.TX_crs + 
+			RCstats.TX_def + RCstats.TX_totcol;
 
 		/*
 		 * This needs improvement.
 		 */
-		pDpa->stats.rx_dropped = 0;	/* no space in linux buffers    */
-		pDpa->stats.tx_dropped = 0;	/* no space available in linux    */
-		pDpa->stats.multicast = 0;	/* multicast packets received    */
+		pDpa->stats.rx_dropped = 0; /* no space in linux buffers   */
+		pDpa->stats.tx_dropped = 0; /* no space available in linux */
+		pDpa->stats.multicast = 0;  /* multicast packets received  */
 		pDpa->stats.collisions = RCstats.TX_totcol;
 
 		/* detailed rx_errors: */
 		pDpa->stats.rx_length_errors = 0;
-		pDpa->stats.rx_over_errors = RCstats.Rcv_orun;	/* receiver ring buff overflow    */
-		pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr;	/* recved pkt with crc error    */
-		pDpa->stats.rx_frame_errors = 0;	/* recv'd frame alignment error */
-		pDpa->stats.rx_fifo_errors = 0;	/* recv'r fifo overrun        */
-		pDpa->stats.rx_missed_errors = 0;	/* receiver missed packet    */
+		pDpa->stats.rx_over_errors = RCstats.Rcv_orun;
+		pDpa->stats.rx_crc_errors = RCstats.Rcv_CRCerr;
+		pDpa->stats.rx_frame_errors = 0;
+		pDpa->stats.rx_fifo_errors = 0;	
+		pDpa->stats.rx_missed_errors = 0;
 
 		/* detailed tx_errors */
 		pDpa->stats.tx_aborted_errors = 0;
@@ -886,8 +821,6 @@
 	RCuser_struct RCuser;
 	PDPA pDpa = dev->priv;
 
-	dprintk ("RCioctl: cmd = 0x%x\n", cmd);
-
 	if (!capable (CAP_NET_ADMIN))
 		return -EPERM;
 
@@ -911,16 +844,12 @@
 
 			switch (RCuser.cmd) {
 			case RCUC_GETFWVER:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETFWVER\n");
 				RCUD_GETFWVER = &RCuser.RCUS_GETFWVER;
 				RCGetFirmwareVer (dev,
 						  (PU8) & RCUD_GETFWVER->
 						  FirmString, NULL);
 				break;
 			case RCUC_GETINFO:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETINFO\n");
 				RCUD_GETINFO = &RCuser.RCUS_GETINFO;
 				RCUD_GETINFO->mem_start = dev->base_addr;
 				RCUD_GETINFO->mem_end =
@@ -929,8 +858,6 @@
 				RCUD_GETINFO->irq = dev->irq;
 				break;
 			case RCUC_GETIPANDMASK:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETIPANDMASK\n");
 				RCUD_GETIPANDMASK = &RCuser.RCUS_GETIPANDMASK;
 				RCGetRavlinIPandMask (dev,
 						      (PU32) &
@@ -940,8 +867,6 @@
 						      NetMask, NULL);
 				break;
 			case RCUC_GETLINKSTATISTICS:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETLINKSTATISTICS\n");
 				RCUD_GETLINKSTATISTICS =
 				    &RCuser.RCUS_GETLINKSTATISTICS;
 				RCGetLinkStatistics (dev,
@@ -950,75 +875,39 @@
 						     StatsReturn, NULL);
 				break;
 			case RCUC_GETLINKSTATUS:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETLINKSTATUS\n");
 				RCUD_GETLINKSTATUS = &RCuser.RCUS_GETLINKSTATUS;
 				RCGetLinkStatus (dev,
 						 (PU32) & RCUD_GETLINKSTATUS->
 						 ReturnStatus, NULL);
 				break;
 			case RCUC_GETMAC:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETMAC\n");
 				RCUD_GETMAC = &RCuser.RCUS_GETMAC;
 				RCGetMAC (dev, NULL);
+				memcpy(RCUD_GETMAC, dev->dev_addr, 8);
 				break;
 			case RCUC_GETPROM:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETPROM\n");
 				RCUD_GETPROM = &RCuser.RCUS_GETPROM;
 				RCGetPromiscuousMode (dev,
 						      (PU32) & RCUD_GETPROM->
 						      PromMode, NULL);
 				break;
 			case RCUC_GETBROADCAST:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETBROADCAST\n");
 				RCUD_GETBROADCAST = &RCuser.RCUS_GETBROADCAST;
 				RCGetBroadcastMode (dev,
 						    (PU32) & RCUD_GETBROADCAST->
 						    BroadcastMode, NULL);
 				break;
 			case RCUC_GETSPEED:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC GETSPEED\n");
 				if (!(dev->flags & IFF_UP)) {
-					printk (KERN_ERR
-						"(rcpci45 driver:) RCioctl, GETSPEED error: interface down\n");
 					return -ENODATA;
 				}
 				RCUD_GETSPEED = &RCuser.RCUS_GETSPEED;
 				RCGetLinkSpeed (dev,
 						(PU32) & RCUD_GETSPEED->
 						LinkSpeedCode, NULL);
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC speed = 0x%u\n",
-					RCUD_GETSPEED->LinkSpeedCode);
 				break;
 			case RCUC_SETIPANDMASK:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC SETIPANDMASK\n");
 				RCUD_SETIPANDMASK = &RCuser.RCUS_SETIPANDMASK;
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New IP Addr = %d.%d.%d.%d, ",
-					(U8) ((RCUD_SETIPANDMASK->
-					       IpAddr) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       IpAddr >> 8) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       IpAddr >> 16) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       IpAddr >> 24) & 0xff));
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New Mask = %d.%d.%d.%d\n",
-					(U8) ((RCUD_SETIPANDMASK->
-					       NetMask) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       NetMask >> 8) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       NetMask >> 16) & 0xff),
-					(U8) ((RCUD_SETIPANDMASK->
-					       NetMask >> 24) & 0xff));
 				RCSetRavlinIPandMask (dev,
 						      (U32) RCUD_SETIPANDMASK->
 						      IpAddr,
@@ -1026,61 +915,33 @@
 						      NetMask);
 				break;
 			case RCUC_SETMAC:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC SETMAC\n");
-				RCUD_SETMAC = &RCuser.RCUS_SETMAC;
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New MAC addr = %02X:%02X:%02X:%02X:%02X:%02X\n",
-					(U8) (RCUD_SETMAC->mac[0]),
-					(U8) (RCUD_SETMAC->mac[1]),
-					(U8) (RCUD_SETMAC->mac[2]),
-					(U8) (RCUD_SETMAC->mac[3]),
-					(U8) (RCUD_SETMAC->mac[4]),
-					(U8) (RCUD_SETMAC->mac[5]));
 				RCSetMAC (dev, (PU8) & RCUD_SETMAC->mac);
 				break;
 			case RCUC_SETSPEED:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC SETSPEED\n");
 				RCUD_SETSPEED = &RCuser.RCUS_SETSPEED;
 				RCSetLinkSpeed (dev,
 						(U16) RCUD_SETSPEED->
 						LinkSpeedCode);
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New speed = 0x%x\n",
-					RCUD_SETSPEED->LinkSpeedCode);
 				break;
 			case RCUC_SETPROM:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC SETPROM\n");
 				RCUD_SETPROM = &RCuser.RCUS_SETPROM;
 				RCSetPromiscuousMode (dev,
 						      (U16) RCUD_SETPROM->
 						      PromMode);
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New prom mode = 0x%x\n",
-					RCUD_SETPROM->PromMode);
 				break;
 			case RCUC_SETBROADCAST:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC SETBROADCAST\n");
 				RCUD_SETBROADCAST = &RCuser.RCUS_SETBROADCAST;
 				RCSetBroadcastMode (dev,
 						    (U16) RCUD_SETBROADCAST->
 						    BroadcastMode);
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC New broadcast mode = 0x%x\n",
-					RCUD_SETBROADCAST->BroadcastMode);
 				break;
 			default:
-				printk (KERN_INFO
-					"(rcpci45 driver:) RC command default\n");
 				RCUD_DEFAULT = &RCuser.RCUS_DEFAULT;
 				RCUD_DEFAULT->rc = 0x11223344;
 				break;
 			}
-			if (copy_to_user
-			    (rq->ifr_data, &RCuser, sizeof (RCuser)))
+			if (copy_to_user (rq->ifr_data, &RCuser, 
+						sizeof (RCuser)))
 				return -EFAULT;
 			break;
 		}		/* RCU_COMMAND */
@@ -1098,15 +959,14 @@
 	/*
 	 * To be completed ...
 	 */
-	dprintk ("RCconfig\n");
 	return 0;
 	if (dev->flags & IFF_UP)	/* can't act on a running interface */
 		return -EBUSY;
 
 	/* Don't allow changing the I/O address */
 	if (map->base_addr != dev->base_addr) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:)  Change I/O address not implemented\n");
+		printk (KERN_WARNING "%s Change I/O address not implemented\n",
+				dev->name);
 		return -EOPNOTSUPP;
 	}
 	return 0;
@@ -1135,44 +995,36 @@
 	if (!numBuffers)
 		return 0;
 	else if (numBuffers > MAX_NMBR_POST_BUFFERS_PER_MSG) {
-		dprintk ("Too many buffers requested!\n");
-		dprintk ("attempting to allocate only 32 buffers\n");
+		printk (KERN_ERR "%s: Too many buffers requested!\n",
+				dev->name);
 		numBuffers = 32;
 	}
 
 	p = (PU32) kmalloc (sizeof (U32) + numBuffers * sizeof (singleB),
-			    GFP_KERNEL);
-
-	dprintk ("TCB = 0x%x\n", (uint) p);
+			    GFP_DMA|GFP_ATOMIC|GFP_KERNEL);
 
 	if (!p) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) RCopen: unable to allocate TCB\n");
+		printk (KERN_WARNING "%s unable to allocate TCB\n",
+				dev->name);
 		return 0;
 	}
 
 	p[0] = 0;		/* Buffer Count */
-	pB = (psingleB) ((U32) p + sizeof (U32));	/* point to the first buffer */
-
-	dprintk ("p[0] = 0x%x, p = 0x%x, pB = 0x%x\n", (uint) p[0], (uint) p,
-		 (uint) pB);
-	dprintk ("pB = 0x%x\n", (uint) pB);
+	pB = (psingleB) ((U32) p + sizeof (U32));/* point to the first buffer */
 
 	for (i = 0; i < numBuffers; i++) {
 		skb = dev_alloc_skb (MAX_ETHER_SIZE + 2);
 		if (!skb) {
-			dprintk
-			    ("Doh! RCopen: unable to allocate enough skbs!\n");
-			if (*p != 0) {	/* did we allocate any buffers at all? */
-				dprintk ("will post only %d buffers \n",
-					 (uint) (*p));
+			printk (KERN_WARNING 
+					"%s: unable to allocate enough skbs!\n",
+					dev->name);
+			if (*p != 0) {	/* did we allocate any buffers */
 				break;
 			} else {
 				kfree (p);	/* Free the TCB */
 				return 0;
 			}
 		}
-		dprintk ("post 0x%x\n", (uint) skb);
 		skb_reserve (skb, 2);	/* Align IP on 16 byte boundaries */
 		pB->context = (U32) skb;
 		pB->scount = 1;	/* segment count */
@@ -1183,18 +1035,16 @@
 	}
 
 	if ((status = RCPostRecvBuffers (dev, (PRCTCB) p)) != RC_RTN_NO_ERROR) {
-		printk (KERN_WARNING
-			"(rcpci45 driver:) Post buffer failed with error code 0x%x!\n",
-			status);
-		pB = (psingleB) ((U32) p + sizeof (U32));	/* point to the first buffer */
+		printk (KERN_WARNING "%s: Post buffer failed, error 0x%x\n",
+				dev->name, status);
+		/* point to the first buffer */
+		pB = (psingleB) ((U32) p + sizeof (U32));
 		while (p[0]) {
 			skb = (struct sk_buff *) pB->context;
-			dprintk ("freeing 0x%x\n", (uint) skb);
 			dev_kfree_skb (skb);
 			p[0]--;
 			pB++;
 		}
-		dprintk ("freed all buffers, p[0] = %d\n", (uint) p[0]);
 	}
 	res = p[0];
 	kfree (p);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sis900.c linux/drivers/net/sis900.c
--- linux.orig/drivers/net/sis900.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/sis900.c	Mon Jan 14 17:27:24 2002
@@ -1,6 +1,6 @@
 /* sis900.c: A SiS 900/7016 PCI Fast Ethernet driver for Linux.
    Copyright 1999 Silicon Integrated System Corporation 
-   Revision:	1.08.01	Aug. 25 2001
+   Revision:	1.08.02	Nov. 30 2001
    
    Modified from the driver which is originally written by Donald Becker.
    
@@ -18,6 +18,7 @@
    preliminary Rev. 1.0 Jan. 18, 1998
    http://www.sis.com.tw/support/databook.htm
 
+   Rev 1.08.02 Nov. 30 2001 Hui-Fen Hsu workaround for EDB & bug fix for dhcp problem
    Rev 1.08.01 Aug. 25 2001 Hui-Fen Hsu update for 630ET & workaround for ICS1893 PHY
    Rev 1.08.00 Jun. 11 2001 Hui-Fen Hsu workaround for RTL8201 PHY and some bug fix
    Rev 1.07.11 Apr.  2 2001 Hui-Fen Hsu updates PCI drivers to use the new pci_set_dma_mask for kernel 2.4.3
@@ -55,18 +56,23 @@
 #include <linux/netdevice.h>
 #include <linux/init.h>
 #include <linux/mii.h>
-
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+
 #include <asm/processor.h>      /* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
-#include <linux/delay.h>
+#include <asm/uaccess.h>	/* User space memory access functions */
 
 #include "sis900.h"
 
+#define SIS900_MODULE_NAME "sis900"
+#define SIS900_DRV_VERSION "v1.08.02 11/30/2001"
+
 static char version[] __devinitdata =
-KERN_INFO "sis900.c: v1.08.01  9/25/2001\n";
+KERN_INFO "sis900.c: " SIS900_DRV_VERSION "\n";
 
 static int max_interrupt_work = 40;
 static int multicast_filter_limit = 128;
@@ -870,6 +876,9 @@
 
 	netif_start_queue(net_dev);
 
+	/* Workaround for EDB */
+	sis900_set_mode(ioaddr, HW_SPEED_10_MBPS, FDX_CAPABLE_HALF_SELECTED);
+
 	/* Enable all known interrupts by setting the interrupt mask. */
 	outl((RxSOVR|RxORN|RxERR|RxOK|TxURN|TxERR|TxIDLE), ioaddr + imr);
 	outl(RxENA | inl(ioaddr + cr), ioaddr + cr);
@@ -1126,6 +1135,7 @@
 			sis900_set_mode(net_dev->base_addr, speed, duplex);
 			pci_read_config_byte(sis_priv->pci_dev, PCI_CLASS_REVISION, &revision);
 			sis630_set_eq(net_dev, revision);
+			netif_start_queue(net_dev);
 		}
 
 		sis_priv->timer.expires = jiffies + HZ;
@@ -1409,6 +1419,12 @@
 	unsigned int  entry;
 	unsigned long flags;
 
+	/* Don't transmit data before the complete of auto-negotiation */
+	if(!sis_priv->autong_complete){
+		netif_stop_queue(net_dev);
+		return 1;
+	}
+
 	spin_lock_irqsave(&sis_priv->lock, flags);
 
 	/* Calculate the next Tx descriptor entry. */
@@ -1767,6 +1783,40 @@
 }
 
 /**
+ *	netdev_ethtool_ioctl: - For the basic support of ethtool
+ *	@net_dev: the net device to command for
+ *	@useraddr: start address of interface request
+ *
+ *	Process ethtool command such as "ehtool -i" to show information
+ */
+
+static int netdev_ethtool_ioctl (struct net_device *net_dev, void *useraddr)
+{
+ 	struct sis900_private *sis_priv = net_dev->priv;
+ 	u32 ethcmd;
+
+	if (copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+		return -EFAULT;
+	
+	switch (ethcmd) {
+	case ETHTOOL_GDRVINFO:
+		{
+			struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+			strcpy (info.driver, SIS900_MODULE_NAME);
+			strcpy (info.version, SIS900_DRV_VERSION);
+			strcpy (info.bus_info, sis_priv->pci_dev->slot_name);
+			if (copy_to_user (useraddr, &info, sizeof (info)))
+				return -EFAULT;
+			return 0;
+		}
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+/**
  *	mii_ioctl: - process MII i/o control command 
  *	@net_dev: the net device to command for
  *	@rq: parameter for command
@@ -1781,6 +1831,9 @@
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
 
 	switch(cmd) {
+	case SIOCETHTOOL:
+		return netdev_ethtool_ioctl(net_dev, (void *) rq->ifr_data);
+
 	case SIOCGMIIPHY:		/* Get address of MII PHY in use. */
 	case SIOCDEVPRIVATE:		/* for binary compat, remove in 2.5 */
 		data->phy_id = sis_priv->mii->phy_addr;
@@ -2091,8 +2144,6 @@
 	pci_release_regions(pci_dev);
 	pci_set_drvdata(pci_dev, NULL);
 }
-
-#define SIS900_MODULE_NAME "sis900"
 
 static struct pci_driver sis900_pci_driver = {
 	name:		SIS900_MODULE_NAME,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/slip.c linux/drivers/net/slip.c
--- linux.orig/drivers/net/slip.c	Sun Sep 30 19:26:07 2001
+++ linux/drivers/net/slip.c	Wed Jan 16 18:32:53 2002
@@ -483,7 +483,7 @@
 		 *      14 Oct 1994 Dmitry Gorodchanin.
 		 */
 #ifdef SL_CHECK_TRANSMIT
-		if (jiffies - dev->trans_start  < 20 * HZ)  {
+		if (time_before(jiffies, dev->trans_start + 20 * HZ))  {
 			/* 20 sec timeout not reached */
 			goto out;
 		}
@@ -1387,7 +1387,7 @@
 	int i;
 
 	if (slip_ctrls != NULL) {
-		unsigned long start = jiffies;
+		unsigned long timeout = jiffies + HZ;
 		int busy = 0;
 
 		/* First of all: check for active disciplines and hangup them.
@@ -1412,7 +1412,7 @@
 				spin_unlock(&slc->ctrl.lock);
 			}
 			local_bh_enable();
-		} while (busy && jiffies - start < 1*HZ);
+		} while (busy && time_before(jiffies, timeout));
 
 		busy = 0;
 		for (i = 0; i < slip_maxdev; i++) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/starfire.c linux/drivers/net/starfire.c
--- linux.orig/drivers/net/starfire.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/starfire.c	Fri Dec 21 19:09:00 2001
@@ -93,13 +93,16 @@
 	- Fixed initialization timing problems
 	- Fixed interrupt mask definitions
 
+	LK1.3.5 (jgarzik)
+	- ethtool NWAY_RST, GLINK, [GS]MSGLVL support
+
 TODO:
 	- implement tx_timeout() properly
 */
 
 #define DRV_NAME	"starfire"
-#define DRV_VERSION	"1.03+LK1.3.4"
-#define DRV_RELDATE	"August 14, 2001"
+#define DRV_VERSION	"1.03+LK1.3.5"
+#define DRV_RELDATE	"November 17, 2001"
 
 #include <linux/version.h>
 #include <linux/module.h>
@@ -1789,6 +1792,47 @@
 		return 0;
 	}
 
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		int tmp;
+		int r = -EINVAL;
+		/* if autoneg is off, it's an error */
+		tmp = mdio_read(dev, np->phys[0], MII_BMCR);
+		if (tmp & BMCR_ANENABLE) {
+			tmp |= (BMCR_ANRESTART);
+			mdio_write(dev, np->phys[0], MII_BMCR, tmp);
+			r = 0;
+		}
+		return r;
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS)
+			edata.data = 1;
+		else
+			edata.data = 0;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
 	default:
 		return -EOPNOTSUPP;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sundance.c linux/drivers/net/sundance.c
--- linux.orig/drivers/net/sundance.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/sundance.c	Wed Feb  6 20:43:12 2002
@@ -16,11 +16,20 @@
 
 	Support and updates available at
 	http://www.scyld.com/network/sundance.html
+
+
+	Version 1.01a (jgarzik):
+	- Replace some MII-related magic numbers with constants
+
+	Version 1.01b (D-Link):
+	- Add new board to PCI ID list
+	
+
 */
 
 #define DRV_NAME	"sundance"
-#define DRV_VERSION	"1.01"
-#define DRV_RELDATE	"4/09/00"
+#define DRV_VERSION	"1.01b"
+#define DRV_RELDATE	"17-Jan-2002"
 
 
 /* The user-configurable values.
@@ -217,8 +226,9 @@
 	{0x1186, 0x1002, 0x1186, 0x1002, 0, 0, 0},
 	{0x1186, 0x1002, 0x1186, 0x1003, 0, 0, 1},
 	{0x1186, 0x1002, 0x1186, 0x1012, 0, 0, 2},
-	{0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
-	{0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+	{0x1186, 0x1002, 0x1186, 0x1040, 0, 0, 3},
+	{0x1186, 0x1002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+	{0x13F0, 0x0201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 5},
 	{0,}
 };
 MODULE_DEVICE_TABLE(pci, sundance_pci_tbl);
@@ -241,6 +251,8 @@
 	 PCI_IOTYPE, 128, CanHaveMII},
 	{"D-Link DFE-580TX 4 port Server Adapter", {0x10121186, 0xffffffff,},
 	 PCI_IOTYPE, 128, CanHaveMII},
+	{"D-Link DFE-530TXS FAST Ethernet Adapter", {0x10021186, 0xffffffff,},
+	 PCI_IOTYPE, 128, CanHaveMII},
 	{"D-Link DL10050-based FAST Ethernet Adapter",
 	 {0x10021186, 0xffffffff,},
 	 PCI_IOTYPE, 128, CanHaveMII},
@@ -442,7 +454,7 @@
 	int irq;
 	int i;
 	long ioaddr;
-	u16 mii_reg0;
+	u16 mii_ctl;
 	void *ring_space;
 	dma_addr_t ring_dma;
 
@@ -581,15 +593,15 @@
 		}
 	}
 	/* Reset PHY */
-	mdio_write (dev, np->phys[0], 0, 0x8000);
+	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_RESET);
 	mdelay (300);
-	mdio_write (dev, np->phys[0], 0, 0x1200);
+	mdio_write (dev, np->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART);
 	/* Force media type */
 	if (!np->an_enable) {
-		mii_reg0 = 0;
-		mii_reg0 |= (np->speed == 100) ? 0x2000 : 0;
-		mii_reg0 |= (np->full_duplex) ? 0x0100 : 0;
-		mdio_write (dev, np->phys[0], 0, mii_reg0);
+		mii_ctl = 0;
+		mii_ctl |= (np->speed == 100) ? BMCR_SPEED100 : 0;
+		mii_ctl |= (np->full_duplex) ? BMCR_FULLDPLX : 0;
+		mdio_write (dev, np->phys[0], MII_BMCR, mii_ctl);
 		printk (KERN_INFO "Override speed=%d, %s duplex\n",
 			np->speed, np->full_duplex ? "Full" : "Half");
 
@@ -797,12 +809,12 @@
 {
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
-	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
-	int negotiated = mii_reg5 & np->advertising;
+	int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA);
+	int negotiated = mii_lpa & np->advertising;
 	int duplex;
 	
 	/* Force media */
-	if (!np->an_enable || mii_reg5 == 0xffff) {
+	if (!np->an_enable || mii_lpa == 0xffff) {
 		if (np->full_duplex)
 			writew (readw (ioaddr + MACCtrl0) | EnbFullDuplex,
 				ioaddr + MACCtrl0);
@@ -1183,7 +1195,7 @@
 {
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
-	u16 mii_reg0, mii_reg4, mii_reg5;
+	u16 mii_ctl, mii_advertise, mii_lpa;
 	int speed;
 
 	if (intr_status & IntrDrvRqst) {
@@ -1199,27 +1211,27 @@
 	}
 	if (intr_status & LinkChange) {
 		if (np->an_enable) {
-			mii_reg4 = mdio_read (dev, np->phys[0], 4);
-			mii_reg5= mdio_read (dev, np->phys[0], 5);
-			mii_reg4 &= mii_reg5;
+			mii_advertise = mdio_read (dev, np->phys[0], MII_ADVERTISE);
+			mii_lpa= mdio_read (dev, np->phys[0], MII_LPA);
+			mii_advertise &= mii_lpa;
 			printk (KERN_INFO "%s: Link changed: ", dev->name);
-			if (mii_reg4 & 0x0100)
+			if (mii_advertise & ADVERTISE_100FULL)
 				printk ("100Mbps, full duplex\n");
-			else if (mii_reg4 & 0x0080)
+			else if (mii_advertise & ADVERTISE_100HALF)
 				printk ("100Mbps, half duplex\n");
-			else if (mii_reg4 & 0x0040)
+			else if (mii_advertise & ADVERTISE_10FULL)
 				printk ("10Mbps, full duplex\n");
-			else if (mii_reg4 & 0x0020)
+			else if (mii_advertise & ADVERTISE_10HALF)
 				printk ("10Mbps, half duplex\n");
 			else
 				printk ("\n");
 
 		} else {
-			mii_reg0 = mdio_read (dev, np->phys[0], 0);
-			speed = (mii_reg0 & 0x2000) ? 100 : 10;
+			mii_ctl = mdio_read (dev, np->phys[0], MII_BMCR);
+			speed = (mii_ctl & BMCR_SPEED100) ? 100 : 10;
 			printk (KERN_INFO "%s: Link changed: %dMbps ,",
 				dev->name, speed);
-			printk ("%s duplex.\n", (mii_reg0 & 0x0100) ?
+			printk ("%s duplex.\n", (mii_ctl & BMCR_FULLDPLX) ?
 				"full" : "half");
 		}
 		check_duplex (dev);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sungem.c linux/drivers/net/sungem.c
--- linux.orig/drivers/net/sungem.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/sungem.c	Tue Feb  5 17:30:24 2002
@@ -1,4 +1,4 @@
-/* $Id: sungem.c,v 1.43 2001/12/05 08:40:54 davem Exp $
+/* $Id: sungem.c,v 1.44.2.5 2002/02/01 21:45:52 davem Exp $
  * sungem.c: Sun GEM ethernet driver.
  *
  * Copyright (C) 2000, 2001 David S. Miller (davem@redhat.com)
@@ -10,6 +10,10 @@
  *  - Get rid of all those nasty mdelay's and replace them
  * with schedule_timeout.
  *  - Implement WOL
+ *  - Currently, forced Gb mode is only supported on bcm54xx
+ *    PHY for which I use the SPD2 bit of the control register.
+ *    On m1011 PHY, I can't force as I don't have the specs, but
+ *    I can at least detect gigabit with autoneg.
  */
 
 #include <linux/module.h>
@@ -57,6 +61,10 @@
 
 #include "sungem.h"
 
+#define DEFAULT_MSG	(NETIF_MSG_DRV		| \
+			 NETIF_MSG_PROBE	| \
+			 NETIF_MSG_LINK)
+
 #define DRV_NAME	"sungem"
 #define DRV_VERSION	"0.96"
 #define DRV_RELDATE	"11/17/01"
@@ -70,16 +78,18 @@
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(gem_debug, "i");
-MODULE_PARM_DESC(gem_debug, "(ignored)");
+MODULE_PARM_DESC(gem_debug, "bitmapped message enable number");
 MODULE_PARM(link_mode, "i");
+MODULE_PARM_DESC(link_mode, "default link mode");
 
+int gem_debug = -1;
 static int link_mode;
 
 static u16 link_modes[] __devinitdata = {
 	BMCR_ANENABLE,			/* 0 : autoneg */
 	0,				/* 1 : 10bt half duplex */
 	BMCR_SPEED100,			/* 2 : 100bt half duplex */
-	BMCR_SPD2, /* verify this */	/* 3 : 1000bt half duplex */
+	BMCR_SPD2, /* bcm54xx only */   /* 3 : 1000bt half duplex */
 	BMCR_FULLDPLX,			/* 4 : 10bt full duplex */
 	BMCR_SPEED100|BMCR_FULLDPLX,	/* 5 : 100bt full duplex */
 	BMCR_SPD2|BMCR_FULLDPLX		/* 6 : 1000bt full duplex */
@@ -88,12 +98,6 @@
 #define GEM_MODULE_NAME	"gem"
 #define PFX GEM_MODULE_NAME ": "
 
-#ifdef GEM_DEBUG
-int gem_debug = GEM_DEBUG;
-#else
-int gem_debug = 1;
-#endif
-
 static struct pci_device_id gem_pci_tbl[] __devinitdata = {
 	{ PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_GEM,
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
@@ -109,6 +113,8 @@
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
 	{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMAC,
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+	{ PCI_VENDOR_ID_APPLE, PCI_DEVICE_ID_APPLE_UNI_N_GMACP,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
 	{0, }
 };
 
@@ -174,6 +180,8 @@
 
 static void gem_handle_mif_event(struct gem *gp, u32 reg_val, u32 changed_bits)
 {
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: mif interrupt\n", gp->dev->name);
 }
 
 static int gem_pcs_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
@@ -181,6 +189,10 @@
 	u32 pcs_istat = readl(gp->regs + PCS_ISTAT);
 	u32 pcs_miistat;
 
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: pcs interrupt, pcs_istat: 0x%x\n",
+			gp->dev->name, pcs_istat);
+
 	if (!(pcs_istat & PCS_ISTAT_LSC)) {
 		printk(KERN_ERR "%s: PCS irq but no link status change???\n",
 		       dev->name);
@@ -230,6 +242,10 @@
 {
 	u32 txmac_stat = readl(gp->regs + MAC_TXSTAT);
 
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: txmac interrupt, txmac_stat: 0x%x\n",
+			gp->dev->name, txmac_stat);
+
 	/* Defer timer expiration is quite normal,
 	 * don't even log the event.
 	 */
@@ -274,12 +290,26 @@
 static int gem_rxmac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
 {
 	u32 rxmac_stat = readl(gp->regs + MAC_RXSTAT);
+	int ret = 0;
+
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: rxmac interrupt, rxmac_stat: 0x%x\n",
+			gp->dev->name, rxmac_stat);
 
 	if (rxmac_stat & MAC_RXSTAT_OFLW) {
-		printk(KERN_ERR "%s: RX MAC fifo overflow.\n",
-		       dev->name);
+		u32 smac = readl(gp->regs + MAC_SMACHINE);
+
+		printk(KERN_ERR "%s: RX MAC fifo overflow smac[%08x].\n",
+		       dev->name, smac);
 		gp->net_stats.rx_over_errors++;
 		gp->net_stats.rx_fifo_errors++;
+
+		if (((smac >> 24) & 0x7) == 0x7) {
+			/* Due to a bug, the chip is hung in this case
+			 * and a full reset is necessary.
+			 */
+			ret = 1;
+		}
 	}
 
 	if (rxmac_stat & MAC_RXSTAT_ACE)
@@ -294,13 +324,17 @@
 	/* We do not track MAC_RXSTAT_FCE and MAC_RXSTAT_VCE
 	 * events.
 	 */
-	return 0;
+	return ret;
 }
 
 static int gem_mac_interrupt(struct net_device *dev, struct gem *gp, u32 gem_status)
 {
 	u32 mac_cstat = readl(gp->regs + MAC_CSTAT);
 
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: mac interrupt, mac_cstat: 0x%x\n",
+			gp->dev->name, mac_cstat);
+
 	/* This interrupt is just for pause frame and pause
 	 * tracking.  It is useful for diagnostics and debug
 	 * but probably by default we will mask these events.
@@ -401,11 +435,17 @@
 {
 	if (gem_status & GREG_STAT_RXNOBUF) {
 		/* Frame arrived, no free RX buffers available. */
+		if (netif_msg_rx_err(gp))
+			printk(KERN_DEBUG "%s: no buffer for rx frame\n",
+				gp->dev->name);
 		gp->net_stats.rx_dropped++;
 	}
 
 	if (gem_status & GREG_STAT_RXTAGERR) {
 		/* corrupt RX tag framing */
+		if (netif_msg_rx_err(gp))
+			printk(KERN_DEBUG "%s: corrupt rx tag framing\n",
+				gp->dev->name);
 		gp->net_stats.rx_errors++;
 
 		goto do_reset;
@@ -444,7 +484,7 @@
 	return 0;
 
 do_reset:
-	gp->reset_task_pending = 1;
+	gp->reset_task_pending = 2;
 	schedule_task(&gp->reset_task);
 
 	return 1;
@@ -454,6 +494,10 @@
 {
 	int entry, limit;
 
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: tx interrupt, gem_status: 0x%x\n",
+			gp->dev->name, gem_status);
+
 	entry = gp->tx_old;
 	limit = ((gem_status & GREG_STAT_TXNR) >> GREG_STAT_TXNR_SHIFT);
 	while (entry != limit) {
@@ -463,6 +507,9 @@
 		u32 dma_len;
 		int frag;
 
+		if (netif_msg_tx_done(gp))
+			printk(KERN_DEBUG "%s: tx done, slot %d\n",
+				gp->dev->name, entry);
 		skb = gp->tx_skbs[entry];
 		if (skb_shinfo(skb)->nr_frags) {
 			int last = entry + skb_shinfo(skb)->nr_frags;
@@ -534,6 +581,10 @@
 {
 	int entry, drops;
 
+	if (netif_msg_intr(gp))
+		printk(KERN_DEBUG "%s: rx interrupt, done: %d, rx_new: %d\n",
+			gp->dev->name, readl(gp->regs + RXDMA_DONE), gp->rx_new);
+
 	entry = gp->rx_new;
 	drops = 0;
 	for (;;) {
@@ -785,6 +836,9 @@
 	if (TX_BUFFS_AVAIL(gp) <= 0)
 		netif_stop_queue(dev);
 
+	if (netif_msg_tx_queued(gp))
+		printk(KERN_DEBUG "%s: tx queued, slot %d, skblen %d\n",
+		       dev->name, entry, skb->len);
 	writel(gp->tx_new, gp->regs + TXDMA_KICK);
 	spin_unlock_irq(&gp->lock);
 
@@ -958,6 +1012,14 @@
 			*fd = 1;
 		if (val & (LPA_100FULL | LPA_100HALF))
 			*spd = 100;
+
+		if (gp->phy_mod == phymod_m1011) {
+			val = phy_read(gp, 0x0a);
+			if (val & 0xc00)
+				*spd = 1000;
+			if (val & 0x800)
+				*fd = 1;
+		}
 	}
 }
 
@@ -992,8 +1054,9 @@
 		speed = 1000;
 	}
 
-	printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n",
-	       gp->dev->name, speed, (full_duplex ? "full" : "half"));
+	if (netif_msg_link(gp))
+		printk(KERN_INFO "%s: Link is up at %d Mbps, %s-duplex.\n",
+			gp->dev->name, speed, (full_duplex ? "full" : "half"));
 
 	val = (MAC_TXCFG_EIPG0 | MAC_TXCFG_NGU);
 	if (full_duplex) {
@@ -1058,15 +1121,21 @@
 
 static int gem_mdio_link_not_up(struct gem *gp)
 {
+	u16 val;
+	
 	if (gp->lstate == link_force_ret) {
-		printk(KERN_INFO "%s: Autoneg failed again, keeping"
-			" forced mode\n", gp->dev->name);
+		if (netif_msg_link(gp))
+			printk(KERN_INFO "%s: Autoneg failed again, keeping"
+				" forced mode\n", gp->dev->name);
 		phy_write(gp, MII_BMCR, gp->link_fcntl);
 		gp->timer_ticks = 5;
 		gp->lstate = link_force_ok;
 	} else if (gp->lstate == link_aneg) {
-		u16 val = phy_read(gp, MII_BMCR);
+		val = phy_read(gp, MII_BMCR);
 
+		if (netif_msg_link(gp))
+			printk(KERN_INFO "%s: switching to forced 100bt\n",
+				gp->dev->name);
 		/* Try forced modes. */
 		val &= ~(BMCR_ANRESTART | BMCR_ANENABLE);
 		val &= ~(BMCR_FULLDPLX);
@@ -1079,14 +1148,16 @@
 		 * If already at 10Mbps, warn user about the
 		 * situation every 10 ticks.
 		 */
-		u16 val = phy_read(gp, MII_BMCR);
+		val = phy_read(gp, MII_BMCR);
 		if (val & BMCR_SPEED100) {
 			val &= ~BMCR_SPEED100;
 			phy_write(gp, MII_BMCR, val);
 			gp->timer_ticks = 5;
-		} else {
+			if (netif_msg_link(gp))
+				printk(KERN_INFO "%s: switching to forced 10bt\n",
+					gp->dev->name);
+		} else
 			return 1;
-		}
 	}
 	return 0;
 }
@@ -1115,7 +1186,8 @@
 		/* Reset the chip & rings */
 		gem_stop(gp);
 		gem_init_rings(gp, 0);
-		gem_init_hw(gp, 0);
+		gem_init_hw(gp,
+			    (gp->reset_task_pending == 2));
 
 		netif_wake_queue(gp->dev);
 	}
@@ -1139,22 +1211,16 @@
 	if (gp->phy_type == phy_mii_mdio0 ||
 	    gp->phy_type == phy_mii_mdio1) {
 		u16 val = phy_read(gp, MII_BMSR);
+		u16 cntl = phy_read(gp, MII_BMCR);
 		int up;
 
 		/* When using autoneg, we really wait for ANEGCOMPLETE or we may
 		 * get a "transcient" incorrect link state
 		 */
-#if 0
-		{
-			u16 cntl = phy_read(gp, MII_BMCR);
-			if (cntl & BMCR_ANENABLE)
-				up = (val & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS);
-			else
-				up = (val & BMSR_LSTATUS) != 0;
-		}
-#else
-		up = (val & BMSR_LSTATUS) != 0;
-#endif
+		if (cntl & BMCR_ANENABLE)
+			up = (val & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS)) == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS);
+		else
+			up = (val & BMSR_LSTATUS) != 0;
 		if (up) {
 			/* Ok, here we got a link. If we had it due to a forced
 			 * fallback, and we were configured for autoneg, we do
@@ -1165,8 +1231,9 @@
 				gp->lstate = link_force_ret;
 				gp->link_fcntl = phy_read(gp, MII_BMCR);
 				gp->timer_ticks = 5;
-				printk(KERN_INFO "%s: Got link after fallback, retrying autoneg"
-				       " once...\n", gp->dev->name);
+				if (netif_msg_link(gp))
+					printk(KERN_INFO "%s: Got link after fallback, retrying"
+						" autoneg once...\n", gp->dev->name);
 				phy_write(gp, MII_BMCR,
 					  gp->link_fcntl | BMCR_ANENABLE | BMCR_ANRESTART);
 			} else if (gp->lstate != link_up) {
@@ -1182,7 +1249,9 @@
 			 */
 			if (gp->lstate == link_up) {
 				gp->lstate = link_down;
-				printk(KERN_INFO "%s: Link down\n", gp->dev->name);
+				if (netif_msg_link(gp))
+					printk(KERN_INFO "%s: Link down\n",
+						gp->dev->name);
 				gp->reset_task_pending = 1;
 				schedule_task(&gp->reset_task);
 				restart = 1;
@@ -1527,6 +1596,11 @@
 			gem_init_bcm5411_phy(gp);
 			gp->gigabit_capable = 1;
 			break;
+		case 0x1410c60:
+			printk("M1011 (Marvel ?)\n");
+			gp->phy_mod = phymod_m1011;
+			gp->gigabit_capable = 1;
+			break;
 
 		case 0x18074c0:
 			printk("Lucent\n");
@@ -1539,7 +1613,7 @@
 			break;
 
 		default:
-			printk("Unknown\n");
+			printk("Unknown (Using generic mode)\n");
 			gp->phy_mod = phymod_generic;
 			break;
 		};
@@ -1610,6 +1684,12 @@
 		writel(val, gp->regs + PCS_SCTRL);
 		gp->gigabit_capable = 1;
 	}
+
+	/* BMCR_SPD2 is a broadcom 54xx specific thing afaik */
+	if (gp->phy_mod != phymod_bcm5400 && gp->phy_mod != phymod_bcm5401 &&
+	    gp->phy_mod != phymod_bcm5411)
+	    	gp->link_cntl &= ~BMCR_SPD2;
+	    
 }
 
 static void gem_init_dma(struct gem *gp)
@@ -1651,6 +1731,58 @@
 
 #define CRC_POLYNOMIAL_LE 0xedb88320UL  /* Ethernet CRC, little endian */
 
+static u32
+gem_setup_multicast(struct gem *gp)
+{
+	u32 rxcfg = 0;
+	int i;
+	
+	if ((gp->dev->flags & IFF_ALLMULTI) ||
+	    (gp->dev->mc_count > 256)) {
+	    	for (i=0; i<16; i++)
+			writel(0xffff, gp->regs + MAC_HASH0 + (i << 2));
+		rxcfg |= MAC_RXCFG_HFE;
+	} else if (gp->dev->flags & IFF_PROMISC) {
+		rxcfg |= MAC_RXCFG_PROM;
+	} else {
+		u16 hash_table[16];
+		u32 crc, poly = CRC_POLYNOMIAL_LE;
+		struct dev_mc_list *dmi = gp->dev->mc_list;
+		int i, j, bit, byte;
+
+		for (i = 0; i < 16; i++)
+			hash_table[i] = 0;
+
+		for (i = 0; i < gp->dev->mc_count; i++) {
+			char *addrs = dmi->dmi_addr;
+
+			dmi = dmi->next;
+
+			if (!(*addrs & 1))
+				continue;
+
+			crc = 0xffffffffU;
+			for (byte = 0; byte < 6; byte++) {
+				for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
+					int test;
+
+					test = ((bit ^ crc) & 0x01);
+					crc >>= 1;
+					if (test)
+						crc = crc ^ poly;
+				}
+			}
+			crc >>= 24;
+			hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf));
+		}
+	    	for (i=0; i<16; i++)
+			writel(hash_table[i], gp->regs + MAC_HASH0 + (i << 2));
+		rxcfg |= MAC_RXCFG_HFE;
+	}
+
+	return rxcfg;
+}
+
 static void gem_init_mac(struct gem *gp)
 {
 	unsigned char *e = &gp->dev->dev_addr[0];
@@ -1691,75 +1823,7 @@
 	writel(0, gp->regs + MAC_AF21MSK);
 	writel(0, gp->regs + MAC_AF0MSK);
 
-	rxcfg = 0;
-	if ((gp->dev->flags & IFF_ALLMULTI) ||
-	    (gp->dev->mc_count > 256)) {
-		writel(0xffff, gp->regs + MAC_HASH0);
-		writel(0xffff, gp->regs + MAC_HASH1);
-		writel(0xffff, gp->regs + MAC_HASH2);
-		writel(0xffff, gp->regs + MAC_HASH3);
-		writel(0xffff, gp->regs + MAC_HASH4);
-		writel(0xffff, gp->regs + MAC_HASH5);
-		writel(0xffff, gp->regs + MAC_HASH6);
-		writel(0xffff, gp->regs + MAC_HASH7);
-		writel(0xffff, gp->regs + MAC_HASH8);
-		writel(0xffff, gp->regs + MAC_HASH9);
-		writel(0xffff, gp->regs + MAC_HASH10);
-		writel(0xffff, gp->regs + MAC_HASH11);
-		writel(0xffff, gp->regs + MAC_HASH12);
-		writel(0xffff, gp->regs + MAC_HASH13);
-		writel(0xffff, gp->regs + MAC_HASH14);
-		writel(0xffff, gp->regs + MAC_HASH15);
-	} else if (gp->dev->flags & IFF_PROMISC) {
-		rxcfg |= MAC_RXCFG_PROM;
-	} else {
-		u16 hash_table[16];
-		u32 crc, poly = CRC_POLYNOMIAL_LE;
-		struct dev_mc_list *dmi = gp->dev->mc_list;
-		int i, j, bit, byte;
-
-		for (i = 0; i < 16; i++)
-			hash_table[i] = 0;
-
-		for (i = 0; i < gp->dev->mc_count; i++) {
-			char *addrs = dmi->dmi_addr;
-
-			dmi = dmi->next;
-
-			if (!(*addrs & 1))
-				continue;
-
-			crc = 0xffffffffU;
-			for (byte = 0; byte < 6; byte++) {
-				for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
-					int test;
-
-					test = ((bit ^ crc) & 0x01);
-					crc >>= 1;
-					if (test)
-						crc = crc ^ poly;
-				}
-			}
-			crc >>= 24;
-			hash_table[crc >> 4] |= 1 << (crc & 0xf);
-		}
-		writel(hash_table[0], gp->regs + MAC_HASH0);
-		writel(hash_table[1], gp->regs + MAC_HASH1);
-		writel(hash_table[2], gp->regs + MAC_HASH2);
-		writel(hash_table[3], gp->regs + MAC_HASH3);
-		writel(hash_table[4], gp->regs + MAC_HASH4);
-		writel(hash_table[5], gp->regs + MAC_HASH5);
-		writel(hash_table[6], gp->regs + MAC_HASH6);
-		writel(hash_table[7], gp->regs + MAC_HASH7);
-		writel(hash_table[8], gp->regs + MAC_HASH8);
-		writel(hash_table[9], gp->regs + MAC_HASH9);
-		writel(hash_table[10], gp->regs + MAC_HASH10);
-		writel(hash_table[11], gp->regs + MAC_HASH11);
-		writel(hash_table[12], gp->regs + MAC_HASH12);
-		writel(hash_table[13], gp->regs + MAC_HASH13);
-		writel(hash_table[14], gp->regs + MAC_HASH14);
-		writel(hash_table[15], gp->regs + MAC_HASH15);
-	}
+	rxcfg = gem_setup_multicast(gp);
 
 	writel(0, gp->regs + MAC_NCOLL);
 	writel(0, gp->regs + MAC_FASUCC);
@@ -1827,9 +1891,8 @@
 	u32 mif_cfg;
 
 	/* On Apple's sungem, we can't rely on registers as the chip
-	 * was been powered down by the firmware. We do the PHY lookup
-	 * when the interface is opened and we configure the driver
-	 * with known values.
+	 * was been powered down by the firmware. The PHY is looked
+	 * up later on.
 	 */
 	if (pdev->vendor == PCI_VENDOR_ID_APPLE) {
 		gp->phy_type = phy_mii_mdio0;
@@ -1954,7 +2017,8 @@
 
 	pmac_call_feature(PMAC_FTR_GMAC_ENABLE, gp->of_node, 0, 1);
 
-	udelay(100);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout((21 * HZ) / 1000);
 
 	pci_read_config_word(gp->pdev, PCI_COMMAND, &cmd);
 	cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
@@ -2024,7 +2088,8 @@
 				  val & ~MII_BCM5201_AUXMODE2_LOWPOWER);
 #endif				
 			phy_write(gp, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
-		}
+		} else if (gp->phy_mod == phymod_m1011)
+			phy_write(gp, MII_BMCR, BMCR_PDOWN);
 
 		/* According to Apple, we must set the MDIO pins to this begnign
 		 * state or we may 1) eat more current, 2) damage some PHYs
@@ -2052,16 +2117,14 @@
 		schedule();
 	
 	/* Actually stop the chip */
-	if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+	if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE) {
 		gem_stop_phy(gp);
-	else
-		gem_stop(gp);
-
 #ifdef CONFIG_ALL_PPC
-	/* Power down the chip */
-	if (gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
+		/* Power down the chip */
 		gem_apple_powerdown(gp);
 #endif /* CONFIG_ALL_PPC */
+	} else
+		gem_stop(gp);
 }
 
 static void gem_pm_task(void *data)
@@ -2123,6 +2186,7 @@
 	 */
 	if (request_irq(gp->pdev->irq, gem_interrupt,
 			SA_SHIRQ, dev->name, (void *)dev)) {
+		printk(KERN_ERR "%s: failed to request irq !\n", gp->dev->name);
 #ifdef CONFIG_ALL_PPC
 		if (!hw_was_up && gp->pdev->vendor == PCI_VENDOR_ID_APPLE)
 			gem_apple_powerdown(gp);
@@ -2285,91 +2349,28 @@
 static void gem_set_multicast(struct net_device *dev)
 {
 	struct gem *gp = dev->priv;
-
+	u32 rxcfg, rxcfg_new;
+	int limit = 10000;
+	
 	if (!gp->hw_running)
 		return;
 		
 	netif_stop_queue(dev);
 
-	if ((gp->dev->flags & IFF_ALLMULTI) ||
-	    (gp->dev->mc_count > 256)) {
-		writel(0xffff, gp->regs + MAC_HASH0);
-		writel(0xffff, gp->regs + MAC_HASH1);
-		writel(0xffff, gp->regs + MAC_HASH2);
-		writel(0xffff, gp->regs + MAC_HASH3);
-		writel(0xffff, gp->regs + MAC_HASH4);
-		writel(0xffff, gp->regs + MAC_HASH5);
-		writel(0xffff, gp->regs + MAC_HASH6);
-		writel(0xffff, gp->regs + MAC_HASH7);
-		writel(0xffff, gp->regs + MAC_HASH8);
-		writel(0xffff, gp->regs + MAC_HASH9);
-		writel(0xffff, gp->regs + MAC_HASH10);
-		writel(0xffff, gp->regs + MAC_HASH11);
-		writel(0xffff, gp->regs + MAC_HASH12);
-		writel(0xffff, gp->regs + MAC_HASH13);
-		writel(0xffff, gp->regs + MAC_HASH14);
-		writel(0xffff, gp->regs + MAC_HASH15);
-	} else if (gp->dev->flags & IFF_PROMISC) {
-		u32 rxcfg = readl(gp->regs + MAC_RXCFG);
-		int limit = 10000;
-
-		writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
-		while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) {
-			if (!limit--)
-				break;
-			udelay(10);
-		}
-
-		rxcfg |= MAC_RXCFG_PROM;
-		writel(rxcfg, gp->regs + MAC_RXCFG);
-	} else {
-		u16 hash_table[16];
-		u32 crc, poly = CRC_POLYNOMIAL_LE;
-		struct dev_mc_list *dmi = gp->dev->mc_list;
-		int i, j, bit, byte;
-
-		for (i = 0; i < 16; i++)
-			hash_table[i] = 0;
-
-		for (i = 0; i < dev->mc_count; i++) {
-			char *addrs = dmi->dmi_addr;
-
-			dmi = dmi->next;
-
-			if (!(*addrs & 1))
-				continue;
+	rxcfg = readl(gp->regs + MAC_RXCFG);
+	rxcfg_new = gem_setup_multicast(gp);
+	
+	writel(rxcfg & ~MAC_RXCFG_ENAB, gp->regs + MAC_RXCFG);
+	while (readl(gp->regs + MAC_RXCFG) & MAC_RXCFG_ENAB) {
+		if (!limit--)
+			break;
+		udelay(10);
+	}
 
-			crc = 0xffffffffU;
-			for (byte = 0; byte < 6; byte++) {
-				for (bit = *addrs++, j = 0; j < 8; j++, bit >>= 1) {
-					int test;
+	rxcfg &= ~(MAC_RXCFG_PROM | MAC_RXCFG_HFE);
+	rxcfg |= rxcfg_new;
 
-					test = ((bit ^ crc) & 0x01);
-					crc >>= 1;
-					if (test)
-						crc = crc ^ poly;
-				}
-			}
-			crc >>= 24;
-			hash_table[crc >> 4] |= 1 << (crc & 0xf);
-		}
-		writel(hash_table[0], gp->regs + MAC_HASH0);
-		writel(hash_table[1], gp->regs + MAC_HASH1);
-		writel(hash_table[2], gp->regs + MAC_HASH2);
-		writel(hash_table[3], gp->regs + MAC_HASH3);
-		writel(hash_table[4], gp->regs + MAC_HASH4);
-		writel(hash_table[5], gp->regs + MAC_HASH5);
-		writel(hash_table[6], gp->regs + MAC_HASH6);
-		writel(hash_table[7], gp->regs + MAC_HASH7);
-		writel(hash_table[8], gp->regs + MAC_HASH8);
-		writel(hash_table[9], gp->regs + MAC_HASH9);
-		writel(hash_table[10], gp->regs + MAC_HASH10);
-		writel(hash_table[11], gp->regs + MAC_HASH11);
-		writel(hash_table[12], gp->regs + MAC_HASH12);
-		writel(hash_table[13], gp->regs + MAC_HASH13);
-		writel(hash_table[14], gp->regs + MAC_HASH14);
-		writel(hash_table[15], gp->regs + MAC_HASH15);
-	}
+	writel(rxcfg, gp->regs + MAC_RXCFG);
 
 	/* Hrm... we may walk on the reset task here... */
 	netif_wake_queue(dev);
@@ -2491,7 +2492,7 @@
 	case ETHTOOL_GMSGLVL: {
 		struct ethtool_value edata = { cmd: ETHTOOL_GMSGLVL };
 
-		edata.data = gem_debug;
+		edata.data = gp->msg_enable;
 		if (copy_to_user(ep_user, &edata, sizeof(edata)))
 			return -EFAULT;
 		return 0;
@@ -2503,7 +2504,7 @@
 
 		if (copy_from_user(&edata, ep_user, sizeof(edata)))
 			return -EFAULT;
-		gem_debug = edata.data;
+		gp->msg_enable = edata.data;
 		return 0;
 	}
 
@@ -2699,6 +2700,8 @@
 	dev->base_addr = (long) pdev;
 	gp->dev = dev;
 
+	gp->msg_enable = (gem_debug < 0 ? DEFAULT_MSG : gem_debug);
+
 	spin_lock_init(&gp->lock);
 	init_MUTEX(&gp->pm_sem);
 
@@ -2733,25 +2736,15 @@
 	 * not have properly shut down the PHY.
 	 */
 #ifdef CONFIG_ALL_PPC
-	if (pdev->vendor == PCI_VENDOR_ID_APPLE) {
+	if (pdev->vendor == PCI_VENDOR_ID_APPLE)
 		gem_apple_powerup(gp);
-		if (gem_check_invariants(gp))
-			goto err_out_iounmap;
-		gem_stop(gp);
-		gp->hw_running = 1;
-		gem_init_phy(gp);
-		gem_begin_auto_negotiation(gp, NULL);
-	}
 #endif
-	/* Non Apple hardware, we just reset the chip and check
-	 * for invariants
-	 */
-	if (pdev->vendor != PCI_VENDOR_ID_APPLE) {
-		gem_stop(gp);
-		if (gem_check_invariants(gp))
-			goto err_out_iounmap;
-		gp->hw_running = 1;
-	}
+	gem_stop(gp);
+	if (gem_check_invariants(gp))
+		goto err_out_iounmap;
+	gp->hw_running = 1;
+	gem_init_phy(gp);
+	gem_begin_auto_negotiation(gp, NULL);
 
 	/* It is guarenteed that the returned buffer will be at least
 	 * PAGE_SIZE aligned.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sungem.h linux/drivers/net/sungem.h
--- linux.orig/drivers/net/sungem.h	Fri Dec 21 17:41:54 2001
+++ linux/drivers/net/sungem.h	Mon Feb  4 16:47:32 2002
@@ -1,4 +1,4 @@
-/* $Id: sungem.h,v 1.10 2001/11/29 03:57:33 davem Exp $
+/* $Id: sungem.h,v 1.10.2.2 2002/01/23 15:40:02 davem Exp $
  * sungem.h: Definitions for Sun GEM ethernet driver.
  *
  * Copyright (C) 2000 David S. Miller (davem@redhat.com)
@@ -941,6 +941,7 @@
 	phymod_bcm5400,
 	phymod_bcm5401,
 	phymod_bcm5411,
+	phymod_m1011,
 };
 
 enum link_state {
@@ -972,6 +973,8 @@
 	struct sk_buff *rx_skbs[RX_RING_SIZE];
 	struct sk_buff *tx_skbs[RX_RING_SIZE];
 
+	u32			msg_enable;
+
 	struct net_device_stats net_stats;
 
 	enum gem_phy_type	phy_type;
@@ -995,8 +998,8 @@
 	volatile int		reset_task_pending;
 	
 	/* Diagnostic counters and state. */
-	u64 pause_entered;
-	u16 pause_last_time_recvd;
+	u64			pause_entered;
+	u16			pause_last_time_recvd;
 
 	dma_addr_t gblock_dvma;
 	struct pci_dev *pdev;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sunlance.c linux/drivers/net/sunlance.c
--- linux.orig/drivers/net/sunlance.c	Tue Oct 30 23:08:11 2001
+++ linux/drivers/net/sunlance.c	Thu Jan 17 19:37:52 2002
@@ -1,4 +1,4 @@
-/* $Id: sunlance.c,v 1.109 2001/10/21 06:35:29 davem Exp $
+/* $Id: sunlance.c,v 1.109.2.1 2002/01/14 10:07:56 davem Exp $
  * lance.c: Linux/Sparc/Lance driver
  *
  *	Written 1995, 1996 by Miguel de Icaza
@@ -923,13 +923,13 @@
 
 	last_dev = dev;
 
+	STOP_LANCE(lp);
+
 	if (request_irq(dev->irq, &lance_interrupt, SA_SHIRQ,
 			lancestr, (void *) dev)) {
 		printk(KERN_ERR "Lance: Can't get irq %s\n", __irq_itoa(dev->irq));
 		return -EAGAIN;
 	}
-
-	STOP_LANCE(lp);
 
 	/* On the 4m, setup the ledma to provide the upper bits for buffers */
 	if (lp->dregs) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/sunqe.c linux/drivers/net/sunqe.c
--- linux.orig/drivers/net/sunqe.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/sunqe.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: sunqe.c,v 1.52 2001/10/18 08:18:08 davem Exp $
+/* $Id: sunqe.c,v 1.52.2.1 2001/12/21 00:52:47 davem Exp $
  * sunqe.c: Sparc QuadEthernet 10baseT SBUS card driver.
  *          Once again I am out to prove that every ethernet
  *          controller out there can be most efficiently programmed
@@ -494,6 +494,7 @@
 				spin_unlock(&qep->lock);
 			}
 	next:
+			;
 		}
 		qec_status >>= 4;
 		channel++;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/tokenring/abyss.c linux/drivers/net/tokenring/abyss.c
--- linux.orig/drivers/net/tokenring/abyss.c	Thu Sep 13 23:04:43 2001
+++ linux/drivers/net/tokenring/abyss.c	Wed Feb 13 17:37:40 2002
@@ -433,7 +433,7 @@
 	return 0;
 }
 
-static void __exit abyss_detach (struct pci_dev *pdev)
+static void __devexit abyss_detach (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 	
@@ -451,7 +451,7 @@
 	name:		"abyss",
 	id_table:	abyss_pci_tbl,
 	probe:		abyss_attach,
-	remove:		abyss_detach,
+	remove:		__devexit_p(abyss_detach),
 };
 
 static int __init abyss_init (void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/tokenring/tmspci.c linux/drivers/net/tokenring/tmspci.c
--- linux.orig/drivers/net/tokenring/tmspci.c	Thu Sep 13 23:04:43 2001
+++ linux/drivers/net/tokenring/tmspci.c	Wed Feb 13 17:37:40 2002
@@ -220,7 +220,7 @@
 	return val;
 }
 
-static void __exit tms_pci_detach (struct pci_dev *pdev)
+static void __devexit tms_pci_detach (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
 
@@ -238,7 +238,7 @@
 	name:		"tmspci",
 	id_table:	tmspci_pci_tbl,
 	probe:		tms_pci_attach,
-	remove:		tms_pci_detach,
+	remove:		__devexit_p(tms_pci_detach),
 };
 
 static int __init tms_pci_init (void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/tulip/ChangeLog linux/drivers/net/tulip/ChangeLog
--- linux.orig/drivers/net/tulip/ChangeLog	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/tulip/ChangeLog	Wed Feb 13 16:35:50 2002
@@ -1,3 +1,22 @@
+2002-02-07  Uwe Bonnes  <bon@elektron.ikp.physik.tu-darmstadt.de>
+
+	* tulip_core (tulip_pci_tbl[]):
+	Add PCI id for comet tulip clone.
+
+2001-12-19  John Zielinski
+
+	* tulip_core.c (tulip_up, tulip_init_one):
+	More places to revert PHY autoconfiguration bit removal.
+
+2001-12-18  Jeff Garzik  <jgarzik@mandrakesoft.com>
+
+	* tulip.h: revert PHY autoconfiguration bit removal
+
+2001-12-16  Andrew Lambeth  <wal@vmware.com>
+
+	* tulip_core.c (tulip_start_xmit): Use the more-portable
+	spin_lock_irqsave.
+
 2001-11-13  David S. Miller  <davem@redhat.com>
 
 	* tulip_core.c (tulip_mwi_config): Kill unused label early_out.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/tulip/tulip.h linux/drivers/net/tulip/tulip.h
--- linux.orig/drivers/net/tulip/tulip.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/tulip/tulip.h	Mon Jan 14 17:27:24 2002
@@ -197,8 +197,8 @@
 	csr13_cac = (1<<2), /* CSR13/14/15 autoconfiguration */
 	csr13_srl = (1<<0), /* When reset, resets all SIA functions, machines */
 
-	csr13_mask_auibnc = (csr13_eng | csr13_aui | csr13_srl),
-	csr13_mask_10bt = (csr13_eng | csr13_srl),
+	csr13_mask_auibnc = (csr13_eng | csr13_aui | csr13_srl | csr13_cac),
+	csr13_mask_10bt = (csr13_eng | csr13_srl | csr13_cac),
 };
 
 enum t21143_csr6_bits {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/tulip/tulip_core.c linux/drivers/net/tulip/tulip_core.c
--- linux.orig/drivers/net/tulip/tulip_core.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/tulip/tulip_core.c	Wed Feb 13 16:35:50 2002
@@ -207,6 +207,7 @@
 	{ 0x1317, 0x1985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x13D1, 0xAB02, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x13D1, 0xAB03, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
+	{ 0x13D1, 0xAB08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x104A, 0x0981, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x104A, 0x2774, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMET },
 	{ 0x11F6, 0x9881, PCI_ANY_ID, PCI_ANY_ID, 0, 0, COMPEX9881 },
@@ -399,7 +400,7 @@
 		if (tp->sym_advertise & 0x0040)
 			tp->csr6 |= FullDuplex;
 		outl(tp->csr6, ioaddr + CSR6);
-		outl(0x0000EF01, ioaddr + CSR13);
+		outl(0x0000EF05, ioaddr + CSR13);
 
 	} else if (tp->chip_id == DC21142) {
 		if (tp->mii_cnt) {
@@ -701,8 +702,9 @@
 	int entry;
 	u32 flag;
 	dma_addr_t mapping;
+	unsigned long eflags;
 
-	spin_lock_irq(&tp->lock);
+	spin_lock_irqsave(&tp->lock, eflags);
 
 	/* Calculate the next Tx descriptor entry. */
 	entry = tp->cur_tx % TX_RING_SIZE;
@@ -737,7 +739,7 @@
 	/* Trigger an immediate transmit demand. */
 	outl(0, dev->base_addr + CSR1);
 
-	spin_unlock_irq(&tp->lock);
+	spin_unlock_irqrestore(&tp->lock, eflags);
 
 	dev->trans_start = jiffies;
 
@@ -1745,7 +1747,7 @@
 		outl(0xFFFFFFFF, ioaddr + CSR14);
 		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
 		outl(inl(ioaddr + CSR6) | csr6_fd, ioaddr + CSR6);
-		outl(0x0000EF01, ioaddr + CSR13);
+		outl(0x0000EF05, ioaddr + CSR13);
 		break;
 	case DC21040:
 		outl(0x00000000, ioaddr + CSR13);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- linux.orig/drivers/net/via-rhine.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/via-rhine.c	Mon Jan 14 17:27:24 2002
@@ -76,8 +76,17 @@
 
 	LK1.1.12:
 	- Martin Eriksson: Allow Memory-Mapped IO to be enabled.
+
+	LK1.1.13 (jgarzik):
+	- Add ethtool support
+	- Replace some MII-related magic numbers with constants
+	
 */
 
+#define DRV_NAME	"via-rhine"
+#define DRV_VERSION	"1.1.13"
+#define DRV_RELDATE	"Nov-17-2001"
+
 
 /* A few user-configurable values.
    These may be modified when a driver module is loaded. */
@@ -151,17 +160,19 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/mii.h>
+#include <linux/ethtool.h>
 #include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/irq.h>
+#include <asm/uaccess.h>
 
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
-KERN_INFO "via-rhine.c:v1.10-LK1.1.12  03/11/2001  Written by Donald Becker\n"
+KERN_INFO DRV_NAME ".c:v1.10-LK" DRV_VERSION "  " DRV_RELDATE "  Written by Donald Becker\n"
 KERN_INFO "  http://www.scyld.com/network/via-rhine.html\n";
 
-static char shortname[] = "via-rhine";
+static char shortname[] = DRV_NAME;
 
 
 /* This driver was written to use PCI memory space, however most versions
@@ -470,6 +481,7 @@
 	unsigned char phys[MAX_MII_CNT];			/* MII device addresses. */
 	unsigned int mii_cnt;			/* number of MIIs found, but only the first one is used */
 	u16 mii_status;						/* last read MII status */
+	struct mii_if_info mii_if;
 };
 
 static int  mdio_read(struct net_device *dev, int phy_id, int location);
@@ -485,7 +497,7 @@
 static void via_rhine_error(struct net_device *dev, int intr_status);
 static void via_rhine_set_rx_mode(struct net_device *dev);
 static struct net_device_stats *via_rhine_get_stats(struct net_device *dev);
-static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
 static int  via_rhine_close(struct net_device *dev);
 static inline void clear_tally_counters(long ioaddr);
 
@@ -683,6 +695,9 @@
 	np->chip_id = chip_id;
 	np->drv_flags = via_rhine_chip_info[chip_id].drv_flags;
 	np->pdev = pdev;
+	np->mii_if.dev = dev;
+	np->mii_if.mdio_read = mdio_read;
+	np->mii_if.mdio_write = mdio_write;
 
 	if (dev->mem_start)
 		option = dev->mem_start;
@@ -690,16 +705,16 @@
 	/* The lower four bits are the media type. */
 	if (option > 0) {
 		if (option & 0x200)
-			np->full_duplex = 1;
+			np->mii_if.full_duplex = 1;
 		np->default_port = option & 15;
 	}
 	if (card_idx < MAX_UNITS  &&  full_duplex[card_idx] > 0)
-		np->full_duplex = 1;
+		np->mii_if.full_duplex = 1;
 
-	if (np->full_duplex) {
+	if (np->mii_if.full_duplex) {
 		printk(KERN_INFO "%s: Set to forced full duplex, autonegotiation"
 			   " disabled.\n", dev->name);
-		np->duplex_lock = 1;
+		np->mii_if.duplex_lock = 1;
 	}
 
 	/* The chip-specific entries in the device structure. */
@@ -708,7 +723,7 @@
 	dev->stop = via_rhine_close;
 	dev->get_stats = via_rhine_get_stats;
 	dev->set_multicast_list = via_rhine_set_rx_mode;
-	dev->do_ioctl = mii_ioctl;
+	dev->do_ioctl = via_rhine_ioctl;
 	dev->tx_timeout = via_rhine_tx_timeout;
 	dev->watchdog_timeo = TX_TIMEOUT;
 	if (np->drv_flags & ReqTxAlign)
@@ -735,10 +750,10 @@
 			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
-				np->advertising = mdio_read(dev, phy, 4);
+				np->mii_if.advertising = mdio_read(dev, phy, 4);
 				printk(KERN_INFO "%s: MII PHY found at address %d, status "
 					   "0x%4.4x advertising %4.4x Link %4.4x.\n",
-					   dev->name, phy, mii_status, np->advertising,
+					   dev->name, phy, mii_status, np->mii_if.advertising,
 					   mdio_read(dev, phy, 5));
 
 				/* set IFF_RUNNING */
@@ -749,12 +764,13 @@
 			}
 		}
 		np->mii_cnt = phy_idx;
+		np->mii_if.phy_id = np->phys[0];
 	}
 
 	/* Allow forcing the media type. */
 	if (option > 0) {
 		if (option & 0x220)
-			np->full_duplex = 1;
+			np->mii_if.full_duplex = 1;
 		np->default_port = option & 0x3ff;
 		if (np->default_port & 0x330) {
 			/* FIXME: shouldn't someone check this variable? */
@@ -968,7 +984,7 @@
 		   ioaddr + IntrEnable);
 
 	np->chip_cmd = CmdStart|CmdTxOn|CmdRxOn|CmdNoTxPoll;
-	if (np->duplex_lock)
+	if (np->mii_if.duplex_lock)
 		np->chip_cmd |= CmdFDuplex;
 	writew(np->chip_cmd, ioaddr + ChipCmd);
 
@@ -1010,12 +1026,12 @@
 		switch (regnum) {
 		case 0:							/* Is user forcing speed/duplex? */
 			if (value & 0x9000)			/* Autonegotiation. */
-				np->duplex_lock = 0;
+				np->mii_if.duplex_lock = 0;
 			else
-				np->full_duplex = (value & 0x0100) ? 1 : 0;
+				np->mii_if.full_duplex = (value & 0x0100) ? 1 : 0;
 			break;
 		case 4:
-			np->advertising = value;
+			np->mii_if.advertising = value;
 			break;
 		}
 	}
@@ -1059,7 +1075,7 @@
 		printk(KERN_DEBUG "%s: Done via_rhine_open(), status %4.4x "
 			   "MII status: %4.4x.\n",
 			   dev->name, readw(ioaddr + ChipCmd),
-			   mdio_read(dev, np->phys[0], 1));
+			   mdio_read(dev, np->phys[0], MII_BMSR));
 
 	netif_start_queue(dev);
 
@@ -1077,19 +1093,19 @@
 {
 	struct netdev_private *np = dev->priv;
 	long ioaddr = dev->base_addr;
-	int mii_reg5 = mdio_read(dev, np->phys[0], 5);
-	int negotiated = mii_reg5 & np->advertising;
+	int mii_lpa = mdio_read(dev, np->phys[0], MII_LPA);
+	int negotiated = mii_lpa & np->mii_if.advertising;
 	int duplex;
 
-	if (np->duplex_lock  ||  mii_reg5 == 0xffff)
+	if (np->mii_if.duplex_lock  ||  mii_lpa == 0xffff)
 		return;
 	duplex = (negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040;
-	if (np->full_duplex != duplex) {
-		np->full_duplex = duplex;
+	if (np->mii_if.full_duplex != duplex) {
+		np->mii_if.full_duplex = duplex;
 		if (debug)
 			printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link"
 				   " partner capability of %4.4x.\n", dev->name,
-				   duplex ? "full" : "half", np->phys[0], mii_reg5);
+				   duplex ? "full" : "half", np->phys[0], mii_lpa);
 		if (duplex)
 			np->chip_cmd |= CmdFDuplex;
 		else
@@ -1117,7 +1133,7 @@
 	via_rhine_check_duplex(dev);
 
 	/* make IFF_RUNNING follow the MII status bit "Link established" */
-	mii_status = mdio_read(dev, np->phys[0], 1);
+	mii_status = mdio_read(dev, np->phys[0], MII_BMSR);
 	if ( (mii_status & MIILink) != (np->mii_status & MIILink) ) {
 		if (mii_status & MIILink)
 			netif_carrier_on(dev);
@@ -1141,7 +1157,7 @@
 	printk (KERN_WARNING "%s: Transmit timed out, status %4.4x, PHY status "
 		"%4.4x, resetting...\n",
 		dev->name, readw (ioaddr + IntrStatus),
-		mdio_read (dev, np->phys[0], 1));
+		mdio_read (dev, np->phys[0], MII_BMSR));
 
 	dev->if_port = 0;
 
@@ -1456,14 +1472,14 @@
 		if (readb(ioaddr + MIIStatus) & 0x02) {
 			/* Link failed, restart autonegotiation. */
 			if (np->drv_flags & HasDavicomPhy)
-				mdio_write(dev, np->phys[0], 0, 0x3300);
+				mdio_write(dev, np->phys[0], MII_BMCR, 0x3300);
 		} else
 			via_rhine_check_duplex(dev);
 		if (debug)
 			printk(KERN_ERR "%s: MII status changed: Autonegotiation "
 				   "advertising %4.4x  partner %4.4x.\n", dev->name,
-			   mdio_read(dev, np->phys[0], 4),
-			   mdio_read(dev, np->phys[0], 5));
+			   mdio_read(dev, np->phys[0], MII_ADVERTISE),
+			   mdio_read(dev, np->phys[0], MII_LPA));
 	}
 	if (intr_status & IntrStatsMax) {
 		np->stats.rx_crc_errors	+= readw(ioaddr + RxCRCErrs);
@@ -1573,12 +1589,98 @@
 	writeb(np->rx_thresh | rx_mode, ioaddr + RxConfig);
 }
 
-static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+static int via_rhine_ethtool_ioctl (struct net_device *dev, void *useraddr)
+{
+	struct netdev_private *np = dev->priv;
+	u32 ethcmd;
+
+	if (get_user(ethcmd, (u32 *)useraddr))
+		return -EFAULT;
+
+	switch (ethcmd) {
+	case ETHTOOL_GDRVINFO: {
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, DRV_NAME);
+		strcpy (info.version, DRV_VERSION);
+		strcpy (info.bus_info, np->pdev->slot_name);
+		if (copy_to_user (useraddr, &info, sizeof (info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		if (!(np->drv_flags & CanHaveMII))
+			break;
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&np->mii_if, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (!(np->drv_flags & CanHaveMII))
+			break;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&np->mii_if, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		if (!(np->drv_flags & CanHaveMII))
+			break;
+		return mii_nway_restart(&np->mii_if);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		if (!(np->drv_flags & CanHaveMII))
+			break;
+		edata.data = mii_link_ok(&np->mii_if);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
+	default:
+		break;
+	}
+
+	return -EOPNOTSUPP;
+}
+static int via_rhine_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
 	struct netdev_private *np = dev->priv;
 	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&rq->ifr_data;
 	unsigned long flags;
 	int retval;
+
+	if (cmd == SIOCETHTOOL)
+		return via_rhine_ethtool_ioctl(dev, (void *) rq->ifr_data);
 
 	spin_lock_irqsave(&np->lock, flags);
 	retval = 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wan/Makefile linux/drivers/net/wan/Makefile
--- linux.orig/drivers/net/wan/Makefile	Sun Aug 12 17:38:48 2001
+++ linux/drivers/net/wan/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the Linux network (wan) device drivers.
 #
-# 3 Aug 2000, Christoph Hellwig <hch@caldera.de>
+# 3 Aug 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wan/cosa.c linux/drivers/net/wan/cosa.c
--- linux.orig/drivers/net/wan/cosa.c	Thu Sep 13 23:04:43 2001
+++ linux/drivers/net/wan/cosa.c	Tue Feb  5 18:11:43 2002
@@ -105,13 +105,6 @@
 #include <net/syncppp.h>
 #include "cosa.h"
 
-/* Linux version stuff */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1)
-typedef struct wait_queue *wait_queue_head_t;
-#define DECLARE_WAITQUEUE(wait, current) \
-	struct wait_queue wait = { current, NULL }
-#endif
-
 /* Maximum length of the identification string. */
 #define COSA_MAX_ID_STRING	128
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wan/sdla_ppp.c linux/drivers/net/wan/sdla_ppp.c
--- linux.orig/drivers/net/wan/sdla_ppp.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wan/sdla_ppp.c	Mon Jan 14 18:53:53 2002
@@ -2473,7 +2473,7 @@
 #endif
 
 		default:
-			printk(KERN_INFO "%s: ERROR: Unsuported PPP Mode Selected\n",
+			printk(KERN_INFO "%s: ERROR: Unsupported PPP Mode Selected\n",
 					card->devname);
 			printk(KERN_INFO "%s:        PPP IP Modes: STATIC, PEER or HOST\n",
 					card->devname);	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- linux.orig/drivers/net/wavelan.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wavelan.c	Thu Jan 10 19:55:29 2002
@@ -4019,7 +4019,8 @@
 
 	dev->irq = irq;
 
-	request_region(ioaddr, sizeof(ha_t), "wavelan");
+	if (!request_region(ioaddr, sizeof(ha_t), "wavelan"))
+		return -EBUSY;
 
 	dev->mem_start = 0x0000;
 	dev->mem_end = 0x0000;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/winbond-840.c linux/drivers/net/winbond-840.c
--- linux.orig/drivers/net/winbond-840.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/winbond-840.c	Wed Feb 13 16:40:38 2002
@@ -36,6 +36,8 @@
 		power management.
 		support for big endian descriptors
 			Copyright (C) 2001 Manfred Spraul
+  	* ethtool support (jgarzik)
+	* Replace some MII-related magic numbers with constants (jgarzik)
   
 	TODO:
 	* enable pci_power_off
@@ -43,8 +45,8 @@
 */
   
 #define DRV_NAME	"winbond-840"
-#define DRV_VERSION	"1.01-c"
-#define DRV_RELDATE	"6/30/2000"
+#define DRV_VERSION	"1.01-d"
+#define DRV_RELDATE	"Nov-17-2001"
 
 
 /* Automatically extracted configuration info:
@@ -344,7 +346,7 @@
 	struct w840_rx_desc *rx_ring;
 	dma_addr_t	rx_addr[RX_RING_SIZE];
 	struct w840_tx_desc *tx_ring;
-	dma_addr_t	tx_addr[RX_RING_SIZE];
+	dma_addr_t	tx_addr[TX_RING_SIZE];
 	dma_addr_t ring_dma_addr;
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
@@ -363,14 +365,11 @@
 	unsigned int cur_tx, dirty_tx;
 	unsigned int tx_q_bytes;
 	unsigned int tx_full;				/* The Tx queue is full. */
-	/* These values are keep track of the transceiver/media in use. */
-	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int duplex_lock:1;
 	/* MII transceiver section. */
 	int mii_cnt;						/* MII device addresses. */
-	u16 advertising;					/* NWay media advertisement */
 	unsigned char phys[MII_CNT];		/* MII device addresses, but only the first is used */
 	u32 mii;
+	struct mii_if_info mii_if;
 };
 
 static int  eeprom_read(long ioaddr, int location);
@@ -453,6 +452,9 @@
 	np->chip_id = chip_idx;
 	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
 	spin_lock_init(&np->lock);
+	np->mii_if.dev = dev;
+	np->mii_if.mdio_read = mdio_read;
+	np->mii_if.mdio_write = mdio_write;
 	
 	pci_set_drvdata(pdev, dev);
 
@@ -462,16 +464,16 @@
 	/* The lower four bits are the media type. */
 	if (option > 0) {
 		if (option & 0x200)
-			np->full_duplex = 1;
+			np->mii_if.full_duplex = 1;
 		if (option & 15)
 			printk(KERN_INFO "%s: ignoring user supplied media type %d",
 				dev->name, option & 15);
 	}
 	if (find_cnt < MAX_UNITS  &&  full_duplex[find_cnt] > 0)
-		np->full_duplex = 1;
+		np->mii_if.full_duplex = 1;
 
-	if (np->full_duplex)
-		np->duplex_lock = 1;
+	if (np->mii_if.full_duplex)
+		np->mii_if.duplex_lock = 1;
 
 	/* The chip-specific entries in the device structure. */
 	dev->open = &netdev_open;
@@ -496,18 +498,19 @@
 	if (np->drv_flags & CanHaveMII) {
 		int phy, phy_idx = 0;
 		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
-			int mii_status = mdio_read(dev, phy, 1);
+			int mii_status = mdio_read(dev, phy, MII_BMSR);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
-				np->advertising = mdio_read(dev, phy, 4);
-				np->mii = (mdio_read(dev, phy, 2) << 16)+
-						mdio_read(dev, phy, 3);
+				np->mii_if.advertising = mdio_read(dev, phy, MII_ADVERTISE);
+				np->mii = (mdio_read(dev, phy, MII_PHYSID1) << 16)+
+						mdio_read(dev, phy, MII_PHYSID2);
 				printk(KERN_INFO "%s: MII PHY %8.8xh found at address %d, status "
 					   "0x%4.4x advertising %4.4x.\n",
-					   dev->name, np->mii, phy, mii_status, np->advertising);
+					   dev->name, np->mii, phy, mii_status, np->mii_if.advertising);
 			}
 		}
 		np->mii_cnt = phy_idx;
+		np->mii_if.phy_id = np->phys[0];
 		if (phy_idx == 0) {
 				printk(KERN_WARNING "%s: MII PHY not found -- this device may "
 					   "not operate correctly.\n", dev->name);
@@ -654,7 +657,7 @@
 	int i;
 
 	if (location == 4  &&  phy_id == np->phys[0])
-		np->advertising = value;
+		np->mii_if.advertising = value;
 
 	if (mii_preamble_required)
 		mdio_sync(mdio_addr);
@@ -728,12 +731,12 @@
 	int duplex, fasteth, result, mii_reg;
 
 	/* BSMR */
-	mii_reg = mdio_read(dev, np->phys[0], 1);
+	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
 
 	if (mii_reg == 0xffff)
 		return np->csr6;
 	/* reread: the link status bit is sticky */
-	mii_reg = mdio_read(dev, np->phys[0], 1);
+	mii_reg = mdio_read(dev, np->phys[0], MII_BMSR);
 	if (!(mii_reg & 0x4)) {
 		if (netif_carrier_ok(dev)) {
 			if (debug)
@@ -759,18 +762,18 @@
 		 * Instead bit 9 and 13 of the BMCR are updated to the result
 		 * of the negotiation..
 		 */
-		mii_reg = mdio_read(dev, np->phys[0], 0);
-		duplex = mii_reg & 0x100;
-		fasteth = mii_reg & 0x2000;
+		mii_reg = mdio_read(dev, np->phys[0], MII_BMCR);
+		duplex = mii_reg & BMCR_FULLDPLX;
+		fasteth = mii_reg & BMCR_SPEED100;
 	} else {
 		int negotiated;
-		mii_reg	= mdio_read(dev, np->phys[0], 5);
-		negotiated = mii_reg & np->advertising;
+		mii_reg	= mdio_read(dev, np->phys[0], MII_LPA);
+		negotiated = mii_reg & np->mii_if.advertising;
 
-		duplex = (negotiated & 0x0100) || ((negotiated & 0x02C0) == 0x0040);
+		duplex = (negotiated & LPA_100FULL) || ((negotiated & 0x02C0) == LPA_10FULL);
 		fasteth = negotiated & 0x380;
 	}
-	duplex |= np->duplex_lock;
+	duplex |= np->mii_if.duplex_lock;
 	/* remove fastether and fullduplex */
 	result = np->csr6 & ~0x20000200;
 	if (duplex)
@@ -822,7 +825,7 @@
 	/* and restart them with the new configuration */
 	writel(np->csr6, ioaddr + NetworkConfig);
 	if (new & 0x200)
-		np->full_duplex = 1;
+		np->mii_if.full_duplex = 1;
 }
 
 static void netdev_timer(unsigned long data)
@@ -1131,7 +1134,7 @@
 			if (tx_status & 0x0C80) np->stats.tx_carrier_errors++;
 			if (tx_status & 0x0200) np->stats.tx_window_errors++;
 			if (tx_status & 0x0002) np->stats.tx_fifo_errors++;
-			if ((tx_status & 0x0080) && np->full_duplex == 0)
+			if ((tx_status & 0x0080) && np->mii_if.full_duplex == 0)
 				np->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
 			if (tx_status & 0x0100) np->stats.collisions16++;
@@ -1484,6 +1487,56 @@
 		return 0;
 	}
 
+	/* get settings */
+	case ETHTOOL_GSET: {
+		struct ethtool_cmd ecmd = { ETHTOOL_GSET };
+		spin_lock_irq(&np->lock);
+		mii_ethtool_gset(&np->mii_if, &ecmd);
+		spin_unlock_irq(&np->lock);
+		if (copy_to_user(useraddr, &ecmd, sizeof(ecmd)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set settings */
+	case ETHTOOL_SSET: {
+		int r;
+		struct ethtool_cmd ecmd;
+		if (copy_from_user(&ecmd, useraddr, sizeof(ecmd)))
+			return -EFAULT;
+		spin_lock_irq(&np->lock);
+		r = mii_ethtool_sset(&np->mii_if, &ecmd);
+		spin_unlock_irq(&np->lock);
+		return r;
+	}
+	/* restart autonegotiation */
+	case ETHTOOL_NWAY_RST: {
+		return mii_nway_restart(&np->mii_if);
+	}
+	/* get link status */
+	case ETHTOOL_GLINK: {
+		struct ethtool_value edata = {ETHTOOL_GLINK};
+		edata.data = mii_link_ok(&np->mii_if);
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+
+	/* get message-level */
+	case ETHTOOL_GMSGLVL: {
+		struct ethtool_value edata = {ETHTOOL_GMSGLVL};
+		edata.data = debug;
+		if (copy_to_user(useraddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		return 0;
+	}
+	/* set message-level */
+	case ETHTOOL_SMSGLVL: {
+		struct ethtool_value edata;
+		if (copy_from_user(&edata, useraddr, sizeof(edata)))
+			return -EFAULT;
+		debug = edata.data;
+		return 0;
+	}
         }
 	
 	return -EOPNOTSUPP;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/airo.c linux/drivers/net/wireless/airo.c
--- linux.orig/drivers/net/wireless/airo.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/airo.c	Wed Dec 26 16:16:25 2001
@@ -47,7 +47,7 @@
 #include <asm/uaccess.h>
 
 #ifdef CONFIG_PCI
-static struct pci_device_id card_ids[] = __devinitdata {
+static struct pci_device_id card_ids[] __devinitdata = {
 	{ 0x14b9, 1, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0x14b9, 0x4500, PCI_ANY_ID, PCI_ANY_ID },
 	{ 0x14b9, 0x4800, PCI_ANY_ID, PCI_ANY_ID, },
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/airo_cs.c linux/drivers/net/wireless/airo_cs.c
--- linux.orig/drivers/net/wireless/airo_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/airo_cs.c	Mon Jan 14 18:53:53 2002
@@ -244,6 +244,11 @@
 	
 	/* Allocate space for private device-specific data */
 	local = kmalloc(sizeof(local_info_t), GFP_KERNEL);
+	if (!local) {
+		printk(KERN_ERR "airo_cs: no memory for new device\n");
+		kfree (link);
+		return NULL;
+	}
 	memset(local, 0, sizeof(local_info_t));
 	link->priv = local;
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/airport.c linux/drivers/net/wireless/airport.c
--- linux.orig/drivers/net/wireless/airport.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/airport.c	Mon Jan 21 19:01:05 2002
@@ -1,4 +1,4 @@
-/* airport.c 0.06f
+/* airport.c 0.09b
  *
  * A driver for "Hermes" chipset based Apple Airport wireless
  * card.
@@ -11,6 +11,8 @@
  *  0.06 : fix possible hang on powerup, add sleep support
  */
 
+#include <linux/config.h>
+
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
@@ -33,26 +35,27 @@
 #include <linux/pmu.h>
 
 #include <asm/prom.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/irq.h>
 
 #include "hermes.h"
 #include "orinoco.h"
 
-static char version[] __initdata = "airport.c 0.06f (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
+static char version[] __initdata = "airport.c 0.09b (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
 MODULE_LICENSE("Dual MPL/GPL");
 EXPORT_NO_SYMBOLS;
 
-typedef struct dldwd_card {
+struct airport {
 	struct device_node* node;
 	int irq_requested;
 	int ndev_registered;
 	int open;
 	/* Common structure (fully included), see orinoco.h */
-	struct dldwd_priv priv;
-} dldwd_card_t;
+	struct orinoco_private priv;
+};
 
 #ifdef CONFIG_PMAC_PBOOK
 static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when);
@@ -65,9 +68,8 @@
  * Function prototypes
  */
 
-static dldwd_priv_t* airport_attach(struct device_node *of_node);
-static void airport_detach(dldwd_priv_t* priv);
-static int airport_init(struct net_device *dev);
+static struct orinoco_private* airport_attach(struct device_node *of_node);
+static void airport_detach(struct orinoco_private* priv);
 static int airport_open(struct net_device *dev);
 static int airport_stop(struct net_device *dev);
 
@@ -81,58 +83,38 @@
    device numbers are used to derive the corresponding array index.
 */
 
-static dldwd_priv_t *airport_dev;
-
-static int airport_init(struct net_device *dev)
-{
-	dldwd_priv_t *priv = dev->priv;
-	int rc;
-	
-	TRACE_ENTER(priv->ndev.name);
-
-	MOD_INC_USE_COUNT;
-
-	rc = dldwd_init(dev);
-	if (!rc)
-		priv->hw_ready = 1;
-
-	MOD_DEC_USE_COUNT;
-
-	return rc;
-}
+static struct orinoco_private *airport_dev;
 
 static int
 airport_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = dev->priv;
+	struct airport* card = (struct airport *)priv->card;
 	int rc;
 
-	TRACE_ENTER(priv->ndev.name);
+	netif_device_attach(dev);
 
-	rc = dldwd_reset(priv);
+	rc = orinoco_reset(priv);
 	if (rc)
 		airport_stop(dev);
 	else {
 		card->open = 1;
-		netif_device_attach(dev);
+		netif_start_queue(dev);
 	}
 
-//	TRACE_EXIT(priv->ndev.name);
-
 	return rc;
 }
 
 static int
 airport_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = dev->priv;
+	struct airport* card = (struct airport *)priv->card;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	netif_stop_queue(dev);
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	card->open = 0;
 
 	TRACE_EXIT(priv->ndev.name);
@@ -144,16 +126,14 @@
 static int
 airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
-	dldwd_priv_t *priv;
-	struct net_device *ndev;
-	dldwd_card_t* card;
+	struct orinoco_private *priv = airport_dev;
+	struct hermes *hw = &priv->hw;
+	struct net_device *dev = &priv->ndev;
+	struct airport* card = (struct airport *)priv->card;
 	int rc;
 	
-	if (!airport_dev)
+	if (! airport_dev)
 		return PBOOK_SLEEP_OK;
-	priv = airport_dev;
-	ndev = &priv->ndev;
-	card = (dldwd_card_t *)priv->card;
 
 	switch (when) {
 	case PBOOK_SLEEP_REQUEST:
@@ -161,41 +141,41 @@
 	case PBOOK_SLEEP_REJECT:
 		break;
 	case PBOOK_SLEEP_NOW:
-		printk(KERN_INFO "%s: Airport entering sleep mode\n", ndev->name);
-		netif_device_detach(ndev);
-		if (card->open)
-			dldwd_shutdown(priv);
-		disable_irq(ndev->irq);
-		feature_set_airport_power(card->node, 0);
-		priv->hw_ready = 0;
+		printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name);
+		if (card->open) {
+			netif_stop_queue(dev);
+			orinoco_shutdown(priv);
+			netif_device_detach(dev);
+		}
+		disable_irq(dev->irq);
+		pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
 		break;
 	case PBOOK_WAKE:
-		printk(KERN_INFO "%s: Airport waking up\n", ndev->name);
-		feature_set_airport_power(card->node, 1);
+		printk(KERN_INFO "%s: Airport waking up\n", dev->name);
+		pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
 		mdelay(200);
-		hermes_reset(&priv->hw);
-		priv->hw_ready = 1;		
-		rc = dldwd_reset(priv);
+		hermes_reset(hw);
+		rc = orinoco_reset(priv);
 		if (rc)
 			printk(KERN_ERR "airport: Error %d re-initing card !\n", rc);
 		else if (card->open)
-			netif_device_attach(ndev);
-		enable_irq(ndev->irq);
+			netif_device_attach(dev);
+		enable_irq(dev->irq);
 		break;
 	}
 	return PBOOK_SLEEP_OK;
 }
 #endif /* CONFIG_PMAC_PBOOK */
 
-static dldwd_priv_t*
+static struct orinoco_private*
 airport_attach(struct device_node* of_node)
 {
-	dldwd_priv_t *priv;
+	struct orinoco_private *priv;
 	struct net_device *ndev;
-	dldwd_card_t* card;
+	struct airport* card;
 	hermes_t *hw;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	if (of_node->n_addrs < 1 || of_node->n_intrs < 1) {
 		printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n");
@@ -216,14 +196,24 @@
 	hw = &priv->hw;
 	card->node = of_node;
 
+	if (!request_OF_resource(of_node, 0, " (airport)")) {
+		printk(KERN_ERR "airport: can't request IO resource !\n");
+		kfree(card);
+		return NULL;
+	}
+	
 	/* Setup the common part */
-	if (dldwd_setup(priv) < 0) {
+	if (orinoco_setup(priv) < 0) {
+		release_OF_resource(of_node, 0);
 		kfree(card);
 		return NULL;
 	}
 
+
+	ndev->name[0] = '\0';	/* register_netdev will give us an ethX name */
+	SET_MODULE_OWNER(ndev);
+
 	/* Overrides */
-	ndev->init = airport_init;
 	ndev->open = airport_open;
 	ndev->stop = airport_stop;
 
@@ -234,21 +224,19 @@
 	hermes_struct_init(hw, ndev->base_addr);
 		
 	/* Power up card */
-	feature_set_airport_power(card->node, 1);
+	pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ);
 
 	/* Reset it before we get the interrupt */
 	hermes_reset(hw);
 
-	if (request_irq(ndev->irq, dldwd_interrupt, 0, "Airport", (void *)priv)) {
+	if (request_irq(ndev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
 		printk(KERN_ERR "airport: Couldn't get IRQ %d\n", ndev->irq);
 		goto failed;
 	}
 	card->irq_requested = 1;
 	
-	/* register_netdev will give us an ethX name */
-	ndev->name[0] = '\0';
 	/* Tell the stack we exist */
 	if (register_netdev(ndev) != 0) {
 		printk(KERN_ERR "airport: register_netdev() failed\n");
@@ -257,10 +245,8 @@
 	printk(KERN_DEBUG "airport: card registered for interface %s\n", ndev->name);
 	card->ndev_registered = 1;
 
-	SET_MODULE_OWNER(ndev);
-
 	/* And give us the proc nodes for debugging */
-	if (dldwd_proc_dev_init(priv) != 0)
+	if (orinoco_proc_dev_init(priv) != 0)
 		printk(KERN_ERR "airport: Failed to create /proc node for %s\n",
 		       ndev->name);
 
@@ -279,14 +265,12 @@
   ======================================================================*/
 
 static void
-airport_detach(dldwd_priv_t *priv)
+airport_detach(struct orinoco_private *priv)
 {
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct airport* card = (struct airport *)priv->card;
 
-	priv->hw_ready = 0;
-	
 	/* Unregister proc entry */
-	dldwd_proc_dev_cleanup(priv);
+	orinoco_proc_dev_cleanup(priv);
 
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_unregister_sleep_notifier(&airport_sleep_notifier);
@@ -299,12 +283,13 @@
 		free_irq(priv->ndev.irq, priv);
 	card->irq_requested = 0;
 
-// FIXME
-//	if (ndev->base_addr)
-//		iounmap(ndev->base_addr + _IO_BASE);
-//	ndev->base_addr = 0;
+	if (priv->ndev.base_addr)
+		iounmap((void *)(priv->ndev.base_addr + (unsigned long)_IO_BASE));
+	priv->ndev.base_addr = 0;
+
+	release_OF_resource(card->node, 0);
 	
-	feature_set_airport_power(card->node, 0);
+	pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
 	current->state = TASK_UNINTERRUPTIBLE;
 	schedule_timeout(HZ);
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/hermes.c linux/drivers/net/wireless/hermes.c
--- linux.orig/drivers/net/wireless/hermes.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/hermes.c	Mon Jan 21 19:01:05 2002
@@ -53,10 +53,12 @@
 
 #include "hermes.h"
 
-static char version[] __initdata = "hermes.c: 3 Oct 2001 David Gibson <hermes@gibson.dropbear.id.au>";
+static char version[] __initdata = "hermes.c: 16 Jan 2002 David Gibson <hermes@gibson.dropbear.id.au>";
 MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller");
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
+#ifdef MODULE_LICENSE
 MODULE_LICENSE("Dual MPL/GPL");
+#endif
 
 /* These are maximum timeouts. Most often, card wil react much faster */
 #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */
@@ -75,9 +77,9 @@
 #include <stdarg.h>
 
 #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \
-			printk(#stuff);} while (0)
+			printk(stuff);} while (0)
 
-#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(#stuff)
+#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff)
 
 #else /* ! HERMES_DEBUG */
 
@@ -98,10 +100,17 @@
 */
 static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
 {
+	int k = CMD_BUSY_TIMEOUT;
 	u16 reg;
 
-	/* First check that the command register is not busy */
+	/* First wait for the command register to unbusy */
 	reg = hermes_read_regn(hw, CMD);
+	while ( (reg & HERMES_CMD_BUSY) && k ) {
+		k--;
+		udelay(1);
+		reg = hermes_read_regn(hw, CMD);
+	}
+	DEBUG(3, "hermes_issue_cmd: did %d retries.\n", CMD_BUSY_TIMEOUT-k);
 	if (reg & HERMES_CMD_BUSY) {
 		return -EBUSY;
 	}
@@ -223,8 +232,8 @@
 			       hw->iobase);
 			err = -ENODEV;
 		} else 
-			printk(KERN_ERR "hermes @ 0x%x: CMD register busy in hermes_issue_command().\n",
-			       hw->iobase);
+			printk(KERN_ERR "hermes @ 0x%x: Error %d issuing command.\n",
+			       hw->iobase, err);
 		goto out;
 	}
 
@@ -325,7 +334,7 @@
 
 	k = BAP_BUSY_TIMEOUT;
 	reg = hermes_read_reg(hw, oreg);
-	while ((reg & HERMES_OFFSET_BUSY) & k) {
+	while ((reg & HERMES_OFFSET_BUSY) && k) {
 		k--;
 		udelay(1);
 		reg = hermes_read_reg(hw, oreg);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/hermes.h linux/drivers/net/wireless/hermes.h
--- linux.orig/drivers/net/wireless/hermes.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/hermes.h	Mon Jan 21 19:01:05 2002
@@ -138,7 +138,7 @@
 
 /*--- Regulate Commands --------------------------*/
 #define		HERMES_CMD_NOTIFY		(0x0010)
-#define		HERMES_CMD_INQ			(0x0011)
+#define		HERMES_CMD_INQUIRE		(0x0011)
 
 /*--- Configure Commands --------------------------*/
 #define		HERMES_CMD_ACCESS		(0x0021)
@@ -150,88 +150,106 @@
 #define		HERMES_MONITOR_DISABLE		(0x000f)
 
 /*
- * Configuration RIDs
+ * Frame structures and constants
  */
 
-#define		HERMES_RID_CNF_PORTTYPE		(0xfc00)
-#define		HERMES_RID_CNF_MACADDR		(0xfc01)
-#define		HERMES_RID_CNF_DESIRED_SSID	(0xfc02)
-#define		HERMES_RID_CNF_CHANNEL		(0xfc03)
-#define		HERMES_RID_CNF_OWN_SSID		(0xfc04)
-#define		HERMES_RID_CNF_SYSTEM_SCALE	(0xfc06)
-#define		HERMES_RID_CNF_MAX_DATA_LEN	(0xfc07)
-#define		HERMES_RID_CNF_PM_ENABLE	(0xfc09)
-#define		HERMES_RID_CNF_PM_MCAST_RX	(0xfc0b)
-#define		HERMES_RID_CNF_PM_PERIOD	(0xfc0c)
-#define		HERMES_RID_CNF_PM_HOLDOVER	(0xfc0d)
-#define		HERMES_RID_CNF_NICKNAME		(0xfc0e)
-#define		HERMES_RID_CNF_WEP_ON		(0xfc20)
-#define		HERMES_RID_CNF_MWO_ROBUST	(0xfc25)
-#define		HERMES_RID_CNF_MULTICAST_LIST	(0xfc80)
-#define		HERMES_RID_CNF_CREATEIBSS	(0xfc81)
-#define		HERMES_RID_CNF_FRAG_THRESH	(0xfc82)
-#define		HERMES_RID_CNF_RTS_THRESH	(0xfc83)
-#define		HERMES_RID_CNF_TX_RATE_CTRL	(0xfc84)
-#define		HERMES_RID_CNF_PROMISCUOUS	(0xfc85)
-#define		HERMES_RID_CNF_KEYS		(0xfcb0)
-#define		HERMES_RID_CNF_TX_KEY		(0xfcb1)
-#define		HERMES_RID_CNF_TICKTIME		(0xfce0)
-
-#define		HERMES_RID_CNF_INTERSIL_WEP_ON	(0xfc28)
-#define		HERMES_RID_CNF_INTERSIL_TX_KEY	(0xfc23)
-#define		HERMES_RID_CNF_INTERSIL_KEY0	(0xfc24)
-#define		HERMES_RID_CNF_INTERSIL_KEY1	(0xfc25)
-#define		HERMES_RID_CNF_INTERSIL_KEY2	(0xfc26)
-#define		HERMES_RID_CNF_INTERSIL_KEY3	(0xfc27)
-#define		HERMES_RID_CNF_SYMBOL_MANDATORY_BSSID	(0xfc21)
-#define		HERMES_RID_CNF_SYMBOL_AUTH_TYPE		(0xfc2A)
-#define		HERMES_RID_CNF_SYMBOL_BASIC_RATES	(0xfc8A)
-#define		HERMES_RID_CNF_SYMBOL_PREAMBLE		(0xfc8C)
-
-/*
- * Information RIDs
- */
-#define		HERMES_RID_CHANNEL_LIST		(0xfd10)
-#define		HERMES_RID_STAIDENTITY		(0xfd20)
-#define		HERMES_RID_CURRENT_SSID		(0xfd41)
-#define		HERMES_RID_CURRENT_BSSID	(0xfd42)
-#define		HERMES_RID_COMMSQUALITY		(0xfd43)
-#define 	HERMES_RID_CURRENT_TX_RATE	(0xfd44)
-#define 	HERMES_RID_SHORT_RETRY_LIMIT	(0xfd48)
-#define 	HERMES_RID_LONG_RETRY_LIMIT	(0xfd49)
-#define 	HERMES_RID_MAX_TX_LIFETIME	(0xfd4A)
-#define		HERMES_RID_WEP_AVAIL		(0xfd4f)
-#define		HERMES_RID_CURRENT_CHANNEL	(0xfdc1)
-#define		HERMES_RID_DATARATES		(0xfdc6)
-#define		HERMES_RID_SYMBOL_SECONDARY_VER	(0xfd24)
-#define		HERMES_RID_SYMBOL_KEY_LENGTH	(0xfc2B)
+#define HERMES_DESCRIPTOR_OFFSET	0
+#define HERMES_802_11_OFFSET		(14)
+#define HERMES_802_3_OFFSET		(14+32)
+#define HERMES_802_2_OFFSET		(14+32+14)
 
-/*
- * Frame structures and constants
- */
+struct hermes_rx_descriptor {
+	u16 status;
+	u32 time;
+	u8 silence;
+	u8 signal;
+	u8 rate;
+	u8 rxflow;
+	u32 reserved;
+} __attribute__ ((packed));
+
+#define HERMES_RXSTAT_ERR		(0x0003)
+#define	HERMES_RXSTAT_BADCRC		(0x0001)
+#define	HERMES_RXSTAT_UNDECRYPTABLE	(0x0002)
+#define	HERMES_RXSTAT_MACPORT		(0x0700)
+#define	HERMES_RXSTAT_MSGTYPE		(0xE000)
+#define	HERMES_RXSTAT_1042		(0x2000)	/* RFC-1042 frame */
+#define	HERMES_RXSTAT_TUNNEL		(0x4000)	/* bridge-tunnel encoded frame */
+#define	HERMES_RXSTAT_WMP		(0x6000)	/* Wavelan-II Management Protocol frame */
 
-typedef struct hermes_frame_desc {
-	/* Hermes - i.e. little-endian byte-order */
+struct hermes_tx_descriptor {
 	u16 status;
-	u16 res1, res2;
-	u16 q_info;
-	u16 res3, res4;
-	u16 tx_ctl;
-} __attribute__ ((packed)) hermes_frame_desc_t;
-
-#define		HERMES_RXSTAT_ERR		(0x0003)
-#define		HERMES_RXSTAT_MACPORT		(0x0700)
-#define		HERMES_RXSTAT_MSGTYPE		(0xE000)
-
-#define		HERMES_RXSTAT_BADCRC		(0x0001)
-#define		HERMES_RXSTAT_UNDECRYPTABLE	(0x0002)
-
-/* RFC-1042 encoded frame */
-#define		HERMES_RXSTAT_1042		(0x2000)
-/* Bridge-tunnel encoded frame */
-#define		HERMES_RXSTAT_TUNNEL		(0x4000)
-/* Wavelan-II Management Protocol frame */
-#define		HERMES_RXSTAT_WMP		(0x6000)
+	u16 reserved1;
+	u16 reserved2;
+	u32 sw_support;
+	u8 retry_count;
+	u8 tx_rate;
+	u16 tx_control;	
+} __attribute__ ((packed));
+
+#define HERMES_TXSTAT_RETRYERR		(0x0001)
+#define HERMES_TXSTAT_AGEDERR		(0x0002)
+#define HERMES_TXSTAT_DISCON		(0x0004)
+#define HERMES_TXSTAT_FORMERR		(0x0008)
+
+#define HERMES_TXCTRL_TX_OK		(0x0002)	/* ?? interrupt on Tx complete */
+#define HERMES_TXCTRL_TX_EX		(0x0004)	/* ?? interrupt on Tx exception */
+#define HERMES_TXCTRL_802_11		(0x0008)	/* We supply 802.11 header */
+#define HERMES_TXCTRL_ALT_RTRY		(0x0020)
+
+/* Inquiry constants and data types */
+
+#define HERMES_INQ_TALLIES		(0xF100)
+#define HERMES_INQ_SCAN			(0xF101)
+#define HERMES_INQ_LINKSTATUS		(0xF200)
+
+struct hermes_tallies_frame {
+	u16 TxUnicastFrames;
+	u16 TxMulticastFrames;
+	u16 TxFragments;
+	u16 TxUnicastOctets;
+	u16 TxMulticastOctets;
+	u16 TxDeferredTransmissions;
+	u16 TxSingleRetryFrames;
+	u16 TxMultipleRetryFrames;
+	u16 TxRetryLimitExceeded;
+	u16 TxDiscards;
+	u16 RxUnicastFrames;
+	u16 RxMulticastFrames;
+	u16 RxFragments;
+	u16 RxUnicastOctets;
+	u16 RxMulticastOctets;
+	u16 RxFCSErrors;
+	u16 RxDiscards_NoBuffer;
+	u16 TxDiscardsWrongSA;
+	u16 RxWEPUndecryptable;
+	u16 RxMsgInMsgFragments;
+	u16 RxMsgInBadMsgFragments;
+	/* Those last are probably not available in very old firmwares */
+	u16 RxDiscards_WEPICVError;
+	u16 RxDiscards_WEPExcluded;
+} __attribute__ ((packed));
+
+/* Grabbed from wlan-ng - Thanks Mark... - Jean II
+ * This is the result of a scan inquiry command */
+/* Structure describing info about an Access Point */
+struct hermes_scan_apinfo {
+	u16 channel;		/* Channel where the AP sits */
+	u16 noise;		/* Noise level */
+	u16 level;		/* Signal level */
+	u8 bssid[ETH_ALEN];	/* MAC address of the Access Point */
+	u16 beacon_interv;	/* Beacon interval ? */
+	u16 capabilities;	/* Capabilities ? */
+	u8 essid[32];		/* ESSID of the network */
+	u8 rates[10];		/* Bit rate supported */
+	u16 proberesp_rate;	/* ???? */
+} __attribute__ ((packed));
+/* Container */
+struct hermes_scan_frame {
+	u16 rsvd;                   /* ??? */
+	u16 scanreason;             /* ??? */
+	struct hermes_scan_apinfo aps[35];        /* Scan result */
+} __attribute__ ((packed));
 
 #ifdef __KERNEL__
 
@@ -246,16 +264,6 @@
 	u16 status, resp0, resp1, resp2;
 } hermes_response_t;
 
-/* "ID" structure - used for ESSID and station nickname */
-typedef struct hermes_id {
-	u16 len;
-	u16 val[16];
-} __attribute__ ((packed)) hermes_id_t;
-
-typedef struct hermes_multicast {
-	u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
-} __attribute__ ((packed)) hermes_multicast_t;
-
 /* Register access convenience macros */
 #define hermes_read_reg(hw, off) (inw((hw)->iobase + (off)))
 #define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off)))
@@ -310,8 +318,17 @@
 {
 	hermes_response_t resp;
 
-	return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), 
+	return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8), 
 				 0, &resp);
+}
+
+/* Initiate an INQUIRE command (tallies or scan).  The result will come as an
+ * information frame in __dldwd_ev_info() */
+static inline int hermes_inquire(hermes_t *hw, u16 rid)
+{
+	hermes_response_t resp;
+
+	return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, &resp);
 }
 
 #define HERMES_BYTES_TO_RECLEN(n) ( ((n) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 )
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/hermes_rid.h linux/drivers/net/wireless/hermes_rid.h
--- linux.orig/drivers/net/wireless/hermes_rid.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/net/wireless/hermes_rid.h	Mon Jan 14 16:18:06 2002
@@ -0,0 +1,153 @@
+#ifndef _HERMES_RID_H
+#define _HERMES_RID_H
+
+/*
+ * Configuration RIDs
+ */
+#define HERMES_RID_CNFPORTTYPE			0xFC00	/* used */
+#define HERMES_RID_CNFOWNMACADDR		0xFC01	/* used */
+#define HERMES_RID_CNFDESIREDSSID		0xFC02	/* used */
+#define HERMES_RID_CNFOWNCHANNEL		0xFC03	/* used */
+#define HERMES_RID_CNFOWNSSID			0xFC04	/* used */
+#define HERMES_RID_CNFOWNATIMWINDOW		0xFC05
+#define HERMES_RID_CNFSYSTEMSCALE		0xFC06	/* used */
+#define HERMES_RID_CNFMAXDATALEN		0xFC07
+#define HERMES_RID_CNFWDSADDRESS		0xFC08
+#define HERMES_RID_CNFPMENABLED			0xFC09	/* used */
+#define HERMES_RID_CNFPMEPS			0xFC0A
+#define HERMES_RID_CNFMULTICASTRECEIVE		0xFC0B	/* used */
+#define HERMES_RID_CNFMAXSLEEPDURATION		0xFC0C	/* used */
+#define HERMES_RID_CNFPMHOLDOVERDURATION	0xFC0D	/* used */
+#define HERMES_RID_CNFOWNNAME			0xFC0E	/* used */
+#define HERMES_RID_CNFOWNDTIMPERIOD		0xFC10
+#define HERMES_RID_CNFWDSADDRESS1		0xFC11
+#define HERMES_RID_CNFWDSADDRESS2		0xFC12
+#define HERMES_RID_CNFWDSADDRESS3		0xFC13
+#define HERMES_RID_CNFWDSADDRESS4		0xFC14
+#define HERMES_RID_CNFWDSADDRESS5		0xFC15
+#define HERMES_RID_CNFWDSADDRESS6		0xFC16
+#define HERMES_RID_CNFMULTICASTPMBUFFERING	0xFC17
+#define HERMES_RID_CNFWEPENABLED_AGERE		0xFC20	/* used */
+#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL	0xFC21
+#define HERMES_RID_CNFWEPDEFAULTKEYID		0xFC23	/* used */
+#define HERMES_RID_CNFDEFAULTKEY0		0xFC24	/* used */
+#define HERMES_RID_CNFDEFAULTKEY1		0xFC25	/* used */
+#define HERMES_RID_CNFMWOROBUST_AGERE		0xFC25	/* used */
+#define HERMES_RID_CNFDEFAULTKEY2		0xFC26	/* used */
+#define HERMES_RID_CNFDEFAULTKEY3		0xFC27	/* used */
+#define HERMES_RID_CNFWEPFLAGS_INTERSIL		0xFC28	/* used */
+#define HERMES_RID_CNFWEPKEYMAPPINGTABLE	0xFC29
+#define HERMES_RID_CNFAUTHENTICATION		0xFC2A	/* used */
+#define HERMES_RID_CNFMAXASSOCSTA		0xFC2B
+#define	HERMES_RID_CNFKEYLENGTH_SYMBOL		0xFC2B
+#define HERMES_RID_CNFTXCONTROL			0xFC2C
+#define HERMES_RID_CNFROAMINGMODE		0xFC2D
+#define HERMES_RID_CNFHOSTAUTHENTICATION	0xFC2E
+#define HERMES_RID_CNFRCVCRCERROR		0xFC30
+#define HERMES_RID_CNFMMLIFE			0xFC31
+#define HERMES_RID_CNFALTRETRYCOUNT		0xFC32
+#define HERMES_RID_CNFBEACONINT			0xFC33
+#define HERMES_RID_CNFAPPCFINFO			0xFC34
+#define HERMES_RID_CNFSTAPCFINFO		0xFC35
+#define HERMES_RID_CNFPRIORITYQUSAGE		0xFC37
+#define HERMES_RID_CNFTIMCTRL			0xFC40
+#define HERMES_RID_CNFTHIRTY2TALLY		0xFC42
+#define HERMES_RID_CNFENHSECURITY		0xFC43
+#define HERMES_RID_CNFGROUPADDRESSES		0xFC80	/* used */
+#define HERMES_RID_CNFCREATEIBSS		0xFC81	/* used */
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD	0xFC82	/* used */
+#define HERMES_RID_CNFRTSTHRESHOLD		0xFC83	/* used */
+#define HERMES_RID_CNFTXRATECONTROL		0xFC84	/* used */
+#define HERMES_RID_CNFPROMISCUOUSMODE		0xFC85	/* used */
+#define HERMES_RID_CNFBASICRATES_SYMBOL		0xFC8A
+#define HERMES_RID_CNFPREAMBLE_SYMBOL		0xFC8C	/* used */
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0	0xFC90
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1	0xFC91
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2	0xFC92
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3	0xFC93
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4	0xFC94
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5	0xFC95
+#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6	0xFC96
+#define HERMES_RID_CNFRTSTHRESHOLD0		0xFC97
+#define HERMES_RID_CNFRTSTHRESHOLD1		0xFC98
+#define HERMES_RID_CNFRTSTHRESHOLD2		0xFC99
+#define HERMES_RID_CNFRTSTHRESHOLD3		0xFC9A
+#define HERMES_RID_CNFRTSTHRESHOLD4		0xFC9B
+#define HERMES_RID_CNFRTSTHRESHOLD5		0xFC9C
+#define HERMES_RID_CNFRTSTHRESHOLD6		0xFC9D
+#define HERMES_RID_CNFSHORTPREAMBLE		0xFCB0
+#define HERMES_RID_CNFWEPKEYS_AGERE		0xFCB0	/* used */
+#define HERMES_RID_CNFEXCLUDELONGPREAMBLE	0xFCB1
+#define HERMES_RID_CNFTXKEY_AGERE		0xFCB1	/* used */
+#define HERMES_RID_CNFAUTHENTICATIONRSPTO	0xFCB2
+#define HERMES_RID_CNFBASICRATES		0xFCB3
+#define HERMES_RID_CNFSUPPORTEDRATES		0xFCB4
+#define HERMES_RID_CNFTICKTIME			0xFCE0	/* used */
+#define HERMES_RID_CNFSCANREQUEST		0xFCE1
+#define HERMES_RID_CNFJOINREQUEST		0xFCE2
+#define HERMES_RID_CNFAUTHENTICATESTATION	0xFCE3
+#define HERMES_RID_CNFCHANNELINFOREQUEST	0xFCE4
+
+/*
+ * Information RIDs
+ */
+#define HERMES_RID_MAXLOADTIME			0xFD00
+#define HERMES_RID_DOWNLOADBUFFER		0xFD01
+#define HERMES_RID_PRIID			0xFD02
+#define HERMES_RID_PRISUPRANGE			0xFD03
+#define HERMES_RID_CFIACTRANGES			0xFD04
+#define HERMES_RID_NICSERNUM			0xFD0A
+#define HERMES_RID_NICID			0xFD0B
+#define HERMES_RID_MFISUPRANGE			0xFD0C
+#define HERMES_RID_CFISUPRANGE			0xFD0D
+#define HERMES_RID_CHANNELLIST			0xFD10	/* used */
+#define HERMES_RID_REGULATORYDOMAINS		0xFD11
+#define HERMES_RID_TEMPTYPE			0xFD12
+#define HERMES_RID_CIS				0xFD13
+#define HERMES_RID_STAID			0xFD20	/* used */
+#define HERMES_RID_STASUPRANGE			0xFD21
+#define HERMES_RID_MFIACTRANGES			0xFD22
+#define HERMES_RID_CFIACTRANGES2		0xFD23
+#define HERMES_RID_SECONDARYVERSION_SYMBOL	0xFD24	/* used */
+#define HERMES_RID_PORTSTATUS			0xFD40
+#define HERMES_RID_CURRENTSSID			0xFD41	/* used */
+#define HERMES_RID_CURRENTBSSID			0xFD42	/* used */
+#define HERMES_RID_COMMSQUALITY			0xFD43	/* used */
+#define HERMES_RID_CURRENTTXRATE		0xFD44	/* used */
+#define HERMES_RID_CURRENTBEACONINTERVAL	0xFD45
+#define HERMES_RID_CURRENTSCALETHRESHOLDS	0xFD46
+#define HERMES_RID_PROTOCOLRSPTIME		0xFD47
+#define HERMES_RID_SHORTRETRYLIMIT		0xFD48	/* used */
+#define HERMES_RID_LONGRETRYLIMIT		0xFD49	/* used */
+#define HERMES_RID_MAXTRANSMITLIFETIME		0xFD4A	/* used */
+#define HERMES_RID_MAXRECEIVELIFETIME		0xFD4B
+#define HERMES_RID_CFPOLLABLE			0xFD4C
+#define HERMES_RID_AUTHENTICATIONALGORITHMS	0xFD4D
+#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED	0xFD4F
+#define HERMES_RID_CURRENTTXRATE1		0xFD80
+#define HERMES_RID_CURRENTTXRATE2		0xFD81
+#define HERMES_RID_CURRENTTXRATE3		0xFD82
+#define HERMES_RID_CURRENTTXRATE4		0xFD83
+#define HERMES_RID_CURRENTTXRATE5		0xFD84
+#define HERMES_RID_CURRENTTXRATE6		0xFD85
+#define HERMES_RID_OWNMACADDR			0xFD86
+#define HERMES_RID_SCANRESULTSTABLE		0xFD88
+#define HERMES_RID_PHYTYPE			0xFDC0
+#define HERMES_RID_CURRENTCHANNEL		0xFDC1	/* used */
+#define HERMES_RID_CURRENTPOWERSTATE		0xFDC2
+#define HERMES_RID_CCAMODE			0xFDC3
+#define HERMES_RID_SUPPORTEDDATARATES		0xFDC6	/* used */
+#define HERMES_RID_BUILDSEQ			0xFFFE
+#define HERMES_RID_FWID				0xFFFF
+
+/* "ID" structure - used for ESSID and station nickname */
+struct hermes_idstring {
+	u16 len;
+	u16 val[16];
+} __attribute__ ((packed));
+
+typedef struct hermes_multicast {
+	u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
+} __attribute__ ((packed)) hermes_multicast_t;
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/ieee802_11.h linux/drivers/net/wireless/ieee802_11.h
--- linux.orig/drivers/net/wireless/ieee802_11.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/net/wireless/ieee802_11.h	Mon Jan 14 16:18:06 2002
@@ -0,0 +1,67 @@
+#ifndef _IEEE802_11_H
+#define _IEEE802_11_H
+
+struct ieee802_11_hdr {
+	u16 frame_ctl;
+	u16 duration_id;
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+	u8 addr3[ETH_ALEN];
+	u16 seq_ctl;
+	u8 addr4[ETH_ALEN];
+	u16 data_len;
+} __attribute__ ((packed));
+
+/* Frame control field constants */
+#define IEEE802_11_FCTL_VERS		0x0002
+#define IEEE802_11_FCTL_FTYPE		0x000c
+#define IEEE802_11_FCTL_STYPE		0x00f0
+#define IEEE802_11_FCTL_TODS		0x0100
+#define IEEE802_11_FCTL_FROMDS		0x0200
+#define IEEE802_11_FCTL_MOREFRAGS	0x0400
+#define IEEE802_11_FCTL_RETRY		0x0800
+#define IEEE802_11_FCTL_PM		0x1000
+#define IEEE802_11_FCTL_MOREDATA	0x2000
+#define IEEE802_11_FCTL_WEP		0x4000
+#define IEEE802_11_FCTL_ORDER		0x8000
+
+#define IEEE802_11_FTYPE_MGMT		0x0000
+#define IEEE802_11_FTYPE_CTL		0x0004
+#define IEEE802_11_FTYPE_DATA		0x0008
+
+/* management */
+#define IEEE802_11_STYPE_ASSOC_REQ	0x0000
+#define IEEE802_11_STYPE_ASSOC_RESP 	0x0010
+#define IEEE802_11_STYPE_REASSOC_REQ	0x0020
+#define IEEE802_11_STYPE_REASSOC_RESP	0x0030
+#define IEEE802_11_STYPE_PROBE_REQ	0x0040
+#define IEEE802_11_STYPE_PROBE_RESP	0x0050
+#define IEEE802_11_STYPE_BEACON		0x0080
+#define IEEE802_11_STYPE_ATIM		0x0090
+#define IEEE802_11_STYPE_DISASSOC	0x00A0
+#define IEEE802_11_STYPE_AUTH		0x00B0
+#define IEEE802_11_STYPE_DEAUTH		0x00C0
+
+/* control */
+#define IEEE802_11_STYPE_PSPOLL		0x00A0
+#define IEEE802_11_STYPE_RTS		0x00B0
+#define IEEE802_11_STYPE_CTS		0x00C0
+#define IEEE802_11_STYPE_ACK		0x00D0
+#define IEEE802_11_STYPE_CFEND		0x00E0
+#define IEEE802_11_STYPE_CFENDACK	0x00F0
+
+/* data */
+#define IEEE802_11_STYPE_DATA		0x0000
+#define IEEE802_11_STYPE_DATA_CFACK	0x0010
+#define IEEE802_11_STYPE_DATA_CFPOLL	0x0020
+#define IEEE802_11_STYPE_DATA_CFACKPOLL	0x0030
+#define IEEE802_11_STYPE_NULLFUNC	0x0040
+#define IEEE802_11_STYPE_CFACK		0x0050
+#define IEEE802_11_STYPE_CFPOLL		0x0060
+#define IEEE802_11_STYPE_CFACKPOLL	0x0070
+
+#define IEEE802_11_SCTL_FRAG		0x000F
+#define IEEE802_11_SCTL_SEQ		0xFFF0
+
+#endif /* _IEEE802_11_H */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/orinoco.c linux/drivers/net/wireless/orinoco.c
--- linux.orig/drivers/net/wireless/orinoco.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/orinoco.c	Mon Jan 21 19:01:05 2002
@@ -1,4 +1,4 @@
-/* orinoco.c 0.08a	- (formerly known as dldwd_cs.c and orinoco_cs.c)
+/* orinoco.c 0.09b	- (formerly known as dldwd_cs.c and orinoco_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -197,18 +197,67 @@
  *	o Fixed bad bug in WEP key handling on Intersil and Symbol firmware,
  *	  which led to an instant crash on big-endian machines.
  *
- * TODO - Jean II
- *	o inline functions (lots of candidate, need to reorder code)
- *	o Test PrismII/Symbol cards & firmware versions
- *	o Mini-PCI support (some people have reported success - JII)
+ * v0.08a -> v0.08b - 20/11/2001 - David Gibson
+ *	o Lots of cleanup and bugfixes in orinoco_plx.c
+ *	o Cleanup to handling of Tx rate setting.
+ *	o Removed support for old encapsulation method.
+ *	o Removed old "dldwd" names.
+ *	o Split RID constants into a new file hermes_rid.h
+ *	o Renamed RID constants to match linux-wlan-ng and prism2.o
+ *	o Bugfixes in hermes.c
+ *	o Poke the PLX's INTCSR register, so it actually starts
+ *	  generating interrupts.  These cards might actually work now.
+ *	o Update to wireless extensions v12 (Jean II)
+ *	o Support for tallies and inquire command (Jean II)
+ *	o Airport updates for newer PPC kernels (BenH)
+ *
+ * v0.08b -> v0.09 - 21/12/2001 - David Gibson
+ *	o Some new PCI IDs for PLX cards.
+ *	o Removed broken attempt to do ALLMULTI reception.  Just use
+ *	  promiscuous mode instead
+ *	o Preliminary work for list-AP (Jean II)
+ *	o Airport updates from (BenH)
+ *	o Eliminated racy hw_ready stuff
+ *	o Fixed generation of fake events in irq handler.  This should
+ *	  finally kill the EIO problems (Jean II & dgibson)
+ *	o Fixed breakage of bitrate set/get on Agere firmware (Jean II)
+ *
+ * v0.09 -> v0.09a - 2/1/2002 - David Gibson
+ *	o Fixed stupid mistake in multicast list handling, triggering
+ *	  a BUG()
+ *
+ * v0.09a -> v0.09b - 16/1/2002 - David Gibson
+ *	o Fixed even stupider mistake in new interrupt handling, which
+ *	  seriously broke things on big-endian machines.
+ *	o Removed a bunch of redundand includes and exports.
+ *	o Removed a redundant MOD_{INC,DEC}_USE_COUNT pair in airport.c
+ *	o Don't attempt to do hardware level multicast reception on
+ *	  Intersil firmware, just go promisc instead.
+ *	o Typo fixed in hermes_issue_cmd()
+ *	o Eliminated WIRELESS_SPY #ifdefs
+ *	o Status code reported on Tx exceptions
+ *	o Moved netif_wake_queue() from ALLOC interrupts to TX and TXEXC
+ *	  interrupts, which should fix the timeouts we're seeing.
+ *
+ * TODO
  *	o Find and kill remaining Tx timeout problems
+ *	o Fix WEP / order of iwconfig wierdness on Intersil firmware
+ *	o Convert /proc debugging stuff to seqfile
+ *	o Re-assess our encapsulation detection strategy
+ *	o Handle de-encapsulation within NET framework, provide 802.11
+ *	  headers
+ *	o Fix possible races in SPY handling.
+ *	o Take the xmit lock when calling orinoco_reset from an
+ *	  ioctl(), because that protects the multicast list.
+ *	o Fix allocation lengths.
  */
 /* Notes on locking:
  *
  * The basic principle of operation is that everything except the
  * interrupt handler is serialized through a single spinlock in the
- * dldwd_priv_t structure, using dldwd_lock() and
- * dldwd_unlock() (which in turn use spin_lock_bh() and spin_unlock_bh()).
+ * struct orinoco_private structure, using dldwd_lock() and
+ * dldwd_unlock() (which in turn use spin_lock_bh() and
+ * spin_unlock_bh()).
  *
  * The kernel's IRQ handling stuff ensures that the interrupt handler
  * does not re-enter itself. The interrupt handler is written such
@@ -216,10 +265,11 @@
  * that the Rx path uses one of the Hermes chipset's BAPs while
  * everything else uses the other.
  *
- * Actually, the current updating of the statistics from the interrupt
- * handler is unsafe.  However all it can do is perturb the
- * packet/byte counts slightly, so we just put up with it.  We could
- * fix this to use atomic types, but it's probably not worth it.
+ * Actually, strictly speaking, the updating of the statistics from
+ * the interrupt handler isn't safe without a lock.  However the worst
+ * that can happen is that we perturb the packet/byte counts slightly.
+ * We could fix this to use atomic types, but it's probably not worth
+ * it.
  *
  * The big exception is that that we don't want the irq handler
  * running when we actually reset or shut down the card, because
@@ -251,33 +301,63 @@
 #include <linux/wireless.h>
 #include <linux/list.h>
 
-#include <pcmcia/version.h>
-#include <pcmcia/cs_types.h>
-#include <pcmcia/cs.h>
-#include <pcmcia/cistpl.h>
-#include <pcmcia/cisreg.h>
-#include <pcmcia/ds.h>
-#include <pcmcia/bus_ops.h>
-
 #include "hermes.h"
+#include "hermes_rid.h"
 #include "orinoco.h"
+#include "ieee802_11.h"
+
+/* Wireless extensions backwares compatibility */
+#ifndef SIOCIWFIRSTPRIV
+#define SIOCIWFIRSTPRIV		SIOCDEVPRIVATE
+#endif /* SIOCIWFIRSTPRIV */
+
+/* We do this this way to avoid ifdefs in the actual code */
+#ifdef WIRELESS_SPY
+#define SPY_NUMBER(priv)	(priv->spy_number)
+#else
+#define SPY_NUMBER(priv)	0
+#endif /* WIRELESS_SPY */
 
-static char version[] __initdata = "orinoco.c 0.08a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco.c 0.09b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards");
+#ifdef MODULE_LICENSE
 MODULE_LICENSE("Dual MPL/GPL");
+#endif
 
 /* Level of debugging. Used in the macros in orinoco.h */
 #ifdef ORINOCO_DEBUG
-int dldwd_debug = ORINOCO_DEBUG;
-MODULE_PARM(dldwd_debug, "i");
+int orinoco_debug = ORINOCO_DEBUG;
+MODULE_PARM(orinoco_debug, "i");
 #endif
 
-int use_old_encaps = 0;
-MODULE_PARM(use_old_encaps, "i");
+#define ORINOCO_MIN_MTU		256
+#define ORINOCO_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
 
 #define SYMBOL_MAX_VER_LEN	(14)
+#define LTV_BUF_SIZE		128
+#define USER_BAP		0
+#define IRQ_BAP			1
+#define ORINOCO_MACPORT		0
+#define MAX_IRQLOOPS_PER_IRQ	10
+#define MAX_IRQLOOPS_PER_JIFFY	(20000/HZ)	/* Based on a guestimate of how many events the
+						   device can legitimately generate */
+#define TX_NICBUF_SIZE		2048
+#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
+#define LARGE_KEY_SIZE		13
+#define SMALL_KEY_SIZE		5
+#define MAX_FRAME_SIZE		2304
 
+#define DUMMY_FID		0xFFFF
+
+#define MAX_MULTICAST(priv)	(priv->firmware_type == FIRMWARE_TYPE_AGERE ? \
+				 HERMES_MAX_MULTICAST : 0)
+
+/*
+ * Data tables
+ */
+
+/* The frequency of each channel in MHz */
 const long channel_frequency[] = {
 	2412, 2417, 2422, 2427, 2432, 2437, 2442,
 	2447, 2452, 2457, 2462, 2467, 2472, 2484
@@ -285,39 +365,24 @@
 
 #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
 
-/* This tables gives the actual meanings of the bitrate IDs returned by the firmware.
-   It gives the rate in halfMb/s, negative indicates auto mode */
-const int rate_list[] = { 0, 2, 4, -22, 11, 22, -4, -11, 0, 0, 0, 0};
-
-#define NUM_RATES (sizeof(rate_list) / sizeof(rate_list[0]))
-
-struct p80211_hdr {
-	u16 frame_ctl;
-	u16 duration_id;
-	u8 addr1[ETH_ALEN];
-	u8 addr2[ETH_ALEN];
-	u8 addr3[ETH_ALEN];
-	u16 seq_ctl;
-	u8 addr4[ETH_ALEN];
-	u16 data_len;
-} __attribute__ ((packed));
+/* This tables gives the actual meanings of the bitrate IDs returned by the firmware. */
+struct {
+	int bitrate; /* in 100s of kilbits */
+	int automatic;
+	u16 agere_txratectrl;
+	u16 intersil_txratectrl;
+} bitrate_table[] = {
+	{110, 1,  3, 15}, /* Entry 0 is the default */
+	{10,  0,  1,  1},
+	{10,  1,  1,  1},
+	{20,  0,  2,  2},
+	{20,  1,  6,  3},
+	{55, 0,  4,  4},
+	{55, 1,  7,  7},
+	{110, 0,  5,  8},
+};
 
-/* Frame control field constants */
-#define DLDWD_FCTL_VERS			0x0002
-#define DLDWD_FCTL_FTYPE		0x000c
-#define DLDWD_FCTL_STYPE		0x00f0
-#define DLDWD_FCTL_TODS			0x0100
-#define DLDWD_FCTL_FROMDS		0x0200
-#define DLDWD_FCTL_MOREFRAGS		0x0400
-#define DLDWD_FCTL_RETRY		0x0800
-#define DLDWD_FCTL_PM			0x1000
-#define DLDWD_FCTL_MOREDATA		0x2000
-#define DLDWD_FCTL_WEP			0x4000
-#define DLDWD_FCTL_ORDER		0x8000
-
-#define DLDWD_FTYPE_MGMT		0x0000
-#define DLDWD_FTYPE_CTL			0x0004
-#define DLDWD_FTYPE_DATA		0x0008
+#define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0]))
 
 struct p8022_hdr {
 	u8 dsap;
@@ -326,16 +391,24 @@
 	u8 oui[3];
 } __attribute__ ((packed));
 
-struct dldwd_frame_hdr {
-	hermes_frame_desc_t desc;
-	struct p80211_hdr p80211;
+struct orinoco_rxframe_hdr {
+	struct hermes_rx_descriptor desc;
+	struct ieee802_11_hdr p80211;
 	struct ethhdr p8023;
 	struct p8022_hdr p8022;
 	u16 ethertype;
 } __attribute__ ((packed));
 
-#define P8023_OFFSET		(sizeof(hermes_frame_desc_t) + \
-				sizeof(struct p80211_hdr))
+struct orinoco_txframe_hdr {
+	struct hermes_tx_descriptor desc;
+	struct ieee802_11_hdr p80211;
+	struct ethhdr p8023;
+	struct p8022_hdr p8022;
+	u16 ethertype;
+} __attribute__ ((packed));
+
+#define P8023_OFFSET		(sizeof(struct hermes_rx_descriptor) + \
+				sizeof(struct ieee802_11_hdr))
 #define ENCAPS_OVERHEAD		(sizeof(struct p8022_hdr) + 2)
 
 /* 802.2 LLL header SNAP used for SNAP encapsulation over 802.11 */
@@ -343,114 +416,109 @@
 	0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}
 };
 
-struct p8022_hdr old_encaps_hdr = {
-	0xaa, 0xaa, 0x03, {0x00, 0x00, 0xf8}
-};
-
-/* How many times to retry if we get an EIO reading the BAP in the Rx path */
-#define RX_EIO_RETRY		10
-
-typedef struct dldwd_commsqual {
+typedef struct orinoco_commsqual {
 	u16 qual, signal, noise;
-} __attribute__ ((packed)) dldwd_commsqual_t;
+} __attribute__ ((packed)) orinoco_commsqual_t;
 
 /*
  * Function prototypes
  */
 
-static void dldwd_stat_gather(struct net_device *dev,
+static void orinoco_stat_gather(struct net_device *dev,
 			      struct sk_buff *skb,
-			      struct dldwd_frame_hdr *hdr);
+			      struct orinoco_rxframe_hdr *hdr);
 
-static struct net_device_stats *dldwd_get_stats(struct net_device *dev);
-static struct iw_statistics *dldwd_get_wireless_stats(struct net_device *dev);
+static struct net_device_stats *orinoco_get_stats(struct net_device *dev);
+static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev);
 
 /* Hardware control routines */
 
-static int __dldwd_hw_reset(dldwd_priv_t *priv);
-static int __dldwd_hw_setup_wep(dldwd_priv_t *priv);
-static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN]);
-static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active, char buf[IW_ESSID_MAX_SIZE+1]);
-static long dldwd_hw_get_freq(dldwd_priv_t *priv);
-static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
-				    s32 *rates, int max);
+static int __orinoco_hw_reset(struct orinoco_private *priv);
+static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
+static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
+static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]);
+static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
+			      char buf[IW_ESSID_MAX_SIZE+1]);
+static long orinoco_hw_get_freq(struct orinoco_private *priv);
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
+				    int32_t *rates, int max);
 
 /* Interrupt handling routines */
-static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw);
-static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw);
-
-static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
-static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
-static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
-static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
-static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
-static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
-static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
-static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
-static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
-static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
-static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
-static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
-static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
-static void __dldwd_set_multicast_list(struct net_device *dev);
+static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw);
+static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw);
+
+static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq);
+static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
+static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
+static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
+static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
+static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
+static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
+static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
+static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
+static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
+static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
+static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq);
+static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq);
+static void __orinoco_set_multicast_list(struct net_device *dev);
 
 /* /proc debugging stuff */
-static int dldwd_proc_init(void);
-static void dldwd_proc_cleanup(void);
+static int orinoco_proc_init(void);
+static void orinoco_proc_cleanup(void);
 
 /*
  * Inline functions
  */
 static inline void
-dldwd_lock(dldwd_priv_t *priv)
+orinoco_lock(struct orinoco_private *priv)
 {
 	spin_lock_bh(&priv->lock);
 }
 
 static inline void
-dldwd_unlock(dldwd_priv_t *priv)
+orinoco_unlock(struct orinoco_private *priv)
 {
 	spin_unlock_bh(&priv->lock);
 }
 
 static inline int
-dldwd_irqs_allowed(dldwd_priv_t *priv)
+orinoco_irqs_allowed(struct orinoco_private *priv)
 {
-	return test_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	return test_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 }
 
 static inline void
-__dldwd_stop_irqs(dldwd_priv_t *priv)
+__orinoco_stop_irqs(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
 
 	hermes_set_irqmask(hw, 0);
-	clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
-	while (test_bit(DLDWD_STATE_INIRQ, &priv->state))
+	clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
+	while (test_bit(ORINOCO_STATE_INIRQ, &priv->state))
 		;
 }
 
 static inline void
-__dldwd_start_irqs(dldwd_priv_t *priv, u16 irqmask)
+__orinoco_start_irqs(struct orinoco_private *priv, u16 irqmask)
 {
 	hermes_t *hw = &priv->hw;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	__cli();
-	set_bit(DLDWD_STATE_DOIRQ, &priv->state);
+	set_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 	hermes_set_irqmask(hw, irqmask);
 	__sti();
 
@@ -458,7 +526,7 @@
 }
 
 static inline void
-set_port_type(dldwd_priv_t *priv)
+set_port_type(struct orinoco_private *priv)
 {
 	switch (priv->iw_mode) {
 	case IW_MODE_INFRA:
@@ -481,13 +549,13 @@
 }
 
 extern void
-dldwd_set_multicast_list(struct net_device *dev)
+orinoco_set_multicast_list(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 
-	dldwd_lock(priv);
-	__dldwd_set_multicast_list(dev);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	__orinoco_set_multicast_list(dev);
+	orinoco_unlock(priv);
 }
 
 /*
@@ -495,63 +563,54 @@
  */
 
 static int
-__dldwd_hw_reset(dldwd_priv_t *priv)
+__orinoco_hw_reset(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
-	int err;
 
-	if (! priv->broken_reset)
-		return hermes_reset(hw);
-	else {
-		hw->inten = 0;
-		hermes_write_regn(hw, INTEN, 0);
-		err = hermes_disable_port(hw, 0);
-		hermes_write_regn(hw, EVACK, 0xffff);
-		return err;
-	}
+	return hermes_reset(hw);
 }
 
 void
-dldwd_shutdown(dldwd_priv_t *priv)
+orinoco_shutdown(struct orinoco_private *priv)
 {
 /* 	hermes_t *hw = &priv->hw; */
 	int err = 0;
 
 	TRACE_ENTER(priv->ndev.name);
 
-	dldwd_lock(priv);
-	__dldwd_stop_irqs(priv);
+	orinoco_lock(priv);
+	__orinoco_stop_irqs(priv);
 
-	err = __dldwd_hw_reset(priv);
+	err = __orinoco_hw_reset(priv);
 	if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */
 		printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev.name, err);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 }
 
 int
-dldwd_reset(dldwd_priv_t *priv)
+orinoco_reset(struct orinoco_private *priv)
 {
 	struct net_device *dev = &priv->ndev;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t idbuf;
+	struct hermes_idstring idbuf;
 	int frame_size;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	/* Stop other people bothering us */
-	dldwd_lock(priv);
-	__dldwd_stop_irqs(priv);
+	orinoco_lock(priv);
+	__orinoco_stop_irqs(priv);
 
 	/* Check if we need a card reset */
 	if((priv->need_card_reset) && (priv->card_reset_handler != NULL))
 		priv->card_reset_handler(priv);
 
 	/* Do standard firmware reset if we can */
-	err = __dldwd_hw_reset(priv);
+	err = __orinoco_hw_reset(priv);
 	if (err)
 		goto out;
 
@@ -568,11 +627,11 @@
 	
 	/* Set up the link mode */
 	
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PORTTYPE, priv->port_type);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type);
 	if (err)
 		goto out;
 	if (priv->has_ibss) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CREATEIBSS,
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS,
 					   priv->allow_ibss);
 		if (err)
 			goto out;
@@ -588,7 +647,7 @@
 
 	/* Set up encryption */
 	if (priv->has_wep) {
-		err = __dldwd_hw_setup_wep(priv);
+		err = __orinoco_hw_setup_wep(priv);
 		if (err) {
 			printk(KERN_ERR "%s: Error %d activating WEP.\n",
 			       dev->name, err);
@@ -600,7 +659,7 @@
 	idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
 	memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
 	err = hermes_write_ltv(hw, USER_BAP, (priv->port_type == 3) ?
-			       HERMES_RID_CNF_OWN_SSID : HERMES_RID_CNF_DESIRED_SSID,
+			       HERMES_RID_CNFOWNSSID : HERMES_RID_CNFDESIREDSSID,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2),
 			       &idbuf);
 	if (err)
@@ -609,58 +668,62 @@
 	/* Set the station name */
 	idbuf.len = cpu_to_le16(strlen(priv->nick));
 	memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val));
-	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+	err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 			       HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2),
 			       &idbuf);
 	if (err)
 		goto out;
 
 	/* Set the channel/frequency */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_CHANNEL, priv->channel);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel);
 	if (err)
 		goto out;
 
 	/* Set AP density */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, priv->ap_density);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, priv->ap_density);
 	if (err)
 		goto out;
 
 	/* Set RTS threshold */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, priv->rts_thresh);
+	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh);
 	if (err)
 		goto out;
 
 	/* Set fragmentation threshold or MWO robustness */
 	if (priv->has_mwo)
 		err = hermes_write_wordrec(hw, USER_BAP,
-					   HERMES_RID_CNF_MWO_ROBUST, priv->mwo_robust);
+					   HERMES_RID_CNFMWOROBUST_AGERE,
+					   priv->mwo_robust);
 	else
 		err = hermes_write_wordrec(hw, USER_BAP,
-					   HERMES_RID_CNF_FRAG_THRESH, priv->frag_thresh);
+					   HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+					   priv->frag_thresh);
 	if (err)
 		goto out;
 
 	/* Set bitrate */
-	err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL,
-				   priv->tx_rate_ctrl);
+	err = __orinoco_hw_set_bitrate(priv);
 	if (err)
 		goto out;
 
 	/* Set power management */
 	if (priv->has_pm) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE,
+		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED,
 					   priv->pm_on);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFMULTICASTRECEIVE,
 					   priv->pm_mcast);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFMAXSLEEPDURATION,
 					   priv->pm_period);
 		if (err)
 			goto out;
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPMHOLDOVERDURATION,
 					   priv->pm_timeout);
 		if (err)
 			goto out;
@@ -668,7 +731,8 @@
 
 	/* Set preamble - only for Symbol so far... */
 	if (priv->has_preamble) {
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_PREAMBLE,
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPREAMBLE_SYMBOL,
 					   priv->preamble);
 		if (err) {
 			printk(KERN_WARNING "%s: Can't set preamble!\n", dev->name);
@@ -678,28 +742,62 @@
 
 	/* Set promiscuity / multicast*/
 	priv->promiscuous = 0;
-	priv->allmulti = 0;
 	priv->mc_count = 0;
-	__dldwd_set_multicast_list(dev);
-	
-	err = hermes_enable_port(hw, DLDWD_MACPORT);
-	if (err)
-		goto out;
+	__orinoco_set_multicast_list(dev);
 	
-	__dldwd_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
+	__orinoco_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC |
 			   HERMES_EV_TX | HERMES_EV_TXEXC |
 			   HERMES_EV_WTERR | HERMES_EV_INFO |
 			   HERMES_EV_INFDROP);
 
+	err = hermes_enable_port(hw, ORINOCO_MACPORT);
+	if (err)
+		goto out;
+
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 
 	return err;
 }
 
-static int __dldwd_hw_setup_wep(dldwd_priv_t *priv)
+static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
+{
+	hermes_t *hw = &priv->hw;
+	int err = 0;
+
+	TRACE_ENTER(priv->ndev.name);
+
+	if (priv->bitratemode >= BITRATE_TABLE_SIZE) {
+		printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n",
+		       priv->ndev.name, priv->bitratemode);
+		return -EINVAL;
+	}
+
+	switch (priv->firmware_type) {
+	case FIRMWARE_TYPE_AGERE:
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFTXRATECONTROL,
+					   bitrate_table[priv->bitratemode].agere_txratectrl);
+		break;
+	case FIRMWARE_TYPE_INTERSIL:
+	case FIRMWARE_TYPE_SYMBOL:
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFTXRATECONTROL,
+					   bitrate_table[priv->bitratemode].intersil_txratectrl);
+		break;
+	default:
+		BUG();
+	}
+
+	TRACE_EXIT(priv->ndev.name);
+
+	return err;
+}
+
+
+static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
@@ -709,17 +807,23 @@
 	TRACE_ENTER(priv->ndev.name);
 
 	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style WEP */
+	case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
 		if (priv->wep_on) {
-			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_KEY, priv->tx_key);
+			err = hermes_write_wordrec(hw, USER_BAP,
+						   HERMES_RID_CNFTXKEY_AGERE,
+						   priv->tx_key);
 			if (err)
 				return err;
 			
-			err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_KEYS, &priv->keys);
+			err = HERMES_WRITE_RECORD(hw, USER_BAP,
+						  HERMES_RID_CNFWEPKEYS_AGERE,
+						  &priv->keys);
 			if (err)
 				return err;
 		}
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_WEP_ON, priv->wep_on);
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFWEPENABLED_AGERE,
+					   priv->wep_on);
 		if (err)
 			return err;
 		break;
@@ -728,15 +832,15 @@
 	case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
 		master_wep_flag = 0;		/* Off */
 		if (priv->wep_on) {
-/*  			int keylen; */
+			int keylen;
 			int i;
 
 			/* Fudge around firmware weirdness */
-/*  			keylen = priv->keys[priv->tx_key].len; */
+			keylen = le16_to_cpu(priv->keys[priv->tx_key].len);
 
 			/* Write all 4 keys */
-			for(i = 0; i < MAX_KEYS; i++) {
-				int keylen = le16_to_cpu(priv->keys[i].len);
+			for(i = 0; i < ORINOCO_MAX_KEYS; i++) {
+/*  				int keylen = le16_to_cpu(priv->keys[i].len); */
 
 				if (keylen > LARGE_KEY_SIZE) {
 					printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n",
@@ -747,7 +851,7 @@
 				printk("About to write key %d, keylen=%d\n",
 				       i, keylen);				     
 				err = hermes_write_ltv(hw, USER_BAP,
-						       HERMES_RID_CNF_INTERSIL_KEY0 + i,
+						       HERMES_RID_CNFDEFAULTKEY0 + i,
 						       HERMES_BYTES_TO_RECLEN(keylen),
 						       priv->keys[i].data);
 				if (err)
@@ -755,7 +859,7 @@
 			}
 
 			/* Write the index of the key used in transmission */
-			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_TX_KEY,
+			err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID,
 						   priv->tx_key);
 			if (err)
 				return err;
@@ -771,7 +875,8 @@
 					auth_flag = 2;
 				else
 					auth_flag = 1;
-				err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_AUTH_TYPE, auth_flag);
+				err = hermes_write_wordrec(hw, USER_BAP,
+							   HERMES_RID_CNFAUTHENTICATION, auth_flag);
 				if (err)
 					return err;
 				/* Master WEP setting is always 3 */
@@ -787,7 +892,9 @@
 		}
 		
 		/* Master WEP setting : on/off */
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_INTERSIL_WEP_ON, master_wep_flag);
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFWEPFLAGS_INTERSIL,
+					   master_wep_flag);
 		if (err)
 			return err;	
 
@@ -806,48 +913,48 @@
 	return 0;
 }
 
-static int dldwd_hw_get_bssid(dldwd_priv_t *priv, char buf[ETH_ALEN])
+static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN])
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_BSSID,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
 			      ETH_ALEN, NULL, buf);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_hw_get_essid(dldwd_priv_t *priv, int *active,
+static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
 			      char buf[IW_ESSID_MAX_SIZE+1])
 {
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t essidbuf;
+	struct hermes_idstring essidbuf;
 	char *p = (char *)(&essidbuf.val);
 	int len;
 
 	TRACE_ENTER(priv->ndev.name);
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (strlen(priv->desired_essid) > 0) {
 		/* We read the desired SSID from the hardware rather
 		   than from priv->desired_essid, just in case the
 		   firmware is allowed to change it on us. I'm not
 		   sure about this */
-		/* My guess is that the OWN_SSID should always be whatever
+		/* My guess is that the OWNSSID should always be whatever
 		 * we set to the card, whereas CURRENT_SSID is the one that
 		 * may change... - Jean II */
 		u16 rid;
 
 		*active = 1;
 
-		rid = (priv->port_type == 3) ? HERMES_RID_CNF_OWN_SSID :
-			HERMES_RID_CNF_DESIRED_SSID;
+		rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID :
+			HERMES_RID_CNFDESIREDSSID;
 		
 		err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf),
 				      NULL, &essidbuf);
@@ -856,7 +963,7 @@
 	} else {
 		*active = 0;
 
-		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_SSID,
+		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID,
 				      sizeof(essidbuf), NULL, &essidbuf);
 		if (err)
 			goto fail_unlock;
@@ -869,14 +976,14 @@
 	buf[len] = '\0';
 
  fail_unlock:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	TRACE_EXIT(priv->ndev.name);
 
 	return err;       
 }
 
-static long dldwd_hw_get_freq(dldwd_priv_t *priv)
+static long orinoco_hw_get_freq(struct orinoco_private *priv)
 {
 	
 	hermes_t *hw = &priv->hw;
@@ -884,9 +991,9 @@
 	u16 channel;
 	long freq = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_CHANNEL, &channel);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel);
 	if (err)
 		goto out;
 
@@ -901,27 +1008,27 @@
 	freq = channel_frequency[channel-1] * 100000;
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	if (err > 0)
 		err = -EBUSY;
 	return err ? err : freq;
 }
 
-static int dldwd_hw_get_bitratelist(dldwd_priv_t *priv, int *numrates,
-				    s32 *rates, int max)
+static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
+				    int32_t *rates, int max)
 {
 	hermes_t *hw = &priv->hw;
-	hermes_id_t list;
+	struct hermes_idstring list;
 	unsigned char *p = (unsigned char *)&list.val;
 	int err = 0;
 	int num;
 	int i;
 
-	dldwd_lock(priv);
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_DATARATES, sizeof(list),
-			      NULL, &list);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES,
+			      sizeof(list), NULL, &list);
+	orinoco_unlock(priv);
 
 	if (err)
 		return err;
@@ -937,19 +1044,20 @@
 	return 0;
 }
 
+#if 0
 #ifndef ORINOCO_DEBUG
-static inline void show_rx_frame(struct dldwd_frame_hdr *frame) {}
+static inline void show_rx_frame(struct orinoco_rxframe_hdr *frame) {}
 #else
-static void show_rx_frame(struct dldwd_frame_hdr *frame)
+static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
 {
 	printk(KERN_DEBUG "RX descriptor:\n");
 	printk(KERN_DEBUG "  status      = 0x%04x\n", frame->desc.status);
-	printk(KERN_DEBUG "  res1        = 0x%04x\n", frame->desc.res1);
-	printk(KERN_DEBUG "  res2        = 0x%04x\n", frame->desc.res2);
-	printk(KERN_DEBUG "  q_info      = 0x%04x\n", frame->desc.q_info);
-	printk(KERN_DEBUG "  res3        = 0x%04x\n", frame->desc.res3);
-	printk(KERN_DEBUG "  res4        = 0x%04x\n", frame->desc.res4);
-	printk(KERN_DEBUG "  tx_ctl      = 0x%04x\n", frame->desc.tx_ctl);
+	printk(KERN_DEBUG "  time        = 0x%08x\n", frame->desc.time);
+	printk(KERN_DEBUG "  silence     = 0x%02x\n", frame->desc.silence);
+	printk(KERN_DEBUG "  signal      = 0x%02x\n", frame->desc.signal);
+	printk(KERN_DEBUG "  rate        = 0x%02x\n", frame->desc.rate);
+	printk(KERN_DEBUG "  rxflow      = 0x%02x\n", frame->desc.rxflow);
+	printk(KERN_DEBUG "  reserved    = 0x%08x\n", frame->desc.reserved);
 
 	printk(KERN_DEBUG "IEEE 802.11 header:\n");
 	printk(KERN_DEBUG "  frame_ctl   = 0x%04x\n",
@@ -997,96 +1105,99 @@
 	printk(KERN_DEBUG "  ethertype  = 0x%04x\n", frame->ethertype);
 }
 #endif
+#endif
 
 /*
  * Interrupt handler
  */
-void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs)
+void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev_id;
+	struct orinoco_private *priv = (struct orinoco_private *) dev_id;
 	hermes_t *hw = &priv->hw;
 	struct net_device *dev = &priv->ndev;
-	int count = IRQ_LOOP_MAX;
+	int count = MAX_IRQLOOPS_PER_IRQ;
 	u16 evstat, events;
-	static int old_time = 0, timecount = 0; /* Eugh, revolting hack for now */
+	/* These are used to detect a runaway interrupt situation */
+	/* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy,
+	 * we panic and shut down the hardware */
+	static int last_irq_jiffy = 0; /* jiffies value the last time we were called */
+	static int loops_this_jiffy = 0;
 
-	if (test_and_set_bit(DLDWD_STATE_INIRQ, &priv->state))
+	if (test_and_set_bit(ORINOCO_STATE_INIRQ, &priv->state))
 		BUG();
 
-	if (! dldwd_irqs_allowed(priv)) {
-		clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+	if (! orinoco_irqs_allowed(priv)) {
+		clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
 		return;
 	}
 
-	DEBUG(3, "%s: dldwd_interrupt()\n", priv->ndev.name);
+	DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev.name);
 
-	while (1) {
-		if (jiffies != old_time)
-			timecount = 0;
-		if ( (++timecount > 50) || (! count--) ) {
+	evstat = hermes_read_regn(hw, EVSTAT);
+	events = evstat & hw->inten;
+	
+	if (! events) { /* Sometimes the card generates Tx interrupts without setting EVSTAT,
+			   or so I've heard - FIXME does it really happen? */
+		printk(KERN_WARNING "%s: Null event in orinoco_interrupt!\n", priv->ndev.name);
+		__orinoco_ev_alloc(priv, hw);
+	}
+
+	if (jiffies != last_irq_jiffy)
+		loops_this_jiffy = 0;
+	last_irq_jiffy = jiffies;
+
+	while (events && count--) {
+		DEBUG(4, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n",
+		      count, evstat);
+		
+		if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) {
 			printk(KERN_CRIT "%s: IRQ handler is looping too \
 much! Shutting down.\n",
 			       dev->name);
 			/* Perform an emergency shutdown */
-			clear_bit(DLDWD_STATE_DOIRQ, &priv->state);
+			clear_bit(ORINOCO_STATE_DOIRQ, &priv->state);
 			hermes_set_irqmask(hw, 0);
 			break;
 		}
 
-		evstat = hermes_read_regn(hw, EVSTAT);
-		DEBUG(3, "__dldwd_interrupt(): count=%d EVSTAT=0x%04x inten=0x%04x\n",
-		      count, evstat, hw->inten);
-
-		events = evstat & hw->inten;
-
-		if (! events) {
-			if (netif_queue_stopped(dev)) {
-				/* There seems to be a firmware bug which
-				   sometimes causes the card to give an
-				   interrupt with no event set, when there
-				   sould be a Tx completed event. */
-				DEBUG(3, "%s: Interrupt with no event (ALLOCFID=0x%04x)\n",
-				      dev->name, (int)hermes_read_regn(hw, ALLOCFID));
-				events = HERMES_EV_TX | HERMES_EV_ALLOC;
-			} else /* Nothing's happening, we're done */
-				break;
-		}
-
 		/* Check the card hasn't been removed */
 		if (! hermes_present(hw)) {
-			DEBUG(0, "dldwd_interrupt(): card removed\n");
+			DEBUG(0, "orinoco_interrupt(): card removed\n");
 			break;
 		}
 
 		if (events & HERMES_EV_TICK)
-			__dldwd_ev_tick(priv, hw);
+			__orinoco_ev_tick(priv, hw);
 		if (events & HERMES_EV_WTERR)
-			__dldwd_ev_wterr(priv, hw);
+			__orinoco_ev_wterr(priv, hw);
 		if (events & HERMES_EV_INFDROP)
-			__dldwd_ev_infdrop(priv, hw);
+			__orinoco_ev_infdrop(priv, hw);
 		if (events & HERMES_EV_INFO)
-			__dldwd_ev_info(priv, hw);
+			__orinoco_ev_info(priv, hw);
 		if (events & HERMES_EV_RX)
-			__dldwd_ev_rx(priv, hw);
+			__orinoco_ev_rx(priv, hw);
 		if (events & HERMES_EV_TXEXC)
-			__dldwd_ev_txexc(priv, hw);
+			__orinoco_ev_txexc(priv, hw);
 		if (events & HERMES_EV_TX)
-			__dldwd_ev_tx(priv, hw);
+			__orinoco_ev_tx(priv, hw);
 		if (events & HERMES_EV_ALLOC)
-			__dldwd_ev_alloc(priv, hw);
+			__orinoco_ev_alloc(priv, hw);
 		
 		hermes_write_regn(hw, EVACK, events);
-	}
 
-	clear_bit(DLDWD_STATE_INIRQ, &priv->state);
+		evstat = hermes_read_regn(hw, EVSTAT);
+		events = evstat & hw->inten;
+	};
+
+	clear_bit(ORINOCO_STATE_INIRQ, &priv->state);
 }
 
-static void __dldwd_ev_tick(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
 {
 	printk(KERN_DEBUG "%s: TICK\n", priv->ndev.name);
 }
 
-static void __dldwd_ev_wterr(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw)
 {
 	/* This seems to happen a fair bit under load, but ignoring it
 	   seems to work fine...*/
@@ -1094,53 +1205,111 @@
 	      priv->ndev.name);
 }
 
-static void __dldwd_ev_infdrop(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
 {
 	printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev.name);
 }
 
-static void __dldwd_ev_info(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
 {
-	DEBUG(3, "%s: Information frame received.\n", priv->ndev.name);
-	/* We don't actually do anything about it - we assume the MAC
-	   controller can deal with it */
+	struct net_device *dev = &priv->ndev;
+	u16 infofid;
+	struct {
+		u16 len;
+		u16 type;
+	} __attribute__ ((packed)) info;
+	int err;
+
+	/* This is an answer to an INQUIRE command that we did earlier,
+	 * or an information "event" generated by the card
+	 * The controller return to us a pseudo frame containing
+	 * the information in question - Jean II */
+	infofid = hermes_read_regn(hw, INFOFID);
+	DEBUG(3, "%s: __dldwd_ev_info(): INFOFID=0x%04x\n", dev->name,
+	      infofid);
+
+	/* Read the info frame header - don't try too hard */
+	err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info),
+			       infofid, 0);
+	if (err) {
+		printk(KERN_ERR "%s: error %d reading info frame. "
+		       "Frame dropped.\n", dev->name, err);
+		return;
+	}
+	
+	switch (le16_to_cpu(info.type)) {
+	case HERMES_INQ_TALLIES: {
+		struct hermes_tallies_frame tallies;
+		struct iw_statistics *wstats = &priv->wstats;
+		int len = le16_to_cpu(info.len) - 1;
+		
+		if (len > (sizeof(tallies) / 2)) {
+			DEBUG(1, "%s: tallies frame too long.\n", dev->name);
+			len = sizeof(tallies) / 2;
+		}
+		
+		/* Read directly the data (no seek) */
+		hermes_read_words(hw, HERMES_DATA1, (void *) &tallies, len);
+		
+		/* Increment our various counters */
+		/* wstats->discard.nwid - no wrong BSSID stuff */
+		wstats->discard.code +=
+			le16_to_cpu(tallies.RxWEPUndecryptable);
+		if (len == (sizeof(tallies) / 2))  
+			wstats->discard.code +=
+				le16_to_cpu(tallies.RxDiscards_WEPICVError) +
+				le16_to_cpu(tallies.RxDiscards_WEPExcluded);
+		wstats->discard.misc +=
+			le16_to_cpu(tallies.TxDiscardsWrongSA);
+#if WIRELESS_EXT > 11
+		wstats->discard.fragment +=
+			le16_to_cpu(tallies.RxMsgInBadMsgFragments);
+		wstats->discard.retries +=
+			le16_to_cpu(tallies.TxRetryLimitExceeded);
+		/* wstats->miss.beacon - no match */
+#if ORINOCO_DEBUG > 3
+		/* Hack for debugging - should not be taken as an example */
+		wstats->discard.nwid += le16_to_cpu(tallies.TxUnicastFrames);
+		wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames);
+#endif
+#endif /* WIRELESS_EXT > 11 */
+	}
+	break;
+	default:
+		DEBUG(1, "%s: Unknown information frame received (type %04x).\n",
+		      priv->ndev.name, le16_to_cpu(info.type));
+		/* We don't actually do anything about it */
+		break;
+	}
 }
 
-static void __dldwd_ev_rx(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
 	struct iw_statistics *wstats = &priv->wstats;
 	struct sk_buff *skb = NULL;
-	int l = RX_EIO_RETRY;
 	u16 rxfid, status;
 	int length, data_len, data_off;
 	char *p;
-	struct dldwd_frame_hdr hdr;
+	struct orinoco_rxframe_hdr hdr;
 	struct ethhdr *eh;
 	int err;
 
 	rxfid = hermes_read_regn(hw, RXFID);
-	DEBUG(3, "__dldwd_ev_rx(): RXFID=0x%04x\n", rxfid);
+	DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid);
 
 	/* We read in the entire frame header here. This isn't really
 	   necessary, since we ignore most of it, but it's
 	   conceptually simpler. We can tune this later if
 	   necessary. */
-	do {
-		err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr),
-				       rxfid, 0);
-	} while ( (err == -EIO) && (--l) );
+	err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), rxfid, 0);
 	if (err) {
-		if (err == -EIO)
-			DEBUG(1, "%s: EIO reading frame header.\n", dev->name);
-		else
-			printk(KERN_ERR "%s: error %d reading frame header. "
-			       "Frame dropped.\n", dev->name, err);
+		printk(KERN_ERR "%s: error %d reading frame header. "
+		       "Frame dropped.\n", dev->name, err);
 		stats->rx_errors++;
 		goto drop;
 	}
-	DEBUG(2, "%s: BAP read suceeded: l=%d\n", dev->name, l);
 
 	status = le16_to_cpu(hdr.desc.status);
 	
@@ -1148,7 +1317,6 @@
 		if ((status & HERMES_RXSTAT_ERR) == HERMES_RXSTAT_BADCRC) {
 			stats->rx_crc_errors++;
 			DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
-			show_rx_frame(&hdr);
 		} else if ((status & HERMES_RXSTAT_ERR)
 			   == HERMES_RXSTAT_UNDECRYPTABLE) {
 			wstats->discard.code++;
@@ -1228,10 +1396,8 @@
 	}
 
 	p = skb_put(skb, data_len);
-	do {
-		err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
-				       rxfid, data_off);
-	} while ( (err == -EIO) && (--l) );
+	err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len),
+			       rxfid, data_off);
 	if (err) {
 		if (err == -EIO)
 			DEBUG(1, "%s: EIO reading frame header.\n", dev->name);
@@ -1241,7 +1407,6 @@
 		stats->rx_errors++;
 		goto drop;
 	}
-	DEBUG(2, "%s: BAP read suceeded: l=%d\n", dev->name, l);
 
 	dev->last_rx = jiffies;
 	skb->dev = dev;
@@ -1249,7 +1414,7 @@
 	skb->ip_summed = CHECKSUM_NONE;
 	
 	/* Process the wireless stats if needed */
-	dldwd_stat_gather(dev, skb, &hdr);
+	orinoco_stat_gather(dev, skb, &hdr);
 
 	/* Pass the packet to the networking stack */
 	netif_rx(skb);
@@ -1264,45 +1429,77 @@
 	return;
 }
 
-static void __dldwd_ev_txexc(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
+	u16 fid = hermes_read_regn(hw, TXCOMPLFID);
+	struct hermes_tx_descriptor desc;
+	int err = 0;
 
-	printk(KERN_WARNING "%s: Tx error!\n", dev->name);
+	if (fid == DUMMY_FID)
+		return; /* Nothing's really happened */
 
-	netif_wake_queue(dev);
+	err = hermes_bap_pread(hw, USER_BAP, &desc, sizeof(desc), fid, 0);
+	if (err) {
+		printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
+		       "(FID=%04X error %d)\n",
+		       dev->name, fid, err);
+	} else {
+		printk(KERN_INFO "%s: Tx error, status %d (FID=%04X)\n",
+		       dev->name, le16_to_cpu(desc.status), fid);
+	}
+	
 	stats->tx_errors++;
+	netif_wake_queue(dev);
+
+	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __dldwd_ev_tx(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
 {
 	struct net_device *dev = &priv->ndev;
 	struct net_device_stats *stats = &priv->stats;
+/*  	u16 fid = hermes_read_regn(hw, TXCOMPLFID); */
+
+	/* We don't generally use the Tx event (to cut down on
+	   interrupts) - we do the transmit complet processing once
+	   the transmit buffer is reclaimed in __orinoco_ev_alloc() ,
+	   hence nothing here */
 
-	DEBUG(3, "%s: Transmit completed\n", dev->name);
+/*  	DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev.name, fid); */
 
 	stats->tx_packets++;
 	netif_wake_queue(dev);
+
+	hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
 }
 
-static void __dldwd_ev_alloc(dldwd_priv_t *priv, hermes_t *hw)
+static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw)
 {
-	u16 allocfid;
+	struct net_device *dev = &priv->ndev;
+	u16 fid = hermes_read_regn(hw, ALLOCFID);
 
-	allocfid = hermes_read_regn(hw, ALLOCFID);
-	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, allocfid);
+	DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev.name, fid);
 
-	/* For some reason we don't seem to get transmit completed events properly */
-	if (allocfid == priv->txfid)
-		__dldwd_ev_tx(priv, hw);
+	/* We don't generally request Tx complete events to cut down
+	   on the number of interrupts, so we do trasmit complete
+	   processing here, which happens once the firmware is done
+	   with the transmit buffer */
+
+	if (fid != priv->txfid) {
+		if (fid != DUMMY_FID)
+			printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n",
+			       dev->name, fid);
+		return;
+	}
 
-/* 	hermes_write_regn(hw, ALLOCFID, 0); */
+	hermes_write_regn(hw, ALLOCFID, DUMMY_FID);
 }
 
 static void determine_firmware(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err;
 	struct sta_id {
@@ -1311,8 +1508,7 @@
 	u32 firmver;
 
 	/* Get the firmware version */
-	err = HERMES_READ_RECORD(hw, USER_BAP,
-				 HERMES_RID_STAIDENTITY, &sta_id);
+	err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_STAID, &sta_id);
 	if (err) {
 		printk(KERN_WARNING "%s: Error %d reading firmware info. Wildly guessing capabilities...\n",
 		       dev->name, err);
@@ -1338,10 +1534,8 @@
 		       "version %d.%02d\n", dev->name,
 		       sta_id.major, sta_id.minor);
 
-		priv->firmware_type = FIRMWARE_TYPE_LUCENT;
-		priv->tx_rate_ctrl = 0x3;	/* 11 Mb/s auto */
+		priv->firmware_type = FIRMWARE_TYPE_AGERE;
 		priv->need_card_reset = 0;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 0;
 		priv->has_port3 = 1;		/* Still works in 7.28 */
 		priv->has_ibss = (firmver >= 0x60006);
@@ -1353,7 +1547,7 @@
 		priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
 		priv->has_preamble = 0;
 		priv->ibss_port = 1;
-		/* Tested with Lucent firmware :
+		/* Tested with Agere firmware :
 		 *	1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
 		 * Tested CableTron firmware : 4.32 => Anton */
 	} else if ((sta_id.vendor == 2) &&
@@ -1365,7 +1559,8 @@
 
 		memset(tmp, 0, sizeof(tmp));
 		/* Get the Symbol firmware version */
-		err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SYMBOL_SECONDARY_VER,
+		err = hermes_read_ltv(hw, USER_BAP,
+				      HERMES_RID_SECONDARYVERSION_SYMBOL,
 				      SYMBOL_MAX_VER_LEN, NULL, &tmp);
 		if (err) {
 			printk(KERN_WARNING
@@ -1390,9 +1585,7 @@
 		       tmp, firmver);
 
 		priv->firmware_type = FIRMWARE_TYPE_SYMBOL;
-		priv->tx_rate_ctrl = 0xF;	/* 11 Mb/s auto */
 		priv->need_card_reset = 1;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 1;
 		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x20000);
@@ -1415,9 +1608,7 @@
 		       sta_id.major, sta_id.minor);
 
 		priv->firmware_type = FIRMWARE_TYPE_INTERSIL;
-		priv->tx_rate_ctrl = 0xF;	/* 11 Mb/s auto */
 		priv->need_card_reset = 0;
-		priv->broken_reset = 0;
 		priv->broken_allocate = 0;
 		priv->has_port3 = 1;
 		priv->has_ibss = (firmver >= 0x00007); /* FIXME */
@@ -1431,7 +1622,7 @@
 			priv->ibss_port = 0;
 		else {
 			printk(KERN_NOTICE "%s: Intersil firmware earlier "
-			       "than v0.08 - several features not supported.",
+			       "than v0.08 - several features not supported\n",
 			       dev->name);
 			priv->ibss_port = 1;
 		}
@@ -1443,18 +1634,18 @@
  */
 
 int
-dldwd_init(struct net_device *dev)
+orinoco_init(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	hermes_id_t nickbuf;
+	struct hermes_idstring nickbuf;
 	u16 reclen;
 	int len;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	/* Do standard firmware reset */
 	err = hermes_reset(hw);
@@ -1467,20 +1658,20 @@
 	determine_firmware(dev);
 
 	if (priv->has_port3)
-		printk(KERN_DEBUG "%s: Ad-hoc demo mode supported.\n", dev->name);
+		printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
 	if (priv->has_ibss)
-		printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported.\n",
+		printk(KERN_DEBUG "%s: IEEE standard IBSS ad-hoc mode supported\n",
 		       dev->name);
 	if (priv->has_wep) {
 		printk(KERN_DEBUG "%s: WEP supported, ", dev->name);
 		if (priv->has_big_wep)
-			printk("\"128\"-bit key.\n");
+			printk("104-bit key\n");
 		else
-			printk("40-bit key.\n");
+			printk("40-bit key\n");
 	}
 
 	/* Get the MAC address */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_MACADDR,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
 			      ETH_ALEN, NULL, dev->dev_addr);
 	if (err) {
 		printk(KERN_WARNING "%s: failed to read MAC address!\n",
@@ -1494,15 +1685,15 @@
 	       dev->dev_addr[5]);
 
 	/* Get the station name */
-	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNF_NICKNAME,
+	err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME,
 			      sizeof(nickbuf), &reclen, &nickbuf);
 	if (err) {
-		printk(KERN_ERR "%s: failed to read station name!n",
+		printk(KERN_ERR "%s: failed to read station name\n",
 		       dev->name);
 		goto out;
 	}
 	if (nickbuf.len)
-		len = min_t(u16, IW_ESSID_MAX_SIZE, le16_to_cpu(nickbuf.len));
+		len = min(IW_ESSID_MAX_SIZE, (int)le16_to_cpu(nickbuf.len));
 	else
 		len = min(IW_ESSID_MAX_SIZE, 2 * reclen);
 	memcpy(priv->nick, &nickbuf.val, len);
@@ -1511,7 +1702,8 @@
 	printk(KERN_DEBUG "%s: Station name \"%s\"\n", dev->name, priv->nick);
 
 	/* Get allowed channels */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNEL_LIST, &priv->channel_mask);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CHANNELLIST,
+				  &priv->channel_mask);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read channel list!\n",
 		       dev->name);
@@ -1519,14 +1711,15 @@
 	}
 
 	/* Get initial AP density */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &priv->ap_density);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &priv->ap_density);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read AP density!\n", dev->name);
 		goto out;
 	}
 
 	/* Get initial RTS threshold */
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_RTS_THRESH, &priv->rts_thresh);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD,
+				  &priv->rts_thresh);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read RTS threshold!\n", dev->name);
 		goto out;
@@ -1534,10 +1727,11 @@
 
 	/* Get initial fragmentation settings */
 	if (priv->has_mwo)
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMWOROBUST_AGERE,
 					  &priv->mwo_robust);
 	else
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH,
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
 					  &priv->frag_thresh);
 	if (err) {
 		printk(KERN_ERR "%s: failed to read fragmentation settings!\n", dev->name);
@@ -1548,14 +1742,16 @@
 	if (priv->has_pm) {
 		priv->pm_on = 0;
 		priv->pm_mcast = 1;
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMAXSLEEPDURATION,
 					  &priv->pm_period);
 		if (err) {
 			printk(KERN_ERR "%s: failed to read power management period!\n",
 			       dev->name);
 			goto out;
 		}
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER,
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFPMHOLDOVERDURATION,
 					  &priv->pm_timeout);
 		if (err) {
 			printk(KERN_ERR "%s: failed to read power management timeout!\n",
@@ -1566,7 +1762,8 @@
 
 	/* Preamble setup */
 	if (priv->has_preamble) {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYMBOL_PREAMBLE, &priv->preamble);
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL,
+					  &priv->preamble);
 		if (err)
 			goto out;
 	}
@@ -1578,55 +1775,52 @@
 	set_port_type(priv);
 
 	priv->promiscuous = 0;
-	priv->allmulti = 0;
 	priv->wep_on = 0;
 	priv->tx_key = 0;
 
 	printk(KERN_DEBUG "%s: ready\n", dev->name);
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return err;
 }
 
 struct net_device_stats *
-dldwd_get_stats(struct net_device *dev)
+orinoco_get_stats(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	
 	return &priv->stats;
 }
 
 struct iw_statistics *
-dldwd_get_wireless_stats(struct net_device *dev)
+orinoco_get_wireless_stats(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	hermes_t *hw = &priv->hw;
 	struct iw_statistics *wstats = &priv->wstats;
 	int err = 0;
 
-	if (!priv->hw_ready)
-		return NULL;
+	if (! netif_device_present(dev))
+		return NULL; /* FIXME: We may be able to do better than this */
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (priv->iw_mode == IW_MODE_ADHOC) {
 		memset(&wstats->qual, 0, sizeof(wstats->qual));
-#ifdef WIRELESS_SPY
 		/* If a spy address is defined, we report stats of the
 		 * first spy address - Jean II */
-		if (priv->spy_number > 0) {
+		if (SPY_NUMBER(priv)) {
 			wstats->qual.qual = priv->spy_stat[0].qual;
 			wstats->qual.level = priv->spy_stat[0].level;
 			wstats->qual.noise = priv->spy_stat[0].noise;
 			wstats->qual.updated = priv->spy_stat[0].updated;
 		}
-#endif /* WIRELESS_SPY */
 	} else {
-		dldwd_commsqual_t cq;
+		orinoco_commsqual_t cq;
 
 		err = HERMES_READ_RECORD(hw, USER_BAP,
 					 HERMES_RID_COMMSQUALITY, &cq);
@@ -1640,7 +1834,14 @@
 		wstats->qual.updated = 7;
 	}
 
-	dldwd_unlock(priv);
+	/* We can't really wait for the tallies inquiry command to
+	 * complete, so we just use the previous results and trigger
+	 * a new tallies inquiry command for next time - Jean II */
+	/* FIXME: Hmm.. seems a bit ugly, I wonder if there's a way to
+	   do better - dgibson */
+	err = hermes_inquire(hw, HERMES_INQ_TALLIES);
+               
+	orinoco_unlock(priv);
 
 	if (err)
 		return NULL;
@@ -1648,11 +1849,10 @@
 	return wstats;
 }
 
-#ifdef WIRELESS_SPY
-static inline void dldwd_spy_gather(struct net_device *dev, u_char *mac,
+static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac,
 				    int level, int noise)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	int i;
 
 	/* Gather wireless spy statistics: for each packet, compare the
@@ -1665,14 +1865,13 @@
 			priv->spy_stat[i].updated = 7;
 		}
 }
-#endif /* WIRELESS_SPY */
 
 void
-dldwd_stat_gather( struct net_device *dev,
+orinoco_stat_gather( struct net_device *dev,
 		   struct sk_buff *skb,
-		   struct dldwd_frame_hdr *hdr)
+		   struct orinoco_rxframe_hdr *hdr)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 
 	/* Using spy support with lots of Rx packets, like in an
 	 * infrastructure (AP), will really slow down everything, because
@@ -1682,24 +1881,18 @@
 	 * Note that to get here, you need both WIRELESS_SPY
 	 * compiled in AND some addresses in the list !!!
 	 */
-#ifdef WIRELESS_SPY
 	/* Note : gcc will optimise the whole section away if
 	 * WIRELESS_SPY is not defined... - Jean II */
-	if (priv->spy_number > 0) {
-		u8 *stats = (u8 *) &(hdr->desc.q_info);
-		/* This code may look strange. Everywhere we are using 16 bit
-		 * ints except here. I've verified that these are are the
-		 * correct values. Please check on PPC - Jean II */
-
-		dldwd_spy_gather(dev, skb->mac.raw + ETH_ALEN, (int)stats[1], (int)stats[0]);
+	if (SPY_NUMBER(priv)) {
+		orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN,
+				   hdr->desc.signal, hdr->desc.silence);
 	}
-#endif /* WIRELESS_SPY */
 }
 
 int
-dldwd_xmit(struct sk_buff *skb, struct net_device *dev)
+orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	struct net_device_stats *stats = &priv->stats;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
@@ -1707,7 +1900,7 @@
 	char *p;
 	struct ethhdr *eh;
 	int len, data_len, data_off;
-	struct dldwd_frame_hdr hdr;
+	struct orinoco_txframe_hdr hdr;
 	hermes_response_t resp;
 
 	if (! netif_running(dev)) {
@@ -1723,9 +1916,10 @@
 		return 1;
 	}
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	/* Length of the packet body */
+	/* FIXME: what if the skb is smaller than this? */
 	len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN);
 
 	eh = (struct ethhdr *)skb->data;
@@ -1734,7 +1928,11 @@
 	memset(&hdr, 0, sizeof(hdr));
 	memcpy(hdr.p80211.addr1, eh->h_dest, ETH_ALEN);
 	memcpy(hdr.p80211.addr2, eh->h_source, ETH_ALEN);
-	hdr.p80211.frame_ctl = DLDWD_FTYPE_DATA;
+	hdr.p80211.frame_ctl = IEEE802_11_FTYPE_DATA;
+
+	/* Request an interrupt on Tx failures, but not sucesses (we
+           use the buffer reclaim allocation event instead */
+	hdr.desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_EX | HERMES_TXCTRL_TX_OK);
 
 	/* Encapsulate Ethernet-II frames */
 	if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
@@ -1751,24 +1949,12 @@
 		hdr.p8023.h_proto = htons(data_len + ENCAPS_OVERHEAD);
 		
 		/* 802.2 header */
-		if (! use_old_encaps) 
-			memcpy(&hdr.p8022, &encaps_hdr,
-			       sizeof(encaps_hdr));
-		else
-			memcpy(&hdr.p8022, &encaps_hdr,
-			       sizeof(old_encaps_hdr));
+		memcpy(&hdr.p8022, &encaps_hdr, sizeof(encaps_hdr));
 			
 		hdr.ethertype = eh->h_proto;
 		err  = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
 					 txfid, 0);
 		if (err) {
-			if (err == -EIO)
-				/* We get these errors reported by the
-				   firmware every so often apparently at
-				   random.  Let the upper layers
-				   handle the retry */
-				DEBUG(1, "%s: DEBUG: EIO writing packet header to BAP\n", dev->name);
-			else
 				printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
 				       dev->name, err);
 			stats->tx_errors++;
@@ -1795,11 +1981,8 @@
 	/* Round up for odd length packets */
 	err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off);
 	if (err) {
-		if (err == -EIO)
-			DEBUG(1, "%s: DEBUG: EIO writing packet header to BAP\n", dev->name);
-		else
-			printk(KERN_ERR "%s: Error %d writing packet header to BAP",
-			       dev->name, err);
+		printk(KERN_ERR "%s: Error %d writing packet header to BAP\n",
+		       dev->name, err);
 		stats->tx_errors++;
 		goto fail;
 	}
@@ -1812,26 +1995,27 @@
 		goto fail;
 	}
 
+	atomic_inc(&priv->queue_length);
 	dev->trans_start = jiffies;
 	stats->tx_bytes += data_off + data_len;
 
 	netif_stop_queue(dev);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	dev_kfree_skb(skb);
 
 	return 0;
  fail:
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	return err;
 }
 
 void
-dldwd_tx_timeout(struct net_device *dev)
+orinoco_tx_timeout(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
 	struct net_device_stats *stats = &priv->stats;
 	int err = 0;
 
@@ -1839,7 +2023,7 @@
 
 	stats->tx_errors++;
 
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
 		printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n",
 		       dev->name, err);
@@ -1849,9 +2033,9 @@
 	}
 }
 
-static int dldwd_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
+static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 	int mode;
 	struct iw_range range;
@@ -1866,9 +2050,9 @@
 
 	rrq->length = sizeof(range);
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	mode = priv->iw_mode;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	memset(&range, 0, sizeof(range));
 
@@ -1904,13 +2088,25 @@
 		range.max_qual.qual = 0;
 		range.max_qual.level = 0;
 		range.max_qual.noise = 0;
+#if WIRELESS_EXT > 11
+		range.avg_qual.qual = 0;
+		range.avg_qual.level = 0;
+		range.avg_qual.noise = 0;
+#endif /* WIRELESS_EXT > 11 */
+
 	} else {
 		range.max_qual.qual = 0x8b - 0x2f;
 		range.max_qual.level = 0x2f - 0x95 - 1;
 		range.max_qual.noise = 0x2f - 0x95 - 1;
+#if WIRELESS_EXT > 11
+		/* Need to get better values */
+		range.avg_qual.qual = 0x24;
+		range.avg_qual.level = 0xC2;
+		range.avg_qual.noise = 0x9E;
+#endif /* WIRELESS_EXT > 11 */
 	}
 
-	err = dldwd_hw_get_bitratelist(priv, &numrates,
+	err = orinoco_hw_get_bitratelist(priv, &numrates,
 				       range.bitrate, IW_MAX_BITRATES);
 	if (err)
 		return err;
@@ -1929,9 +2125,9 @@
 	range.min_frag = 256;
 	range.max_frag = 2346;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	if (priv->has_wep) {
-		range.max_encoding_tokens = MAX_KEYS;
+		range.max_encoding_tokens = ORINOCO_MAX_KEYS;
 
 		range.encoding_size[0] = SMALL_KEY_SIZE;
 		range.num_encoding_sizes = 1;
@@ -1944,7 +2140,7 @@
 		range.num_encoding_sizes = 0;
 		range.max_encoding_tokens = 0;
 	}
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 		
 	range.min_pmp = 0;
 	range.max_pmp = 65535000;
@@ -1976,30 +2172,30 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
 	int setindex = priv->tx_key;
 	int enable = priv->wep_on;
 	int restricted = priv->wep_restrict;
 	u16 xlen = 0;
 	int err = 0;
-	char keybuf[MAX_KEY_SIZE];
+	char keybuf[ORINOCO_MAX_KEY_SIZE];
 	
 	if (erq->pointer) {
 		/* We actually have a key to set */
-		if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > MAX_KEY_SIZE) )
+		if ( (erq->length < SMALL_KEY_SIZE) || (erq->length > ORINOCO_MAX_KEY_SIZE) )
 			return -EINVAL;
 		
 		if (copy_from_user(keybuf, erq->pointer, erq->length))
 			return -EFAULT;
 	}
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
 	if (erq->pointer) {
-		if (erq->length > MAX_KEY_SIZE) {
+		if (erq->length > ORINOCO_MAX_KEY_SIZE) {
 			err = -E2BIG;
 			goto out;
 		}
@@ -2010,7 +2206,7 @@
 			goto out;
 		}
 		
-		if ((index < 0) || (index >= MAX_KEYS))
+		if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
 			index = priv->tx_key;
 		
 		if (erq->length > SMALL_KEY_SIZE) {
@@ -2029,7 +2225,7 @@
 		/* Important note : if the user do "iwconfig eth0 enc off",
 		 * we will arrive there with an index of -1. This is valid
 		 * but need to be taken care off... Jean II */
-		if ((index < 0) || (index >= MAX_KEYS)) {
+		if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) {
 			if((index != -1) || (erq->flags == 0)) {
 				err = -EINVAL;
 				goto out;
@@ -2062,22 +2258,22 @@
 	priv->wep_restrict = restricted;
 	
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
-	return 0;
+	return err;
 }
 
-static int dldwd_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int index = (erq->flags & IW_ENCODE_INDEX) - 1;
 	u16 xlen = 0;
-	char keybuf[MAX_KEY_SIZE];
+	char keybuf[ORINOCO_MAX_KEY_SIZE];
 
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	if ((index < 0) || (index >= MAX_KEYS))
+	if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
 		index = priv->tx_key;
 
 	erq->flags = 0;
@@ -2086,7 +2282,7 @@
 	erq->flags |= index + 1;
 	
 	/* Only for symbol cards - Jean II */
-	if (priv->firmware_type != FIRMWARE_TYPE_LUCENT) {
+	if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
 		if(priv->wep_restrict)
 			erq->flags |= IW_ENCODE_RESTRICTED;
 		else
@@ -2098,10 +2294,10 @@
 	erq->length = xlen;
 
 	if (erq->pointer) {
-		memcpy(keybuf, priv->keys[index].data, MAX_KEY_SIZE);
+		memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
 	}
 	
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	if (erq->pointer) {
 		if (copy_to_user(erq->pointer, keybuf, xlen))
@@ -2111,9 +2307,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setessid(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
 
 	/* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
@@ -2131,25 +2327,25 @@
 		essidbuf[erq->length] = '\0';
 	}
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid));
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
+static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char essidbuf[IW_ESSID_MAX_SIZE+1];
 	int active;
 	int err = 0;
 
 	TRACE_ENTER(dev->name);
 
-	err = dldwd_hw_get_essid(priv, &active, essidbuf);
+	err = orinoco_hw_get_essid(priv, &active, essidbuf);
 	if (err)
 		return err;
 
@@ -2164,9 +2360,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setnick(struct net_device *dev, struct iw_point *nrq)
+static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
 
 	if (nrq->length > IW_ESSID_MAX_SIZE)
@@ -2179,23 +2375,23 @@
 
 	nickbuf[nrq->length] = '\0';
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	memcpy(priv->nick, nickbuf, sizeof(priv->nick));
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
+static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	char nickbuf[IW_ESSID_MAX_SIZE+1];
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	nrq->length = strlen(nickbuf)+1;
 
@@ -2205,9 +2401,9 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
+static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int chan = -1;
 
 	/* We can only use this in Ad-Hoc demo mode to set the operating
@@ -2236,23 +2432,23 @@
 	     ! (priv->channel_mask & (1 << (chan-1)) ) )
 		return -EINVAL;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->channel = chan;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
+static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	u16 val;
 	int err;
 
-	dldwd_lock(priv);
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_SYSTEM_SCALE, &val);
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val);
+	orinoco_unlock(priv);
 
 	if (err)
 		return err;
@@ -2263,24 +2459,24 @@
 	return 0;
 }
 
-static int dldwd_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
+static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = srq->value;
 
 	if ((val < 1) || (val > 3))
 		return -EINVAL;
 	
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->ap_density = val;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = rrq->value;
 
 	if (rrq->disabled)
@@ -2289,19 +2485,19 @@
 	if ( (val < 0) || (val > 2347) )
 		return -EINVAL;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->rts_thresh = val;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
+static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (priv->has_mwo) {
 		if (frq->disabled)
@@ -2323,22 +2519,24 @@
 		}
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
+static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 val;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
 	if (priv->has_mwo) {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_MWO_ROBUST, &val);
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CNFMWOROBUST_AGERE,
+					  &val);
 		if (err)
 			val = 0;
 
@@ -2346,7 +2544,8 @@
 		frq->disabled = ! val;
 		frq->fixed = 0;
 	} else {
-		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_FRAG_THRESH, &val);
+		err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD,
+					  &val);
 		if (err)
 			val = 0;
 
@@ -2355,158 +2554,122 @@
 		frq->fixed = 1;
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	
 	return err;
 }
 
-static int dldwd_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
-	int rate_ctrl = -1;
-	int fixed, upto;
-	int brate;
+	int ratemode = -1;
+	int bitrate; /* 100s of kilobits */
 	int i;
+	
+	/* As the user space doesn't know our highest rate, it uses -1
+	 * to ask us to set the highest rate.  Test it using "iwconfig
+	 * ethX rate auto" - Jean II */
+	if (rrq->value == -1)
+		bitrate = 110;
+	else {
+		if (rrq->value % 100000)
+			return -EINVAL;
+		bitrate = rrq->value / 100000;
+	}
 
-	dldwd_lock(priv);
-
-	/* Normalise value */
-	brate = rrq->value / 500000;
+	if ( (bitrate != 10) && (bitrate != 20) &&
+	     (bitrate != 55) && (bitrate != 110) )
+		return -EINVAL;
 
-	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style rate */
-		if (! rrq->fixed) {
-			if (brate > 0)
-				brate = -brate;
-			else
-				brate = -22;
-		}
-	
-		for (i = 0; i < NUM_RATES; i++)
-			if (rate_list[i] == brate) {
-				rate_ctrl = i;
-				break;
-			}
-	
-		if ( (rate_ctrl < 1) || (rate_ctrl >= NUM_RATES) )
-			err = -EINVAL;
-		else
-			priv->tx_rate_ctrl = rate_ctrl;
-		break;
-	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
-	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
-		switch(brate) {
-		case 0:
-			fixed = 0x0;
-			upto = 0xF;
-			break;
-		case 2:
-			fixed = 0x1;
-			upto = 0x1;
-			break;
-		case 4:
-			fixed = 0x2;
-			upto = 0x3;
-			break;
-		case 11:
-			fixed = 0x4;
-			upto = 0x7;
-			break;
-		case 22:
-			fixed = 0x8;
-			upto = 0xF;
+	for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+		if ( (bitrate_table[i].bitrate == bitrate) &&
+		     (bitrate_table[i].automatic == ! rrq->fixed) ) {
+			ratemode = i;
 			break;
-		default:
-			fixed = 0x0;
-			upto = 0x0;
 		}
-		if (rrq->fixed)
-			rate_ctrl = fixed;
-		else
-			rate_ctrl = upto;
-		if (rate_ctrl == 0)
-			err = -EINVAL;
-		else
-			priv->tx_rate_ctrl = rate_ctrl;
-		break;
-	}
+	
+	if (ratemode == -1)
+		return -EINVAL;
 
-	dldwd_unlock(priv);
+	orinoco_lock(priv);
+	priv->bitratemode = ratemode;
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
+	int ratemode;
+	int i;
 	u16 val;
-	int brate = 0;
 
-	dldwd_lock(priv);
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_TX_RATE_CTRL, &val);
-	if (err)
-		goto out;
-	
-	switch (priv->firmware_type) {
-	case FIRMWARE_TYPE_LUCENT: /* Lucent style rate */
-		brate = rate_list[val];
-	
-		if (brate < 0) {
-			rrq->fixed = 0;
+	orinoco_lock(priv);
 
-			err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_TX_RATE, &val);
-			if (err)
-				goto out;
+	ratemode = priv->bitratemode;
+
+	if ( (ratemode < 0) || (ratemode > BITRATE_TABLE_SIZE) )
+		BUG();
+
+	rrq->value = bitrate_table[ratemode].bitrate * 100000;
+	rrq->fixed = ! bitrate_table[ratemode].automatic;
+	rrq->disabled = 0;
 
+	/* If the interface is running we try to find more about the
+	   current mode */
+	if (netif_running(dev)) {
+		err = hermes_read_wordrec(hw, USER_BAP,
+					  HERMES_RID_CURRENTTXRATE, &val);
+		if (err)
+			goto out;
+		
+		switch (priv->firmware_type) {
+		case FIRMWARE_TYPE_AGERE: /* Lucent style rate */
+			/* Note : in Lucent firmware, the return value of
+			 * HERMES_RID_CURRENTTXRATE is the bitrate in Mb/s,
+			 * and therefore is totally different from the
+			 * encoding of HERMES_RID_CNFTXRATECONTROL.
+			 * Don't forget that 6Mb/s is really 5.5Mb/s */
 			if (val == 6)
-				brate = 11;
+				rrq->value = 5500000;
 			else
-				brate = 2*val;
-		} else
-			rrq->fixed = 1;
-		break;
-	case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
-	case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
-		/* Check if auto or fixed (crude approximation) */
-		if((val & 0x1) && (val > 1)) {
-			rrq->fixed = 0;
-
-			err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENT_TX_RATE, &val);
-			if (err)
-				goto out;
-		} else
-			rrq->fixed = 1;
+				rrq->value = val * 1000000;
+                        break;
+		case FIRMWARE_TYPE_INTERSIL: /* Intersil style rate */
+		case FIRMWARE_TYPE_SYMBOL: /* Symbol style rate */
+			for (i = 0; i < BITRATE_TABLE_SIZE; i++)
+				if (bitrate_table[i].intersil_txratectrl == val) {
+					ratemode = i;
+					break;
+				}
+			if (i >= BITRATE_TABLE_SIZE)
+				printk(KERN_INFO "%s: Unable to determine current bitrate (0x%04hx)\n",
+				       dev->name, val);
 
-		if(val >= 8)
-			brate = 22;
-		else if(val >= 4)
-			brate = 11;
-		else if(val >= 2)
-			brate = 4;
-		else
-			brate = 2;
-		break;
+			rrq->value = bitrate_table[ratemode].bitrate * 100000;
+			break;
+		default:
+			BUG();
+		}
 	}
 
-	rrq->value = brate * 500000;
-	rrq->disabled = 0;
-
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
+static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int err = 0;
 
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	if (prq->disabled) {
 		priv->pm_on = 0;
@@ -2546,33 +2709,34 @@
 	}
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
+static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 enable, period, timeout, mcast;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_ENABLE, &enable);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_PERIOD, &period);
+	err = hermes_read_wordrec(hw, USER_BAP,
+				  HERMES_RID_CNFMAXSLEEPDURATION, &period);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_HOLDOVER, &timeout);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMHOLDOVERDURATION, &timeout);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNF_PM_MCAST_RX, &mcast);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFMULTICASTRECEIVE, &mcast);
 	if (err)
 		goto out;
 
@@ -2591,30 +2755,33 @@
 		prq->flags |= IW_POWER_UNICAST_R;
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
 #if WIRELESS_EXT > 10
-static int dldwd_ioctl_getretry(struct net_device *dev, struct iw_param *rrq)
+static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
 	u16 short_limit, long_limit, lifetime;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORT_RETRY_LIMIT, &short_limit);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT,
+				  &short_limit);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONG_RETRY_LIMIT, &long_limit);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_LONGRETRYLIMIT,
+				  &long_limit);
 	if (err)
 		goto out;
 
-	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAX_TX_LIFETIME, &lifetime);
+	err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_MAXTRANSMITLIFETIME,
+				  &lifetime);
 	if (err)
 		goto out;
 
@@ -2638,46 +2805,46 @@
 	}
 
  out:
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 #endif /* WIRELESS_EXT > 10 */
 
-static int dldwd_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	priv->ibss_port = val ;
 
 	/* Actually update the mode we are using */
 	set_port_type(priv);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 	return 0;
 }
 
-static int dldwd_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	*val = priv->ibss_port;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
-static int dldwd_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int val = *( (int *) wrq->u.name );
 	int err = 0;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	switch (val) {
 	case 0: /* Try to do IEEE ad-hoc mode */
 		if (! priv->has_ibss) {
@@ -2706,28 +2873,28 @@
 		/* Actually update the mode we are using */
 		set_port_type(priv);
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getport3(struct net_device *dev, struct iwreq *wrq)
+static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	int *val = (int *)wrq->u.name;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 	*val = priv->prefer_port3;
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return 0;
 }
 
 /* Spy is used for link quality/strength measurements in Ad-Hoc mode
  * Jean II */
-static int dldwd_ioctl_setspy(struct net_device *dev, struct iw_point *srq)
+static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct sockaddr address[IW_MAX_SPY];
 	int number = srq->length;
 	int i;
@@ -2745,9 +2912,9 @@
 	}
 
 	/* Make sure nobody mess with the structure while we do */
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
-	/* dldwd_lock() doesn't disable interrupts, so make sure the
+	/* orinoco_lock() doesn't disable interrupts, so make sure the
 	 * interrupt rx path don't get confused while we copy */
 	priv->spy_number = 0;
 
@@ -2774,20 +2941,20 @@
 	}
 
 	/* Now, let the others play */
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	return err;
 }
 
-static int dldwd_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
+static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct sockaddr address[IW_MAX_SPY];
 	struct iw_quality spy_stat[IW_MAX_SPY];
 	int number;
 	int i;
 
-	dldwd_lock(priv);
+	orinoco_lock(priv);
 
 	number = priv->spy_number;
 	if ((number > 0) && (srq->pointer)) {
@@ -2807,7 +2974,7 @@
 			priv->spy_stat[i].updated = 0;
 	}
 
-	dldwd_unlock(priv);
+	orinoco_unlock(priv);
 
 	/* Push stuff to user space */
 	srq->length = number;
@@ -2822,22 +2989,22 @@
 }
 
 int
-dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	struct iwreq *wrq = (struct iwreq *)rq;
 	int err = 0;
 	int changed = 0;
 
 	TRACE_ENTER(dev->name);
 
-	/* In theory, we could allow most of the the SET stuff to be done
-	 * In practice, the laps of time at startup when the card is not
-	 * ready is very short, so why bother...
-	 * Note that hw_ready is different from up/down (ifconfig), when
-	 * the device is not yet up, it is usually already ready...
-	 * Jean II */
-	if (!priv->hw_ready)
+	/* In theory, we could allow most of the the SET stuff to be
+	 * done In practice, the laps of time at startup when the card
+	 * is not ready is very short, so why bother...  Note that
+	 * netif_device_present is different from up/down (ifconfig),
+	 * when the device is not yet up, it is usually already
+	 * ready...  Jean II */
+	if (! netif_device_present(dev))
 		return -ENODEV;
 
 	switch (cmd) {
@@ -2849,17 +3016,17 @@
 	case SIOCGIWAP:
 		DEBUG(1, "%s: SIOCGIWAP\n", dev->name);
 		wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
-		err = dldwd_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
+		err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
 		break;
 
 	case SIOCGIWRANGE:
 		DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name);
-		err = dldwd_ioctl_getiwrange(dev, &wrq->u.data);
+		err = orinoco_ioctl_getiwrange(dev, &wrq->u.data);
 		break;
 
 	case SIOCSIWMODE:
 		DEBUG(1, "%s: SIOCSIWMODE\n", dev->name);
-		dldwd_lock(priv);
+		orinoco_lock(priv);
 		switch (wrq->u.mode) {
 		case IW_MODE_ADHOC:
 			if (! (priv->has_ibss || priv->has_port3) )
@@ -2880,14 +3047,14 @@
 			break;
 		}
 		set_port_type(priv);
-		dldwd_unlock(priv);
+		orinoco_unlock(priv);
 		break;
 
 	case SIOCGIWMODE:
 		DEBUG(1, "%s: SIOCGIWMODE\n", dev->name);
-		dldwd_lock(priv);
+		orinoco_lock(priv);
 		wrq->u.mode = priv->iw_mode;
-		dldwd_unlock(priv);
+		orinoco_unlock(priv);
 		break;
 
 	case SIOCSIWENCODE:
@@ -2897,7 +3064,7 @@
 			break;
 		}
 
-		err = dldwd_ioctl_setiwencode(dev, &wrq->u.encoding);
+		err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding);
 		if (! err)
 			changed = 1;
 		break;
@@ -2914,54 +3081,54 @@
 			break;
 		}
 
-		err = dldwd_ioctl_getiwencode(dev, &wrq->u.encoding);
+		err = orinoco_ioctl_getiwencode(dev, &wrq->u.encoding);
 		break;
 
 	case SIOCSIWESSID:
 		DEBUG(1, "%s: SIOCSIWESSID\n", dev->name);
-		err = dldwd_ioctl_setessid(dev, &wrq->u.essid);
+		err = orinoco_ioctl_setessid(dev, &wrq->u.essid);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWESSID:
 		DEBUG(1, "%s: SIOCGIWESSID\n", dev->name);
-		err = dldwd_ioctl_getessid(dev, &wrq->u.essid);
+		err = orinoco_ioctl_getessid(dev, &wrq->u.essid);
 		break;
 
 	case SIOCSIWNICKN:
 		DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name);
-		err = dldwd_ioctl_setnick(dev, &wrq->u.data);
+		err = orinoco_ioctl_setnick(dev, &wrq->u.data);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWNICKN:
 		DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name);
-		err = dldwd_ioctl_getnick(dev, &wrq->u.data);
+		err = orinoco_ioctl_getnick(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWFREQ:
 		DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name);
-		wrq->u.freq.m = dldwd_hw_get_freq(priv);
+		wrq->u.freq.m = orinoco_hw_get_freq(priv);
 		wrq->u.freq.e = 1;
 		break;
 
 	case SIOCSIWFREQ:
 		DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name);
-		err = dldwd_ioctl_setfreq(dev, &wrq->u.freq);
+		err = orinoco_ioctl_setfreq(dev, &wrq->u.freq);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWSENS:
 		DEBUG(1, "%s: SIOCGIWSENS\n", dev->name);
-		err = dldwd_ioctl_getsens(dev, &wrq->u.sens);
+		err = orinoco_ioctl_getsens(dev, &wrq->u.sens);
 		break;
 
 	case SIOCSIWSENS:
 		DEBUG(1, "%s: SIOCSIWSENS\n", dev->name);
-		err = dldwd_ioctl_setsens(dev, &wrq->u.sens);
+		err = orinoco_ioctl_setsens(dev, &wrq->u.sens);
 		if (! err)
 			changed = 1;
 		break;
@@ -2975,45 +3142,45 @@
 
 	case SIOCSIWRTS:
 		DEBUG(1, "%s: SIOCSIWRTS\n", dev->name);
-		err = dldwd_ioctl_setrts(dev, &wrq->u.rts);
+		err = orinoco_ioctl_setrts(dev, &wrq->u.rts);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCSIWFRAG:
 		DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name);
-		err = dldwd_ioctl_setfrag(dev, &wrq->u.frag);
+		err = orinoco_ioctl_setfrag(dev, &wrq->u.frag);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWFRAG:
 		DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name);
-		err = dldwd_ioctl_getfrag(dev, &wrq->u.frag);
+		err = orinoco_ioctl_getfrag(dev, &wrq->u.frag);
 		break;
 
 	case SIOCSIWRATE:
 		DEBUG(1, "%s: SIOCSIWRATE\n", dev->name);
-		err = dldwd_ioctl_setrate(dev, &wrq->u.bitrate);
+		err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWRATE:
 		DEBUG(1, "%s: SIOCGIWRATE\n", dev->name);
-		err = dldwd_ioctl_getrate(dev, &wrq->u.bitrate);
+		err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate);
 		break;
 
 	case SIOCSIWPOWER:
 		DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name);
-		err = dldwd_ioctl_setpower(dev, &wrq->u.power);
+		err = orinoco_ioctl_setpower(dev, &wrq->u.power);
 		if (! err)
 			changed = 1;
 		break;
 
 	case SIOCGIWPOWER:
 		DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name);
-		err = dldwd_ioctl_getpower(dev, &wrq->u.power);
+		err = orinoco_ioctl_getpower(dev, &wrq->u.power);
 		break;
 
 	case SIOCGIWTXPOW:
@@ -3033,44 +3200,44 @@
 
 	case SIOCGIWRETRY:
 		DEBUG(1, "%s: SIOCGIWRETRY\n", dev->name);
-		err = dldwd_ioctl_getretry(dev, &wrq->u.retry);
+		err = orinoco_ioctl_getretry(dev, &wrq->u.retry);
 		break;
 #endif /* WIRELESS_EXT > 10 */
 
 	case SIOCSIWSPY:
 		DEBUG(1, "%s: SIOCSIWSPY\n", dev->name);
 
-		err = dldwd_ioctl_setspy(dev, &wrq->u.data);
+		err = orinoco_ioctl_setspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWSPY:
 		DEBUG(1, "%s: SIOCGIWSPY\n", dev->name);
 
-		err = dldwd_ioctl_getspy(dev, &wrq->u.data);
+		err = orinoco_ioctl_getspy(dev, &wrq->u.data);
 		break;
 
 	case SIOCGIWPRIV:
 		DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name);
 		if (wrq->u.data.pointer) {
 			struct iw_priv_args privtab[] = {
-				{ SIOCDEVPRIVATE + 0x0, 0, 0, "force_reset" },
-				{ SIOCDEVPRIVATE + 0x1, 0, 0, "card_reset" },
-				{ SIOCDEVPRIVATE + 0x2,
+				{ SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
+				{ SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
+				{ SIOCIWFIRSTPRIV + 0x2,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_port3" },
-				{ SIOCDEVPRIVATE + 0x3, 0,
+				{ SIOCIWFIRSTPRIV + 0x3, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_port3" },
-				{ SIOCDEVPRIVATE + 0x4,
+				{ SIOCIWFIRSTPRIV + 0x4,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_preamble" },
-				{ SIOCDEVPRIVATE + 0x5, 0,
+				{ SIOCIWFIRSTPRIV + 0x5, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_preamble" },
-				{ SIOCDEVPRIVATE + 0x6,
+				{ SIOCIWFIRSTPRIV + 0x6,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  0, "set_ibssport" },
-				{ SIOCDEVPRIVATE + 0x7, 0,
+				{ SIOCIWFIRSTPRIV + 0x7, 0,
 				  IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
 				  "get_ibssport" }
 			};
@@ -3085,8 +3252,8 @@
 		}
 		break;
 	       
-	case SIOCDEVPRIVATE + 0x0: /* force_reset */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x0 (force_reset)\n",
+	case SIOCIWFIRSTPRIV + 0x0: /* force_reset */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x0 (force_reset)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3094,11 +3261,11 @@
 		}
 		
 		printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
-		dldwd_reset(priv);
+		orinoco_reset(priv);
 		break;
 
-	case SIOCDEVPRIVATE + 0x1: /* card_reset */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x1 (card_reset)\n",
+	case SIOCIWFIRSTPRIV + 0x1: /* card_reset */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x1 (card_reset)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3108,30 +3275,30 @@
 		printk(KERN_DEBUG "%s: Forcing card reset!\n", dev->name);
 		if(priv->card_reset_handler != NULL)
 			priv->card_reset_handler(priv);
-		dldwd_reset(priv);
+		orinoco_reset(priv);
 		break;
 
-	case SIOCDEVPRIVATE + 0x2: /* set_port3 */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x2 (set_port3)\n",
+	case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x2 (set_port3)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
 		}
 
-		err = dldwd_ioctl_setport3(dev, wrq);
+		err = orinoco_ioctl_setport3(dev, wrq);
 		if (! err)
 			changed = 1;
 		break;
 
-	case SIOCDEVPRIVATE + 0x3: /* get_port3 */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x3 (get_port3)\n",
+	case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x3 (get_port3)\n",
 		      dev->name);
-		err = dldwd_ioctl_getport3(dev, wrq);
+		err = orinoco_ioctl_getport3(dev, wrq);
 		break;
 
-	case SIOCDEVPRIVATE + 0x4: /* set_preamble */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x4 (set_preamble)\n",
+	case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
@@ -3146,46 +3313,46 @@
 		if(priv->has_preamble) {
 			int val = *( (int *) wrq->u.name );
 
-			dldwd_lock(priv);
+			orinoco_lock(priv);
 			if(val)
 				priv->preamble = 1;
 			else
 				priv->preamble = 0;
-			dldwd_unlock(priv);
+			orinoco_unlock(priv);
 			changed = 1;
 		} else
 			err = -EOPNOTSUPP;
 		break;
 
-	case SIOCDEVPRIVATE + 0x5: /* get_preamble */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x5 (get_preamble)\n",
+	case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n",
 		      dev->name);
 		if(priv->has_preamble) {
 			int *val = (int *)wrq->u.name;
 
-			dldwd_lock(priv);
+			orinoco_lock(priv);
 			*val = priv->preamble;
-			dldwd_unlock(priv);
+			orinoco_unlock(priv);
 		} else
 			err = -EOPNOTSUPP;
 		break;
-	case SIOCDEVPRIVATE + 0x6: /* set_ibssport */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x6 (set_ibssport)\n",
+	case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n",
 		      dev->name);
 		if (! capable(CAP_NET_ADMIN)) {
 			err = -EPERM;
 			break;
 		}
 
-		err = dldwd_ioctl_setibssport(dev, wrq);
+		err = orinoco_ioctl_setibssport(dev, wrq);
 		if (! err)
 			changed = 1;
 		break;
 
-	case SIOCDEVPRIVATE + 0x7: /* get_ibssport */
-		DEBUG(1, "%s: SIOCDEVPRIVATE + 0x7 (get_ibssport)\n",
+	case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */
+		DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x7 (get_ibssport)\n",
 		      dev->name);
-		err = dldwd_ioctl_getibssport(dev, wrq);
+		err = orinoco_ioctl_getibssport(dev, wrq);
 		break;
 
 
@@ -3194,14 +3361,13 @@
 	}
 	
 	if (! err && changed && netif_running(dev)) {
-		err = dldwd_reset(priv);
+		err = orinoco_reset(priv);
 		if (err) {
 			/* Ouch ! What are we supposed to do ? */
 			printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n",
 			       dev->name);
-			netif_stop_queue(dev);
-			dldwd_shutdown(priv);
-			priv->hw_ready = 0;
+			netif_device_detach(dev);
+			orinoco_shutdown(priv);
 		}
 	}		
 
@@ -3211,11 +3377,11 @@
 }
 
 int
-dldwd_change_mtu(struct net_device *dev, int new_mtu)
+orinoco_change_mtu(struct net_device *dev, int new_mtu)
 {
 	TRACE_ENTER(dev->name);
 
-	if ( (new_mtu < DLDWD_MIN_MTU) || (new_mtu > DLDWD_MAX_MTU) )
+	if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
 		return -EINVAL;
 
 	dev->mtu = new_mtu;
@@ -3226,108 +3392,72 @@
 }
 
 static void
-__dldwd_set_multicast_list(struct net_device *dev)
+__orinoco_set_multicast_list(struct net_device *dev)
 {
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = dev->priv;
 	hermes_t *hw = &priv->hw;
 	int err = 0;
-	int promisc, allmulti, mc_count;
+	int promisc, mc_count;
 
 	/* We'll wait until it's ready. Anyway, the network doesn't call us
 	 * here until we are open - Jean II */
-	if (!priv->hw_ready)
+	/* FIXME: do we need this test at all? */
+	if (! netif_device_present(dev))
 		return;
 
-
 	TRACE_ENTER(dev->name);
 
-	DEBUG(3, "dev->flags=0x%x, priv->promiscuous=%d, dev->mc_count=%d priv->mc_count=%d\n",
-	      dev->flags, priv->promiscuous, dev->mc_count, priv->mc_count);
-
 	/* The Hermes doesn't seem to have an allmulti mode, so we go
 	 * into promiscuous mode and let the upper levels deal. */
-	if ( (dev->flags & IFF_PROMISC) ) {
+	if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) ||
+	     (dev->mc_count > MAX_MULTICAST(priv)) ) {
 		promisc = 1;
-		allmulti = 0;
 		mc_count = 0;
-	} else if ( (dev->flags & IFF_ALLMULTI) ||
-		    (dev->mc_count > HERMES_MAX_MULTICAST) ) {
-		promisc = 0;
-		allmulti = 1;
-		mc_count = HERMES_MAX_MULTICAST;
 	} else {
 		promisc = 0;
-		allmulti = 0;
 		mc_count = dev->mc_count;
 	}
 
-	DEBUG(3, "promisc=%d mc_count=%d\n",
-	      promisc, mc_count);
-
-	if (promisc != priv->promiscuous) { /* Don't touch the hardware if we don't have to */
-		err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNF_PROMISCUOUS,
+	if (promisc != priv->promiscuous) {
+		err = hermes_write_wordrec(hw, USER_BAP,
+					   HERMES_RID_CNFPROMISCUOUSMODE,
 					   promisc);
 		if (err) {
-			printk(KERN_ERR "%s: Error %d setting promiscuity to %d.\n",
-			       dev->name, err, promisc);
+			printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n",
+			       dev->name, err);
 		} else 
 			priv->promiscuous = promisc;
-	}
 
-	if (allmulti) {
-		/* FIXME: This method of doing allmulticast reception
-		   comes from the NetBSD driver. Haven't actually
-		   tested whether it works or not. */
-		hermes_multicast_t mclist;
+		mc_count = 0;
+	}
 
-		memset(&mclist, 0, sizeof(mclist));
-		err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST, &mclist);
-		if (err)
-			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
-			       dev->name, err);
-		else
-			priv->allmulti = 1;
-		       
-	} else if (mc_count || (! mc_count && priv->mc_count) ) {
+	if (mc_count || priv->mc_count) {
 		struct dev_mc_list *p = dev->mc_list;
 		hermes_multicast_t mclist;
 		int i;
 
 		for (i = 0; i < mc_count; i++) {
-			/* First some paranoid checks */
-			if (! p) {
-				printk(KERN_ERR "%s: Multicast list shorter than mc_count.\n",
-				       dev->name);
-				break;
-			}
-			if (p->dmi_addrlen != ETH_ALEN) {
-
-				printk(KERN_ERR "%s: Bad address size (%d) in multicast list.\n",
-				       dev->name, p->dmi_addrlen);
-				break;
-			}
-
+			/* Paranoia: */
+			if (! p)
+				BUG(); /* Multicast list shorter than mc_count */
+			if (p->dmi_addrlen != ETH_ALEN)
+				BUG(); /* Bad address size in multicast list */
+			
 			memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN);
 			p = p->next;
 		}
-
-		/* More paranoia */
+		
 		if (p)
-			printk(KERN_ERR "%s: Multicast list longer than mc_count.\n",
-			       dev->name);
+			printk(KERN_WARNING "Multicast list is longer than mc_count\n");
 
-		priv->mc_count = i;			
-
-		DEBUG(3, "priv->mc_count = %d\n", priv->mc_count);
-
-		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNF_MULTICAST_LIST,
+		err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES,
 				       HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN),
 				       &mclist);
 		if (err)
 			printk(KERN_ERR "%s: Error %d setting multicast list.\n",
 			       dev->name, err);
 		else
-			priv->allmulti = 0;
+			priv->mc_count = mc_count;
 	}
 
 	/* Since we can set the promiscuous flag when it wasn't asked
@@ -3337,11 +3467,6 @@
 	else
 		dev->flags &= ~IFF_PROMISC;
 
-	if (priv->allmulti)
-		dev->flags |= IFF_ALLMULTI;
-	else
-		dev->flags &= ~IFF_ALLMULTI;
-
 	TRACE_EXIT(dev->name);
 }
 
@@ -3420,16 +3545,17 @@
 }
 
 static int
-dldwd_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
+orinoco_proc_get_hermes_regs(char *page, char **start, off_t requested_offset,
 			   int requested_len, int *eof, void *data)
 {
-	dldwd_priv_t *dev = (dldwd_priv_t *)data;
-	hermes_t *hw = &dev->hw;
+	struct orinoco_private *priv = (struct orinoco_private *)data;
+	struct net_device *dev = &priv->ndev;
+	hermes_t *hw = &priv->hw;
 	char *buf;
 	int total = 0, slop = 0;
 
 	/* Hum, in this case hardware register are probably not readable... */
-	if (!dev->hw_ready)
+	if (! netif_device_present(dev))
 		return -ENODEV;
 
 	buf = page;
@@ -3478,57 +3604,130 @@
 #define DISPLAY_WORDS	0
 #define DISPLAY_BYTES	1
 #define DISPLAY_STRING	2
+#define DISPLAY_XSTRING	3
 } record_table[] = {
-#define RTCNFENTRY(name, type) { HERMES_RID_CNF_##name, #name, 0, LTV_BUF_SIZE, type }
-	RTCNFENTRY(PORTTYPE, DISPLAY_WORDS),
-	RTCNFENTRY(MACADDR, DISPLAY_BYTES),
-	RTCNFENTRY(DESIRED_SSID, DISPLAY_STRING),
-	RTCNFENTRY(CHANNEL, DISPLAY_WORDS),
-	RTCNFENTRY(OWN_SSID, DISPLAY_STRING),
-	RTCNFENTRY(SYSTEM_SCALE, DISPLAY_WORDS),
-	RTCNFENTRY(MAX_DATA_LEN, DISPLAY_WORDS),
-	RTCNFENTRY(PM_ENABLE, DISPLAY_WORDS),
-	RTCNFENTRY(PM_MCAST_RX, DISPLAY_WORDS),
-	RTCNFENTRY(PM_PERIOD, DISPLAY_WORDS),
-	RTCNFENTRY(NICKNAME, DISPLAY_STRING),
-	RTCNFENTRY(WEP_ON, DISPLAY_WORDS),
-	RTCNFENTRY(MWO_ROBUST, DISPLAY_WORDS),
-	RTCNFENTRY(MULTICAST_LIST, DISPLAY_BYTES),
-	RTCNFENTRY(CREATEIBSS, DISPLAY_WORDS),
-	RTCNFENTRY(FRAG_THRESH, DISPLAY_WORDS),
-	RTCNFENTRY(RTS_THRESH, DISPLAY_WORDS),
-	RTCNFENTRY(TX_RATE_CTRL, DISPLAY_WORDS),
-	RTCNFENTRY(PROMISCUOUS, DISPLAY_WORDS),
-	RTCNFENTRY(KEYS, DISPLAY_BYTES),
-	RTCNFENTRY(TX_KEY, DISPLAY_WORDS),
-	RTCNFENTRY(TICKTIME, DISPLAY_WORDS),
-	RTCNFENTRY(INTERSIL_TX_KEY, DISPLAY_WORDS),
-	RTCNFENTRY(INTERSIL_KEY0, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY1, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY2, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_KEY3, DISPLAY_BYTES),
-	RTCNFENTRY(INTERSIL_WEP_ON, DISPLAY_WORDS),
-#undef RTCNFENTRY
-#define RTINFENTRY(name,type) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, type }
-	RTINFENTRY(CHANNEL_LIST, DISPLAY_WORDS),
-	RTINFENTRY(STAIDENTITY, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_SSID, DISPLAY_STRING),
-	RTINFENTRY(CURRENT_BSSID, DISPLAY_BYTES),
-	RTINFENTRY(COMMSQUALITY, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_TX_RATE, DISPLAY_WORDS),
-	RTINFENTRY(WEP_AVAIL, DISPLAY_WORDS),
-	RTINFENTRY(CURRENT_CHANNEL, DISPLAY_WORDS),
-	RTINFENTRY(DATARATES, DISPLAY_BYTES),
-#undef RTINFENTRY
+#define CNF_WORDS(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
+#define CNF_BYTES(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
+#define CNF_STRING(name) { HERMES_RID_CNF##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
+	CNF_WORDS(PORTTYPE),
+	CNF_BYTES(OWNMACADDR),
+	CNF_STRING(DESIREDSSID),
+	CNF_WORDS(OWNCHANNEL),
+	CNF_STRING(OWNSSID),
+	CNF_WORDS(OWNATIMWINDOW),
+	CNF_WORDS(SYSTEMSCALE),
+	CNF_WORDS(MAXDATALEN),
+	CNF_WORDS(PMENABLED),
+	CNF_WORDS(PMEPS),
+	CNF_WORDS(MULTICASTRECEIVE),
+	CNF_WORDS(MAXSLEEPDURATION),
+	CNF_WORDS(PMHOLDOVERDURATION),
+	CNF_STRING(OWNNAME),
+	CNF_WORDS(OWNDTIMPERIOD),
+	CNF_WORDS(MULTICASTPMBUFFERING),
+	CNF_WORDS(WEPENABLED_AGERE),
+	CNF_WORDS(MANDATORYBSSID_SYMBOL),
+	CNF_WORDS(WEPDEFAULTKEYID),
+	CNF_BYTES(DEFAULTKEY0),
+	CNF_BYTES(DEFAULTKEY1),
+	CNF_WORDS(MWOROBUST_AGERE),
+	CNF_BYTES(DEFAULTKEY2),
+	CNF_BYTES(DEFAULTKEY3),
+	CNF_WORDS(WEPFLAGS_INTERSIL),
+	CNF_WORDS(WEPKEYMAPPINGTABLE),
+	CNF_WORDS(AUTHENTICATION),
+	CNF_WORDS(MAXASSOCSTA),
+	CNF_WORDS(KEYLENGTH_SYMBOL),
+	CNF_WORDS(TXCONTROL),
+	CNF_WORDS(ROAMINGMODE),
+	CNF_WORDS(HOSTAUTHENTICATION),
+	CNF_WORDS(RCVCRCERROR),
+	CNF_WORDS(MMLIFE),
+	CNF_WORDS(ALTRETRYCOUNT),
+	CNF_WORDS(BEACONINT),
+	CNF_WORDS(APPCFINFO),
+	CNF_WORDS(STAPCFINFO),
+	CNF_WORDS(PRIORITYQUSAGE),
+	CNF_WORDS(TIMCTRL),
+	CNF_WORDS(THIRTY2TALLY),
+	CNF_WORDS(ENHSECURITY),
+	CNF_BYTES(GROUPADDRESSES),
+	CNF_WORDS(CREATEIBSS),
+	CNF_WORDS(FRAGMENTATIONTHRESHOLD),
+	CNF_WORDS(RTSTHRESHOLD),
+	CNF_WORDS(TXRATECONTROL),
+	CNF_WORDS(PROMISCUOUSMODE),
+	CNF_WORDS(BASICRATES_SYMBOL),
+	CNF_WORDS(PREAMBLE_SYMBOL),
+	CNF_WORDS(SHORTPREAMBLE),
+	CNF_BYTES(WEPKEYS_AGERE),
+	CNF_WORDS(EXCLUDELONGPREAMBLE),
+	CNF_WORDS(TXKEY_AGERE),
+	CNF_WORDS(AUTHENTICATIONRSPTO),
+	CNF_WORDS(BASICRATES),
+	CNF_WORDS(SUPPORTEDRATES),
+	CNF_WORDS(TICKTIME),
+	CNF_WORDS(SCANREQUEST),
+	CNF_WORDS(JOINREQUEST),
+	CNF_WORDS(AUTHENTICATESTATION),
+	CNF_WORDS(CHANNELINFOREQUEST),
+#undef CNF_WORDS
+#undef CNF_BYTES
+#undef CNF_STRING
+#define INF_WORDS(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_WORDS }
+#define INF_BYTES(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_BYTES }
+#define INF_STRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_STRING }
+#define INF_XSTRING(name) { HERMES_RID_##name, #name, 0, LTV_BUF_SIZE, DISPLAY_XSTRING }
+	INF_WORDS(MAXLOADTIME),
+	INF_WORDS(DOWNLOADBUFFER),
+	INF_WORDS(PRIID),
+	INF_WORDS(PRISUPRANGE),
+	INF_WORDS(CFIACTRANGES),
+	INF_WORDS(NICSERNUM),
+	INF_WORDS(NICID),
+	INF_WORDS(MFISUPRANGE),
+	INF_WORDS(CFISUPRANGE),
+	INF_WORDS(CHANNELLIST),
+	INF_WORDS(REGULATORYDOMAINS),
+	INF_WORDS(TEMPTYPE),
+/*  	INF_BYTES(CIS), */
+	INF_WORDS(STAID),
+	INF_STRING(CURRENTSSID),
+	INF_BYTES(CURRENTBSSID),
+	INF_WORDS(COMMSQUALITY),
+	INF_WORDS(CURRENTTXRATE),
+	INF_WORDS(CURRENTBEACONINTERVAL),
+	INF_WORDS(CURRENTSCALETHRESHOLDS),
+	INF_WORDS(PROTOCOLRSPTIME),
+	INF_WORDS(SHORTRETRYLIMIT),
+	INF_WORDS(LONGRETRYLIMIT),
+	INF_WORDS(MAXTRANSMITLIFETIME),
+	INF_WORDS(MAXRECEIVELIFETIME),
+	INF_WORDS(CFPOLLABLE),
+	INF_WORDS(AUTHENTICATIONALGORITHMS),
+	INF_WORDS(PRIVACYOPTIONIMPLEMENTED),
+	INF_BYTES(OWNMACADDR),
+	INF_WORDS(SCANRESULTSTABLE),
+	INF_WORDS(PHYTYPE),
+	INF_WORDS(CURRENTCHANNEL),
+	INF_WORDS(CURRENTPOWERSTATE),
+	INF_WORDS(CCAMODE),
+	INF_WORDS(SUPPORTEDDATARATES),
+	INF_BYTES(BUILDSEQ),
+	INF_XSTRING(FWID)
+#undef INF_WORDS
+#undef INF_BYTES
+#undef INF_STRING
 };
 #define NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) )
 
 static int
-dldwd_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
+orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
 			   int requested_len, int *eof, void *data)
 {
-	dldwd_priv_t *dev = (dldwd_priv_t *)data;
-	hermes_t *hw = &dev->hw;
+	struct orinoco_private *priv = (struct orinoco_private *)data;
+	struct net_device *dev = &priv->ndev;
+	hermes_t *hw = &priv->hw;
 	char *buf;
 	int total = 0, slop = 0;
 	int i;
@@ -3536,7 +3735,7 @@
 	int err;
 
 	/* Hum, in this case hardware register are probably not readable... */
-	if (!dev->hw_ready)
+	if (! netif_device_present(dev))
 		return -ENODEV;
 		
 	buf = page;
@@ -3554,6 +3753,7 @@
 		val8 = kmalloc(maxlen + 2, GFP_KERNEL);
 		if (! val8)
 			return -ENOMEM;
+		memset(val8, 0, maxlen + 2);
 
 		err = hermes_read_ltv(hw, USER_BAP, rid, maxlen,
 				      &length, val8);
@@ -3562,6 +3762,8 @@
 			continue;
 		}
 		val16 = (u16 *)val8;
+		if (length == 0)
+			continue;
 
 		buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
 			       rid, length, (length-1)*2);
@@ -3588,6 +3790,9 @@
 			val8[len] = '\0';
 			buf += sprintf(buf, "\"%s\"", (char *)&val16[1]);
 			break;
+		case DISPLAY_XSTRING:
+			
+			buf += sprintf(buf, "'%s'", (char *)val8);
 		}
 
 		buf += sprintf(buf, "\n");
@@ -3609,65 +3814,67 @@
 /* initialise the /proc subsystem for the hermes driver, creating the
  * separate entries */
 static int
-dldwd_proc_init(void)
+orinoco_proc_init(void)
 {
 	int err = 0;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	/* create the directory for it to sit in */
 	dir_base = create_proc_entry("hermes", S_IFDIR, &proc_root);
 	if (dir_base == NULL) {
 		printk(KERN_ERR "Unable to initialise /proc/hermes.\n");
-		dldwd_proc_cleanup();
+		orinoco_proc_cleanup();
 		err = -ENOMEM;
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return err;
 }
 
 int
-dldwd_proc_dev_init(dldwd_priv_t *dev)
+orinoco_proc_dev_init(struct orinoco_private *priv)
 {
-	struct net_device *ndev = &dev->ndev;
+	struct net_device *dev = &priv->ndev;
 
-	dev->dir_dev = NULL;
+	priv->dir_dev = NULL;
 	/* create the directory for it to sit in */
-	dev->dir_dev = create_proc_entry(ndev->name, S_IFDIR | S_IRUGO | S_IXUGO,
-					 dir_base);
-	if (dev->dir_dev == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s.\n",  ndev->name);
+	priv->dir_dev = create_proc_entry(dev->name, S_IFDIR | S_IRUGO | S_IXUGO,
+					  dir_base);
+	if (priv->dir_dev == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s.\n",  dev->name);
 		goto fail;
 	}
 
-	dev->dir_regs = NULL;
-	dev->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
-					       dev->dir_dev, dldwd_proc_get_hermes_regs, dev);
-	if (dev->dir_regs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/regs.\n",  ndev->name);
+	priv->dir_regs = NULL;
+	priv->dir_regs = create_proc_read_entry("regs", S_IFREG | S_IRUGO,
+						priv->dir_dev, orinoco_proc_get_hermes_regs, priv);
+	if (priv->dir_regs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/regs.\n",  dev->name);
 		goto fail;
 	}
 
-	dev->dir_recs = NULL;
-	dev->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
-					       dev->dir_dev, dldwd_proc_get_hermes_recs, dev);
-	if (dev->dir_recs == NULL) {
-		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/recs.\n",  ndev->name);
+	priv->dir_recs = NULL;
+	priv->dir_recs = create_proc_read_entry("recs", S_IFREG | S_IRUGO,
+						priv->dir_dev, orinoco_proc_get_hermes_recs, priv);
+	if (priv->dir_recs == NULL) {
+		printk(KERN_ERR "Unable to initialise /proc/hermes/%s/recs.\n",  dev->name);
 		goto fail;
 	}
 
 	return 0;
  fail:
-	dldwd_proc_dev_cleanup(dev);
+	orinoco_proc_dev_cleanup(priv);
 	return -ENOMEM;
 }
 
 void
-dldwd_proc_dev_cleanup(dldwd_priv_t *priv)
+orinoco_proc_dev_cleanup(struct orinoco_private *priv)
 {
-	struct net_device *ndev = &priv->ndev;
+	struct net_device *dev = &priv->ndev;
+
+	TRACE_ENTER(priv->ndev.name);
 
 	if (priv->dir_regs) {
 		remove_proc_entry("regs", priv->dir_dev);
@@ -3678,26 +3885,28 @@
 		priv->dir_recs = NULL;
 	}		
 	if (priv->dir_dev) {
-		remove_proc_entry(ndev->name, dir_base);
+		remove_proc_entry(dev->name, dir_base);
 		priv->dir_dev = NULL;
 	}
+
+	TRACE_EXIT(priv->ndev.name);
 }
 
 static void
-dldwd_proc_cleanup(void)
+orinoco_proc_cleanup(void)
 {
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	if (dir_base) {
 		remove_proc_entry("hermes", &proc_root);
 		dir_base = NULL;
 	}
 	
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
 int
-dldwd_setup(dldwd_priv_t* priv)
+orinoco_setup(struct orinoco_private* priv)
 {
 	struct net_device *dev = &priv->ndev;;
 
@@ -3709,20 +3918,20 @@
 
 	/* Setup up default routines */
 	priv->card_reset_handler = NULL;	/* Caller may override */
-	dev->init = dldwd_init;
+	dev->init = orinoco_init;
 	dev->open = NULL;		/* Caller *must* override */
 	dev->stop = NULL;
-	dev->hard_start_xmit = dldwd_xmit;
-	dev->tx_timeout = dldwd_tx_timeout;
-	dev->watchdog_timeo = HZ; /* 4 second timeout */
+	dev->hard_start_xmit = orinoco_xmit;
+	dev->tx_timeout = orinoco_tx_timeout;
+	dev->watchdog_timeo = HZ; /* 1 second timeout */
 
-	dev->get_stats = dldwd_get_stats;
-	dev->get_wireless_stats = dldwd_get_wireless_stats;
+	dev->get_stats = orinoco_get_stats;
+	dev->get_wireless_stats = orinoco_get_wireless_stats;
 
-	dev->do_ioctl = dldwd_ioctl;
+	dev->do_ioctl = orinoco_ioctl;
 
-	dev->change_mtu = dldwd_change_mtu;
-	dev->set_multicast_list = dldwd_set_multicast_list;
+	dev->change_mtu = orinoco_change_mtu;
+	dev->set_multicast_list = orinoco_set_multicast_list;
 
 	netif_stop_queue(dev);
 
@@ -3730,36 +3939,25 @@
 }
 
 #ifdef ORINOCO_DEBUG
-EXPORT_SYMBOL(dldwd_debug);
+EXPORT_SYMBOL(orinoco_debug);
 #endif
-EXPORT_SYMBOL(dldwd_init);
-EXPORT_SYMBOL(dldwd_xmit);
-EXPORT_SYMBOL(dldwd_tx_timeout);
-EXPORT_SYMBOL(dldwd_ioctl);
-EXPORT_SYMBOL(dldwd_change_mtu);
-EXPORT_SYMBOL(dldwd_set_multicast_list);
-EXPORT_SYMBOL(dldwd_shutdown);
-EXPORT_SYMBOL(dldwd_reset);
-EXPORT_SYMBOL(dldwd_setup);
-EXPORT_SYMBOL(dldwd_proc_dev_init);
-EXPORT_SYMBOL(dldwd_proc_dev_cleanup);
-EXPORT_SYMBOL(dldwd_interrupt);
+EXPORT_SYMBOL(orinoco_shutdown);
+EXPORT_SYMBOL(orinoco_reset);
+EXPORT_SYMBOL(orinoco_setup);
+EXPORT_SYMBOL(orinoco_proc_dev_init);
+EXPORT_SYMBOL(orinoco_proc_dev_cleanup);
+EXPORT_SYMBOL(orinoco_interrupt);
 
-static int __init init_dldwd(void)
+static int __init init_orinoco(void)
 {
-	int err;
-
-	err = dldwd_proc_init();
-
 	printk(KERN_DEBUG "%s\n", version);
-
-	return 0;
+	return orinoco_proc_init();
 }
 
-static void __exit exit_dldwd(void)
+static void __exit exit_orinoco(void)
 {
-	dldwd_proc_cleanup();
+	orinoco_proc_cleanup();
 }
 
-module_init(init_dldwd);
-module_exit(exit_dldwd);
+module_init(init_orinoco);
+module_exit(exit_orinoco);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/orinoco.h linux/drivers/net/wireless/orinoco.h
--- linux.orig/drivers/net/wireless/orinoco.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/orinoco.h	Mon Jan 21 19:01:05 2002
@@ -8,64 +8,48 @@
 #define _ORINOCO_H
 
 /* To enable debug messages */
-/*  #define ORINOCO_DEBUG		3 */
+//#define ORINOCO_DEBUG		3
 
 #if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10)
-#error "orinoco_cs requires Wireless extensions v10 or later."
+#error "orinoco driver requires Wireless extensions v10 or later."
 #endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */
 #define WIRELESS_SPY		// enable iwspy support
 
+#define ORINOCO_MAX_KEY_SIZE	14
+#define ORINOCO_MAX_KEYS	4
 
-#define DLDWD_MIN_MTU		256
-#define DLDWD_MAX_MTU		(HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
+typedef struct orinoco_key {
+	u16 len;	/* always store little-endian */
+	char data[ORINOCO_MAX_KEY_SIZE];
+} __attribute__ ((packed)) orinoco_key_t;
 
-#define LTV_BUF_SIZE		128
-#define USER_BAP		0
-#define IRQ_BAP			1
-#define DLDWD_MACPORT		0
-#define IRQ_LOOP_MAX		10
-#define TX_NICBUF_SIZE		2048
-#define TX_NICBUF_SIZE_BUG	1585		/* Bug in Symbol firmware */
-#define MAX_KEYS		4
-#define MAX_KEY_SIZE		14
-#define LARGE_KEY_SIZE		13
-#define SMALL_KEY_SIZE		5
-#define MAX_FRAME_SIZE		2304
-
-typedef struct dldwd_key {
-	uint16_t len;	/* always store little-endian */
-	char data[MAX_KEY_SIZE];
-} __attribute__ ((packed)) dldwd_key_t;
-
-typedef dldwd_key_t dldwd_keys_t[MAX_KEYS];
+typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS];
 
 /*====================================================================*/
 
-
-typedef struct dldwd_priv {
+struct orinoco_private {
 	void* card;	/* Pointer to card dependant structure */
 	/* card dependant extra reset code (i.e. bus/interface specific */
-	int (*card_reset_handler)(struct dldwd_priv *);
+	int (*card_reset_handler)(struct orinoco_private *);
 
 	spinlock_t lock;
 	long state;
-#define DLDWD_STATE_INIRQ 0
-#define DLDWD_STATE_DOIRQ 1
-	int hw_ready;	/* HW may be suspended by platform */
+#define ORINOCO_STATE_INIRQ 0
+#define ORINOCO_STATE_DOIRQ 1
+	atomic_t queue_length;
 
 	/* Net device stuff */
 	struct net_device ndev;
 	struct net_device_stats stats;
 	struct iw_statistics wstats;
 
-
 	/* Hardware control variables */
 	hermes_t hw;
-	uint16_t txfid;
+	u16 txfid;
 
 	/* Capabilities of the hardware/firmware */
 	int firmware_type;
-#define FIRMWARE_TYPE_LUCENT 1
+#define FIRMWARE_TYPE_AGERE 1
 #define FIRMWARE_TYPE_INTERSIL 2
 #define FIRMWARE_TYPE_SYMBOL 3
 	int has_ibss, has_port3, prefer_port3, has_ibss_any, ibss_port;
@@ -74,23 +58,26 @@
 	int has_pm;
 	int has_preamble;
 	int need_card_reset, broken_reset, broken_allocate;
-	uint16_t channel_mask;
+	u16 channel_mask;
 
 	/* Current configuration */
-	uint32_t iw_mode;
+	u32 iw_mode;
 	int port_type, allow_ibss;
-	uint16_t wep_on, wep_restrict, tx_key;
-	dldwd_keys_t keys;
+
+	u16 wep_on, wep_restrict, tx_key;
+	orinoco_keys_t keys;
+
+	int bitratemode;
+
  	char nick[IW_ESSID_MAX_SIZE+1];
 	char desired_essid[IW_ESSID_MAX_SIZE+1];
-	uint16_t frag_thresh, mwo_robust;
-	uint16_t channel;
-	uint16_t ap_density, rts_thresh;
-	uint16_t tx_rate_ctrl;
-	uint16_t pm_on, pm_mcast, pm_period, pm_timeout;
-	uint16_t preamble;
+	u16 frag_thresh, mwo_robust;
+	u16 channel;
+	u16 ap_density, rts_thresh;
+	u16 pm_on, pm_mcast, pm_period, pm_timeout;
+	u16 preamble;
 
-	int promiscuous, allmulti, mc_count;
+	int promiscuous, mc_count;
 
 #ifdef WIRELESS_SPY
 	int			spy_number;
@@ -102,16 +89,16 @@
 	struct proc_dir_entry *dir_dev;
 	struct proc_dir_entry *dir_regs;
 	struct proc_dir_entry *dir_recs;
-} dldwd_priv_t;
+};
 
 /*====================================================================*/
 
-extern struct list_head dldwd_instances;
+extern struct list_head orinoco_instances;
 
 #ifdef ORINOCO_DEBUG
-extern int dldwd_debug;
-#define DEBUG(n, args...) do { if (dldwd_debug>(n)) printk(KERN_DEBUG args); } while(0)
-#define DEBUGMORE(n, args...) do { if (dldwd_debug>(n)) printk(args); } while (0)
+extern int orinoco_debug;
+#define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
+#define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0)
 #else
 #define DEBUG(n, args...) do { } while (0)
 #define DEBUGMORE(n, args...) do { } while (0)
@@ -123,20 +110,20 @@
 #define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) )
 
 /* struct net_device methods */
-extern int dldwd_init(struct net_device *dev);
-extern int dldwd_xmit(struct sk_buff *skb, struct net_device *dev);
-extern void dldwd_tx_timeout(struct net_device *dev);
-
-extern int dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-extern int dldwd_change_mtu(struct net_device *dev, int new_mtu);
-extern void dldwd_set_multicast_list(struct net_device *dev);
+extern int orinoco_init(struct net_device *dev);
+extern int orinoco_xmit(struct sk_buff *skb, struct net_device *dev);
+extern void orinoco_tx_timeout(struct net_device *dev);
+
+extern int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+extern int orinoco_change_mtu(struct net_device *dev, int new_mtu);
+extern void orinoco_set_multicast_list(struct net_device *dev);
 
 /* utility routines */
-extern void dldwd_shutdown(dldwd_priv_t *dev);
-extern int dldwd_reset(dldwd_priv_t *dev);
-extern int dldwd_setup(dldwd_priv_t* priv);
-extern int dldwd_proc_dev_init(dldwd_priv_t *dev);
-extern void dldwd_proc_dev_cleanup(dldwd_priv_t *priv);
-extern void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs);
+extern void orinoco_shutdown(struct orinoco_private *dev);
+extern int orinoco_reset(struct orinoco_private *dev);
+extern int orinoco_setup(struct orinoco_private* priv);
+extern int orinoco_proc_dev_init(struct orinoco_private *dev);
+extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv);
+extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
 
-#endif
+#endif /* _ORINOCO_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/orinoco_cs.c linux/drivers/net/wireless/orinoco_cs.c
--- linux.orig/drivers/net/wireless/orinoco_cs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/orinoco_cs.c	Mon Jan 21 19:01:05 2002
@@ -1,4 +1,4 @@
-/* orinoco_cs.c 0.08a	- (formerly known as dldwd_cs.c)
+/* orinoco_cs.c 0.09b	- (formerly known as dldwd_cs.c)
  *
  * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
  * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -44,11 +44,13 @@
 
 /*====================================================================*/
 
-static char version[] __initdata = "orinoco_cs.c 0.08a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
+static char version[] __initdata = "orinoco_cs.c 0.09b (David Gibson <hermes@gibson.dropbear.id.au> and others)";
 
 MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
 MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
+#ifdef MODULE_LICENSE
 MODULE_LICENSE("Dual MPL/GPL");
+#endif
 
 /* Parameters that can be set with 'insmod' */
 
@@ -68,33 +70,31 @@
 MODULE_PARM(reset_cor, "i");
 MODULE_PARM(ignore_cis_vcc, "i");
 
-
 /* Pcmcia specific structure */
-typedef struct dldwd_card {
+struct orinoco_pccard {
 	dev_link_t link;
 	dev_node_t node;
-	int instance;
 
 	/* Common structure (fully included), see orinoco.h */
-	struct dldwd_priv  priv;
-} dldwd_card_t;
+	struct orinoco_private  priv;
+};
 
 /*
  * Function prototypes
  */
 
 /* struct net_device methods */
-static int dldwd_cs_open(struct net_device *dev);
-static int dldwd_cs_stop(struct net_device *dev);
+static int orinoco_cs_open(struct net_device *dev);
+static int orinoco_cs_stop(struct net_device *dev);
 
 /* PCMCIA gumpf */
-static void dldwd_cs_config(dev_link_t * link);
-static void dldwd_cs_release(u_long arg);
-static int dldwd_cs_event(event_t event, int priority,
+static void orinoco_cs_config(dev_link_t * link);
+static void orinoco_cs_release(u_long arg);
+static int orinoco_cs_event(event_t event, int priority,
 		       event_callback_args_t * args);
 
-static dev_link_t *dldwd_cs_attach(void);
-static void dldwd_cs_detach(dev_link_t *);
+static dev_link_t *orinoco_cs_attach(void);
+static void orinoco_cs_detach(dev_link_t *);
 
 /*
    The dev_info variable is the "key" that is used to match up this
@@ -115,7 +115,6 @@
 */
 
 static dev_link_t *dev_list; /* = NULL */
-static int num_instances; /* = 0 */
 
 /*====================================================================*/
 
@@ -127,10 +126,10 @@
 }
 
 static int
-dldwd_cs_open(struct net_device *dev)
+orinoco_cs_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 	int err;
 	
@@ -139,9 +138,9 @@
 	link->open++;
 	netif_device_attach(dev);
 	
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
-		dldwd_cs_stop(dev);
+		orinoco_cs_stop(dev);
 	else
 		netif_start_queue(dev);
 
@@ -151,17 +150,17 @@
 }
 
 static int
-dldwd_cs_stop(struct net_device *dev)
+orinoco_cs_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv;
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 
 	TRACE_ENTER(priv->ndev.name);
 
 	netif_stop_queue(dev);
 
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	
 	link->open--;
 
@@ -179,9 +178,9 @@
  * In fact, this seem necessary for Spectrum cards...
  */
 static int
-dldwd_cs_cor_reset(dldwd_priv_t *priv)
+orinoco_cs_cor_reset(struct orinoco_private *priv)
 {
-	dldwd_card_t* card = (dldwd_card_t *)priv->card;
+	struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
 	dev_link_t *link = &card->link;
 	conf_reg_t reg;
 	u_int default_cor; 
@@ -189,8 +188,8 @@
 	TRACE_ENTER(priv->ndev.name);
 
 	/* Doing it if hardware is gone is guaranteed crash */
-	if(!priv->hw_ready)
-		return(0);
+	if(! (link->state & DEV_CONFIG) )
+		return -ENODEV;
 
 	/* Save original COR value */
 	reg.Function = 0;
@@ -200,7 +199,7 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 	default_cor = reg.Value;
 
-	DEBUG(2, "dldwd : dldwd_cs_cor_reset() : cor=0x%X\n", default_cor);
+	DEBUG(2, "orinoco : orinoco_cs_cor_reset() : cor=0x%X\n", default_cor);
 
 	/* Soft-Reset card */
 	reg.Action = CS_WRITE;
@@ -209,6 +208,7 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 
 	/* Wait until the card has acknowledged our reset */
+	/* FIXME: mdelay() is deprecated -dgibson */
 	mdelay(1);
 
 	/* Restore original COR configuration index */
@@ -216,11 +216,12 @@
 	CardServices(AccessConfigurationRegister, link->handle, &reg);
 
 	/* Wait until the card has finished restarting */
+	/* FIXME: mdelay() is deprecated -dgibson */
 	mdelay(1);
 
 	TRACE_EXIT(priv->ndev.name);
 
-	return(0);
+	return 0;
 }
 
 /* Remove zombie instances (card removed, detach pending) */
@@ -228,17 +229,17 @@
 flush_stale_links(void)
 {
 	dev_link_t *link, *next;
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 	for (link = dev_list; link; link = next) {
 		next = link->next;
 		if (link->state & DEV_STALE_LINK)
-			dldwd_cs_detach(link);
+			orinoco_cs_detach(link);
 	}
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
 /*======================================================================
-  dldwd_cs_attach() creates an "instance" of the driver, allocating
+  orinoco_cs_attach() creates an "instance" of the driver, allocating
   local data structures for one device.  The device is registered
   with Card Services.
   
@@ -248,16 +249,16 @@
   ======================================================================*/
 
 static dev_link_t *
-dldwd_cs_attach(void)
+orinoco_cs_attach(void)
 {
-	dldwd_card_t *card;
-	dldwd_priv_t *priv;
+	struct orinoco_pccard *card;
+	struct orinoco_private *priv;
 	dev_link_t *link;
 	struct net_device *ndev;
 	client_reg_t client_reg;
 	int ret, i;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 	/* A bit of cleanup */
 	flush_stale_links();
 
@@ -272,13 +273,12 @@
 	/* Link both structure together */
 	priv = &(card->priv);
 	priv->card = card;
-	card->instance = num_instances++; /* FIXME: Racy? */
 	link = &card->link;
 	ndev = &priv->ndev;
 	link->priv = priv;
 
 	/* Initialize the dev_link_t structure */
-	link->release.function = &dldwd_cs_release;
+	link->release.function = &orinoco_cs_release;
 	link->release.data = (u_long) link;
 
 	/* Interrupt setup */
@@ -302,15 +302,15 @@
 	link->conf.IntType = INT_MEMORY_AND_IO;
 
 	/* Setup the common part */
-	if(dldwd_setup(priv) < 0) {
+	if(orinoco_setup(priv) < 0) {
 		kfree(card);
 		return NULL;
 	}
 
 	/* Overrides */
-	ndev->open = dldwd_cs_open;
-	ndev->stop = dldwd_cs_stop;
-	priv->card_reset_handler = dldwd_cs_cor_reset;
+	ndev->open = orinoco_cs_open;
+	ndev->stop = orinoco_cs_stop;
+	priv->card_reset_handler = orinoco_cs_cor_reset;
 
 	/* Register with Card Services */
 	link->next = dev_list;
@@ -321,21 +321,21 @@
 	    CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
 	    CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
 	    CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
-	client_reg.event_handler = &dldwd_cs_event;
+	client_reg.event_handler = &orinoco_cs_event;
 	client_reg.Version = 0x0210;
 	client_reg.event_callback_args.client_data = link;
 	ret = CardServices(RegisterClient, &link->handle, &client_reg);
 	if (ret != CS_SUCCESS) {
 		cs_error(link->handle, RegisterClient, ret);
-		dldwd_cs_detach(link);
+		orinoco_cs_detach(link);
 		link = NULL;
 		goto out;
 	}
 
  out:
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 	return link;
-}				/* dldwd_cs_attach */
+}				/* orinoco_cs_attach */
 
 /*======================================================================
   This deletes a driver "instance".  The device is de-registered
@@ -345,12 +345,12 @@
   ======================================================================*/
 
 static void
-dldwd_cs_detach(dev_link_t * link)
+orinoco_cs_detach(dev_link_t * link)
 {
 	dev_link_t **linkp;
-	dldwd_priv_t *priv = link->priv;
+	struct orinoco_private *priv = link->priv;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	/* Locate device structure */
 	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
@@ -388,14 +388,12 @@
 	}
 	kfree(priv->card);
 
-	num_instances--; /* FIXME: Racy? */
-
  out:
-	TRACE_EXIT("dldwd");
-}				/* dldwd_cs_detach */
+	TRACE_EXIT("orinoco");
+}				/* orinoco_cs_detach */
 
 /*======================================================================
-  dldwd_cs_config() is scheduled to run after a CARD_INSERTION event
+  orinoco_cs_config() is scheduled to run after a CARD_INSERTION event
   is received, to configure the PCMCIA socket, and to make the
   device available to the system.
   ======================================================================*/
@@ -407,11 +405,11 @@
 if (CardServices(fn, args) != 0) goto next_entry
 
 static void
-dldwd_cs_config(dev_link_t * link)
+orinoco_cs_config(dev_link_t * link)
 {
 	client_handle_t handle = link->handle;
-	dldwd_priv_t *priv = link->priv;
-	dldwd_card_t *card = (dldwd_card_t *)priv->card;
+	struct orinoco_private *priv = link->priv;
+	struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card;
 	hermes_t *hw = &priv->hw;
 	struct net_device *ndev = &priv->ndev;
 	tuple_t tuple;
@@ -422,7 +420,7 @@
 	cistpl_cftable_entry_t dflt = { 0 };
 	cisinfo_t info;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	CS_CHECK(ValidateCIS, handle, &info);
 
@@ -448,7 +446,7 @@
 	CS_CHECK(GetConfigurationInfo, handle, &conf);
 	link->conf.Vcc = conf.Vcc;
 
-	DEBUG(0, "dldwd_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", 
+	DEBUG(0, "orinoco_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", 
 	      link->conf.ConfigBase, link->conf.Vcc);
 
 	/*
@@ -470,7 +468,7 @@
 		CFG_CHECK(GetTupleData, handle, &tuple);
 		CFG_CHECK(ParseTuple, handle, &tuple, &parse);
 
-		DEBUG(0, "dldwd_cs_config: index = 0x%x, flags = 0x%x\n",
+		DEBUG(0, "orinoco_cs_config: index = 0x%x, flags = 0x%x\n",
 		      cfg->index, cfg->flags);
 
 		if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
@@ -490,14 +488,14 @@
 		if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
 			if (conf.Vcc !=
 			    cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
-				DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
+				DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
 				if(!ignore_cis_vcc)
 					goto next_entry;
 			}
 		} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
 			if (conf.Vcc !=
 			    dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
-				DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
+				DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n",  conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
 				if(!ignore_cis_vcc)
 					goto next_entry;
 			}
@@ -510,7 +508,7 @@
 			link->conf.Vpp1 = link->conf.Vpp2 =
 			    dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
 		
-		DEBUG(0, "dldwd_cs_config: We seem to have configured Vcc and Vpp\n");
+		DEBUG(0, "orinoco_cs_config: We seem to have configured Vcc and Vpp\n");
 
 		/* Do we need to allocate an interrupt? */
 		if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
@@ -570,7 +568,7 @@
 			for (i=0; i<4; i++)
 				link->irq.IRQInfo2 |= 1 << irq_list[i];
 		
-  		link->irq.Handler = dldwd_interrupt; 
+  		link->irq.Handler = orinoco_interrupt; 
   		link->irq.Instance = priv; 
 		
 		CS_CHECK(RequestIRQ, link->handle, &link->irq);
@@ -591,6 +589,12 @@
 	ndev->base_addr = link->io.BasePort1;
 	ndev->irq = link->irq.AssignedIRQ;
 
+	/* Now do a PCMCIA soft reset on the card, to make sure its in
+	   a sane state */
+	/* Optional because it really mess up old Lucent firmwares - Jean II */
+	if (reset_cor)
+		orinoco_cs_cor_reset(priv);
+
 	/* register_netdev will give us an ethX name */
 	ndev->name[0] = '\0';
 	/* Tell the stack we exist */
@@ -618,7 +622,7 @@
 	printk("\n");
 
 	/* And give us the proc nodes for debugging */
-	if (dldwd_proc_dev_init(priv) != 0) {
+	if (orinoco_proc_dev_init(priv) != 0) {
 		printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n",
 		       ndev->name);
 		goto failed;
@@ -627,12 +631,9 @@
 	/* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */
 	SET_MODULE_OWNER(ndev);
 	
-	/* Allow cor_reset, /proc & ioctls to act */
-	priv->hw_ready = 1;
-	
 	/* Do a Pcmcia soft reset of the card (optional) */
-	if(reset_cor)
-		dldwd_cs_cor_reset(priv);
+	if (reset_cor)
+		orinoco_cs_cor_reset(priv);
 
 	/*
 	   At this point, the dev_node_t structure(s) need to be
@@ -642,29 +643,29 @@
 	link->dev = &card->node;
 	link->state &= ~DEV_CONFIG_PENDING;
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return;
 
  cs_failed:
 	cs_error(link->handle, last_fn, last_ret);
  failed:
-	dldwd_cs_release((u_long) link);
+	orinoco_cs_release((u_long) link);
 
-	TRACE_EXIT("dldwd");
-}				/* dldwd_cs_config */
+	TRACE_EXIT("orinoco");
+}				/* orinoco_cs_config */
 
 /*======================================================================
-  After a card is removed, dldwd_cs_release() will unregister the
+  After a card is removed, orinoco_cs_release() will unregister the
   device, and release the PCMCIA configuration.  If the device is
   still open, this will be postponed until it is closed.
   ======================================================================*/
 
 static void
-dldwd_cs_release(u_long arg)
+orinoco_cs_release(u_long arg)
 {
 	dev_link_t *link = (dev_link_t *) arg;
-	dldwd_priv_t *priv = link->priv;
+	struct orinoco_private *priv = link->priv;
 
 	TRACE_ENTER(link->dev->dev_name);
 
@@ -681,7 +682,7 @@
 	}
 
 	/* Unregister proc entry */
-	dldwd_proc_dev_cleanup(priv);
+	orinoco_proc_dev_cleanup(priv);
 
 	/* Don't bother checking to see if these succeed or not */
 	CardServices(ReleaseConfiguration, link->handle);
@@ -692,7 +693,7 @@
 	link->state &= ~DEV_CONFIG;
 
 	TRACE_EXIT(link->dev->dev_name);
-}				/* dldwd_cs_release */
+}				/* orinoco_cs_release */
 
 /*======================================================================
   The card status event handler.  Mostly, this schedules other
@@ -705,39 +706,37 @@
   ======================================================================*/
 
 static int
-dldwd_cs_event(event_t event, int priority,
+orinoco_cs_event(event_t event, int priority,
 		       event_callback_args_t * args)
 {
 	dev_link_t *link = args->client_data;
-	dldwd_priv_t *priv = (dldwd_priv_t *)link->priv;
+	struct orinoco_private *priv = (struct orinoco_private *)link->priv;
 	struct net_device *dev = &priv->ndev;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	switch (event) {
 	case CS_EVENT_CARD_REMOVAL:
-		/* FIXME: Erg.. this whole hw_ready thing looks racy
-		   to me.  this may not be fixable without changin the
-		   PCMCIA subsystem, though */
-		priv->hw_ready = 0;
-		dldwd_shutdown(priv);
 		link->state &= ~DEV_PRESENT;
 		if (link->state & DEV_CONFIG) {
 			netif_stop_queue(dev);
+		}
+		orinoco_shutdown(priv);
+		if (link->state & DEV_CONFIG) {
 			netif_device_detach(dev);
 			mod_timer(&link->release, jiffies + HZ / 20);
 		}
 		break;
 	case CS_EVENT_CARD_INSERTION:
 		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
-		dldwd_cs_config(link);
+		orinoco_cs_config(link);
 		break;
 	case CS_EVENT_PM_SUSPEND:
 
 		link->state |= DEV_SUSPEND;
 		/* Fall through... */
 	case CS_EVENT_RESET_PHYSICAL:
-		dldwd_shutdown(priv);
+		orinoco_shutdown(priv);
 		/* Mark the device as stopped, to block IO until later */
 
 		if (link->state & DEV_CONFIG) {
@@ -757,13 +756,13 @@
 				     &link->conf);
 
 			if (link->open) {
-				if (dldwd_reset(priv) == 0) {
+				if (orinoco_reset(priv) == 0) {
 					netif_device_attach(dev);
 					netif_start_queue(dev);
 				} else {
 					printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n",
 					       dev->name);
-					dldwd_cs_stop(dev);
+					orinoco_cs_stop(dev);
 				}
 			}
 		}
@@ -774,17 +773,17 @@
 		break;
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 
 	return 0;
-}				/* dldwd_cs_event */
+}				/* orinoco_cs_event */
 
 static int __init
-init_dldwd_cs(void)
+init_orinoco_cs(void)
 {
 	servinfo_t serv;
 
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	printk(KERN_DEBUG "%s\n", version);
 
@@ -795,17 +794,17 @@
 		return -1;
 	}
 
-	register_pccard_driver(&dev_info, &dldwd_cs_attach, &dldwd_cs_detach);
+	register_pccard_driver(&dev_info, &orinoco_cs_attach, &orinoco_cs_detach);
 
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 	return 0;
 }
 
 static void __exit
-exit_dldwd_cs(void)
+exit_orinoco_cs(void)
 {
-	TRACE_ENTER("dldwd");
+	TRACE_ENTER("orinoco");
 
 	unregister_pccard_driver(&dev_info);
 
@@ -814,12 +813,12 @@
 	while (dev_list != NULL) {
 		del_timer(&dev_list->release);
 		if (dev_list->state & DEV_CONFIG)
-			dldwd_cs_release((u_long) dev_list);
-		dldwd_cs_detach(dev_list);
+			orinoco_cs_release((u_long) dev_list);
+		orinoco_cs_detach(dev_list);
 	}
 
-	TRACE_EXIT("dldwd");
+	TRACE_EXIT("orinoco");
 }
 
-module_init(init_dldwd_cs);
-module_exit(exit_dldwd_cs);
+module_init(init_orinoco_cs);
+module_exit(exit_orinoco_cs);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/net/wireless/orinoco_plx.c linux/drivers/net/wireless/orinoco_plx.c
--- linux.orig/drivers/net/wireless/orinoco_plx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/net/wireless/orinoco_plx.c	Wed Feb 13 17:37:44 2002
@@ -1,16 +1,8 @@
-/* orinoco_plx.c 0.01
+/* orinoco_plx.c 0.09b
  * 
  * Driver for Prism II devices which would usually be driven by orinoco_cs,
  * but are connected to the PCI bus by a PLX9052. 
  *
- * Specifically here we're talking about the SMC2602W (EZConnect
- * Wireless PCI Adaptor)
- *
- * The actual driving is done by orinoco.c, this is just resource
- * allocation stuff.  The explanation below is courtesy of Ryan Niemi
- * on the linux-wlan-ng list at
- * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
- *
  * Copyright (C) 2001 Daniel Barlow <dan@telent.net>
  *
  * The contents of this file are subject to the Mozilla Public License
@@ -34,6 +26,22 @@
  * provisions above, a recipient may use your version of this file
  * under either the MPL or the GPL.
 
+ * Caution: this is experimental and probably buggy.  For success and
+ * failure reports for different cards and adaptors, see
+ * orinoco_plx_pci_id_table near the end of the file.  If you have a
+ * card we don't have the PCI id for, and looks like it should work,
+ * drop me mail with the id and "it works"/"it doesn't work".
+ *
+ * Note: if everything gets detected fine but it doesn't actually send
+ * or receive packets, your first port of call should probably be to   
+ * try newer firmware in the card.  Especially if you're doing Ad-Hoc
+ * modes
+ *
+ * The actual driving is done by orinoco.c, this is just resource
+ * allocation stuff.  The explanation below is courtesy of Ryan Niemi
+ * on the linux-wlan-ng list at
+ * http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
+
 The PLX9052-based cards (WL11000 and several others) are a different
 beast than the usual PCMCIA-based PRISM2 configuration expected by
 wlan-ng. Here's the general details on how the WL11000 PCI adapter
@@ -95,14 +103,6 @@
 not have time for a while..
 
 ---end of mail---
-
-  Bus  0, device   4, function  0:
-    Network controller: Unknown vendor Unknown device (rev 2).
-      Vendor id=1638. Device id=1100.
-      Medium devsel.  Fast back-to-back capable.  IRQ 10.  
-      I/O at 0x1000 [0x1001].
-      Non-prefetchable 32 bit memory at 0x40000000 [0x40000000].
-      I/O at 0x10c0 [0x10c1].
 */
 
 #include <linux/config.h>
@@ -140,25 +140,31 @@
 #include "hermes.h"
 #include "orinoco.h"
 
+static char version[] __initdata = "orinoco_plx.c 0.09b (Daniel Barlow <dan@telent.net>)";
 MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
 MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
+#ifdef MODULE_LICENSE
 MODULE_LICENSE("Dual MPL/GPL");
+#endif
 
 static dev_info_t dev_info = "orinoco_plx";
 
-#define COR_OFFSET    0x3e0	/* COR attribute offset of Prism2 PC card */
-#define COR_VALUE     0x41	/* Enable PC card with interrupt in level trigger */
+#define COR_OFFSET    (0x3e0 / 2)	/* COR attribute offset of Prism2 PC card */
+#define COR_VALUE     (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
+
+#define PLX_INTCSR       0x4c /* Interrupt Control and Status Register */
+#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
 
 static int orinoco_plx_open(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
 	int err;
 
 	netif_device_attach(dev);
 
-	err = dldwd_reset(priv);
+	err = orinoco_reset(priv);
 	if (err)
-		printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()",
+		printk(KERN_ERR "%s: orinoco_reset failed in orinoco_plx_open()",
 		       dev->name);
 	else
 		netif_start_queue(dev);
@@ -168,108 +174,217 @@
 
 static int orinoco_plx_stop(struct net_device *dev)
 {
-	dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
+	struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
 	netif_stop_queue(dev);
-	dldwd_shutdown(priv);
+	orinoco_shutdown(priv);
 	return 0;
 }
 
 static void
 orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	dldwd_interrupt(irq, ((struct net_device *) dev_id)->priv, regs);
+	orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
 }
 
+static const u16 cis_magic[] = {
+	0x0001, 0x0003, 0x0000, 0x0000, 0x00ff, 0x0017, 0x0004, 0x0067
+};
+
 static int orinoco_plx_init_one(struct pci_dev *pdev,
 				const struct pci_device_id *ent)
 {
-	struct net_device *dev;
-	unsigned long pccard_ioaddr;
+	int err = 0;
+	u16 *attr_mem = NULL;
+	u32 reg, addr;
+	struct orinoco_private *priv = NULL;
+	unsigned long pccard_ioaddr = 0;
+	unsigned long pccard_iolen = 0;
+	struct net_device *dev = NULL;
+	int netdev_registered = 0;
 	int i;
-	int reg;
-	unsigned char *attr_mem;
-	dldwd_priv_t *priv;
 
-	if ((i = pci_enable_device(pdev)))
+	TRACE_ENTER("orinoco_plx");
+
+	err = pci_enable_device(pdev);
+	if (err)
 		return -EIO;
 
 	/* Resource 2 is mapped to the PCMCIA space */
-	attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000);
-	/* and 3 to the PCMCIA slot I/O address space */
-	pccard_ioaddr = pci_resource_start(pdev, 3);
+	attr_mem = ioremap(pci_resource_start(pdev, 2), PAGE_SIZE);
+	if (! attr_mem)
+		goto fail;
+
+	printk(KERN_DEBUG "orinoco_plx: CIS: ");
+	for (i = 0; i < 16; i++) {
+		printk("%02X:", (int)attr_mem[i]);
+	}
+	printk("\n");
 
 	/* Verify whether PC card is present */
-	if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 ||
-	    attr_mem[4] != 0x00 || attr_mem[6] != 0x00 ||
-	    attr_mem[8] != 0xFF || attr_mem[10] != 0x17 ||
-	    attr_mem[12] != 0x04 || attr_mem[14] != 0x67) {
+	/* FIXME: we probably need to be smarted about this */
+	if (memcmp(attr_mem, cis_magic, sizeof(cis_magic)) != 0) {
 		printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n");
-		return -EIO;
+		err = -EIO;
+		goto fail;
 	}
+
 	/* PCMCIA COR is the first byte following CIS: this write should
 	 * enable I/O mode and select level-triggered interrupts */
 	attr_mem[COR_OFFSET] = COR_VALUE;
+	mdelay(1);
 	reg = attr_mem[COR_OFFSET];
-	/* assert(reg==COR_VALUE); doesn't work */
-	iounmap(attr_mem);	/* done with this now, it seems */
-	if (!request_region(pccard_ioaddr,
-			    pci_resource_len(pdev, 3), dev_info)) {
+	if (reg != COR_VALUE) {
+		printk(KERN_ERR "orinoco_plx: Error setting COR value (reg=%x)\n", reg);
+		goto fail;
+	}			
+
+	iounmap(attr_mem);
+	attr_mem = NULL; /* done with this now, it seems */
+
+	/* bjoern: We need to tell the card to enable interrupts, in
+	   case the serial eprom didn't do this already. See the
+	   PLX9052 data book, p8-1 and 8-24 for reference. */
+	addr = pci_resource_start(pdev, 1);
+	reg = 0;
+	reg = inl(addr+PLX_INTCSR);
+	if(reg & PLX_INTCSR_INTEN)
+		printk(KERN_DEBUG "orinoco_plx: "
+		       "Local Interrupt already enabled\n");
+	else {
+		reg |= PLX_INTCSR_INTEN;
+		outl(reg, addr+PLX_INTCSR);
+		reg = inl(addr+PLX_INTCSR);
+		if(!(reg & PLX_INTCSR_INTEN)) {
+			printk(KERN_ERR "orinoco_plx: "
+			       "Couldn't enable Local Interrupts\n");
+			goto fail;
+		}
+	}
+
+	/* and 3 to the PCMCIA slot I/O address space */
+	pccard_ioaddr = pci_resource_start(pdev, 3);
+	pccard_iolen = pci_resource_len(pdev, 3);
+	if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
 		printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n",
-		       pci_resource_len(pdev, 3), pccard_ioaddr);
-		return -EBUSY;
+		       pccard_iolen, pccard_ioaddr);
+		pccard_ioaddr = 0;
+		err = -EBUSY;
+		goto fail;
+	}
+
+	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+	if (! priv) {
+		err = -ENOMEM;
+		goto fail;
 	}
-	if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL)))
-		return -ENOMEM;
 	memset(priv, 0, sizeof(*priv));
+
 	dev = &priv->ndev;
 
-	dldwd_setup(priv);	/* XXX clean up if <0 */
-	dev->irq = pdev->irq;
+	err = orinoco_setup(priv);
+	if (err)
+		goto fail;
 	dev->base_addr = pccard_ioaddr;
 	dev->open = orinoco_plx_open;
 	dev->stop = orinoco_plx_stop;
 	priv->card_reset_handler = NULL; /* We have no reset handler */
+	SET_MODULE_OWNER(dev);
 
 	printk(KERN_DEBUG
-	       "Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n",
-	       pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr);
+	       "Detected Orinoco/Prism2 PLX device at %s irq:%d, io addr:0x%lx\n",
+	       pdev->slot_name, pdev->irq, pccard_ioaddr);
 
-	hermes_struct_init(&(priv->hw), dev->base_addr);	/* XXX */
-	dev->name[0] = '\0';	/* name defaults to ethX */
-	register_netdev(dev);
-	request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name,
-		    dev);
-	if (dldwd_proc_dev_init(priv) != 0) {
-		printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
-		return -EIO;
+	hermes_struct_init(&(priv->hw), dev->base_addr);
+	pci_set_drvdata(pdev, priv);
+
+	err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
+	if (err) {
+		printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
+		err = -EBUSY;
+		goto fail;
 	}
+	dev->irq = pdev->irq;
 
-	SET_MODULE_OWNER(dev);
-	priv->hw_ready = 1;
+	err = register_netdev(dev);
+	if (err)
+		goto fail;
+	netdev_registered = 1;
+
+	err = orinoco_proc_dev_init(priv);
+	if (err)
+		goto fail;
+
+	TRACE_EXIT("orinoco_plx");
 
-	/* if(reset_cor) dldwd_cs_cor_reset(priv); */
 	return 0;		/* succeeded */
+
+ fail:	
+	printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n");
+
+	if (priv) {
+		orinoco_proc_dev_cleanup(priv);
+
+		if (netdev_registered)
+			unregister_netdev(dev);
+		
+		if (dev->irq)
+			free_irq(dev->irq, priv);
+		
+		kfree(priv);
+	}
+
+	if (pccard_ioaddr)
+		release_region(pccard_ioaddr, pccard_iolen);
+
+	if (attr_mem)
+		iounmap(attr_mem);
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_plx");
+	
+	return err;
 }
 
 static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
 {
-	struct net_device *dev = pci_get_drvdata(pdev);
-	dldwd_priv_t *priv = dev->priv;
+	struct orinoco_private *priv = pci_get_drvdata(pdev);
+	struct net_device *dev = &priv->ndev;
 
-	if (!dev)
+	TRACE_ENTER("orinoco_plx");
+
+	if (!priv)
 		BUG();
 
-	dldwd_proc_dev_cleanup(priv);
-	free_irq(dev->irq, dev);
+	orinoco_proc_dev_cleanup(priv);
+
 	unregister_netdev(dev);
-	release_region(dev->base_addr, 0x40);
-	kfree(dev->priv);
-	pci_set_drvdata(pdev, NULL);
+		
+	if (dev->irq)
+		free_irq(dev->irq, priv);
+		
+	kfree(priv);
+
+	release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
+
+	pci_disable_device(pdev);
+
+	TRACE_EXIT("orinoco_plx");
 }
 
 
 static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
-	{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},
+	{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,},	/* Netgear MA301 */
+#if 0
+	{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,},	/* Correga */
+#endif
+	{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},	/* SMC EZConnect SMC2602W,
+							   Eumitcom PCI WL11000,
+							   Addtron AWA-100*/
+	{0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,},	/* Global Sun Tech GL24110P */
+	{0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,},	/* Reported working, but unknown */
+	{0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,},	/* Linksys WDT11 */
+	{0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,},	/* USR 2415 */
 	{0,},
 };
 
@@ -286,12 +401,15 @@
 
 static int __init orinoco_plx_init(void)
 {
+	printk(KERN_DEBUG "%s\n", version);
 	return pci_module_init(&orinoco_plx_driver);
 }
 
 extern void __exit orinoco_plx_exit(void)
 {
 	pci_unregister_driver(&orinoco_plx_driver);
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(HZ);
 }
 
 module_init(orinoco_plx_init);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/ChangeLog linux/drivers/parport/ChangeLog
--- linux.orig/drivers/parport/ChangeLog	Tue Jan 22 17:51:23 2002
+++ linux/drivers/parport/ChangeLog	Mon Jan 14 19:08:50 2002
@@ -1,3 +1,56 @@
+2002-01-13  Niels Kristian Bech Jensen  <nkbj@image.dk>
+
+	* parport_pc.c: Change some occurrences of frob_set_mode to
+	ECR_WRITE.  This fixes PLIP.
+
+2002-01-04  Tim Waugh  <twaugh@redhat.com>
+
+	* share.c (parport_claim_or_block): Sleep interruptibly to prevent
+	a possible deadlock.
+
+2001-12-07  Damian Gruszka  <damian.gruszka@VisionSystems.de>
+
+	* parport_pc.c (ECR_WRITE): Define.  If there are forbidden bits
+	in the ECR register for some chips, this will be a useful place to
+	put that knowledge.
+	(change_mode): Use ECR_WRITE.
+	(parport_pc_restore_state): Likewise.
+	(parport_ECPPS2_supported): Likewise.
+	(parport_ECPEPP_supported): Likewise.
+	(irq_probe_EPP): Likewise.
+	(programmable_irq_support): Likewise.
+	(programmable_dma_support): Likewise.
+	(parport_pc_probe_port): Likewise.
+
+	(frob_set_mode): New function.  Set the mode bits of the ECR.
+	(get_fifo_residue): Use frob_set_mode.
+	(parport_pc_ecpepp_read_data): Likewise.
+	(parport_pc_ecpepp_write_data): Likewise.
+	(parport_pc_ecpepp_read_addr): Likewise.
+	(parport_pc_ecpepp_write_addr): Likewise.
+	(parport_pc_compat_write_block_pio): Likewise.
+	(parport_pc_ecp_write_block_pio): Likewise.
+	(parport_ECR_present): Likewise.
+	(parport_ECP_supported): Likewise.
+	(parport_EPP_supported): Likewise.
+	(parport_ECPEPP_supported): Likewise.
+	(programmable_irq_support): Likewise.
+	(irq_probe_ECP): Likewise.
+	(programmable_dma_support): Likewise.
+
+	(parport_pc_enable_irq): Only enable interrupts if we know which
+	IRQ line they will come from.
+	(parport_pc_init_state): Set nErrIntrEn at initialisation.
+	(parport_pc_restore_state): Only write writable bits of CTR.
+	(parport_irq_probe): If no IRQ is found, take ackIntEn out of the
+	writable bit set.
+
+2001-12-07  Tim Waugh  <twaugh@redhat.com>
+
+	* parport_pc.c (parport_pc_fifo_write_block_pio): Correct typo.
+	(parport_pc_init_state): Only set ackIntEn if we know which IRQ
+	line the interrupts will come from.
+
 2001-12-07  Tim Waugh  <twaugh@redhat.com>
 
 	* ieee1284_ops.c (parport_ieee1284_epp_write_addr,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- linux.orig/drivers/parport/parport_pc.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/parport/parport_pc.c	Mon Jan 14 19:08:50 2002
@@ -73,6 +73,8 @@
 #define ECR_VND 05
 #define ECR_TST 06
 #define ECR_CNF 07
+#define ECR_MODE_MASK 0xe0
+#define ECR_WRITE(p,v) frob_econtrol((p),0xff,(v))
 
 #undef DEBUG
 
@@ -100,13 +102,22 @@
 static void frob_econtrol (struct parport *pb, unsigned char m,
 			   unsigned char v)
 {
-	unsigned char ectr = inb (ECONTROL (pb));
+	unsigned char ectr = 0;
+
+	if (m != 0xff)
+		ectr = inb (ECONTROL (pb));
+
 	DPRINTK (KERN_DEBUG "frob_econtrol(%02x,%02x): %02x -> %02x\n",
 		m, v, ectr, (ectr & ~m) ^ v);
 
 	outb ((ectr & ~m) ^ v, ECONTROL (pb));
 }
 
+static void __inline__ frob_set_mode (struct parport *p, int mode)
+{
+	frob_econtrol (p, ECR_MODE_MASK, mode << 5);
+}
+
 #ifdef CONFIG_PARPORT_PC_FIFO
 /* Safely change the mode bits in the ECR 
    Returns:
@@ -117,7 +128,6 @@
 static int change_mode(struct parport *p, int m)
 {
 	const struct parport_pc_private *priv = p->physport->private_data;
-	int ecr = ECONTROL(p);
 	unsigned char oecr;
 	int mode;
 
@@ -129,7 +139,7 @@
 	}
 
 	/* Bits <7:5> contain the mode. */
-	oecr = inb (ecr);
+	oecr = inb (ECONTROL (p));
 	mode = (oecr >> 5) & 0x7;
 	if (mode == m) return 0;
 
@@ -166,13 +176,13 @@
 		/* We have to go through mode 001 */
 		oecr &= ~(7 << 5);
 		oecr |= ECR_PS2 << 5;
-		outb (oecr, ecr);
+		ECR_WRITE (p, oecr);
 	}
 
 	/* Set the mode. */
 	oecr &= ~(7 << 5);
 	oecr |= m << 5;
-	outb (oecr, ecr);
+	ECR_WRITE (p, oecr);
 	return 0;
 }
 
@@ -197,10 +207,10 @@
 		residue);
 
 	/* Reset the FIFO. */
-	frob_econtrol (p, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (p, ECR_PS2);
 
 	/* Now change to config mode and clean up. FIXME */
-	frob_econtrol (p, 0xe0, ECR_CNF << 5);
+	frob_set_mode (p, ECR_CNF);
 	cnfga = inb (CONFIGA (p));
 	printk (KERN_DEBUG "%s: cnfgA contains 0x%02x\n", p->name, cnfga);
 
@@ -213,7 +223,7 @@
 	 * PWord != 1 byte. */
 
 	/* Back to PS2 mode. */
-	frob_econtrol (p, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (p, ECR_PS2);
 
 	DPRINTK (KERN_DEBUG "*** get_fifo_residue: done residue collecting (ecr = 0x%2.2x)\n", inb (ECONTROL (p)));
 	return residue;
@@ -331,7 +341,8 @@
 
 void parport_pc_enable_irq(struct parport *p)
 {
-	__parport_pc_frob_control (p, 0x10, 0x10);
+	if (p->irq != PARPORT_IRQ_NONE)
+		__parport_pc_frob_control (p, 0x10, 0x10);
 }
 
 void parport_pc_data_forward (struct parport *p)
@@ -346,8 +357,14 @@
 
 void parport_pc_init_state(struct pardevice *dev, struct parport_state *s)
 {
-	s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0);
-	s->u.pc.ecr = 0x24;
+	s->u.pc.ctr = 0xc;
+	if (dev->irq_func &&
+	    dev->port->irq != PARPORT_IRQ_NONE)
+		/* Set ackIntEn */
+		s->u.pc.ctr |= 0x10;
+
+	s->u.pc.ecr = 0x34; /* NetMos chip can cause problems 0x24;
+			     * D.Gruszka VScom */
 }
 
 void parport_pc_save_state(struct parport *p, struct parport_state *s)
@@ -361,10 +378,11 @@
 void parport_pc_restore_state(struct parport *p, struct parport_state *s)
 {
 	struct parport_pc_private *priv = p->physport->private_data;
-	outb (s->u.pc.ctr, CONTROL (p));
-	priv->ctr = s->u.pc.ctr;
+	register unsigned char c = s->u.pc.ctr & priv->ctr_writable;
+	outb (c, CONTROL (p));
+	priv->ctr = c;
 	if (priv->ecr)
-		outb (s->u.pc.ecr, ECONTROL (p));
+		ECR_WRITE (p, s->u.pc.ecr);
 }
 
 #ifdef CONFIG_PARPORT_1284
@@ -516,11 +534,11 @@
 {
 	size_t got;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_data_reverse (port);
 	parport_pc_write_control (port, 0x4);
 	got = parport_pc_epp_read_data (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return got;
 }
@@ -531,11 +549,11 @@
 {
 	size_t written;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_write_control (port, 0x4);
 	parport_pc_data_forward (port);
 	written = parport_pc_epp_write_data (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return written;
 }
@@ -545,11 +563,11 @@
 {
 	size_t got;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_data_reverse (port);
 	parport_pc_write_control (port, 0x4);
 	got = parport_pc_epp_read_addr (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return got;
 }
@@ -560,11 +578,11 @@
 {
 	size_t written;
 
-	frob_econtrol (port, 0xe0, ECR_EPP << 5);
+	frob_set_mode (port, ECR_EPP);
 	parport_pc_write_control (port, 0x4);
 	parport_pc_data_forward (port);
 	written = parport_pc_epp_write_addr (port, buf, length, flags);
-	frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+	frob_set_mode (port, ECR_PS2);
 
 	return written;
 }
@@ -612,7 +630,7 @@
 			/* FIFO is full. Wait for interrupt. */
 
 			/* Clear serviceIntr */
-			outb (ecrval & ~(1<<2), ECONTROL (port));
+			ECR_WRITE (port, ecrval & ~(1<<2));
 		false_alarm:
 			ret = parport_wait_event (port, HZ);
 			if (ret < 0) break;
@@ -663,7 +681,7 @@
 		left--;
         }
 
-dump_parport_state ("leave fifo_write_block_dma", port);
+dump_parport_state ("leave fifo_write_block_pio", port);
 	return length - left;
 }
 
@@ -840,7 +858,7 @@
 		printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
 
 		/* Prevent further data transfer. */
-		frob_econtrol (port, 0xe0, ECR_TST << 5);
+		frob_set_mode (port, ECR_TST);
 
 		/* Adjust for the contents of the FIFO. */
 		for (written -= priv->fifo_depth; ; written++) {
@@ -852,7 +870,7 @@
 		}
 
 		/* Reset the FIFO and return to PS2 mode. */
-		frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+		frob_set_mode (port, ECR_PS2);
 	}
 
 	r = parport_wait_peripheral (port,
@@ -937,7 +955,7 @@
 		printk (KERN_DEBUG "%s: FIFO is stuck\n", port->name);
 
 		/* Prevent further data transfer. */
-		frob_econtrol (port, 0xe0, ECR_TST << 5);
+		frob_set_mode (port, ECR_TST);
 
 		/* Adjust for the contents of the FIFO. */
 		for (written -= priv->fifo_depth; ; written++) {
@@ -949,7 +967,7 @@
 		}
 
 		/* Reset the FIFO and return to PS2 mode. */
-		frob_econtrol (port, 0xe0, ECR_PS2 << 5);
+		frob_set_mode (port, ECR_PS2);
 
 		/* Host transfer recovery. */
 		parport_pc_data_reverse (port); /* Must be in PS2 mode */
@@ -1116,7 +1134,7 @@
 			}
 
 			/* Clear serviceIntr */
-			outb (ecrval & ~(1<<2), ECONTROL (port));
+			ECR_WRITE (port, ecrval & ~(1<<2));
 		false_alarm:
 dump_parport_state ("waiting", port);
 			ret = parport_wait_event (port, HZ);
@@ -1719,7 +1737,7 @@
 	if ((inb (ECONTROL (pb)) & 0x3 ) != 0x1)
 		goto no_reg;
 
-	outb (0x34, ECONTROL (pb));
+	ECR_WRITE (pb, 0x34);
 	if (inb (ECONTROL (pb)) != 0x35)
 		goto no_reg;
 
@@ -1727,7 +1745,7 @@
 	outb (0xc, CONTROL (pb));
 	
 	/* Go to mode 000 */
-	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+	frob_set_mode (pb, ECR_SPP);
 
 	return 1;
 
@@ -1797,8 +1815,8 @@
 		return 0;
 
 	/* Find out FIFO depth */
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
-	outb (ECR_TST << 5, ECONTROL (pb)); /* TEST FIFO */
+	ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
+	ECR_WRITE (pb, ECR_TST << 5); /* TEST FIFO */
 	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02); i++)
 		outb (0xaa, FIFO (pb));
 
@@ -1807,7 +1825,7 @@
 	 * it doesn't support ECP or FIFO MODE
 	 */
 	if (i == 1024) {
-		outb (ECR_SPP << 5, ECONTROL (pb));
+		ECR_WRITE (pb, ECR_SPP << 5);
 		return 0;
 	}
 
@@ -1837,9 +1855,9 @@
 	priv->writeIntrThreshold = i;
 
 	/* Find out readIntrThreshold */
-	frob_econtrol (pb, 0xe0, ECR_PS2 << 5); /* Reset FIFO and enable PS2 */
+	frob_set_mode (pb, ECR_PS2); /* Reset FIFO and enable PS2 */
 	parport_pc_data_reverse (pb); /* Must be in PS2 mode */
-	frob_econtrol (pb, 0xe0, ECR_TST << 5); /* Test FIFO */
+	frob_set_mode (pb, ECR_TST); /* Test FIFO */
 	frob_econtrol (pb, 1<<2, 1<<2);
 	frob_econtrol (pb, 1<<2, 0);
 	for (i = 1; i <= priv->fifo_depth; i++) {
@@ -1858,8 +1876,8 @@
 
 	priv->readIntrThreshold = i;
 
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
-	outb (0xf4, ECONTROL (pb)); /* Configuration mode */
+	ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
+	ECR_WRITE (pb, 0xf4); /* Configuration mode */
 	config = inb (CONFIGA (pb));
 	pword = (config >> 4) & 0x7;
 	switch (pword) {
@@ -1904,7 +1922,7 @@
 	}
 
 	/* Go back to mode 000 */
-	frob_econtrol (pb, 0xe0, ECR_SPP << 5);
+	frob_set_mode (pb, ECR_SPP);
 
 	return 1;
 }
@@ -1920,11 +1938,9 @@
 		return 0;
 
 	oecr = inb (ECONTROL (pb));
-	outb (ECR_PS2 << 5, ECONTROL (pb));
-	
+	ECR_WRITE (pb, ECR_PS2 << 5);
 	result = parport_PS2_supported(pb);
-
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 	return result;
 }
 
@@ -1956,7 +1972,7 @@
 	if (priv->ecr) {
 		unsigned char i;
 		for (i = 0x00; i < 0x80; i += 0x20) {
-			outb (i, ECONTROL (pb));
+			ECR_WRITE (pb, i);
 			if (clear_epp_timeout (pb)) {
 				/* Phony EPP in ECP. */
 				return 0;
@@ -1987,11 +2003,11 @@
 
 	oecr = inb (ECONTROL (pb));
 	/* Search for SMC style EPP+ECP mode */
-	outb (0x80, ECONTROL (pb));
+	ECR_WRITE (pb, 0x80);
 	outb (0x04, CONTROL (pb));
 	result = parport_EPP_supported(pb);
 
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 
 	if (result) {
 		/* Set up access functions to use ECP+EPP hardware. */
@@ -2028,12 +2044,12 @@
 		PARPORT_IRQ_NONE, 7, 9, 10, 11, 14, 15, 5
 	};
 
-	outb (ECR_CNF << 5, ECONTROL (pb)); /* Configuration MODE */
+	ECR_WRITE (pb, ECR_CNF << 5); /* Configuration MODE */
 
 	intrLine = (inb (CONFIGB (pb)) >> 3) & 0x07;
 	irq = lookup[intrLine];
 
-	outb (oecr, ECONTROL (pb));
+	ECR_WRITE (pb, oecr);
 	return irq;
 }
 
@@ -2045,16 +2061,16 @@
 	sti();
 	irqs = probe_irq_on();
 		
-	outb (ECR_SPP << 5, ECONTROL (pb)); /* Reset FIFO */
-	outb ((ECR_TST << 5) | 0x04, ECONTROL (pb));
-	outb (ECR_TST << 5, ECONTROL (pb));
+	ECR_WRITE (pb, ECR_SPP << 5); /* Reset FIFO */
+	ECR_WRITE (pb, (ECR_TST << 5) | 0x04);
+	ECR_WRITE (pb, ECR_TST << 5);
 
 	/* If Full FIFO sure that writeIntrThreshold is generated */
 	for (i=0; i < 1024 && !(inb (ECONTROL (pb)) & 0x02) ; i++) 
 		outb (0xaa, FIFO (pb));
 		
 	pb->irq = probe_irq_off(irqs);
-	outb (ECR_SPP << 5, ECONTROL (pb));
+	ECR_WRITE (pb, ECR_SPP << 5);
 
 	if (pb->irq <= 0)
 		pb->irq = PARPORT_IRQ_NONE;
@@ -2096,7 +2112,7 @@
 
 	pb->irq = probe_irq_off (irqs);
 	if (pb->modes & PARPORT_MODE_PCECR)
-		outb (oecr, ECONTROL (pb));
+		ECR_WRITE (pb, oecr);
 	parport_pc_write_control(pb, 0xc);
 
 	if (pb->irq <= 0)
@@ -2121,7 +2137,9 @@
  */
 static int __devinit parport_irq_probe(struct parport *pb)
 {
-	const struct parport_pc_private *priv = pb->private_data;
+	struct parport_pc_private *priv = pb->private_data;
+
+	priv->ctr_writable |= 0x10;
 
 	if (priv->ecr) {
 		pb->irq = programmable_irq_support(pb);
@@ -2147,6 +2165,9 @@
 	if (pb->irq == PARPORT_IRQ_NONE)
 		pb->irq = get_superio_irq(pb);
 
+	if (pb->irq == PARPORT_IRQ_NONE)
+		priv->ctr_writable &= ~0x10;
+
 	return pb->irq;
 }
 
@@ -2158,7 +2179,7 @@
 	unsigned char oecr = inb (ECONTROL (p));
 	int dma;
 
-	frob_econtrol (p, 0xe0, ECR_CNF << 5);
+	frob_set_mode (p, ECR_CNF);
 	
 	dma = inb (CONFIGB(p)) & 0x07;
 	/* 000: Indicates jumpered 8-bit DMA if read-only.
@@ -2166,7 +2187,7 @@
 	if ((dma & 0x03) == 0)
 		dma = PARPORT_DMA_NONE;
 
-	outb (oecr, ECONTROL (p));
+	ECR_WRITE (p, oecr);
 	return dma;
 }
 
@@ -2212,7 +2233,7 @@
 	}
 	memcpy (ops, &parport_pc_ops, sizeof (struct parport_operations));
 	priv->ctr = 0xc;
-	priv->ctr_writable = 0xff;
+	priv->ctr_writable = ~0x10;
 	priv->ecr = 0;
 	priv->fifo_depth = 0;
 	priv->dma_buf = 0;
@@ -2374,7 +2395,7 @@
 		 * Put the ECP detected port in PS2 mode.
 		 * Do this also for ports that have ECR but don't do ECP.
 		 */
-		outb (0x34, ECONTROL (p));
+		ECR_WRITE (p, 0x34);
 
 	parport_pc_write_data(p, 0);
 	parport_pc_data_forward (p);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/parport/share.c linux/drivers/parport/share.c
--- linux.orig/drivers/parport/share.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/parport/share.c	Mon Jan  7 13:38:04 2002
@@ -1011,7 +1011,11 @@
 		/* If dev->waiting is clear now, an interrupt
 		   gave us the port and we would deadlock if we slept.  */
 		if (dev->waiting) {
-			sleep_on(&dev->wait_q);
+			interruptible_sleep_on (&dev->wait_q);
+			if (signal_pending (current)) {
+				restore_flags (flags);
+				return -EINTR;
+			}
 			r = 1;
 		} else {
 			r = 0;
@@ -1084,7 +1088,7 @@
 		if (pd->waiting & 2) { /* sleeping in claim_or_block */
 			parport_claim(pd);
 			if (waitqueue_active(&pd->wait_q))
-				wake_up(&pd->wait_q);
+				wake_up_interruptible(&pd->wait_q);
 			return;
 		} else if (pd->wakeup) {
 			pd->wakeup(pd->private);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/pci/pci.ids linux/drivers/pci/pci.ids
--- linux.orig/drivers/pci/pci.ids	Tue Jan 22 17:51:23 2002
+++ linux/drivers/pci/pci.ids	Tue Jan  8 18:08:44 2002
@@ -1179,11 +1179,21 @@
 	0017  Paddington Mac I/O
 	0018  UniNorth FireWire
 	0019  KeyLargo USB
-	001e  UniNorth PCI
+	001e  UniNorth Internal PCI
 	001f  UniNorth PCI
 	0020  UniNorth AGP
-	0021  UniNorth GMAC
+	0021  UniNorth GMAC (Sun GEM)
 	0022  KeyLargo Mac I/O
+	0024  UniNorth/Pangea GMAC (Sun GEM)
+	0025  KeyLargo/Pangea Mac I/O
+	0026  KeyLargo/Pangea USB
+	0027  UniNorth/Pangea AGP
+	0028  UniNorth/Pangea PCI
+	0029  UniNorth/Pangea Internal PCI
+	002d  UniNorth 1.5 AGP
+	002e  UniNorth 1.5 PCI
+	002f  UniNorth 1.5 Internal PCI
+	0030  UniNorth/Pangea FireWire
 106c  Hyundai Electronics America
 	8801  Dual Pentium ISA/PCI Motherboard
 	8802  PowerPC ISA/PCI Motherboard
@@ -2601,6 +2611,8 @@
 	0005  ATP850UF
 	0006  ATP860 NO-BIOS
 	0007  ATP860
+	0008  ATP865 NO-ROM
+	0009  ATP865
 	8002  AEC6710 SCSI-2 Host Adapter
 	8010  AEC6712UW SCSI
 	8020  AEC6712U SCSI
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/pci/quirks.c linux/drivers/pci/quirks.c
--- linux.orig/drivers/pci/quirks.c	Sun Nov 11 18:09:33 2001
+++ linux/drivers/pci/quirks.c	Wed Feb 13 17:44:26 2002
@@ -444,13 +444,15 @@
 static void __init quirk_amd_ordering(struct pci_dev *dev)
 {
 	u32 pcic;
-	
-	pci_read_config_dword(dev, 0x42, &pcic);
-	if((pcic&2)==0)
+	pci_read_config_dword(dev, 0x4C, &pcic);
+	if((pcic&6)!=6)
 	{
-		pcic |= 2;
-		printk(KERN_WARNING "BIOS disabled PCI ordering compliance, so we enabled it again.\n");
-		pci_write_config_dword(dev, 0x42, pcic);		
+		pcic |= 6;
+		printk(KERN_WARNING "BIOS failed to enable PCI standards compliance, fixing this error.\n");
+		pci_write_config_dword(dev, 0x4C, pcic);
+		pci_read_config_dword(dev, 0x84, &pcic);
+		pcic |= (1<<23);	/* Required in this mode */
+		pci_write_config_dword(dev, 0x84, pcic);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/pnp/isapnp.c linux/drivers/pnp/isapnp.c
--- linux.orig/drivers/pnp/isapnp.c	Mon Nov 12 18:02:54 2001
+++ linux/drivers/pnp/isapnp.c	Mon Feb  4 19:11:16 2002
@@ -21,7 +21,7 @@
  *  2000-01-01	Added quirks handling for buggy hardware
  *		Peter Denison <peterd@pnd-pc.demon.co.uk>
  *  2000-06-14	Added isapnp_probe_devs() and isapnp_activate_dev()
- *		Christoph Hellwig <hch@caldera.de>
+ *		Christoph Hellwig <hch@infradead.org>
  *  2001-06-03  Added release_region calls to correspond with
  *		request_region calls when a failure occurs.  Also
  *		added KERN_* constants to printk() calls.
@@ -892,6 +892,7 @@
 		case _STAG_END:
 			if (size > 0)
 				isapnp_skip_bytes(size);
+			isapnp_config_prepare(dev);
 			return 1;
 		default:
 			printk(KERN_ERR "isapnp: unexpected or unknown tag type 0x%x for logical device %i (device %i), ignored\n", type, dev->devfn, card->number);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/Makefile linux/drivers/s390/Makefile
--- linux.orig/drivers/s390/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/Makefile	Fri Dec 21 16:25:30 2001
@@ -7,7 +7,7 @@
 subdir-y := block char misc net
 subdir-m := $(subdir-y)
 
-obj-y := s390io.o s390mach.o s390dyn.o idals.o ccwcache.o
+obj-y := s390io.o s390mach.o s390dyn.o idals.o ccwcache.o sysinfo.o
 export-objs += ccwcache.o idals.o s390dyn.o s390io.o
 
 obj-y += $(foreach dir,$(subdir-y),$(dir)/s390-$(dir).o)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd.c linux/drivers/s390/block/dasd.c
--- linux.orig/drivers/s390/block/dasd.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/block/dasd.c	Fri Dec 21 16:25:30 2001
@@ -113,6 +113,7 @@
 EXPORT_SYMBOL (dasd_default_erp_postaction);
 EXPORT_SYMBOL (dasd_sleep_on_req);
 EXPORT_SYMBOL (dasd_set_normalized_cda);
+EXPORT_SYMBOL (dasd_device_from_kdev);
 
 /* SECTION: Constant definitions to be used within this file */
 
@@ -668,12 +669,18 @@
         /* init devfs array */
 	major_info->gendisk.de_arr = (devfs_handle_t *)
 	    kmalloc (DASD_PER_MAJOR * sizeof (devfs_handle_t), GFP_KERNEL);
+	if(major_info->gendisk.de_arr == NULL)
+		goto out_gd_de_arr;
+
 	memset (major_info->gendisk.de_arr, 0,
 		DASD_PER_MAJOR * sizeof (devfs_handle_t));
 
         /* init flags */
 	major_info->gendisk.flags = (char *)
 	    kmalloc (DASD_PER_MAJOR * sizeof (char), GFP_KERNEL);
+	if(major_info->gendisk.flags == NULL)
+		goto out_gd_flags;
+
 	memset (major_info->gendisk.flags, 0, DASD_PER_MAJOR * sizeof (char));
 
         /* register blockdevice */
@@ -792,10 +799,13 @@
 		major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
 	}
 
-      out_reg_blkdev:
-        kfree (major_info->gendisk.flags);
-        kfree (major_info->gendisk.de_arr);
+out_reg_blkdev:
+	kfree (major_info->gendisk.flags);
+
+out_gd_flags:
+	kfree (major_info->gendisk.de_arr);
 
+out_gd_de_arr:
 	/* Delete the new major info from dasd_major_info if needed */
 	if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
 		kfree (major_info);
@@ -861,7 +871,7 @@
  * finds the device structure corresponding to the kdev supplied as argument
  * in the major_info structures and returns it or NULL when not found
  */
-static inline dasd_device_t *
+dasd_device_t *
 dasd_device_from_kdev (kdev_t kdev)
 {
 	major_info_t *major_info = NULL;
@@ -1873,15 +1883,15 @@
 	dasd_era_t era = dasd_era_none; /* default is everything is okay */
 	devstat_t *stat = (devstat_t *)ds;
 
+        if (stat == NULL) {
+                BUG();
+	}
         DASD_DRIVER_DEBUG_EVENT (6, dasd_int_handler,
                                  "Interrupt: IRQ %02x, stat %02x, devno %04x",
                                  irq,
                                  stat->dstat,
                                  stat->devno);
         asm volatile ("STCK %0":"=m" (now));
-        if (stat == NULL) {
-                BUG();
-	}
 
         /* first of all check for state change pending interrupt */
         if ((stat->dstat & DEV_STAT_ATTENTION ) && 
@@ -2313,6 +2323,18 @@
 			rc = put_user(ver, (int *) data);
 			break;
         }
+	case BLKGETSIZE:{	/* Return device size */
+			long blocks = major_info->gendisk.sizes 
+                                      [MINOR (inp->i_rdev)] << 1;
+			rc = put_user(blocks, (long *) data);
+			break;
+		}
+	case BLKGETSIZE64:{
+			u64 blocks = major_info->gendisk.sizes 
+                                      [MINOR (inp->i_rdev)];
+			rc = put_user(blocks << 10, (u64 *) data);
+			break;
+		}
 	case BLKRRPART:{
 			if (!capable (CAP_SYS_ADMIN)) {
 				rc = -EACCES;
@@ -2508,8 +2530,6 @@
 		break;
 		}
 #endif /* 0 */
-	case BLKGETSIZE:
-	case BLKGETSIZE64:
 	case BLKSSZGET:
 	case BLKROSET:
 	case BLKROGET:
@@ -2647,7 +2667,6 @@
                 invalidate_buffers (inp->i_rdev);
                 if ( device->discipline->owner )
                         __MOD_DEC_USE_COUNT(device->discipline->owner);
-                MOD_DEC_USE_COUNT;
 	} else if ( count == -1 ) { /* paranoia only */
                 atomic_set (&device->open_count,0);
                 printk (KERN_WARNING PRINTK_HEADER
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_3990_erp.c linux/drivers/s390/block/dasd_3990_erp.c
--- linux.orig/drivers/s390/block/dasd_3990_erp.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/block/dasd_3990_erp.c	Fri Dec 21 16:25:30 2001
@@ -237,8 +237,9 @@
 	/* check for 'No Record Found' */
 	if (sense[1] & SNS1_NO_REC_FOUND) {
                 
-                DASD_MESSAGE (KERN_ERR, device, "%s",
-                              "EXAMINE 24: No Record Found detected "
+                DASD_MESSAGE (KERN_ERR, device,
+                              "EXAMINE 24: No Record Found detected %s",
+                              cqr == device->init_cqr ? " " :
                               "- fatal error");
 
                 return dasd_era_fatal;
@@ -305,8 +306,9 @@
                        devstat_t *stat)
 {
 
-	char       *sense = stat->ii.sense.data;
-        dasd_era_t era    = dasd_era_recover;
+	char          *sense  = stat->ii.sense.data;
+        dasd_era_t     era    = dasd_era_recover;
+        dasd_device_t *device = cqr->device;
 
 	/* check for successful execution first */
 	if (stat->cstat == 0x00                                 &&
@@ -327,7 +329,8 @@
 	} 
 
         /* log the erp chain if fatal error occurred */
-        if (era == dasd_era_fatal) {
+        if ((era == dasd_era_fatal  ) &&
+            (cqr != device->init_cqr)   ){
 
                 log_erp_chain (cqr, 
                                0, 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_eckd.c linux/drivers/s390/block/dasd_eckd.c
--- linux.orig/drivers/s390/block/dasd_eckd.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/block/dasd_eckd.c	Fri Dec 21 16:25:30 2001
@@ -585,7 +585,7 @@
 	/* Free the cqr and cleanup device->sizes */
         if ( status != CQR_STATUS_DONE ) {
                 DASD_MESSAGE (KERN_WARNING,device,"%s",
-                              "volume analysis returned fatal error");
+                              "volume analysis returned unformatted disk");
                 return -EMEDIUMTYPE;
         }
 	/* Check Track 0 for Compatible Disk Layout */
@@ -1097,8 +1097,14 @@
                         ccw->flags |= CCW_FLAG_CC;
                         ccw->cmd_code = locate4k_set ? rw_cmd :
                                 dasd_eckd_cdl_cmd (device, recid, req->cmd);
-                        ccw->count = locate4k_set ? byt_per_blk :
-                                dasd_eckd_cdl_reclen (device, recid);
+                        ccw->count = byt_per_blk;
+                        if (!locate4k_set) {
+                                ccw->count = dasd_eckd_cdl_reclen (device,recid);
+                                if (ccw->count < byt_per_blk) {
+                                    memset (bh->b_data + size + ccw->count,
+                                            0xE5, byt_per_blk - ccw->count);
+                                }
+                        }
                         if (dasd_set_normalized_cda (ccw, __pa (bh->b_data+size), rw_cp, device)) {
                                 goto clear_rw_cp;
                         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/block/dasd_int.h linux/drivers/s390/block/dasd_int.h
--- linux.orig/drivers/s390/block/dasd_int.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/block/dasd_int.h	Fri Dec 21 16:25:30 2001
@@ -259,7 +259,6 @@
 typedef int (*dasd_info_fn_t) (struct dasd_device_t *, dasd_information_t *);
 typedef int (*dasd_use_count_fn_t) (int);
 
-
 /*
  * the dasd_discipline_t is
  * sth like a table of virtual functions, if you think of dasd_eckd
@@ -364,6 +363,7 @@
 void dasd_schedule_bh (dasd_device_t *);
 int dasd_sleep_on_req(ccw_req_t*);
 int  dasd_set_normalized_cda ( ccw1_t * cp, unsigned long address, ccw_req_t* request, dasd_device_t* device );
+dasd_device_t * dasd_device_from_kdev (kdev_t kdev);
 
 extern debug_info_t *dasd_debug_area;
 extern int (*genhd_dasd_name) (char *, int, int, struct gendisk *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/misc/chandev.c linux/drivers/s390/misc/chandev.c
--- linux.orig/drivers/s390/misc/chandev.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/misc/chandev.c	Fri Dec 21 16:25:30 2001
@@ -1057,6 +1057,9 @@
 	chandev_add_model(chandev_type_osad,0x3088,0x62,-1,-1,0,default_msck_bits,FALSE,FALSE);
 	/* claw */
 	chandev_add_model(chandev_type_claw,0x3088,0x61,-1,-1,0,default_msck_bits,FALSE,FALSE);
+
+	/* ficon attached ctc */
+	chandev_add_model(chandev_type_escon,0x3088,0x1E,-1,-1,0,default_msck_bits,FALSE,FALSE);
 }
 
 
@@ -3092,7 +3095,7 @@
 				chandevs_detected=TRUE;
 				if(pass)
 				{
-					chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x  0x%04x 0x%02x  0x%04x 0x%02x 0x%02x 0x%016Lx  %-5s %-5s\n",
+					chandev_printf(chan_exit,"0x%04x 0x%04x 0x%02x  0x%04x 0x%02x  0x%04x 0x%02x 0x%02x 0x%016Lx  %-5s%-5s\n",
 						       curr_irq,curr_devinfo.devno,
 						       ( curr_force ? curr_force->chan_type : 
 						       ( curr_model ? curr_model->chan_type : 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/ctcmain.c linux/drivers/s390/net/ctcmain.c
--- linux.orig/drivers/s390/net/ctcmain.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/net/ctcmain.c	Fri Dec 21 16:25:30 2001
@@ -1,5 +1,5 @@
 /*
- * $Id: ctcmain.c,v 1.51 2001/09/24 10:38:02 mschwide Exp $
+ * $Id: ctcmain.c,v 1.55 2001/12/03 14:28:45 felfert Exp $
  *
  * CTC / ESCON network driver
  *
@@ -35,7 +35,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.51 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.55 $
  *
  */
 
@@ -96,7 +96,9 @@
 #ifdef MODULE
 MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
 MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
 MODULE_LICENSE("GPL");
+#endif
 #ifndef CTC_CHANDEV
 MODULE_PARM(ctc, "s");
 MODULE_PARM_DESC(ctc,
@@ -368,7 +370,7 @@
 static __inline__ void ctc_clear_busy(net_device *dev)
 {
 	clear_bit(0, &(((ctc_priv *)dev->priv)->tbusy));
-	netif_start_queue(dev);
+	netif_wake_queue(dev);
 }
 
 static __inline__ int ctc_test_and_set_busy(net_device *dev)
@@ -385,7 +387,7 @@
  */
 static void print_banner(void) {
 	static int printed = 0;
-	char vbuf[] = "$Revision: 1.51 $";
+	char vbuf[] = "$Revision: 1.55 $";
 	char *version = vbuf;
 
 	if (printed)
@@ -396,7 +398,16 @@
 			*p = '\0';
 	} else
 		version = " ??? ";
-	printk(KERN_INFO "CTC driver Version%s initialized\n", version);
+	printk(KERN_INFO
+	       "CTC driver Version%swith"
+#ifndef CTC_CHANDEV
+	       "out"
+#endif
+	       " CHANDEV support"
+#ifdef DEBUG
+	       " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
+#endif
+	       " initialized\n", version);
 	printed = 1;
 }
 
@@ -754,12 +765,13 @@
 		pskb->protocol = ntohs(header->type);
 		header->length -= LL_HEADER_LENGTH;
 		if ((header->length == 0) ||
-		    (header->length > skb_tailroom(pskb))) {
+		    (header->length > skb_tailroom(pskb)) ||
+		    (header->length > len)) {
 			printk(KERN_WARNING
 			       "%s Illegal packet size %d "
-			       "received (MTU=%d), "
+			       "received (MTU=%d blocklen=%d), "
 			       "dropping\n", dev->name, header->length,
-			       dev->mtu);
+			       dev->mtu, len);
 #ifdef DEBUG
 			ctc_dump_skb(pskb, -6);
 #endif
@@ -929,7 +941,8 @@
 		if (ch->trans_skb != NULL)
 			dev_kfree_skb(ch->trans_skb);
 		clear_normalized_cda(&ch->ccw[1]);
-		ch->trans_skb = dev_alloc_skb(ch->max_bufsize);
+		ch->trans_skb = __dev_alloc_skb(ch->max_bufsize,
+						GFP_ATOMIC|GFP_DMA);
 		if (ch->trans_skb == NULL) {
 			if (warn)
 				printk(KERN_WARNING
@@ -1026,7 +1039,6 @@
 			ch->prof.maxmulti = ch->collect_len + 2;
 		if (ch->prof.maxcqueue < skb_queue_len(&ch->collect_queue))
 			ch->prof.maxcqueue = skb_queue_len(&ch->collect_queue);
-		ch->ccw[1].count = ch->collect_len + 2;
 		*((__u16 *)skb_put(ch->trans_skb, 2)) = ch->collect_len + 2;
 		i = 0;
 		while ((skb = skb_dequeue(&ch->collect_queue))) {
@@ -1039,11 +1051,10 @@
 			i++;
 		}
 		ch->collect_len = 0;
+		spin_unlock(&ch->collect_lock);
+		ch->ccw[1].count = ch->trans_skb->len;
 		fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC, CH_EVENT_TIMER, ch);
 		ch->prof.send_stamp = xtime;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 		ch->prof.doios_multi++;
 		if (rc != 0) {
@@ -1052,10 +1063,11 @@
 			fsm_deltimer(&ch->timer);
 			ccw_check_return_code(ch, rc);
 		}
-	} else
+	} else {
+		spin_unlock(&ch->collect_lock);
 		fsm_newstate(fi, CH_STATE_TXIDLE);
+	}
 	ctc_clear_busy(dev);
-	spin_unlock(&ch->collect_lock);
 }
 
 /**
@@ -1146,9 +1158,6 @@
 	if (ctc_checkalloc_buffer(ch, 1))
 		return;
 	ch->ccw[1].count = ch->max_bufsize;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 	rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 	if (rc != 0)
 		ccw_check_return_code(ch, rc);
@@ -1203,9 +1212,6 @@
 	*((__u16 *)ch->trans_skb->data) = CTC_INITIAL_BLOCKLEN;
 	ch->ccw[1].count = 2; /* Transfer only length */
 
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 	fsm_newstate(fi, (CHANNEL_DIRECTION(ch->flags) == READ)
 		     ? CH_STATE_RXINIT : CH_STATE_TXINIT);
 	rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
@@ -1254,9 +1260,6 @@
 			return;
 		ch->ccw[1].count = ch->max_bufsize;
 		fsm_newstate(fi, CH_STATE_RXIDLE);
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[1].cda = %08x\n", ch->ccw[1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[0], (intparm_t)ch, 0xff, 0);
 		if (rc != 0) {
 			fsm_newstate(fi, CH_STATE_RXINIT);
@@ -1716,9 +1719,6 @@
 			fsm_addtimer(&ch->timer, 1000, CH_EVENT_TIMER, ch);
 			if (event == CH_EVENT_TIMER)
 				s390irq_spin_lock_irqsave(ch->irq, saveflags);
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[4].cda = %08x\n", ch->ccw[4].cda);
-#endif
 			rc = do_IO(ch->irq, &ch->ccw[3], (intparm_t)ch, 0xff, 0);
 			if (event == CH_EVENT_TIMER)
 				s390irq_spin_unlock_irqrestore(ch->irq,
@@ -2479,6 +2479,8 @@
 	} else {
 		__u16 block_len;
 		int ccw_idx;
+		struct sk_buff *nskb;
+		unsigned long hi;
 
 		/**
 		 * Protect skb against beeing free'd by upper
@@ -2493,6 +2495,28 @@
 		       LL_HEADER_LENGTH);
 		block_len = skb->len + 2;
 		*((__u16 *)skb_push(skb, 2)) = block_len;
+
+		/**
+		 * IDAL support in CTC is broken, so we have to
+		 * care about skb's above 2G ourselves.
+		 */
+		hi = ((unsigned long)skb->tail + LL_HEADER_LENGTH) >> 31;
+		if (hi) {
+			nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA);
+			if (!nskb) {
+				atomic_dec(&skb->users);
+				skb_pull(skb, LL_HEADER_LENGTH + 2);
+				return -ENOMEM;
+			} else {
+				memcpy(skb_put(nskb, skb->len),
+				       skb->data, skb->len);
+				atomic_inc(&nskb->users);
+				atomic_dec(&skb->users);
+				dev_kfree_skb_irq(skb);
+				skb = nskb;
+			}
+		}
+
 		ch->ccw[4].count = block_len;
 		if (set_normalized_cda(&ch->ccw[4], virt_to_phys(skb->data))) {
 			/**
@@ -2505,6 +2529,7 @@
 				 * Remove our header. It gets added
 				 * again on retransmit.
 				 */
+				atomic_dec(&skb->users);
 				skb_pull(skb, LL_HEADER_LENGTH + 2);
 				return -EBUSY;
 			}
@@ -2522,18 +2547,11 @@
 			ccw_idx = 3;
 		}
 		ch->retry = 0;
-#ifdef DEBUG
-		ctc_dump_skb(skb, 0);
-#endif
 		fsm_newstate(ch->fsm, CH_STATE_TX);
 		fsm_addtimer(&ch->timer, CTC_TIMEOUT_5SEC,
 			     CH_EVENT_TIMER, ch);
 		s390irq_spin_lock_irqsave(ch->irq, saveflags);
 		ch->prof.send_stamp = xtime;
-#ifdef DEBUG
-	printk(KERN_DEBUG "ccw[%d].cda = %08x\n", ccw_idx+1,
-	       ch->ccw[ccw_idx+1].cda);
-#endif
 		rc = do_IO(ch->irq, &ch->ccw[ccw_idx], (intparm_t)ch, 0xff, 0);
 		s390irq_spin_unlock_irqrestore(ch->irq, saveflags);
 		if (ccw_idx == 3)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/fsm.h linux/drivers/s390/net/fsm.h
--- linux.orig/drivers/s390/net/fsm.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/net/fsm.h	Fri Dec 21 16:25:30 2001
@@ -1,4 +1,4 @@
-/* $Id: fsm.h,v 1.3 2001/06/18 16:49:19 felfert Exp $
+/* $Id: fsm.h,v 1.4 2001/09/24 10:38:02 mschwide Exp $
  */
 #ifndef _FSM_H_
 #define _FSM_H_
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/iucv.c linux/drivers/s390/net/iucv.c
--- linux.orig/drivers/s390/net/iucv.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/net/iucv.c	Fri Dec 21 16:25:30 2001
@@ -1,4 +1,4 @@
-/*
+/* 
  * $Id$
  *
  * IUCV network driver
@@ -478,7 +478,7 @@
 b2f0(__u32 code, void *parm)
 {
 	iucv_debug("iparml before b2f0 call:");
-	iucv_dumpit(parm, sizeof(iucv_param.param));
+	iucv_dumpit(parm, sizeof(iucv_param));
 
 	asm volatile (
 		"LRA   1,0(%1)\n\t"
@@ -490,7 +490,7 @@
 		);
 
 	iucv_debug("iparml after b2f0 call:");
-	iucv_dumpit(parm, sizeof(iucv_param.param));
+	iucv_dumpit(parm, sizeof(iucv_param));
 
 	return (unsigned long)*((__u8 *)(parm + 3));
 }
@@ -2126,7 +2126,7 @@
 					iucv_sever (int_buf->ippathid,
 						    no_listener);
 					iucv_debug("add_pathid failed, rc = %d",
-						   (int)add_pathid_result);
+						   rc);
 				} else {
 					interrupt = h->interrupt_table;
 					if (interrupt->ConnectionPending) {
@@ -2150,8 +2150,8 @@
 						h->pgm_data);
 				else
 					iucv_debug("ConnectionComplete not called");
-			}
-			
+			} else
+				iucv_sever(int_buf->ippathid, no_listener);
 			break;
 			
 		case 0x03:		/* connection severed */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/net/netiucv.c linux/drivers/s390/net/netiucv.c
--- linux.orig/drivers/s390/net/netiucv.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/net/netiucv.c	Fri Dec 21 16:25:30 2001
@@ -1,5 +1,5 @@
 /*
- * $Id: netiucv.c,v 1.12 2001/09/24 10:38:02 mschwide Exp $
+ * $Id: netiucv.c,v 1.16 2001/12/03 14:28:45 felfert Exp $
  *
  * IUCV network driver
  *
@@ -28,7 +28,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: IUCV network driver $Revision: 1.12 $
+ * RELEASE-TAG: IUCV network driver $Revision: 1.16 $
  *
  */
 
@@ -189,7 +189,7 @@
 static __inline__ void netiucv_clear_busy(net_device *dev)
 {
 	clear_bit(0, &(((netiucv_priv *)dev->priv)->tbusy));
-	netif_start_queue(dev);
+	netif_wake_queue(dev);
 }
 
 static __inline__ int netiucv_test_and_set_busy(net_device *dev)
@@ -548,7 +548,7 @@
 		header->next -= NETIUCV_HDRLEN;
 		if (skb_tailroom(pskb) < header->next) {
 			printk(KERN_WARNING
-			       "%s: Ilegal next field in iucv header: %d > %d\n",
+			       "%s: Illegal next field in iucv header: %d > %d\n",
 			       dev->name, header->next, skb_tailroom(pskb));
 			return;
 		}
@@ -618,7 +618,8 @@
 	iucv_connection *conn = ev->conn;
 	iucv_MessageComplete *eib = (iucv_MessageComplete *)ev->data;
 	netiucv_priv *privptr = NULL;
-	struct sk_buff *skb = (struct sk_buff *)eib->ipmsgtag;
+			         /* Shut up, gcc! skb is always below 2G. */
+	struct sk_buff *skb = (struct sk_buff *)(unsigned long)eib->ipmsgtag;
 	__u32 txbytes = 0;
 	__u32 txpackets = 0;
 	__u32 stat_maxcq = 0;
@@ -638,10 +639,9 @@
 				(skb->len - NETIUCV_HDRLEN - NETIUCV_HDRLEN);
 		}
 		dev_kfree_skb_any(skb);
-	} else {
-		conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
-		conn->tx_buff->len = 0;
 	}
+	conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head;
+	conn->tx_buff->len = 0;
 	spin_lock_irqsave(&conn->collect_lock, saveflags);
 	while ((skb = skb_dequeue(&conn->collect_queue))) {
 		header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN;
@@ -768,7 +768,8 @@
 		case CONN_STATE_TX:
 			printk(KERN_INFO "%s: Remote dropped connection\n",
 			       netdev->name);
-			iucv_unregister_program(conn->handle);
+			if (conn->handle)
+				iucv_unregister_program(conn->handle);
 			conn->handle = 0;
 			fsm_newstate(fi, CONN_STATE_STOPPED);
 			fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
@@ -881,7 +882,8 @@
 #endif
 	fsm_newstate(fi, CONN_STATE_STOPPED);
 	netiucv_purge_skb_queue(&conn->collect_queue);
-	iucv_unregister_program(conn->handle);
+	if (conn->handle)
+		iucv_unregister_program(conn->handle);
 	conn->handle = 0;
 	fsm_event(privptr->fsm, DEV_EVENT_CONDOWN, netdev);
 }
@@ -1127,7 +1129,10 @@
 		fsm_addtimer(&conn->timer, NETIUCV_TIMEOUT_5SEC,
 			     CONN_EVENT_TIMER, conn);
 		conn->prof.send_stamp = xtime;
-		rc = iucv_send(conn->pathid, NULL, 0, 0, (__u32)nskb, 0,
+		
+		rc = iucv_send(conn->pathid, NULL, 0, 0,
+			       /* Shut up, gcc! nskb is always below 2G. */
+			       (__u32)(((unsigned long)nskb)&0xffffffff), 0,
 			       nskb->data, nskb->len);
 		conn->prof.doios_single++;
 		conn->prof.txlen += skb->len;
@@ -1167,6 +1172,7 @@
 static int
 netiucv_open(net_device *dev) {
 	MOD_INC_USE_COUNT;
+	SET_DEVICE_START(dev, 1);
 	fsm_event(((netiucv_priv *)dev->priv)->fsm, DEV_EVENT_START, dev);
 	return 0;
 }
@@ -1573,9 +1579,7 @@
 	if (file->f_pos == 0) {
 		p += sprintf(p, "Device FSM state: %s\n",
 			     fsm_getstate_str(privptr->fsm));
-		p += sprintf(p, "RX channel FSM state: %s\n",
-			     fsm_getstate_str(privptr->conn->fsm));
-		p += sprintf(p, "TX channel FSM state: %s\n",
+		p += sprintf(p, "Connection FSM state: %s\n",
 			     fsm_getstate_str(privptr->conn->fsm));
 		p += sprintf(p, "Max. TX buffer used: %ld\n",
 			     privptr->conn->prof.maxmulti);
@@ -1970,7 +1974,6 @@
 	dev->addr_len            = 0;
 	dev->type                = ARPHRD_SLIP;
 	dev->tx_queue_len        = NETIUCV_QUEUELEN_DEFAULT;
-	SET_DEVICE_START(dev, 1);
 	dev_init_buffers(dev);
 	dev->flags	         = IFF_POINTOPOINT | IFF_NOARP;
 	return dev;
@@ -2002,7 +2005,7 @@
 static void
 netiucv_banner(void)
 {
-	char vbuf[] = "$Revision: 1.12 $";
+	char vbuf[] = "$Revision: 1.16 $";
 	char *version = vbuf;
 
 	if ((version = strchr(version, ':'))) {
@@ -2118,5 +2121,7 @@
 #ifdef MODULE
 module_init(netiucv_init);
 module_exit(netiucv_exit);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
 MODULE_LICENSE("GPL");
+#endif
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/s390io.c linux/drivers/s390/s390io.c
--- linux.orig/drivers/s390/s390io.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/s390/s390io.c	Fri Dec 21 16:25:30 2001
@@ -6,6 +6,7 @@
  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
  *                             IBM Corporation
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
+ *               Cornelia Huck (cohuck@de.ibm.com) 
  *    ChangeLog: 01/07/2001 Blacklist cleanup (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
  *               01/04/2001 Holger Smolinski (smolinsk@de.ibm.com)
  *                          Fixed lost interrupts and do_adapter_IO
@@ -24,6 +25,9 @@
  *               xx/xx/xxxx some bugfixes & cleanups
  *               08/02/2001 Cornelia Huck not already known devices can be blacklisted
  *                                        by piping to /proc/cio_ignore
+ *               09/xx/2001 couple more fixes
+ *               10/29/2001 Cornelia Huck Blacklisting reworked again
+ *               10/29/2001 Cornelia Huck improved utilization of debug feature
  */
 
 #include <linux/module.h>
@@ -108,7 +112,9 @@
 static void s390_process_subchannels( void);
 static void s390_device_recognition_all( void);
 static void s390_device_recognition_irq( int irq);
+#ifdef CONFIG_PROC_FS
 static void s390_redo_validation(void);
+#endif
 static int  s390_validate_subchannel( int irq, int enable);
 static int  s390_SenseID( int irq, senseid_t *sid, __u8 lpm);
 static int  s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid);
@@ -117,7 +123,9 @@
 static int  enable_subchannel( unsigned int irq);
 static int  disable_subchannel( unsigned int irq);
 
+#ifdef CONFIG_PROC_FS
 static int chan_proc_init( void );
+#endif 
 
 static inline void do_adapter_IO( __u32 intparm );
 
@@ -131,108 +139,68 @@
 
 asmlinkage void do_IRQ( struct pt_regs regs );
 
+#ifdef CONFIG_PROC_FS
 #define MAX_CIO_PROCFS_ENTRIES 0x300
 /* magic number; we want to have some room to spare */
 
 int cio_procfs_device_create(int devno);
 int cio_procfs_device_remove(int devno);
 int cio_procfs_device_purge(void);
+#endif
 
 int cio_notoper_msg = 1;
+#ifdef CONFIG_PROC_FS
 int cio_proc_devinfo = 0; /* switch off the /proc/deviceinfo/ stuff by default
 			     until problems are dealt with */
+#endif
 
 unsigned long s390_irq_count[NR_CPUS]; /* trace how many irqs have occured per cpu... */
 int cio_count_irqs = 1;       /* toggle use here... */
 
 /* 
  * "Blacklisting" of certain devices:
- * Device numbers given in the commandline as blacklist=... won't be known to Linux
+ * Device numbers given in the commandline as cio_ignore=... won't be known to Linux
  * These can be single devices or ranges of devices
  *
- * Introduced by Cornelia Huck <cohuck@de.ibm.com>
- * Most of it shamelessly taken from dasd.c 
+ * 10/23/01 reworked to get rid of lists
  */
 
-typedef struct dev_blacklist_range_t {
-     struct dev_blacklist_range_t *next;  /* next range in list */
-     unsigned int from;                   /* beginning of range */
-     unsigned int to;                     /* end of range */
-     int  kmalloced;	
+static unsigned long bl_dev[2048] = {0,};
     
-} dev_blacklist_range_t;
-
-static dev_blacklist_range_t *dev_blacklist_range_head = NULL; /* Anchor for list of ranges */
-static dev_blacklist_range_t *dev_blacklist_unused_head = NULL;
-
 static spinlock_t blacklist_lock = SPIN_LOCK_UNLOCKED;
-static int nr_blacklisted_ranges = 0;
-
-/* Handling of the blacklist ranges */
-
-static inline void blacklist_range_destroy( dev_blacklist_range_t *range,int locked )
-{
-	 long flags;
-	 
-	 if(!locked)
-		 spin_lock_irqsave( &blacklist_lock, flags ); 
-	 if(!remove_from_list((list **)&dev_blacklist_range_head,(list *)range))
-		 BUG();
-	 nr_blacklisted_ranges--;
-	 if(range->kmalloced)
-		 kfree(range);
-	 else
-		 add_to_list((list **)&dev_blacklist_unused_head,(list *)range); 
-	 if(!locked)
-		 spin_unlock_irqrestore( &blacklist_lock, flags );
-}
-
-
+static int highest_ignored = 0;
+static int nr_ignored = 0;
 
 /* 
  * Function: blacklist_range_add
- * Creates a range from the specified arguments and appends it to the list of
- * blacklisted devices
+ * Blacklist the devices from-to
  */
 
-static inline dev_blacklist_range_t *blacklist_range_add( int from, int to,int locked)
+static inline void blacklist_range_add( int from, int to,int locked)
 {
  
-     dev_blacklist_range_t *range = NULL;
      unsigned long flags;
+     int i;
 
      if (to && (from>to)) {
 	     printk(KERN_WARNING "Invalid blacklist range %x to %x, skipping\n", from, to);
-	     return NULL;
+	     return;
      }
      if(!locked)
 	     spin_lock_irqsave( &blacklist_lock, flags );
-     if(dev_blacklist_unused_head)
-	     range=(dev_blacklist_range_t *)
-		     remove_listhead((list **)&dev_blacklist_unused_head);
-     else if (init_IRQ_complete) {
-	     if((range = ( dev_blacklist_range_t *) 
-		 kmalloc( sizeof( dev_blacklist_range_t ), GFP_KERNEL)))
-		     range->kmalloced=1;
-     } else {
-	     if((range = ( dev_blacklist_range_t *) 
-		 alloc_bootmem( sizeof( dev_blacklist_range_t ) )))
-		     range->kmalloced=0;
-     }
-     if (range)
-     {
-	     add_to_list((list **)&dev_blacklist_range_head,(list *)range);
-	     range->from = from;
-	     if (to == 0) {           /* only a single device is given */
-		     range->to = from;
-	     } else {
-		     range->to = to;
-	     }
-	     nr_blacklisted_ranges++;
+
+     if (!to) 
+	     to = from;
+     for (i=from;i<=to;i++) {
+	     set_bit(i,&bl_dev);
+	     nr_ignored++;
      }
+
+     if (to>=highest_ignored)
+	     highest_ignored = to;
+
      if(!locked)
 	     spin_unlock_irqrestore( &blacklist_lock, flags );
-     return range;
 }
 
 /* 
@@ -242,14 +210,19 @@
 
 static inline void blacklist_range_remove( int from, int to )
 {
-     dev_blacklist_range_t *temp;
      long flags;
+	int i;
 
      spin_lock_irqsave( &blacklist_lock, flags );
-     for ( temp = dev_blacklist_range_head; 
-	   (temp->from != from) && (temp->to != to);
-	   temp = temp->next );
-     blacklist_range_destroy( temp,1 );
+     
+	for (i=from;i<=to;i++) {
+		clear_bit(i,&bl_dev);
+		nr_ignored--;
+	}
+
+	if (to == highest_ignored) 
+		for (highest_ignored=from;(highest_ignored>0) && (!test_bit(highest_ignored,&bl_dev));highest_ignored--);
+
      spin_unlock_irqrestore( &blacklist_lock, flags );
 }
 
@@ -257,12 +230,12 @@
 
 /* 
  * Variable to hold the blacklisted devices given by the parameter line
- * blacklist=...
+ * cio_ignore=...
  */
 char *blacklist[256] = {NULL, };
 
 /*
- * Get the blacklist=... items from the parameter line
+ * Get the cio_ignore=... items from the parameter line
  */
 
 static void blacklist_split_parm_string (char *str)
@@ -282,7 +255,7 @@
 		}
 		blacklist[count] = alloc_bootmem (len * sizeof (char) );
 		if (blacklist == NULL) {
-			printk (KERN_WARNING "can't store blacklist= parameter no %d\n", count + 1);
+			printk (KERN_WARNING "can't store cio_ignore= parameter no %d\n", count + 1);
 			break;
 		}
 		memset (blacklist[count], 0, len * sizeof (char));
@@ -319,7 +292,7 @@
 
 /*
  * Function: blacklist_parse
- * Parse the parameters given to blacklist=... 
+ * Parse the parameters given to cio_ignore=... 
  * Add the blacklisted devices to the blacklist chain
  */
 
@@ -338,11 +311,9 @@
 	       temp++;
 	       to = blacklist_strtoul( temp, &temp );
 	  }
-	  if (!blacklist_range_add( from, to,0 )) {
-	       printk( KERN_WARNING "Blacklisting range from %X to %X failed!\n", from, to);
-	  }
+	  blacklist_range_add( from, to, 0 );
 #ifdef CONFIG_DEBUG_IO
-	  printk( "Blacklisted range from %X to %X\n", from, to );
+	  printk( KERN_INFO "Blacklisted range from %X to %X\n", from, to );
 #endif
 	  str++;
      }
@@ -356,7 +327,7 @@
 void __init blacklist_init( void )
 {
 #ifdef CONFIG_DEBUG_IO     
-     printk( "Reading blacklist...\n");
+     printk( KERN_DEBUG "Reading blacklist...\n");
 #endif
      if (cio_debug_initialized)
 	     debug_sprintf_event(cio_debug_msg_id, 6,
@@ -383,7 +354,7 @@
 {
         int dummy;
 #ifdef CONFIG_DEBUG_IO
-	printk( "Reading blacklist parameters...\n" );
+	printk( KERN_DEBUG "Reading blacklist parameters...\n" );
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 6,
@@ -406,24 +377,14 @@
 
 static inline int is_blacklisted( int devno )
 {
-     dev_blacklist_range_t *temp;
      long flags;
      int retval=0;
 
-     if (dev_blacklist_range_head == NULL) {  
-	  /* no blacklist */
-	  return 0;
-     }
-
      spin_lock_irqsave( &blacklist_lock, flags ); 
-     temp = dev_blacklist_range_head;
-     while (temp) {
-	  if ((temp->from <= devno) && (temp->to >= devno)) {
-		  retval=1;                      /* Deviceno is blacklisted */
-		  break;
-	  }
-	  temp = temp->next;
-     }
+
+     if (test_bit(devno,&bl_dev)) 
+		     retval=1;
+
      spin_unlock_irqrestore( &blacklist_lock, flags );
      return retval;
 }
@@ -435,17 +396,20 @@
 
 void blacklist_free_all_ranges(void) 
 {
-	dev_blacklist_range_t *tmp = dev_blacklist_range_head;
 	unsigned long flags;
+	int i;
 
 	spin_lock_irqsave( &blacklist_lock, flags ); 
-	while (tmp) {
-		blacklist_range_destroy(tmp,1);
-		tmp = dev_blacklist_range_head;
-	}
+
+	for (i=0;i<=highest_ignored;i++)
+		clear_bit(i,&bl_dev);
+	highest_ignored = 0;
+	nr_ignored = 0;
+
 	spin_unlock_irqrestore( &blacklist_lock, flags );
 }
 
+#ifdef CONFIG_PROC_FS
 /*
  * Function: blacklist_parse_proc_parameters
  * parse the stuff which is piped to /proc/cio_ignore
@@ -459,8 +423,6 @@
 	char *param;
 	int from = 0;
 	int to = 0;
-	int changed = 0;
-	dev_blacklist_range_t *range, *temp;
 	long flags;
 	int err = 0;
 
@@ -492,33 +454,9 @@
 				} else {
 					to = from;
 				}
-				spin_lock_irqsave( &blacklist_lock, flags ); 
-				range = dev_blacklist_range_head;
-				while (range != NULL) {
-					temp = range->next;
-					if ((from <= range->from) && (to >= range->to)) {
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from <= range->from) && (to>=range->from) && (to < range->to)) {
-						blacklist_range_add(to+1, range->to,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from > range->from) && (from<=range->to) && (to >= range->to)) {
-						blacklist_range_add(range->from, from-1,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					} else if ((from > range->from) && (to < range->to)) {
-						blacklist_range_add(range->from, from-1,1);
-						blacklist_range_add(to+1, range->to,1);
-						blacklist_range_destroy(range,1);
-						changed = 1;
-					}
-					range = temp;
-				}
-				spin_unlock_irqrestore( &blacklist_lock, flags );
+				blacklist_range_remove( from, to );
 				kfree(param);
 			}
-			if (changed)
 				s390_redo_validation();
 		}
 	} else if (strstr(tmp, "add ")) {
@@ -567,12 +505,6 @@
 				}
 			}
 
-			/*
-			 * Note: We allow for overlapping ranges here, 
-			 * since the user might specify overlapping ranges
-			 * and we walk through all ranges when freeing anyway.
-			 */
-
 			if (!err) 
 				blacklist_range_add(from, to, 1);
 			
@@ -581,11 +513,11 @@
 		}
 
 	} else {
-		printk("cio_ignore: Parse error; try using 'free all|<devno-range>,<devno-range>,...'\n");
-		printk("or 'add <devno-range>,<devno-range>,...'\n");
+		printk( KERN_WARNING "cio_ignore: Parse error; try using 'free all|<devno-range>,<devno-range>,...'\n");
+		printk( KERN_WARNING "or 'add <devno-range>,<devno-range>,...'\n");
 	}
 }
-
+#endif
 /* End of blacklist handling */
 
 
@@ -642,7 +574,7 @@
 	}
 	else
 	{
-		printk( "cio_setup : invalid cio_msg parameter '%s'", parm);
+		printk( KERN_ERR "cio_setup : invalid cio_msg parameter '%s'", parm);
 
 	} /* endif */
 
@@ -658,7 +590,7 @@
 	} else if (!strcmp(parm, "no")) {
 		cio_notoper_msg = 0;
 	} else {
-		printk("cio_notoper_setup: invalid cio_notoper_msg parameter '%s'", parm);
+		printk( KERN_ERR "cio_notoper_setup: invalid cio_notoper_msg parameter '%s'", parm);
 	}
 
 	return 1;
@@ -666,6 +598,7 @@
 	
 __setup("cio_notoper_msg=", cio_notoper_setup);
 
+#ifdef CONFIG_PROC_FS
 static int __init cio_proc_devinfo_setup(char *parm)
 {
 	if (!strcmp(parm, "yes")) {
@@ -673,13 +606,14 @@
 	} else if (!strcmp(parm, "no")) {
 		cio_proc_devinfo = 0;
 	} else {
-		printk("cio_proc_devinfo_setup: invalid parameter '%s'\n",parm);
+		printk( KERN_ERR "cio_proc_devinfo_setup: invalid parameter '%s'\n",parm);
 	}
 
 	return 1;
 }
 
 __setup("cio_proc_devinfo=", cio_proc_devinfo_setup);
+#endif
 
 /*
  * register for adapter interrupts
@@ -695,6 +629,7 @@
 int  s390_register_adapter_interrupt( adapter_int_handler_t handler )
 {
 	int ret = 0;
+	char dbf_txt[15];
 	
 	if (cio_debug_initialized)
 		debug_text_event(cio_debug_trace_id, 4, "rgaint");
@@ -710,6 +645,11 @@
   	
 	spin_unlock( &adapter_lock ); 	
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -717,6 +657,7 @@
 int  s390_unregister_adapter_interrupt( adapter_int_handler_t handler )
 {
 	int ret = 0;
+	char dbf_txt[15];
 	
 	if (cio_debug_initialized)
 		debug_text_event(cio_debug_trace_id, 4, "urgaint");
@@ -732,6 +673,11 @@
   	
 	spin_unlock( &adapter_lock ); 	
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -777,8 +723,7 @@
 
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "reqsp");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "reqsp%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -835,6 +780,9 @@
 		ioinfo[irq]->nopfunc         = not_oper_handler;  	
 	}
 
+	if (cio_debug_initialized)
+		debug_int_event(cio_debug_trace_id, 4, retval);
+
 	return retval;
 }
 
@@ -868,18 +816,13 @@
 	unsigned long flags;
 	int          ret;
 
-	unsigned int count = 0;
 	char dbf_txt[15];
 
 	if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA )
-	{
 		return;
 
-	} /* endif */
-
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 2, "free");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "free%x", irq);
 		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 	}
 
@@ -887,10 +830,7 @@
 
 #ifdef  CONFIG_KERNEL_DEBUG
 	if ( irq != cons_dev )
-	{
-		printk("Trying to free IRQ%d\n",irq);
-
-	} /* endif */
+		printk( KERN_DEBUG "Trying to free IRQ%d\n",irq);
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 2, "Trying to free IRQ %d\n", irq);
@@ -899,10 +839,8 @@
 	 * disable the device and reset all IRQ info if
 	 *  the IRQ is actually owned by the handler ...
 	 */
-	if ( ioinfo[irq]->ui.flags.ready )
-	{
-		if ( dev_id == ioinfo[irq]->irq_desc.dev_id  )
-		{
+	if ( ioinfo[irq]->ui.flags.ready ) {
+		if ( dev_id == ioinfo[irq]->irq_desc.dev_id ) {
 			/* start deregister */
 			ioinfo[irq]->ui.flags.unready = 1;
 
@@ -915,15 +853,9 @@
 				 0xC8C1D3E3,
 				 DOIO_WAIT_FOR_INTERRUPT );
 
-			do
-			{
 				ret = disable_subchannel( irq);
 
-				count++;
-
-				if ( ret == -EBUSY )
-				{
-					int iret;
+			if ( ret == -EBUSY ) {
 
 					/*
 					 * kill it !
@@ -932,45 +864,31 @@
 					 *  an async request, twice halt, then
 					 *  clear.
 					 */
-					if ( count < 2 )
-					{              	
-						iret = halt_IO( irq,
+				ret = halt_IO( irq,
 						                0xC8C1D3E3,
 						                DOIO_WAIT_FOR_INTERRUPT );
    	
-						if ( iret == -EBUSY )
-						{
+				if ( ret == -EBUSY ) {
 							halt_IO( irq, 0xC8C1D3E3, 0);
 							s390irq_spin_unlock_irqrestore( irq, flags);
 							udelay( 200000 ); /* 200 ms */
 							s390irq_spin_lock_irqsave( irq, flags);
 
 						} /* endif */
-					}
-					else
-					{
-						iret = clear_IO( irq,
-						                 0x40C3D3D9,
-						                 DOIO_WAIT_FOR_INTERRUPT );
+				
+				ret = disable_subchannel(irq);
+
+				if (ret == -EBUSY) {
    	
-						if ( iret == -EBUSY )
-						{
-							clear_IO( irq, 0xC8C1D3E3, 0);
+					clear_IO( irq, 0x40C3D3D9,0 );
 							s390irq_spin_unlock_irqrestore( irq, flags);
 							udelay( 1000000 ); /* 1000 ms */
 							s390irq_spin_lock_irqsave( irq, flags);
 
-						} /* endif */
-
-					} /* endif */
-
-					if ( count == 2 )
-					{
 						/* give it a very last try ... */
 						disable_subchannel( irq);
 
-						if ( ioinfo[irq]->ui.flags.busy )
-					        {
+					if ( ioinfo[irq]->ui.flags.busy ) {
 							printk( KERN_CRIT"free_irq(%04X) "
 							       "- device %04X busy, retry "
 							       "count exceeded\n",
@@ -983,39 +901,30 @@
 
 						} /* endif */
 						
-						break; /* sigh, let's give up ... */
-
 					} /* endif */
 
 				} /* endif */
 		
-			} while ( ret == -EBUSY );
-
 			ioinfo[irq]->ui.flags.ready   = 0;
 			ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */
 
 			ioinfo[irq]->nopfunc = NULL;
 
 			s390irq_spin_unlock_irqrestore( irq, flags);
-		}
-		else
-		{
+		} else {
 			s390irq_spin_unlock_irqrestore( irq, flags);
 
-			printk( "free_irq(%04X) : error, "
+			printk( KERN_ERR "free_irq(%04X) : error, "
 			        "dev_id does not match !\n", irq);
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_msg_id, 0, 
 						    "free_irq(%04X) : error, dev_id does not match !\n", irq);
 
 		} /* endif */
-
-	}
-	else
-	{
+	} else {
 		s390irq_spin_unlock_irqrestore( irq, flags);
 
-		printk( "free_irq(%04X) : error, "
+		printk( KERN_ERR "free_irq(%04X) : error, "
 		        "no action block ... !\n", irq);
 		if (cio_debug_initialized)
 			debug_sprintf_event(cio_debug_msg_id, 0,
@@ -1040,8 +949,7 @@
 		return -ENODEV;
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "disirq");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "disirq%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1051,6 +959,9 @@
 
 	synchronize_irq();
 
+	if (cio_debug_initialized) 
+		debug_int_event(cio_debug_trace_id, 4, ret);
+
 	return( ret);
 }
 
@@ -1066,8 +977,7 @@
 		return -ENODEV;
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "enirq");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "enirq%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1075,6 +985,9 @@
 	ret = enable_subchannel(irq);
 	s390irq_spin_unlock_irqrestore(irq, flags);
 
+	if (cio_debug_initialized) 
+		debug_int_event(cio_debug_trace_id, 4, ret);
+
 	return(ret);
 }
 
@@ -1091,8 +1004,7 @@
 	SANITY_CHECK(irq);
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 2, "ensch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "ensch%x", irq);
 		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 	}
 
@@ -1168,6 +1080,11 @@
 
 	}  /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+	}
+
 	return( ret );
 }
 
@@ -1195,8 +1112,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "dissch");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "dissch%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		
@@ -1274,6 +1190,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ret:%d",ret);
+		debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+	}
+
 	return( ret);
 }
 
@@ -1364,8 +1285,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "stIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "stIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1427,6 +1347,11 @@
 	 */
 	ccode = ssch( irq, &(ioinfo[irq]->orb) );
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ccode:%d", ccode);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	switch ( ccode ) {
 	case 0:
 
@@ -1849,8 +1774,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "doIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "doIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1908,8 +1832,7 @@
 	SANITY_CHECK(irq);
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "resIO");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "resIO%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -1922,6 +1845,11 @@
 
 		ccode = rsch( irq);
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+		}
+
 		switch (ccode) {
 		case 0 :
 			break;
@@ -1988,21 +1916,10 @@
 	{
 		ret = 0;
 	}
-#if 0
-	/*
-	 * We don't allow for halt_io with a sync do_IO() requests pending.
-	 */
-	else if (    ioinfo[irq]->ui.flags.syncio
-	          && (flag & DOIO_WAIT_FOR_INTERRUPT))
-	{
-		ret = -EBUSY;
-	}
-#endif
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "haltIO");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "haltIO%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		/*
@@ -2028,6 +1945,11 @@
 		 */
 		ccode = hsch( irq );
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+		}		
+
 		switch ( ccode ) {
 		case 0:
 
@@ -2253,22 +2175,10 @@
 	{
 		ret = 0;
 	}
-#if 0
-	/*
-	 * We don't allow for clear_io with a sync do_IO() requests pending.
-	 *  Concurrent I/O is possible in SMP environments only, but the
-	 *  sync. I/O request can be gated to one CPU at a time only.
-	 */
-	else if ( ioinfo[irq]->ui.flags.syncio )
-	{
-		ret = -EBUSY;
-	}
-#endif
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 2, "clearIO");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "clearIO%x", irq);
 			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
 		}
 		/*
@@ -2294,6 +2204,11 @@
 		 */
 		ccode = csch( irq );
 
+		if (cio_debug_initialized) {
+			sprintf(dbf_txt, "ccode:%d",ccode);
+			debug_text_event(cio_debug_trace_id, 2, dbf_txt);
+		}
+
 		switch ( ccode ) {
 		case 0:
 
@@ -2548,10 +2463,8 @@
 	}
 	
 	
-
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 3, "procIRQ");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "procIRQ%x", irq);
 		debug_text_event(cio_debug_trace_id, 3, dbf_txt);
 	}
 
@@ -2612,6 +2525,11 @@
 	 */
 	ccode = tsch( irq, &(dp->ii.irb) );
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt, "ccode:%d", ccode);
+		debug_text_event(cio_debug_trace_id, 3, dbf_txt);
+	}
+
 	//
 	// We must only accumulate the status if the device is busy already
 	//
@@ -2675,7 +2593,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 		if ( irq != cons_dev )
-			printk( "s390_process_IRQ( %04X ) : "
+			printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 			        "residual count from irb after tsch() %d\n",
 			        irq, dp->rescnt );
 #endif
@@ -2698,7 +2616,7 @@
 			 | SCHN_STAT_INTF_CTRL_CHK )))
 	{
 		if (irq != cons_dev)
-			printk( "Channel-Check or Interface-Control-Check "
+			printk( KERN_WARNING "Channel-Check or Interface-Control-Check "
 				"received\n"
 				" ... device %04X on subchannel %04X, dev_stat "
 				": %02X sch_stat : %02X\n",
@@ -2748,7 +2666,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 		if ( irq != cons_dev )
-			printk( "s390_process_IRQ( %04X ) : "
+			printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 			        "concurrent sense bytes avail %d\n",
 			        irq, dp->scnt );
 #endif
@@ -2800,10 +2718,9 @@
 			
 		ioinfo[irq]->stctl |= stctl;
 
-		ending_status =    ( stctl & SCSW_STCTL_SEC_STATUS                          )
-			|| ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)         )
-			|| ( (fctl == SCSW_FCTL_HALT_FUNC)  && (stctl == SCSW_STCTL_STATUS_PEND) )
-			|| ( (fctl == SCSW_FCTL_CLEAR_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) );
+		ending_status =    ( stctl & SCSW_STCTL_SEC_STATUS )
+			        || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) )
+			        || ( stctl == SCSW_STCTL_STATUS_PEND);
 
 		/*
 		 * Check for unsolicited interrupts - for debug purposes only
@@ -2821,7 +2738,7 @@
 		{
 #ifdef CONFIG_DEBUG_IO
 			if (irq != cons_dev)
-				printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n"
+				printk( KERN_INFO "Unsolicited interrupt received for device %04X on subchannel %04X\n"
 					" ... device status : %02X subchannel status : %02X\n",
 					dp->devno,
 					irq,
@@ -2998,7 +2915,7 @@
 
 #ifdef CONFIG_DEBUG_IO
 				if ( irq != cons_dev )
-					printk( "s390_process_IRQ( %04X ) : "
+					printk( KERN_DEBUG "s390_process_IRQ( %04X ) : "
 						"BASIC SENSE bytes avail %d\n",
 						irq, sense_count );
 #endif
@@ -3274,8 +3191,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "scons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "scons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 		
@@ -3336,8 +3252,7 @@
 	else
 	{
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "rcons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "rcons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 
@@ -3393,8 +3308,7 @@
 	{
 
 		if (cio_debug_initialized) {
-			debug_text_event(cio_debug_trace_id, 4, "wcons");
-			sprintf(dbf_txt, "%x", irq);
+			sprintf(dbf_txt, "wcons%x", irq);
 			debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 		}
 
@@ -3456,8 +3370,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "enisc");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "enisc%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -3573,8 +3486,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "disisc");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "disisc%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -3973,7 +3885,7 @@
 
 	if ( error )
 	{
-		printk( "DIAG X'210' for "
+		printk( KERN_ERR "DIAG X'210' for "
 		        "device %04X returned "
 		        "(cc = %d): vdev class : %02X, "
 			"vdev type : %04X \n ...  rdev class : %02X, rdev type : %04X, rdev model: %02X\n",
@@ -4054,8 +3966,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "rddevch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "rddevch%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4194,8 +4105,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "rdconf");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "rdconf%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4637,8 +4547,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "devrec");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "devrec%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4779,10 +4688,12 @@
 						
 					} 
 				}
+#ifdef CONFIG_PROC_FS
 				if (cio_proc_devinfo) 
 					if (irq < MAX_CIO_PROCFS_ENTRIES) {
 						cio_procfs_device_create(ioinfo[irq]->devno);
 				}
+#endif
 			}
 		}
 		irq++;
@@ -4811,7 +4722,7 @@
 
 	highest_subchannel = (--irq);
 
-	printk( "Highest subchannel number detected (hex) : %04X\n",
+	printk( KERN_INFO "Highest subchannel number detected (hex) : %04X\n",
 	        highest_subchannel);
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 0, 
@@ -4837,8 +4748,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "valsch");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "valsch%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -4876,7 +4786,7 @@
 		 */
 		if ( p_schib->pmcw.st )
 		{
-			printk( "Subchannel %04X reports "
+			printk( KERN_INFO "Subchannel %04X reports "
 			        "non-I/O subchannel type %04X\n",
 			        irq,
 			        p_schib->pmcw.st);
@@ -4898,7 +4808,7 @@
 			   * there is no device and return ENODEV.
 			   */
 #ifdef CONFIG_DEBUG_IO
-			  printk( "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
+			  printk( KERN_DEBUG "Blacklisted device detected at devno %04X\n", p_schib->pmcw.dev );
 #endif
 			  if (cio_debug_initialized)
 				  debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5109,7 +5019,7 @@
 
 							if ( ccode2 != 0 )
 							{
-								printk( " ... msch() (2) failed with CC = %X\n",
+								printk( KERN_ERR " ... msch() (2) failed with CC = %X\n",
 								        ccode2 );
 								if (cio_debug_initialized)
 									debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5128,7 +5038,7 @@
 						}
 						else
 						{
-							printk( " ... msch() (1) failed with CC = %X\n",
+							printk( KERN_ERR " ... msch() (1) failed with CC = %X\n",
 							        ccode2);
 							if (cio_debug_initialized)
 								debug_sprintf_event(cio_debug_msg_id, 0,
@@ -5148,7 +5058,7 @@
 
 				if ( (ccode2 != 0) && (ccode2 != 3) && (!retry) )
 				{
-					printk( " ... msch() retry count for "
+					printk( KERN_ERR " ... msch() retry count for "
 					        "subchannel %04X exceeded, CC = %d\n",
 					        irq,
 					        ccode2);
@@ -5226,8 +5136,7 @@
 	} /* endif */
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "snsID");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "snsID%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -5344,7 +5253,7 @@
 						if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : device %04X on "
+							printk( KERN_DEBUG "SenseID : device %04X on "
 							        "Subchannel %04X "
 							        "reports pending status, "
 							        "retry : %d\n",
@@ -5363,7 +5272,7 @@
 										    retry);		    
 						} /* endif */
 
-						if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
+						else if ( pdevstat->flag & DEVSTAT_FLAG_SENSE_AVAIL )
 						{
 							/*
 							 * if the device doesn't support the SenseID
@@ -5373,7 +5282,7 @@
 							    & (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ) )
 							{
 #ifdef CONFIG_DEBUG_IO
-								printk( "SenseID : device %04X on "
+								printk( KERN_ERR "SenseID : device %04X on "
 								        "Subchannel %04X "
 								        "reports cmd reject or "
 								        "intervention required\n",
@@ -5394,7 +5303,7 @@
 							else
 							{
 #ifdef CONFIG_DEBUG_IO							
-								printk( "SenseID : UC on "
+								printk( KERN_WARNING "SenseID : UC on "
 								        "dev %04X, "
 								        "retry %d, "
 								        "lpum %02X, "
@@ -5449,7 +5358,7 @@
 							  || ( irq_ret        == -ENODEV         ) )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : path %02X for "
+							printk( KERN_ERR "SenseID : path %02X for "
 							        "device %04X on "
 							        "subchannel %04X "
 							        "is 'not operational'\n",
@@ -5479,7 +5388,7 @@
 								DEVSTAT_STATUS_PENDING       ) )
 						{
 #ifdef CONFIG_DEBUG_IO
-							printk( "SenseID : start_IO() for "
+							printk( KERN_INFO "SenseID : start_IO() for "
 							        "device %04X on "
 							        "subchannel %04X "
 							        "returns %d, retry %d, "
@@ -5628,7 +5537,7 @@
 			 *  consider the device "not operational".
 			 */
 #ifdef CONFIG_DEBUG_IO
-			printk( "SenseID : unknown device %04X on subchannel %04X\n",
+			printk( KERN_WARNING "SenseID : unknown device %04X on subchannel %04X\n",
 			        ioinfo[irq]->schib.pmcw.dev,
 			        irq);
 #endif
@@ -5732,8 +5641,7 @@
 	char dbf_txt[15];
 
 	if (cio_debug_initialized) {
-		debug_text_event(cio_debug_trace_id, 4, "dpver");
-		sprintf(dbf_txt, "%x", irq);
+		sprintf(dbf_txt, "dpver%x", irq);
 		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
 	}
 
@@ -5842,7 +5750,7 @@
 						ioinfo[irq]->ui.flags.pgid_supp = 0;
 
 #ifdef CONFIG_DEBUG_IO
-						printk( "PathVerification(%04X) "
+						printk( KERN_WARNING "PathVerification(%04X) "
 						        "- Device %04X doesn't "
 						        " support path grouping\n",
 						        irq,
@@ -5861,7 +5769,7 @@
 				else if ( ret == -EIO ) 
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk("PathVerification(%04X) - I/O error "
+					printk( KERN_ERR "PathVerification(%04X) - I/O error "
 					       "on device %04X\n", irq,
 					       ioinfo[irq]->schib.pmcw.dev);
 #endif
@@ -5876,7 +5784,7 @@
 		    
 				} else {
 #ifdef CONFIG_DEBUG_IO
-					printk( "PathVerification(%04X) "
+					printk( KERN_ERR "PathVerification(%04X) "
 						"- Unexpected error on device %04X\n",
 						irq,
 						ioinfo[irq]->schib.pmcw.dev);
@@ -5914,6 +5822,7 @@
 	devstat_t  devstat;     /* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
         unsigned long flags;
+	char dbf_txt[15];
 
 
 	int        irq_ret = 0; /* return code */
@@ -5929,6 +5838,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"SPID%x",irq);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	if ( !ioinfo[irq]->ui.flags.ready )
 	{
 		/*
@@ -5966,7 +5880,7 @@
 
 		} /* endif */
 
-		spid_ccw[0].cmd_code = 0x5B;	/* suspend multipath reconnect */
+		spid_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;	
 		spid_ccw[0].cda      = 0;
 		spid_ccw[0].count    = 0;
 		spid_ccw[0].flags    = CCW_FLAG_SLI | CCW_FLAG_CC;
@@ -5999,7 +5913,7 @@
 				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk( "SPID - Device %04X "
+					printk( KERN_DEBUG "SPID - Device %04X "
 					        "on Subchannel %04X "
 					        "reports pending status, "
 					        "retry : %d\n",
@@ -6037,6 +5951,18 @@
 					{
 						if ( mpath )
 						{
+							/*
+							 * We now try single path mode.
+							 * Note we must not issue the suspend
+							 * multipath reconnect, or we will get
+							 * a command reject by tapes.
+							 */
+							
+							spid_ccw[0].cmd_code = CCW_CMD_SET_PGID;
+							spid_ccw[0].cda      = (__u32)virt_to_phys( pgid );
+							spid_ccw[0].count    = sizeof( pgid_t);
+							spid_ccw[0].flags    = CCW_FLAG_SLI;
+
 							pgid->inf.fc =   SPID_FUNC_SINGLE_PATH
 							               | SPID_FUNC_ESTABLISH;
 							mpath        = 0;
@@ -6053,7 +5979,7 @@
 					else
 					{
 #ifdef CONFIG_DEBUG_IO
-						printk( "SPID - device %04X,"
+						printk( KERN_WARNING "SPID - device %04X,"
 						        " unit check,"
 						        " retry %d, cnt %02d,"
 						        " sns :"
@@ -6101,7 +6027,7 @@
 					/* don't issue warnings during startup unless requested*/
 					if (init_IRQ_complete || cio_notoper_msg) {   
 						
-						printk( "SPID - Device %04X "
+						printk( KERN_WARNING "SPID - Device %04X "
 							"on Subchannel %04X "
 							"became 'not operational'\n",
 							ioinfo[irq]->schib.pmcw.dev,
@@ -6171,6 +6097,7 @@
 	ccw1_t    *snid_ccw;    /* ccw area for SNID command */
 	devstat_t  devstat;     /* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
+	char dbf_txt[15];
 
 	int        irq_ret = 0; /* return code */
 	int        retry   = 5; /* retry count */
@@ -6185,6 +6112,11 @@
 
 	} /* endif */
 
+	if (cio_debug_initialized) {
+		sprintf(dbf_txt,"SNID%x",irq);
+		debug_text_event(cio_debug_trace_id, 4, dbf_txt);
+	}
+
 	if ( !ioinfo[irq]->ui.flags.ready )
 	{
 		/*
@@ -6261,7 +6193,7 @@
 					else
 					{
 #ifdef CONFIG_DEBUG_IO
-						printk( "SNID - device %04X,"
+						printk( KERN_WARNING "SNID - device %04X,"
 						        " unit check,"
 						        " flag %04X, "
 						        " retry %d, cnt %02d,"
@@ -6310,7 +6242,7 @@
 				{
 					/* don't issue warnings during startup unless requested*/
 					if (init_IRQ_complete || cio_notoper_msg) {  
-						printk( "SNID - Device %04X "
+						printk( KERN_WARNING "SNID - Device %04X "
 							"on Subchannel %04X "
 							"became 'not operational'\n",
 							ioinfo[irq]->schib.pmcw.dev,
@@ -6341,7 +6273,7 @@
 				if ( pdevstat->flag & DEVSTAT_STATUS_PENDING )
 				{
 #ifdef CONFIG_DEBUG_IO
-					printk( "SNID - Device %04X "
+					printk( KERN_INFO "SNID - Device %04X "
 					        "on Subchannel %04X "
 					        "reports pending status, "
 					        "retry : %d\n",
@@ -6361,7 +6293,7 @@
 				} /* endif */
 
 
-				printk( "SNID - device %04X,"
+				printk( KERN_WARNING "SNID - device %04X,"
 				        " start_io() reports rc : %d, retrying ...\n",
 				        ioinfo[irq]->schib.pmcw.dev,
 				        irq_ret);
@@ -6425,7 +6357,7 @@
 	int lock     = 0;
 
 #ifdef CONFIG_DEBUG_CRW
-	printk( "do_crw_pending : starting ...\n");
+	printk( KERN_DEBUG "do_crw_pending : starting ...\n");
 #endif
 	if (cio_debug_initialized) 
 		debug_sprintf_event(cio_debug_crw_id, 2, 
@@ -6440,7 +6372,7 @@
 			irq = pcrwe->crw.rsid;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( KERN_INFO"do_crw_pending : source is "
+			printk( KERN_NOTICE"do_crw_pending : source is "
 			        "subchannel %04X\n", irq);
 #endif
 			if (cio_debug_initialized)
@@ -6465,7 +6397,7 @@
 			} /* endif */
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : subchannel validation - start ...\n");
+			printk( KERN_DEBUG "do_crw_pending : subchannel validation - start ...\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 4,
@@ -6476,7 +6408,7 @@
 				highest_subchannel = irq;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : subchannel validation - done\n");
+			printk( KERN_DEBUG "do_crw_pending : subchannel validation - done\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 4,
@@ -6496,7 +6428,7 @@
 			if ( ioinfo[irq] != INVALID_STORAGE_AREA )
 			{
 #ifdef CONFIG_DEBUG_CRW
-				printk( "do_crw_pending : ioinfo at "
+				printk( KERN_DEBUG "do_crw_pending : ioinfo at "
 #ifdef CONFIG_ARCH_S390X
 					"%08lX\n",
 					(unsigned long)ioinfo[irq]);
@@ -6523,10 +6455,11 @@
 				if ( ioinfo[irq]->ui.flags.oper == 0 )
 				{
 					 not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc;
-					 
+#ifdef CONFIG_PROC_FS					 
 					 /* remove procfs entry */
 					 if (cio_proc_devinfo)
 						 cio_procfs_device_remove(dev_no);
+#endif
 					/*
 					 * If the device has gone
 					 *  call not oper handler        	
@@ -6543,7 +6476,7 @@
 				else
 				{
 #ifdef CONFIG_DEBUG_CRW
-					printk( "do_crw_pending : device "
+					printk( KERN_DEBUG "do_crw_pending : device "
 					        "recognition - start ...\n");
 #endif
 					if (cio_debug_initialized)
@@ -6552,7 +6485,7 @@
 					s390_device_recognition_irq( irq );
 
 #ifdef CONFIG_DEBUG_CRW
-					printk( "do_crw_pending : device "
+					printk( KERN_DEBUG "do_crw_pending : device "
 					        "recognition - done\n");
 #endif
 					if (cio_debug_initialized)
@@ -6573,12 +6506,13 @@
 								pdevreg->oper_func( irq, pdevreg );
 
 						} /* endif */
-
+#ifdef CONFIG_PROC_FS
 						/* add new procfs entry */
 						if (cio_proc_devinfo) 
 							if (highest_subchannel < MAX_CIO_PROCFS_ENTRIES) {
 								cio_procfs_device_create(ioinfo[irq]->devno);
 							}
+#endif
 					}
 					/*
 					 * ... it is and was operational, but
@@ -6586,23 +6520,26 @@
 					 */
 					else if ((ioinfo[irq]->devno != dev_no) && ( ioinfo[irq]->nopfunc != NULL ))   					
 					{
+#ifdef CONFIG_PROC_FS
 						int devno_old = ioinfo[irq]->devno;
+#endif
 						ioinfo[irq]->nopfunc( irq,
 						                      DEVSTAT_REVALIDATE );				
-
+#ifdef CONFIG_PROC_FS
 						/* remove old entry, add new */
 						if (cio_proc_devinfo) {
 							cio_procfs_device_remove(devno_old);
 							cio_procfs_device_create(ioinfo[irq]->devno);
 						}
+#endif
 					} /* endif */
 
 				} /* endif */
-
+#ifdef CONFIG_PROC_FS
 				/* get rid of dead procfs entries */
 				if (cio_proc_devinfo) 
 					cio_procfs_device_purge();
-
+#endif
 			} /* endif */
 
 			break;
@@ -6610,7 +6547,7 @@
 		case CRW_RSC_MONITOR :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "monitoring facility\n");
 #endif
 			if (cio_debug_initialized)
@@ -6623,7 +6560,7 @@
 			chpid = pcrwe->crw.rsid;
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "channel path %02X\n", chpid);
 #endif
 			if (cio_debug_initialized)
@@ -6634,7 +6571,7 @@
 		case CRW_RSC_CONFIG : 	
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "configuration-alert facility\n");
 #endif
 			if (cio_debug_initialized)
@@ -6645,7 +6582,7 @@
 		case CRW_RSC_CSS :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : source is "
+			printk( KERN_NOTICE "do_crw_pending : source is "
 			        "channel subsystem\n");
 #endif
 			if (cio_debug_initialized)
@@ -6656,7 +6593,7 @@
 		default :
 
 #ifdef CONFIG_DEBUG_CRW
-			printk( "do_crw_pending : unknown source\n");
+			printk( KERN_NOTICE "do_crw_pending : unknown source\n");
 #endif
 			if (cio_debug_initialized)
 				debug_sprintf_event(cio_debug_crw_id, 2,
@@ -6670,7 +6607,7 @@
 	} /* endwhile */
 
 #ifdef CONFIG_DEBUG_CRW
-	printk( "do_crw_pending : done\n");
+	printk( KERN_DEBUG "do_crw_pending : done\n");
 #endif
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_crw_id, 2,
@@ -6715,7 +6652,7 @@
 {
 	int ret = 0;
 
-	cio_debug_msg_id = debug_register("cio_msg",2,4,16*sizeof(long));
+	cio_debug_msg_id = debug_register("cio_msg",4,4,16*sizeof(long));
 	if (cio_debug_msg_id != NULL) {
 		debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
 #ifdef CONFIG_DEBUG_IO
@@ -6756,6 +6693,7 @@
 
 __initcall(cio_debug_init);
 
+#ifdef CONFIG_PROC_FS
 /* 
  * Display info on subchannels in /proc/subchannels. 
  * Adapted from procfs stuff in dasd.c by Cornelia Huck, 02/28/01.      
@@ -7163,19 +7101,19 @@
 				entry->cio_chpid_entry = create_proc_entry( "chpids", S_IFREG|S_IRUGO, entry->cio_device_entry);
 				entry->cio_chpid_entry->proc_fops = &cio_chpid_entry_file_ops;
 			} else {
-				printk("Error, could not allocate procfs structure!\n");
+				printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 				remove_proc_entry(buf, cio_procfs_deviceinfo_root);
 				kfree(entry);
 				rc = -ENOMEM;
 			}
 		} else {
-			printk("Error, could not allocate procfs structure!\n");
+			printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 			kfree(entry);
 			rc = -ENOMEM;
 		}
 
 	} else {
-		printk("Error, could not allocate procfs structure!\n");
+		printk( KERN_WARNING "Error, could not allocate procfs structure!\n");
 		rc = -ENOMEM;
 	}
 	return rc;
@@ -7288,15 +7226,14 @@
  */
 
 static struct proc_dir_entry *cio_ignore_proc_entry;
-
 static int cio_ignore_proc_open(struct inode *inode, struct file *file)
 {
 	int rc = 0;
 	int size = 1;
 	int len = 0;
 	tempinfo_t *info;
-	dev_blacklist_range_t *tmp;
 	long flags;
+	int i, j;
 
 	info = (tempinfo_t *) vmalloc(sizeof(tempinfo_t));
 	if (info == NULL) {
@@ -7304,7 +7241,7 @@
 		rc = -ENOMEM;
 	} else {
 		file->private_data = (void *) info;
-		size += nr_blacklisted_ranges * 32;
+		size += nr_ignored * 6;
 		info->data = (char *) vmalloc(size);
 		if (size && info->data == NULL) {
 			printk( KERN_WARNING "No memory available for data\n");
@@ -7312,13 +7249,15 @@
 			rc = -ENOMEM;
 		} else {
 			spin_lock_irqsave( &blacklist_lock, flags ); 
-			tmp = dev_blacklist_range_head;
-			while (tmp) {
-				len += sprintf(info->data+len, "%04x ", tmp->from);
-				if (tmp->to != tmp->from) 
-					len += sprintf(info->data+len, "- %04x", tmp->to);
+			for (i=0;i<=highest_ignored;i++) 
+				if (test_bit(i,&bl_dev)) {
+					len += sprintf(info->data+len, "%04x ", i);
+					for (j=i;(j<=highest_ignored) && (test_bit(j,&bl_dev));j++);
+					j--;
+					if (i != j) 
+						len += sprintf(info->data+len, "- %04x", j);
 				len += sprintf(info->data+len, "\n");
-				tmp = tmp->next;
+					i=j;
 			}
 			spin_unlock_irqrestore( &blacklist_lock, flags );
 			info->len = len;
@@ -7370,7 +7309,7 @@
 	}
 	buffer[user_len]='\0';
 #ifdef CIO_DEBUG_IO
-	printk ("/proc/cio_ignore: '%s'\n", buffer);
+	printk ( KERN_DEBUG "/proc/cio_ignore: '%s'\n", buffer);
 #endif /* CIO_DEBUG_IO */
 	if (cio_debug_initialized)
 		debug_sprintf_event(cio_debug_msg_id, 2, "/proc/cio_ignore: '%s'\n",buffer);
@@ -7491,6 +7430,7 @@
 __initcall(cio_irq_proc_init);	
 
 /* end of procfs stuff */
+#endif
 
 schib_t *s390_get_schib( int irq )
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/s390/sysinfo.c linux/drivers/s390/sysinfo.c
--- linux.orig/drivers/s390/sysinfo.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/s390/sysinfo.c	Fri Dec 21 18:00:38 2001
@@ -0,0 +1,354 @@
+/*
+ *  drivers/s390/sysinfo.c
+ *
+ *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ *    Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <asm/ebcdic.h>
+
+struct sysinfo_1_1_1
+{
+	char reserved_0[32];
+	char manufacturer[16];
+	char type[4];
+	char reserved_1[12];
+	char model[16];
+	char sequence[16];
+	char plant[4];
+};
+
+struct sysinfo_1_2_1
+{
+	char reserved_0[80];
+	char sequence[16];
+	char plant[4];
+	char reserved_1[2];
+	unsigned short cpu_address;
+};
+
+struct sysinfo_1_2_2
+{
+	char reserved_0[32];
+	unsigned int capability;
+	unsigned short cpus_total;
+	unsigned short cpus_configured;
+	unsigned short cpus_standby;
+	unsigned short cpus_reserved;
+	unsigned short adjustment[0];
+};
+
+struct sysinfo_2_2_1
+{
+	char reserved_0[80];
+	char sequence[16];
+	char plant[4];
+	unsigned short cpu_id;
+	unsigned short cpu_address;
+};
+
+struct sysinfo_2_2_2
+{
+	char reserved_0[32];
+	unsigned short lpar_number;
+	char reserved_1;
+	unsigned char characteristics;
+	#define LPAR_CHAR_DEDICATED	(1 << 7)
+	#define LPAR_CHAR_SHARED	(1 << 6)
+	#define LPAR_CHAR_LIMITED	(1 << 5)
+	unsigned short cpus_total;
+	unsigned short cpus_configured;
+	unsigned short cpus_standby;
+	unsigned short cpus_reserved;
+	char name[8];
+	unsigned int caf;
+	char reserved_2[16];
+	unsigned short cpus_dedicated;
+	unsigned short cpus_shared;
+};
+
+struct sysinfo_3_2_2
+{
+	char reserved_0[31];
+	unsigned char count;
+	struct
+	{
+		char reserved_0[4];
+		unsigned short cpus_total;
+		unsigned short cpus_configured;
+		unsigned short cpus_standby;
+		unsigned short cpus_reserved;
+		char name[8];
+		unsigned int caf;
+		char cpi[16];
+		char reserved_1[24];
+
+	} vm[8];
+};
+
+union s390_sysinfo
+{
+	struct sysinfo_1_1_1 sysinfo_1_1_1;
+	struct sysinfo_1_2_1 sysinfo_1_2_1;
+	struct sysinfo_1_2_2 sysinfo_1_2_2;
+	struct sysinfo_2_2_1 sysinfo_2_2_1;
+	struct sysinfo_2_2_2 sysinfo_2_2_2;
+	struct sysinfo_3_2_2 sysinfo_3_2_2;
+};
+
+static inline int stsi (void *sysinfo, 
+                        int fc, int sel1, int sel2)
+{
+	int cc, retv;
+
+#ifndef CONFIG_ARCH_S390X
+	__asm__ __volatile__ (	"lr\t0,%2\n"
+				"\tlr\t1,%3\n"
+				"\tstsi\t0(%4)\n"
+				"0:\tipm\t%0\n"
+				"\tsrl\t%0,28\n"
+				"1:lr\t%1,0\n"
+				".section .fixup,\"ax\"\n"
+				"2:\tlhi\t%0,3\n"
+				"\tbras\t1,3f\n"
+				"\t.long 1b\n"
+				"3:\tl\t1,0(1)\n"
+				"\tbr\t1\n"
+				".previous\n"
+				".section __ex_table,\"a\"\n"
+				"\t.align 4\n"
+				"\t.long 0b,2b\n"
+				".previous\n"
+				: "=d" (cc), "=d" (retv)
+				: "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) 
+				: "cc", "memory", "0", "1" );
+#else
+	__asm__ __volatile__ (	"lr\t0,%2\n"
+				"lr\t1,%3\n"
+				"\tstsi\t0(%4)\n"
+				"0:\tipm\t%0\n"
+				"\tsrl\t%0,28\n"
+				"1:lr\t%1,0\n"
+				".section .fixup,\"ax\"\n"
+				"2:\tlhi\t%0,3\n"
+				"\tjg\t1b\n"
+				".previous\n"
+				".section __ex_table,\"a\"\n"
+				"\t.align 8\n"
+				"\t.quad 0b,2b\n"
+				".previous\n"
+				: "=d" (cc), "=d" (retv)
+				: "d" ((fc << 28) | sel1), "d" (sel2), "a" (sysinfo) 
+				: "cc", "memory", "0", "1" );
+#endif
+
+	return cc? -1 : retv;
+}
+
+static inline int stsi_0 (void)
+{
+	int rc = stsi (NULL, 0, 0, 0);
+	return rc == -1 ? rc : (((unsigned int)rc) >> 28);
+}
+
+static inline int stsi_1_1_1 (struct sysinfo_1_1_1 *info)
+{
+	int rc = stsi (info, 1, 1, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->manufacturer, sizeof(info->manufacturer));
+		EBCASC (info->type, sizeof(info->type));
+		EBCASC (info->model, sizeof(info->model));
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_1_2_1 (struct sysinfo_1_2_1 *info)
+{
+	int rc = stsi (info, 1, 2, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_1_2_2 (struct sysinfo_1_2_2 *info)
+{
+	int rc = stsi (info, 1, 2, 2);
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_2_2_1 (struct sysinfo_2_2_1 *info)
+{
+	int rc = stsi (info, 2, 2, 1);
+	if (rc != -1)
+	{
+		EBCASC (info->sequence, sizeof(info->sequence));
+		EBCASC (info->plant, sizeof(info->plant));
+	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_2_2_2 (struct sysinfo_2_2_2 *info)
+{
+	int rc = stsi (info, 2, 2, 2);
+	if (rc != -1)
+	{
+		EBCASC (info->name, sizeof(info->name));
+  	}
+	return rc == -1 ? rc : 0;
+}
+
+static inline int stsi_3_2_2 (struct sysinfo_3_2_2 *info)
+{
+	int rc = stsi (info, 3, 2, 2);
+	if (rc != -1)
+	{
+		int i;
+		for (i = 0; i < info->count; i++)
+		{
+			EBCASC (info->vm[i].name, sizeof(info->vm[i].name));
+			EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi));
+		}
+	}
+	return rc == -1 ? rc : 0;
+}
+
+
+static int proc_read_sysinfo(char *page, char **start,
+                             off_t off, int count,
+                             int *eof, void *data)
+{
+	unsigned long info_page = get_free_page (GFP_KERNEL); 
+	union s390_sysinfo *info = (union s390_sysinfo *) info_page;
+	int len = 0;
+	int level;
+	int i;
+	
+	if (!info)
+		return 0;
+
+	level = stsi_0 ();
+
+	if (level < 1)
+		goto out;
+	if (stsi_1_1_1 (&info->sysinfo_1_1_1))
+		goto out;
+
+	len += sprintf (page+len, "Manufacturer:         %-16.16s\n",
+			info->sysinfo_1_1_1.manufacturer);
+	len += sprintf (page+len, "Type:                 %-4.4s\n",
+			info->sysinfo_1_1_1.type);
+	len += sprintf (page+len, "Model:                %-16.16s\n",
+			info->sysinfo_1_1_1.model);
+	len += sprintf (page+len, "Sequence Code:        %-16.16s\n",
+			info->sysinfo_1_1_1.sequence);
+	len += sprintf (page+len, "Plant:                %-4.4s\n",
+			info->sysinfo_1_1_1.plant);
+
+	if (stsi_1_2_2 (&info->sysinfo_1_2_2))
+		goto out;
+
+	len += sprintf (page+len, "\n");
+	len += sprintf (page+len, "CPUs Total:           %d\n",
+			info->sysinfo_1_2_2.cpus_total);
+	len += sprintf (page+len, "CPUs Configured:      %d\n",
+			info->sysinfo_1_2_2.cpus_configured);
+	len += sprintf (page+len, "CPUs Standby:         %d\n",
+			info->sysinfo_1_2_2.cpus_standby);
+	len += sprintf (page+len, "CPUs Reserved:        %d\n",
+			info->sysinfo_1_2_2.cpus_reserved);
+
+	len += sprintf (page+len, "Capability:           %d\n",
+			info->sysinfo_1_2_2.capability);
+
+	for (i = 2; i <= info->sysinfo_1_2_2.cpus_total; i++)
+		len += sprintf (page+len, "Adjustment %02d-way:    %d\n",
+				i, info->sysinfo_1_2_2.adjustment[i-2]);
+			
+	if (level < 2)
+		goto out;
+	if (stsi_2_2_2 (&info->sysinfo_2_2_2))
+		goto out;
+
+	len += sprintf (page+len, "\n");
+	len += sprintf (page+len, "LPAR Number:          %d\n",
+			info->sysinfo_2_2_2.lpar_number);
+
+	len += sprintf (page+len, "LPAR Characteristics: ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_DEDICATED)
+		len += sprintf (page+len, "Dedicated ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_SHARED)
+		len += sprintf (page+len, "Shared ");
+	if (info->sysinfo_2_2_2.characteristics & LPAR_CHAR_LIMITED)
+		len += sprintf (page+len, "Limited ");
+	len += sprintf (page+len, "\n");
+
+	len += sprintf (page+len, "LPAR Name:            %-8.8s\n",
+			info->sysinfo_2_2_2.name);
+
+	len += sprintf (page+len, "LPAR Adjustment:      %d\n",
+			info->sysinfo_2_2_2.caf);
+
+	len += sprintf (page+len, "LPAR CPUs Total:      %d\n",
+			info->sysinfo_2_2_2.cpus_total);
+	len += sprintf (page+len, "LPAR CPUs Configured: %d\n",
+			info->sysinfo_2_2_2.cpus_configured);
+	len += sprintf (page+len, "LPAR CPUs Standby:    %d\n",
+			info->sysinfo_2_2_2.cpus_standby);
+	len += sprintf (page+len, "LPAR CPUs Reserved:   %d\n",
+			info->sysinfo_2_2_2.cpus_reserved);
+	len += sprintf (page+len, "LPAR CPUs Dedicated:  %d\n",
+			info->sysinfo_2_2_2.cpus_dedicated);
+	len += sprintf (page+len, "LPAR CPUs Shared:     %d\n",
+			info->sysinfo_2_2_2.cpus_shared);
+
+	if (level < 3)
+		goto out;
+	if (stsi_3_2_2 (&info->sysinfo_3_2_2))
+		goto out;
+
+	for (i = 0; i < info->sysinfo_3_2_2.count; i++)
+	{
+		len += sprintf (page+len, "\n");
+		len += sprintf (page+len, "VM%02d Name:            %-8.8s\n",
+				i, info->sysinfo_3_2_2.vm[i].name);
+		len += sprintf (page+len, "VM%02d Control Program: %-16.16s\n",
+				i, info->sysinfo_3_2_2.vm[i].cpi);
+
+		len += sprintf (page+len, "VM%02d Adjustment:      %d\n",
+				i, info->sysinfo_3_2_2.vm[i].caf);
+
+		len += sprintf (page+len, "VM%02d CPUs Total:      %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_total);
+		len += sprintf (page+len, "VM%02d CPUs Configured: %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_configured);
+		len += sprintf (page+len, "VM%02d CPUs Standby:    %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_standby);
+		len += sprintf (page+len, "VM%02d CPUs Reserved:   %d\n",
+				i, info->sysinfo_3_2_2.vm[i].cpus_reserved);
+	}
+
+out:
+	free_page (info_page);
+        return len;
+}
+
+static __init int create_proc_sysinfo(void)
+{
+	create_proc_read_entry ("sysinfo", 0444, NULL, 
+				proc_read_sysinfo, NULL);
+	return 0;
+}
+
+__initcall(create_proc_sysinfo);
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/aurora.c linux/drivers/sbus/char/aurora.c
--- linux.orig/drivers/sbus/char/aurora.c	Tue Oct 30 23:08:11 2001
+++ linux/drivers/sbus/char/aurora.c	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/*	$Id: aurora.c,v 1.18 2001/10/26 17:59:31 davem Exp $
+/*	$Id: aurora.c,v 1.18.2.1 2002/02/04 22:37:43 davem Exp $
  *	linux/drivers/sbus/char/aurora.c -- Aurora multiport driver
  *
  *	Copyright (c) 1999 by Oliver Aldulea (oli at bv dot ro)
@@ -180,7 +180,7 @@
 #ifdef AURORA_DEBUG
 	printk("aurora_long_delay: start\n");
 #endif
-	for (i = jiffies + delay; i > jiffies; ) ;
+	for (i = jiffies + delay; time_before(jiffies, i); ) ;
 #ifdef AURORA_DEBUG
 	printk("aurora_long_delay: end\n");
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/envctrl.c linux/drivers/sbus/char/envctrl.c
--- linux.orig/drivers/sbus/char/envctrl.c	Thu Oct 11 06:42:47 2001
+++ linux/drivers/sbus/char/envctrl.c	Thu Jan 17 19:37:52 2002
@@ -1,4 +1,4 @@
-/* $Id: envctrl.c,v 1.24 2001/10/08 22:19:51 davem Exp $
+/* $Id: envctrl.c,v 1.24.2.1 2002/01/15 09:01:39 davem Exp $
  * envctrl.c: Temperature and Fan monitoring on Machines providing it.
  *
  * Copyright (C) 1998  Eddie C. Dost  (ecd@skynet.be)
@@ -776,24 +776,19 @@
 static void envctrl_init_adc(struct i2c_child_t *pchild, int node)
 {
 	char chnls_desc[CHANNEL_DESC_SZ];
-	int i, len, j = 0;
-	char *ptr;
+	int i = 0, len;
+	char *pos = chnls_desc;
 
-	/* Firmware describe channels into a stream separated by a '\0'.
-	 * Replace all '\0' with a space.
-	 */
-        len = prom_getproperty(node, "channels-description", chnls_desc,
+	/* Firmware describe channels into a stream separated by a '\0'. */
+	len = prom_getproperty(node, "channels-description", chnls_desc,
 			       CHANNEL_DESC_SZ);
-        for (i = 0; i < len; i++) {
-                if (chnls_desc[i] == '\0')
-                        chnls_desc[i] = ' ';
-        }
-
-	ptr = strtok(chnls_desc, " ");
-	while (ptr != NULL) {
-		envctrl_set_mon(pchild, ptr, j);
-		ptr = strtok(NULL, " ");
-		j++;
+	chnls_desc[CHANNEL_DESC_SZ - 1] = '\0';
+
+	while (len > 0) {
+		int l = strlen(pos) + 1;
+		envctrl_set_mon(pchild, pos, i++);
+		len -= l;
+		pos += l;
 	}
 
 	/* Get optional properties. */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/riowatchdog.c linux/drivers/sbus/char/riowatchdog.c
--- linux.orig/drivers/sbus/char/riowatchdog.c	Thu Oct 11 06:42:47 2001
+++ linux/drivers/sbus/char/riowatchdog.c	Wed Jan 23 20:30:38 2002
@@ -1,4 +1,4 @@
-/* $Id: riowatchdog.c,v 1.3 2001/10/08 22:19:51 davem Exp $
+/* $Id: riowatchdog.c,v 1.3.2.2 2002/01/23 18:48:02 davem Exp $
  * riowatchdog.c - driver for hw watchdog inside Super I/O of RIO
  *
  * Copyright (C) 2001 David S. Miller (davem@redhat.com)
@@ -127,8 +127,11 @@
 static int riowd_ioctl(struct inode *inode, struct file *filp,
 		       unsigned int cmd, unsigned long arg)
 {
-	static struct watchdog_info info = { 0, 0, "Natl. Semiconductor PC97317" };
+	static struct watchdog_info info = {
+	       	WDIOF_SETTIMEOUT, 0, "Natl. Semiconductor PC97317"
+	};
 	unsigned int options;
+	int new_margin;
 
 	switch (cmd) {
 	case WDIOC_GETSUPPORT:
@@ -158,6 +161,18 @@
 			return -EINVAL;
 
 		break;
+
+	case WDIOC_SETTIMEOUT:
+		if (get_user(new_margin, (int *)arg))
+			return -EFAULT;
+		if ((new_margin < 60) || (new_margin > (255 * 60)))
+		    return -EINVAL;
+		riowd_timeout = (new_margin + 59) / 60;
+		riowd_pingtimer();
+		/* Fall */
+
+	case WDIOC_GETTIMEOUT:
+		return put_user(riowd_timeout * 60, (int *)arg);
 
 	default:
 		return -EINVAL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/sunserial.c linux/drivers/sbus/char/sunserial.c
--- linux.orig/drivers/sbus/char/sunserial.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sbus/char/sunserial.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: sunserial.c,v 1.79 2001/04/18 21:06:17 davem Exp $
+/* $Id: sunserial.c,v 1.79.2.2 2002/01/05 01:12:31 davem Exp $
  * serial.c: Serial port driver infrastructure for the Sparc.
  *
  * Copyright (C) 1997  Eddie C. Dost  (ecd@skynet.be)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/char/zs.c linux/drivers/sbus/char/zs.c
--- linux.orig/drivers/sbus/char/zs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sbus/char/zs.c	Thu Jan 17 19:37:52 2002
@@ -1,4 +1,4 @@
-/* $Id: zs.c,v 1.68 2001/10/25 18:48:03 davem Exp $
+/* $Id: zs.c,v 1.68.2.2 2002/01/12 07:04:33 davem Exp $
  * zs.c: Zilog serial port driver for the Sparc.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -7,6 +7,10 @@
  *
  * Fixed to use tty_get_baud_rate().
  *   Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12
+ *
+ * /proc/tty/driver/serial now exists and is readable.
+ *   Alex Buell <alex.buell@tahallah.demon.co.uk>, 2001-12-23
+ *
  */
 
 #include <linux/errno.h>
@@ -486,10 +490,19 @@
 static void receive_chars(struct sun_serial *info, struct pt_regs *regs)
 {
 	struct tty_struct *tty = info->tty;
-	unsigned char ch, stat;
-	int do_queue_task = 1;
+	int do_queue_task = 0;
+
+	while (1) {
+		unsigned char ch, r1;
+
+		r1 = read_zsreg(info->zs_channel, R1);
+		if (r1 & (PAR_ERR | Rx_OVR | CRC_ERR)) {
+			sbus_writeb(ERR_RES, &info->zs_channel->control);
+			ZSDELAY();
+			ZS_WSYNC(info->zs_channel);
+			ZSLOG(REGCTRL, ERR_RES, 1);
+		}
 
-	do {
 		ch = sbus_readb(&info->zs_channel->data);
 		ZSLOG(REGDATA, ch, 0);
 		ch &= info->parity_mask;
@@ -498,17 +511,17 @@
 		/* If this is the console keyboard, we need to handle
 		 * L1-A's here.
 		 */
-		if(info->cons_keyb) {
-			if(ch == SUNKBD_RESET) {
+		if (info->cons_keyb) {
+			if (ch == SUNKBD_RESET) {
 				l1a_state.kbd_id = 1;
 				l1a_state.l1_down = 0;
-			} else if(l1a_state.kbd_id) {
+			} else if (l1a_state.kbd_id) {
 				l1a_state.kbd_id = 0;
-			} else if(ch == SUNKBD_L1) {
+			} else if (ch == SUNKBD_L1) {
 				l1a_state.l1_down = 1;
-			} else if(ch == (SUNKBD_L1|SUNKBD_UP)) {
+			} else if (ch == (SUNKBD_L1|SUNKBD_UP)) {
 				l1a_state.l1_down = 0;
-			} else if(ch == SUNKBD_A && l1a_state.l1_down) {
+			} else if (ch == SUNKBD_A && l1a_state.l1_down) {
 				/* whee... */
 				batten_down_hatches();
 				/* Continue execution... */
@@ -517,16 +530,14 @@
 				return;
 			}
 			sunkbd_inchar(ch, regs);
-			do_queue_task = 0;
 			goto next_char;
 		}
-		if(info->cons_mouse) {
+		if (info->cons_mouse) {
 			sun_mouse_inbyte(ch, 0);
-			do_queue_task = 0;
 			goto next_char;
 		}
-		if(info->is_cons) {
-			if(ch == 0) {
+		if (info->is_cons) {
+			if (ch == 0) {
 				/* whee, break received */
 				batten_down_hatches();
 				/* Continue execution... */
@@ -540,32 +551,42 @@
 		 * documentation for remote target debugging and
 		 * arch/sparc/kernel/sparc-stub.c to see how all this works.
 		 */
-		if((info->kgdb_channel) && (ch =='\003')) {
+		if (info->kgdb_channel && (ch =='\003')) {
 			breakpoint();
 			return;
 		}
 #endif
-		if(!tty)
+		if (!tty)
 			return;
 
+		do_queue_task++;
+
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 			break;
 
 		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = 0;
+		if (r1 & PAR_ERR)
+			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
+		else if (r1 & Rx_OVR)
+			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
+		else if (r1 & CRC_ERR)
+			*tty->flip.flag_buf_ptr++ = TTY_FRAME;
+		else
+			*tty->flip.flag_buf_ptr++ = 0;
 		*tty->flip.char_buf_ptr++ = ch;
 
 	next_char:
-		/* Check if we have another character... */
-		stat = sbus_readb(&info->zs_channel->control);
-		ZSDELAY();
-		ZSLOG(REGCTRL, stat, 0);
-		if (!(stat & Rx_CH_AV))
-			break;
+		{
+			unsigned char stat;
 
-		/* ... and see if it is clean. */
-		stat = read_zsreg(info->zs_channel, R1);
-	} while (!(stat & (PAR_ERR | Rx_OVR | CRC_ERR)));
+			/* Check if we have another character... */
+			stat = sbus_readb(&info->zs_channel->control);
+			ZSDELAY();
+			ZSLOG(REGCTRL, stat, 0);
+			if (!(stat & Rx_CH_AV))
+				break;
+		}
+	}
 
 	if (do_queue_task != 0)
 		queue_task(&tty->flip.tqueue, &tq_timer);
@@ -582,7 +603,7 @@
 		return;
 	}
 
-	if((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
+	if ((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) {
 		/* That's peculiar... */
 		sbus_writeb(RES_Tx_P, &info->zs_channel->control);
 		ZSDELAY();
@@ -599,7 +620,7 @@
 	if (info->xmit_cnt < WAKEUP_CHARS)
 		zs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
-	if(info->xmit_cnt <= 0) {
+	if (info->xmit_cnt <= 0) {
 		sbus_writeb(RES_Tx_P, &info->zs_channel->control);
 		ZSDELAY();
 		ZS_WSYNC(info->zs_channel);
@@ -621,14 +642,14 @@
 	ZS_WSYNC(info->zs_channel);
 	ZSLOG(REGCTRL, RES_EXT_INT, 1);
 #if 0
-	if(status & DCD) {
-		if((info->tty->termios->c_cflag & CRTSCTS) &&
-		   ((info->curregs[3] & AUTO_ENAB)==0)) {
+	if (status & DCD) {
+		if ((info->tty->termios->c_cflag & CRTSCTS) &&
+		    ((info->curregs[3] & AUTO_ENAB)==0)) {
 			info->curregs[3] |= AUTO_ENAB;
 			write_zsreg(info->zs_channel, 3, info->curregs[3]);
 		}
 	} else {
-		if((info->curregs[3] & AUTO_ENAB)) {
+		if ((info->curregs[3] & AUTO_ENAB)) {
 			info->curregs[3] &= ~AUTO_ENAB;
 			write_zsreg(info->zs_channel, 3, info->curregs[3]);
 		}
@@ -638,7 +659,7 @@
 	 * 'break asserted' status change interrupt, call
 	 * the boot prom.
 	 */
-	if(status & BRK_ABRT) {
+	if (status & BRK_ABRT) {
 		if (info->break_abort)
 			batten_down_hatches();
 		if (info->cons_mouse)
@@ -651,110 +672,49 @@
 	return;
 }
 
-static void special_receive(struct sun_serial *info)
-{
-	struct tty_struct *tty = info->tty;
-	unsigned char ch, stat;
-
-	stat = read_zsreg(info->zs_channel, R1);
-	if (stat & (PAR_ERR | Rx_OVR | CRC_ERR)) {
-		ch = sbus_readb(&info->zs_channel->data);
-		ZSDELAY();
-		ZSLOG(REGDATA, ch, 0);
-	}
-
-	if (!tty)
-		goto clear;
-
-	if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-		goto done;
-
-	tty->flip.count++;
-	if(stat & PAR_ERR)
-		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
-	else if(stat & Rx_OVR)
-		*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-	else if(stat & CRC_ERR)
-		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
-
-done:
-	queue_task(&tty->flip.tqueue, &tq_timer);
-clear:
-	sbus_writeb(ERR_RES, &info->zs_channel->control);
-	ZSDELAY();
-	ZS_WSYNC(info->zs_channel);
-	ZSLOG(REGCTRL, ERR_RES, 1);
-}
-
-
 /*
  * This is the serial driver's generic interrupt routine
  */
 void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
 	struct sun_serial *info;
-	unsigned char zs_intreg;
 	int i;
 
 	info = (struct sun_serial *)dev_id;
 	ZSLOG(REGIRQ, 0, 0);
 	for (i = 0; i < NUM_SERIAL; i++) {
-		zs_intreg = read_zsreg(info->zs_next->zs_channel, 2);
-		zs_intreg &= STATUS_MASK;
-
-		/* NOTE: The read register 2, which holds the irq status,
-		 *       does so for both channels on each chip.  Although
-		 *       the status value itself must be read from the B
-		 *       channel and is only valid when read from channel B.
-		 *       When read from channel A, read register 2 contains
-		 *       the value written to write register 2.
-		 */
+		unsigned char r3 = read_zsreg(info->zs_channel, 3);
 
 		/* Channel A -- /dev/ttya or /dev/kbd, could be the console */
-		if (zs_intreg == CHA_Rx_AVAIL) {
-			receive_chars(info, regs);
-			return;
-		}
-		if(zs_intreg == CHA_Tx_EMPTY) {
-			transmit_chars(info);
-			return;
-		}
-		if (zs_intreg == CHA_EXT_STAT) {
-			status_handle(info);
-			return;
-		}
-		if (zs_intreg == CHA_SPECIAL) {
-			special_receive(info);
-			return;
+		if (r3 & (CHAEXT | CHATxIP | CHARxIP)) {
+			sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+			ZSDELAY();
+			ZS_WSYNC(info->zs_channel);
+			ZSLOG(REGCTRL, RES_H_IUS, 1);
+			if (r3 & CHARxIP)
+				receive_chars(info, regs);
+			if (r3 & CHAEXT)
+				status_handle(info);
+			if (r3 & CHATxIP)
+				transmit_chars(info);
 		}
 
 		/* Channel B -- /dev/ttyb or /dev/mouse, could be the console */
-		if(zs_intreg == CHB_Rx_AVAIL) {
-			receive_chars(info->zs_next, regs);
-			return;
-		}
-		if(zs_intreg == CHB_Tx_EMPTY) {
-			transmit_chars(info->zs_next);
-			return;
-		}
-		if (zs_intreg == CHB_EXT_STAT) {
-			status_handle(info->zs_next);
-			return;
+		info = info->zs_next;
+		if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) {
+			sbus_writeb(RES_H_IUS, &info->zs_channel->control);
+			ZSDELAY();
+			ZS_WSYNC(info->zs_channel);
+			ZSLOG(REGCTRL, RES_H_IUS, 1);
+			if (r3 & CHBRxIP)
+				receive_chars(info, regs);
+			if (r3 & CHBEXT)
+				status_handle(info);
+			if (r3 & CHBTxIP)
+				transmit_chars(info);
 		}
 
-		/* NOTE: The default value for the IRQ status in read register
-		 *       2 in channel B is CHB_SPECIAL, so we need to look at
-		 *       read register 3 in channel A to check if this is a
-		 *       real interrupt, or just the default value.
-		 *       Yes... broken hardware...
-		 */
-
-		zs_intreg = read_zsreg(info->zs_channel, 3);
-		if (zs_intreg & CHBRxIP) {
-			special_receive(info->zs_next);
-			return;
-		}
-		info = info->zs_next->zs_next;
+		info = info->zs_next;
 	}
 }
 
@@ -1699,6 +1659,81 @@
 }
 
 /*
+ *
+ * line_info - returns information about each channel
+ *
+ */
+static inline int line_info(char *buf, struct sun_serial *info)
+{
+	unsigned char status;
+	char stat_buf[30];
+	int ret;
+
+	ret = sprintf(buf, "%d: uart:Zilog8530 port:%x irq:%d",
+		info->line, info->port, info->irq);
+
+	cli();
+	status = sbus_readb(&info->zs_channel->control);
+	ZSDELAY();
+	ZSLOG(REGCTRL, status, 0);
+	sti();
+
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (info->curregs[5] & RTS)
+		strcat(stat_buf, "|RTS");
+	if (status & CTS)
+		strcat(stat_buf, "|CTS");
+	if (info->curregs[5] & DTR)
+		strcat(stat_buf, "|DTR");
+	if (status & SYNC)
+		strcat(stat_buf, "|DSR");
+	if (status & DCD)
+		strcat(stat_buf, "|CD");
+
+	ret += sprintf(buf + ret, " baud:%d %s\n", info->zs_baud, stat_buf + 1);
+	return ret;
+}
+
+/*
+ *
+ * zs_read_proc() - called when /proc/tty/driver/serial is read.
+ *
+ */
+int zs_read_proc(char *page, char **start, off_t off, int count,
+                 int *eof, void *data)
+{
+	char *revision = "$Revision: 1.68.2.2 $";
+	char *version, *p;
+	int i, len = 0, l;
+	off_t begin = 0;
+
+	version = strchr(revision, ' ');
+	p = strchr(++version, ' ');
+	*p = '\0';
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", version);
+	*p = ' ';
+
+	for (i = 0; i < NUM_CHANNELS && len < 4000; i++) {
+		l = line_info(page + len, &zs_soft[i]);
+		len += l;
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
  * ------------------------------------------------------------
  * zs_open() and friends
  * ------------------------------------------------------------
@@ -1933,7 +1968,7 @@
 
 static void show_serial_version(void)
 {
-	char *revision = "$Revision: 1.68 $";
+	char *revision = "$Revision: 1.68.2.2 $";
 	char *version, *p;
 
 	version = strchr(revision, ' ');
@@ -2444,8 +2479,8 @@
 	serial_driver.hangup = zs_hangup;
 
 	/* I'm too lazy, someone write versions of this for us. -DaveM */
-	serial_driver.read_proc = 0;
-	serial_driver.proc_entry = 0;
+	/* I just did. :-) -AIB 2001-12-23 */
+	serial_driver.read_proc = zs_read_proc;
 
 	/*
 	 * The callout device is just like normal device except for
@@ -2455,6 +2490,8 @@
 	callout_driver.name = "cua/%d";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
 
 	if (tty_register_driver(&serial_driver))
 		panic("Couldn't register serial driver\n");
@@ -2469,8 +2506,7 @@
 	/* Grab IRQ line before poking the chips so we do
 	 * not lose any interrupts.
 	 */
-	if (request_irq(zilog_irq, zs_interrupt,
-			(SA_INTERRUPT | SA_STATIC_ALLOC),
+	if (request_irq(zilog_irq, zs_interrupt, SA_SHIRQ,
 			"Zilog8530", zs_chain)) {
 		prom_printf("Unable to attach zs intr\n");
 		prom_halt();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sbus/sbus.c linux/drivers/sbus/sbus.c
--- linux.orig/drivers/sbus/sbus.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sbus/sbus.c	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: sbus.c,v 1.95 2001/03/15 02:11:10 davem Exp $
+/* $Id: sbus.c,v 1.95.2.3 2002/01/05 01:12:31 davem Exp $
  * sbus.c:  SBus support routines.
  *
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
@@ -231,6 +231,7 @@
 				return;
 			}
 			regs[regnum].which_io = ranges[rngnum].ot_parent_space;
+			regs[regnum].phys_addr -= ranges[rngnum].ot_child_base;
 			regs[regnum].phys_addr += ranges[rngnum].ot_parent_base;
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
--- linux.orig/drivers/scsi/3w-xxxx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/3w-xxxx.c	Tue Feb  5 17:29:43 2002
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By: 	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -106,6 +106,21 @@
                  Add entire aen code string list.
    1.02.00.010 - Cleanup queueing code, fix jbod thoughput.
                  Fix get_param for specific units.
+   1.02.00.011 - Fix bug in tw_aen_complete() where aen's could be lost.
+                 Fix tw_aen_drain_queue() to display useful info at init.
+                 Set tw_host->max_id for 12 port cards.
+                 Add ioctl support for raw command packet post from userspace
+                 with sglist fragments (parameter and io).
+   1.02.00.012 - Fix read capacity to under report by 1 sector to fix get
+                 last sector ioctl.
+   1.02.00.013 - Fix bug where more AEN codes weren't coming out during
+                 driver initialization.
+                 Improved handling of PCI aborts.
+   1.02.00.014 - Fix bug in tw_findcards() where AEN code could be lost.
+                 Increase timeout in tw_aen_drain_queue() to 30 seconds.
+   1.02.00.015 - Re-write raw command post with data ioctl method.
+   1.02.00.016 - Set max_cmd_len for 3dm.
+                 Set max sectors to 256 for non raid5 & 6XXX.          
 */
 
 #include <linux/module.h>
@@ -114,7 +129,6 @@
 MODULE_DESCRIPTION ("3ware Storage Controller Linux Driver");
 MODULE_LICENSE("GPL");
 
-
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/time.h>
@@ -149,11 +163,11 @@
 
 /* Notifier block to get a notify on system shutdown/halt/reboot */
 static struct notifier_block tw_notifier = {
-  tw_halt, NULL, 0
+	tw_halt, NULL, 0
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.010";
+char *tw_driver_version="1.02.00.016";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -164,6 +178,7 @@
 {
 	TW_Param *param;
 	unsigned short aen;
+	int error = 0;
 
 	dprintk(KERN_WARNING "3w-xxxx: tw_aen_complete()\n");
 	if (tw_dev->alignment_virtual_address[request_id] == NULL) {
@@ -182,12 +197,15 @@
 			if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
 				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s%d.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff], aen >> 8);
 			} else {
-				printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
+				if (aen != 0x0) 
+					printk(KERN_WARNING "3w-xxxx: scsi%d: AEN: %s.\n", tw_dev->host->host_no, tw_aen_string[aen & 0xff]);
 			}
-		} else
+		} else {
 			printk(KERN_WARNING "3w-xxxx: scsi%d: Received AEN %d.\n", tw_dev->host->host_no, aen);
+		}
 	}
-	tw_dev->aen_count++;
+	if (aen != 0x0) 
+		tw_dev->aen_count++;
 
 	/* Now queue the code */
 	tw_dev->aen_queue[tw_dev->aen_tail] = aen;
@@ -203,8 +221,18 @@
 			tw_dev->aen_head = tw_dev->aen_head + 1;
 		}
 	}
-	tw_dev->state[request_id] = TW_S_COMPLETED;
-	tw_state_request_finish(tw_dev, request_id);
+
+	if (aen != TW_AEN_QUEUE_EMPTY) {
+		error = tw_aen_read_queue(tw_dev, request_id);
+		if (error) {
+			printk(KERN_WARNING "3w-xxxx: scsi%d: Error completing AEN.\n", tw_dev->host->host_no);
+			tw_dev->state[request_id] = TW_S_COMPLETED;
+			tw_state_request_finish(tw_dev, request_id);
+		}
+	} else {
+		tw_dev->state[request_id] = TW_S_COMPLETED;
+		tw_state_request_finish(tw_dev, request_id);
+	}
 
 	return 0;
 } /* End tw_aen_complete() */
@@ -235,10 +263,11 @@
 	status_reg_addr = tw_dev->registers.status_reg_addr;
 	response_que_addr = tw_dev->registers.response_que_addr;
 
-	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 15)) {
+	if (tw_poll_status(tw_dev, TW_STATUS_ATTENTION_INTERRUPT, 30)) {
 		dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): No attention interrupt for card %d.\n", tw_device_extension_count);
 		return 1;
 	}
+	tw_clear_attention_interrupt(tw_dev);
 
 	/* Initialize command packet */
 	if (tw_dev->command_packet_virtual_address[request_id] == NULL) {
@@ -286,7 +315,7 @@
 	do {
 		/* Post command packet */
 		outl(command_que_value, command_que_addr);
-    
+
 		/* Now poll for completion */
 		for (i=0;i<imax;i++) {
 			mdelay(5);
@@ -324,7 +353,7 @@
 				queue = 0;
 				switch (aen_code) {
 					case TW_AEN_QUEUE_EMPTY:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_EMPTY.\n");
+						dprintk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
 						if (first_reset != 1) {
 							continue;
 						} else {
@@ -332,51 +361,28 @@
 						}
 						break;
 					case TW_AEN_SOFT_RESET:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_SOFT_RESET.\n");
 						if (first_reset == 0) {
 							first_reset = 1;
 						} else {
+							printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+							tw_dev->aen_count++;
 							queue = 1;
 						}
 						break;
-					case TW_AEN_DEGRADED_MIRROR:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_DEGRADED_MIRROR.\n");
-						queue = 1;
-						break;
-					case TW_AEN_CONTROLLER_ERROR:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_CONTROLLER_ERROR.\n");
-						queue = 1;
-						break;
-					case TW_AEN_REBUILD_FAIL:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_FAIL.\n");
-						queue = 1;
-						break;
-					case TW_AEN_REBUILD_DONE:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_REBUILD_DONE.\n");
-						queue = 1;
-						break;
-					case TW_AEN_QUEUE_FULL:
-						dprintk(KERN_NOTICE "3w-xxxx: tw_aen_drain_queue(): Found TW_AEN_QUEUE_FULL.\n");
-						queue = 1;
-						break;
-					case TW_AEN_APORT_TIMEOUT:
-						printk(KERN_WARNING "3w-xxxx: Received drive timeout AEN on port %d, check drive and drive cables.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_DRIVE_ERROR:
-						printk(KERN_WARNING "3w-xxxx: Received drive error AEN on port %d, check/replace cabling, or possible bad drive.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_SMART_FAIL:
-						printk(KERN_WARNING "3w-xxxx: Received S.M.A.R.T. threshold AEN on port %d, check drive/cooling, or possible bad drive.\n", aen >> 8);
-						queue = 1;
-						break;
-					case TW_AEN_SBUF_FAIL:
-						printk(KERN_WARNING "3w-xxxx: Received SBUF integrity check failure AEN, reseat card or bad card.\n");
-						queue = 1;
-						break;
 					default:
-						dprintk(KERN_WARNING "3w-xxxx: tw_aen_drain_queue(): Unknown AEN code 0x%x.\n", aen_code);
+						if (aen == 0x0ff) {
+							printk(KERN_WARNING "3w-xxxx: AEN: AEN queue overflow.\n");
+						} else {
+							if ((aen & 0x0ff) < TW_AEN_STRING_MAX) {
+								if ((tw_aen_string[aen & 0xff][strlen(tw_aen_string[aen & 0xff])-1]) == '#') {
+									printk(KERN_WARNING "3w-xxxx: AEN: %s%d.\n", tw_aen_string[aen & 0xff], aen >> 8);
+								} else {
+									printk(KERN_WARNING "3w-xxxx: AEN: %s.\n", tw_aen_string[aen & 0xff]);
+								}
+							} else
+								printk(KERN_WARNING "3w-xxxx: Received AEN %d.\n", aen);
+						}
+						tw_dev->aen_count++;
 						queue = 1;
 				}
 
@@ -606,7 +612,7 @@
 	}
 } /* End tw_copy_mem_info() */
 
-/* This function will print readable messages from statsu register errors */
+/* This function will print readable messages from status register errors */
 void tw_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
 {
 	dprintk(KERN_WARNING "3w-xxxx: tw_decode_bits()\n");
@@ -619,6 +625,11 @@
 	case TW_STATUS_MICROCONTROLLER_ERROR:
 		printk(KERN_WARNING "3w-xxxx: Microcontroller Error.\n");
 		break;
+	case TW_STATUS_PCI_ABORT:
+		printk(KERN_WARNING "3w-xxxx: PCI Abort: clearing.\n");
+		outl(TW_CONTROL_CLEAR_PCI_ABORT, tw_dev->registers.control_reg_addr);
+		pci_write_config_word(tw_dev->tw_pci_dev, PCI_STATUS, TW_PCI_CLEAR_PCI_ABORT);
+		break;
   }
 } /* End tw_decode_bits() */
 
@@ -689,11 +700,22 @@
 	u32 control_reg_value, control_reg_addr;
 
 	control_reg_addr = tw_dev->registers.control_reg_addr;
+	control_reg_value = (TW_CONTROL_ENABLE_INTERRUPTS |
+			     TW_CONTROL_UNMASK_RESPONSE_INTERRUPT);
+	outl(control_reg_value, control_reg_addr);
+} /* End tw_enable_interrupts() */
+
+/* This function will enable interrupts on the controller */
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev)
+{
+	u32 control_reg_value, control_reg_addr;
+
+	control_reg_addr = tw_dev->registers.control_reg_addr;
 	control_reg_value = (TW_CONTROL_CLEAR_ATTENTION_INTERRUPT |
 			     TW_CONTROL_UNMASK_RESPONSE_INTERRUPT |
 			     TW_CONTROL_ENABLE_INTERRUPTS);
 	outl(control_reg_value, control_reg_addr);
-} /* End tw_enable_interrupts() */
+} /* End tw_enable_and_clear_interrupts() */
 
 /* This function will find and initialize all cards */
 int tw_findcards(Scsi_Host_Template *tw_host) 
@@ -761,14 +783,14 @@
 			  
 				error = tw_aen_drain_queue(tw_dev);
 				if (error) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: AEN drain failed for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
 
 				/* Check for controller errors */
 				if (tw_check_errors(tw_dev)) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: Controller errors found, retrying for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
@@ -776,7 +798,7 @@
 				/* Empty the response queue */
 				error = tw_empty_response_que(tw_dev);
 				if (error) {
-					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+					printk(KERN_WARNING "3w-xxxx: Couldn't empty response queue, retrying for card %d.\n", numcards);
 					tries++;
 					continue;
 				}
@@ -786,7 +808,7 @@
 			}
 
 			if (tries >= TW_MAX_RESET_TRIES) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Controller errors, card not responding, check all cabling for card %d.\n", numcards);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
@@ -816,7 +838,7 @@
 
 			error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
 			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+				printk(KERN_WARNING "3w-xxxx: Connection initialization failed for card %d.\n", numcards);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
@@ -841,16 +863,27 @@
 		/* Register the card with the kernel SCSI layer */
 			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
 			if (host == NULL) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards-1);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): scsi_register() failed for card %d.\n", numcards);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
 				kfree(tw_dev);
 				continue;
 			}
 
+			/* Set max target id's */
+			host->max_id = TW_MAX_UNITS;
+
+			/* Set max cdb size in bytes */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,15)			
+			host->max_cmd_len = TW_MAX_CDB_LEN;
+#endif
+
 			/* Set max sectors per io */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7)
-			host->max_sectors = TW_MAX_SECTORS;
+			if ((tw_dev->num_raid_five > 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID))
+				host->max_sectors = TW_MAX_BOUNCE_SECTORS;
+			else
+				host->max_sectors = TW_MAX_SECTORS;
 #endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,4)
@@ -872,7 +905,7 @@
 				tw_device_extension_count = numcards;
 				tw_dev2->host = host;
 			} else { 
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards);
 				scsi_unregister(host);
 				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
 				tw_free_device_extension(tw_dev);
@@ -1052,6 +1085,7 @@
 		tw_dev->state[i] = TW_S_INITIAL;
 		tw_dev->ioctl_size[i] = 0;
 		tw_dev->aen_queue[i] = 0;
+		tw_dev->ioctl_data[i] = NULL;
 	}
 
 	for (i=0;i<TW_MAX_UNITS;i++) {
@@ -1297,12 +1331,12 @@
 	/* Now allocate raid5 bounce buffers */
 	if ((num_raid_five != 0) && (tw_dev->tw_pci_dev->device == TW_DEVICE_ID)) {
 		for (i=0;i<TW_MAX_BOUNCEBUF;i++) {
-			tw_allocate_memory(tw_dev, i, sizeof(TW_Sector)*TW_MAX_SECTORS, 2);
+			tw_allocate_memory(tw_dev, i, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS, 2);
 			if (tw_dev->bounce_buffer[i] == NULL) {
 				printk(KERN_WARNING "3w-xxxx: Bounce buffer allocation failed.\n");
 				return 1;
 			}
-			memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*TW_MAX_SECTORS);
+			memset(tw_dev->bounce_buffer[i], 0, sizeof(TW_Sector)*TW_MAX_BOUNCE_SECTORS);
 		}
 	}
   
@@ -1330,7 +1364,7 @@
 
 	if (tw_dev->tw_pci_dev->irq == irq) {
 		spin_lock(&tw_dev->tw_lock);
-		dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt()\n");
+		dprintk(KERN_WARNING "3w-xxxx: tw_interrupt()\n");
 
 		/* Read the registers */
 		status_reg_addr = tw_dev->registers.status_reg_addr;
@@ -1453,18 +1487,21 @@
 						tw_dev->srb[request_id]->result = (DID_BAD_TARGET << 16);
 						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
 					}
-					if (error) {
+					if (error == 1) {
 						/* Tell scsi layer there was an error */
 						dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
 						tw_dev->srb[request_id]->result = (DID_RESET << 16);
-					} else {
+					}
+					if (error == 0) {
 						/* Tell scsi layer command was a success */
 						tw_dev->srb[request_id]->result = (DID_OK << 16);
 					}
-					tw_dev->state[request_id] = TW_S_COMPLETED;
-					tw_state_request_finish(tw_dev, request_id);
-					tw_dev->posted_request_count--;
-					tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					if (error != 2) {
+						tw_dev->state[request_id] = TW_S_COMPLETED;
+						tw_state_request_finish(tw_dev, request_id);
+						tw_dev->posted_request_count--;
+						tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+					}
 					status_reg_value = inl(status_reg_addr);
 					if (tw_check_bits(status_reg_value)) {
 						dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Unexpected bits.\n");
@@ -1477,19 +1514,21 @@
 	}
 	spin_unlock_irqrestore(&io_request_lock, flags);
 	clear_bit(TW_IN_INTR, &tw_dev->flags);
-}	/* End tw_interrupt() */
+} /* End tw_interrupt() */
 
 /* This function handles ioctls from userspace to the driver */
 int tw_ioctl(TW_Device_Extension *tw_dev, int request_id)
 {
 	unsigned char opcode;
-	int bufflen;
+	int bufflen, error = 0;
 	TW_Param *param;
-	TW_Command *command_packet;
+	TW_Command *command_packet, *command_save;
 	u32 param_value;
 	TW_Ioctl *ioctl = NULL;
 	TW_Passthru *passthru = NULL;
-	int tw_aen_code;
+	int tw_aen_code, i, use_sg;
+	char *data_ptr;
+	int total_bytes = 0;
 
 	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
 	if (ioctl == NULL) {
@@ -1610,6 +1649,159 @@
 				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
 				return 1;
 			}
+		case TW_CMD_PACKET_WITH_DATA:
+			dprintk(KERN_WARNING "3w-xxxx: tw_ioctl(): caught TW_CMD_PACKET_WITH_DATA.\n");
+			command_save = (TW_Command *)tw_dev->alignment_virtual_address[request_id];
+			if (command_save == NULL) {
+				printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad alignment virtual address.\n", tw_dev->host->host_no);
+				return 1;
+			}
+			if (ioctl->data != NULL) {
+				/* Copy down the command packet */
+				memcpy(command_packet, ioctl->data, sizeof(TW_Command));
+				memcpy(command_save, ioctl->data, sizeof(TW_Command));
+				command_packet->request_id = request_id;
+
+				/* Now deal with the two possible sglists */
+				if (command_packet->byte0.sgl_offset == 2) {
+					use_sg = command_packet->size - 3;
+					for (i=0;i<use_sg;i++)
+						total_bytes+=command_packet->byte8.param.sgl[i].length;
+					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					if (!tw_dev->ioctl_data[request_id]) {
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						return 1;
+					}
+
+					/* Copy param sglist into the kernel */
+					data_ptr = tw_dev->ioctl_data[request_id];
+					for (i=0;i<use_sg;i++) {
+						if ((u32 *)command_packet->byte8.param.sgl[i].address != NULL) {
+							error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.param.sgl[i].address, command_packet->byte8.param.sgl[i].length);
+							if (error) {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist from userspace.\n", tw_dev->host->host_no);
+								goto tw_ioctl_bail;
+							}
+						} else {
+							printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+							tw_dev->srb[request_id]->result = (DID_RESET << 16);
+							goto tw_ioctl_bail;
+						}
+						data_ptr+=command_packet->byte8.param.sgl[i].length;
+					}
+					command_packet->size = 4;
+					command_packet->byte8.param.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.param.sgl[0].length = total_bytes;
+				}
+				if (command_packet->byte0.sgl_offset == 3) {
+					use_sg = command_packet->size - 4;
+					for (i=0;i<use_sg;i++)
+						total_bytes+=command_packet->byte8.io.sgl[i].length;
+					tw_dev->ioctl_data[request_id] = kmalloc(total_bytes, GFP_ATOMIC);
+					if (!tw_dev->ioctl_data[request_id]) {
+						printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): kmalloc failed for request_id %d.\n", tw_dev->host->host_no, request_id);
+						return 1;
+					}
+					if (command_packet->byte0.opcode == TW_OP_WRITE) {
+						/* Copy io sglist into the kernel */
+						data_ptr = tw_dev->ioctl_data[request_id];
+						for (i=0;i<use_sg;i++) {
+							if ((u32 *)command_packet->byte8.io.sgl[i].address != NULL) {
+								error = copy_from_user(data_ptr, (u32 *)command_packet->byte8.io.sgl[i].address, command_packet->byte8.io.sgl[i].length);
+								if (error) {
+									printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist from userspace.\n", tw_dev->host->host_no);
+									goto tw_ioctl_bail;
+								}
+							} else {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+								tw_dev->srb[request_id]->result = (DID_RESET << 16);
+								goto tw_ioctl_bail;
+							}
+							data_ptr+=command_packet->byte8.io.sgl[i].length;
+						}
+					}
+					command_packet->size = 5;
+					command_packet->byte8.io.sgl[0].address = virt_to_bus(tw_dev->ioctl_data[request_id]);
+					command_packet->byte8.io.sgl[0].length = total_bytes;
+				}
+
+				spin_unlock_irq(&io_request_lock);
+				spin_unlock_irq(&tw_dev->tw_lock);
+
+				/* Finally post the command packet */
+				tw_post_command_packet(tw_dev, request_id);
+
+				mdelay(TW_IOCTL_WAIT_TIME);
+				spin_lock_irq(&tw_dev->tw_lock);
+				spin_lock_irq(&io_request_lock);
+
+				if (signal_pending(current)) {
+					dprintk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Signal pending, aborting ioctl().\n", tw_dev->host->host_no);
+					tw_dev->srb[request_id]->result = (DID_OK << 16);
+					goto tw_ioctl_bail;
+				}
+
+				tw_dev->srb[request_id]->result = (DID_OK << 16);
+				/* Now copy up the param or io sglist to userspace */
+				if (command_packet->byte0.sgl_offset == 2) {
+					use_sg = command_save->size - 3;
+					data_ptr = phys_to_virt(command_packet->byte8.param.sgl[0].address);
+					for (i=0;i<use_sg;i++) {
+						if ((u32 *)command_save->byte8.param.sgl[i].address != NULL) {
+							error = copy_to_user((u32 *)command_save->byte8.param.sgl[i].address, data_ptr, command_save->byte8.param.sgl[i].length);
+							if (error) {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying param sglist to userspace.\n", tw_dev->host->host_no);
+								goto tw_ioctl_bail;
+							}
+							dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.param.sgl[i].length, current->pid);
+							data_ptr+=command_save->byte8.param.sgl[i].length;
+						} else {
+							printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad param sgl address.\n", tw_dev->host->host_no);
+							tw_dev->srb[request_id]->result = (DID_RESET << 16);
+							goto tw_ioctl_bail;
+						}
+					}
+				}
+				if (command_packet->byte0.sgl_offset == 3) {
+					use_sg = command_save->size - 4;
+					if (command_packet->byte0.opcode == TW_OP_READ) {
+						data_ptr = phys_to_virt(command_packet->byte8.io.sgl[0].address);
+						for(i=0;i<use_sg;i++) {
+							if ((u32 *)command_save->byte8.io.sgl[i].address != NULL) {
+								error = copy_to_user((u32 *)command_save->byte8.io.sgl[i].address, data_ptr, command_save->byte8.io.sgl[i].length);
+								if (error) {
+									printk(KERN_WARNING "3w-xxxx: scsi%d: Error copying io sglist to userspace.\n", tw_dev->host->host_no);
+									goto tw_ioctl_bail;
+								}
+								dprintk(KERN_WARNING "3w-xxxx: scsi%d: Copied %ld bytes to pid %d.\n", tw_dev->host->host_no, command_save->byte8.io.sgl[i].length, current->pid);
+								data_ptr+=command_save->byte8.io.sgl[i].length;
+							} else {
+								printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Bad io sgl address.\n", tw_dev->host->host_no);
+								tw_dev->srb[request_id]->result = (DID_RESET << 16);
+								goto tw_ioctl_bail;
+							}
+						}
+					}
+				}
+				
+			tw_ioctl_bail:
+
+				/* Free up sglist memory */
+				if (tw_dev->ioctl_data[request_id])
+					kfree(tw_dev->ioctl_data[request_id]);
+				else
+					printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl(): Error freeing ioctl data.\n", tw_dev->host->host_no);
+				
+				/* Now complete the io */
+				tw_dev->state[request_id] = TW_S_COMPLETED;
+				tw_state_request_finish(tw_dev, request_id);
+				tw_dev->posted_request_count--;
+				tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+				return 0;
+			} else {
+				printk(KERN_WARNING "3w-xxxx: tw_ioctl(): ioctl->data NULL.\n");
+				return 1;
+			}
 		default:
 			printk(KERN_WARNING "3w-xxxx: Unknown ioctl 0x%x.\n", opcode);
 			tw_dev->state[request_id] = TW_S_COMPLETED;
@@ -1653,6 +1845,7 @@
 	TW_Param *param;
 	TW_Ioctl *ioctl = NULL;
 	TW_Passthru *passthru = NULL;
+	TW_Command *command_packet;
 
 	ioctl = (TW_Ioctl *)tw_dev->srb[request_id]->request_buffer;
 	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete()\n");
@@ -1661,6 +1854,13 @@
 		printk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): Request buffer NULL.\n");
 		return 1;
 	}
+
+	command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+	if (command_packet == NULL) {
+		printk(KERN_WARNING "3w-xxxx: scsi%d: tw_ioctl_complete(): Bad command packet virtual address.\n", tw_dev->host->host_no);
+		return 1;
+	}
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_ioctl_complete(): Request_bufflen = %d\n", tw_dev->srb[request_id]->request_bufflen);
 
 	ioctl = (TW_Ioctl *)buff;
@@ -1669,6 +1869,9 @@
 			passthru = (TW_Passthru *)ioctl->data;
 			memcpy(buff, tw_dev->alignment_virtual_address[request_id], passthru->sector_count * 512);
 			break;
+		case TW_CMD_PACKET_WITH_DATA:
+			dprintk(KERN_WARNING "3w-xxxx: tw_ioctl_complete(): caught TW_CMD_PACKET_WITH_DATA.\n");
+			return 2; /* Special case for isr to not complete io */
 		default:
 			memset(buff, 0, tw_dev->srb[request_id]->request_bufflen);
 			param = (TW_Param *)tw_dev->alignment_virtual_address[request_id];
@@ -1819,7 +2022,7 @@
 
 		error = tw_aen_drain_queue(tw_dev);
 		if (error) {
-			printk(KERN_WARNING "3w-xxxx: scsi%d: Card not responding, retrying.\n", tw_dev->host->host_no);
+			printk(KERN_WARNING "3w-xxxx: scsi%d: AEN drain failed, retrying.\n", tw_dev->host->host_no);
 			tries++;
 			continue;
 		}
@@ -1855,7 +2058,7 @@
 	}
 
 	/* Re-enable interrupts */
-	tw_enable_interrupts(tw_dev);
+	tw_enable_and_clear_interrupts(tw_dev);
 
 	return 0;
 } /* End tw_reset_sequence() */
@@ -2381,6 +2584,9 @@
 	capacity = (param_data[3] << 24) | (param_data[2] << 16) | 
 		   (param_data[1] << 8) | param_data[0];
 
+	/* Subtract one sector to fix get last sector ioctl */
+	capacity -= 1;
+
 	dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_read_capacity_complete(): Capacity = 0x%x.\n", capacity);
 
 	/* Number of LBA's */
@@ -2671,7 +2877,7 @@
 	}
 
 	/* Re-enable interrupts */
-	tw_enable_interrupts(tw_dev);
+	tw_enable_and_clear_interrupts(tw_dev);
 
 	return 0;
 } /* End tw_shutdown_device() */
@@ -2719,7 +2925,7 @@
 	int id = 0;
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_state_request_start()\n");
-
+	
 	/* Obtain next free request_id */
 	do {
 		if (tw_dev->free_head == tw_dev->free_wrap) {
@@ -2749,7 +2955,6 @@
 } /* End tw_unmask_command_interrupt() */
 
 /* Now get things going */
-
 static Scsi_Host_Template driver_template = TWXXXX;
 #include "scsi_module.c"
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/3w-xxxx.h linux/drivers/scsi/3w-xxxx.h
--- linux.orig/drivers/scsi/3w-xxxx.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/3w-xxxx.h	Tue Feb  5 17:29:43 2002
@@ -6,7 +6,7 @@
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
                      Brad Strand <linux@3ware.com>
 
-   Copyright (C) 1999-2001 3ware Inc.
+   Copyright (C) 1999-2002 3ware Inc.
 
    Kernel compatablity By:	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -62,7 +62,7 @@
 static char *tw_aen_string[] = {
 	"AEN queue empty",                      // 0x000
 	"Soft reset occurred",                  // 0x001
-	"Mirorr degraded: Unit #",              // 0x002
+	"Unit degraded: Unit #",                // 0x002
 	"Controller error",                     // 0x003 
 	"Rebuild failed: Unit #",               // 0x004
 	"Rebuild complete: Unit #",             // 0x005
@@ -90,10 +90,12 @@
 	"DCB unsupported version: Port #",      // 0x028
 	"Verify started: Unit #",               // 0x029
 	"Verify failed: Port #",                // 0x02A
-	"Verify complete: Unit #"               // 0x02B
+	"Verify complete: Unit #",              // 0x02B
+	"Overwrote bad sector during rebuild: Port #",  //0x2C
+	"Encountered bad sector during rebuild: Port #" //0x2D
 };
 
-#define TW_AEN_STRING_MAX                      0x02C
+#define TW_AEN_STRING_MAX                      0x02E
 
 /* Control register bit definitions */
 #define TW_CONTROL_CLEAR_HOST_INTERRUPT	       0x00080000
@@ -108,6 +110,7 @@
 #define TW_CONTROL_DISABLE_INTERRUPTS	       0x00000040
 #define TW_CONTROL_ISSUE_HOST_INTERRUPT	       0x00000020
 #define TW_CONTROL_CLEAR_PARITY_ERROR          0x00800000
+#define TW_CONTROL_CLEAR_PCI_ABORT             0x00100000
 
 /* Status register bit definitions */
 #define TW_STATUS_MAJOR_VERSION_MASK	       0xF0000000
@@ -140,6 +143,7 @@
 #define TW_DEVICE_ID2 (0x1001)  /* 7000 series controller */
 #define TW_NUMDEVICES 2
 #define TW_PCI_CLEAR_PARITY_ERRORS 0xc100
+#define TW_PCI_CLEAR_PCI_ABORT     0x2000
 
 /* Command packet opcodes */
 #define TW_OP_NOP	      0x0
@@ -153,6 +157,7 @@
 #define TW_OP_AEN_LISTEN      0x1c
 #define TW_CMD_PACKET         0x1d
 #define TW_ATA_PASSTHRU       0x1e
+#define TW_CMD_PACKET_WITH_DATA 0x1f
 
 /* Asynchronous Event Notification (AEN) Codes */
 #define TW_AEN_QUEUE_EMPTY       0x0000
@@ -191,12 +196,11 @@
 #define TW_MAX_AEN_TRIES                      100
 #define TW_UNIT_ONLINE                        1
 #define TW_IN_INTR                            1
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
 #define TW_MAX_SECTORS                        256
-#else
-#define TW_MAX_SECTORS                        128
-#endif 
+#define TW_MAX_BOUNCE_SECTORS                 128
 #define TW_AEN_WAIT_TIME                      1000
+#define TW_IOCTL_WAIT_TIME                    (1 * HZ) /* 1 second */
+#define TW_MAX_CDB_LEN                        16
 
 /* Macros */
 #define TW_STATUS_ERRORS(x) \
@@ -262,7 +266,9 @@
 } TW_Command;
 
 typedef struct TAG_TW_Ioctl {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,15)
 	int buffer;
+#endif
 	unsigned char opcode;
 	unsigned short table_id;
 	unsigned char parameter_id;
@@ -381,6 +387,7 @@
 	unsigned char		aen_head;
 	unsigned char		aen_tail;
 	long			flags; /* long req'd for set_bit --RR */
+	char			*ioctl_data[TW_Q_LENGTH];
 } TW_Device_Extension;
 
 /* Function prototypes */
@@ -397,6 +404,7 @@
 void tw_disable_interrupts(TW_Device_Extension *tw_dev);
 int tw_empty_response_que(TW_Device_Extension *tw_dev);
 void tw_enable_interrupts(TW_Device_Extension *tw_dev);
+void tw_enable_and_clear_interrupts(TW_Device_Extension *tw_dev);
 int tw_findcards(Scsi_Host_Template *tw_host);
 void tw_free_device_extension(TW_Device_Extension *tw_dev);
 int tw_initconnection(TW_Device_Extension *tw_dev, int message_credits);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c7,8xx.c linux/drivers/scsi/53c7,8xx.c
--- linux.orig/drivers/scsi/53c7,8xx.c	Thu Oct 25 20:53:48 2001
+++ linux/drivers/scsi/53c7,8xx.c	Mon Feb  4 18:15:41 2002
@@ -1867,7 +1867,7 @@
 	 */
 
 	timeout = jiffies + 5 * HZ / 10;
-	while ((hostdata->test_completed == -1) && jiffies < timeout) {
+	while ((hostdata->test_completed == -1) && time_before(jiffies, timeout)) {
 		barrier();
 		cpu_relax();
 	}
@@ -1953,7 +1953,7 @@
 	    restore_flags(flags);
 
 	    timeout = jiffies + 5 * HZ;	/* arbitrary */
-	    while ((hostdata->test_completed == -1) && jiffies < timeout) {
+	    while ((hostdata->test_completed == -1) && time_before(jiffies, timeout)) {
 	    	barrier();
 		cpu_relax();
 	    }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.c linux/drivers/scsi/53c700.c
--- linux.orig/drivers/scsi/53c700.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/53c700.c	Mon Jan  7 15:35:16 2002
@@ -51,6 +51,14 @@
 
 /* CHANGELOG
  *
+ * Version 2.7
+ *
+ * Fixed scripts problem which caused certain devices (notably CDRWs)
+ * to hang on initial INQUIRY.  Updated NCR_700_readl/writel to use
+ * __raw_readl/writel for parisc compatibility (Thomas
+ * Bogendoerfer). Added missing SCp->request_bufflen initialisation
+ * for sense requests (Ryan Bradetich).
+ *
  * Version 2.6
  *
  * Following test of the 64 bit parisc kernel by Richard Hirst,
@@ -96,7 +104,7 @@
  * Initial modularisation from the D700.  See NCR_D700.c for the rest of
  * the changelog.
  * */
-#define NCR_700_VERSION "2.6"
+#define NCR_700_VERSION "2.7"
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -1049,6 +1057,7 @@
 						    slot->pCmd,
 						    SCp->cmd_len,
 						    PCI_DMA_TODEVICE);
+				SCp->request_bufflen = sizeof(SCp->sense_buffer);
 				slot->dma_handle = pci_map_single(hostdata->pci_dev, SCp->sense_buffer, sizeof(SCp->sense_buffer), PCI_DMA_FROMDEVICE);
 				slot->SG[0].ins = bS_to_host(SCRIPT_MOVE_DATA_IN | sizeof(SCp->sense_buffer));
 				slot->SG[0].pAddr = bS_to_host(slot->dma_handle);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.h linux/drivers/scsi/53c700.h
--- linux.orig/drivers/scsi/53c700.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/53c700.h	Mon Jan  7 15:35:16 2002
@@ -210,7 +210,7 @@
 struct NCR_700_Host_Parameters {
 	/* These must be filled in by the calling driver */
 	int	clock;			/* board clock speed in MHz */
-	__u32	base;			/* the base for the port (copied to host) */
+	unsigned long	base;		/* the base for the port (copied to host) */
 	struct pci_dev	*pci_dev;
 	__u32	dmode_extra;	/* adjustable bus settings */
 	__u32	differential:1;	/* if we are differential */
@@ -503,7 +503,7 @@
 static inline __u32
 NCR_700_readl(struct Scsi_Host *host, __u32 reg)
 {
-	__u32 value = readl(host->base + reg);
+	__u32 value = __raw_readl(host->base + reg);
 	const struct NCR_700_Host_Parameters *hostdata __attribute__((unused))
 		= (struct NCR_700_Host_Parameters *)host->hostdata[0];
 #if 1
@@ -536,7 +536,7 @@
 		BUG();
 #endif
 
-	writel(bS_to_host(value), host->base + reg);
+	__raw_writel(bS_to_host(value), host->base + reg);
 }
 #elif defined(CONFIG_53C700_IO_MAPPED)
 static inline __u8
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c700.scr linux/drivers/scsi/53c700.scr
--- linux.orig/drivers/scsi/53c700.scr	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/53c700.scr	Mon Jan  7 15:35:16 2002
@@ -242,7 +242,7 @@
 
 SendIdentifyMsg:
 	CALL	SendMessage
-	JUMP	SendCommand
+	CLEAR	ATN
 
 IgnoreMsgBeforeCommand:
 	CLEAR	ACK
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/53c7xx.c linux/drivers/scsi/53c7xx.c
--- linux.orig/drivers/scsi/53c7xx.c	Thu Oct 25 20:53:48 2001
+++ linux/drivers/scsi/53c7xx.c	Mon Feb  4 18:15:41 2002
@@ -1627,7 +1627,7 @@
 	 */
 
 	timeout = jiffies + 5 * HZ / 10;
-	while ((hostdata->test_completed == -1) && jiffies < timeout)
+	while ((hostdata->test_completed == -1) && time_before(jiffies, timeout))
 		barrier();
 
 	failed = 1;
@@ -1713,7 +1713,7 @@
 	    restore_flags(flags);
 
 	    timeout = jiffies + 5 * HZ;	/* arbitrary */
-	    while ((hostdata->test_completed == -1) && jiffies < timeout)
+	    while ((hostdata->test_completed == -1) && time_before(jiffies, timeout))
 	    	barrier();
 
 	    NCR53c7x0_write32 (DSA_REG, 0);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux.orig/drivers/scsi/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/Config.in	Tue Jan  8 18:08:44 2002
@@ -214,7 +214,7 @@
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    dep_tristate 'SCSI debugging host simulator (EXPERIMENTAL)' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 fi
-if [ "$CONFIG_PPC" = "y" ]; then
+if [ "$CONFIG_ALL_PPC" = "y" ]; then
    dep_tristate 'MESH (Power Mac internal SCSI) support' CONFIG_SCSI_MESH $CONFIG_SCSI
    if [ "$CONFIG_SCSI_MESH" != "n" ]; then
       int '  maximum synchronous transfer rate (MB/s) (0 = async)' CONFIG_SCSI_MESH_SYNC_RATE 5
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux.orig/drivers/scsi/Makefile	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for linux/drivers/scsi
 #
-# 30 May 2000, Christoph Hellwig <chhellwig@gmx.net>
+# 30 May 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 # 20 Sep 2000, Torben Mathiasen <tmm@image.dk>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/NCR5380.c linux/drivers/scsi/NCR5380.c
--- linux.orig/drivers/scsi/NCR5380.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/NCR5380.c	Mon Feb  4 18:37:11 2002
@@ -612,7 +612,7 @@
  *	Locks: disables irqs, takes and frees io_request_lock
  */
  
-void NCR5380_timer_fn(unsigned long unused)
+static void NCR5380_timer_fn(unsigned long unused)
 {
 	struct Scsi_Host *instance;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/NCR_D700.c linux/drivers/scsi/NCR_D700.c
--- linux.orig/drivers/scsi/NCR_D700.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/NCR_D700.c	Mon Jan  7 15:35:16 2002
@@ -36,6 +36,10 @@
 
 /* CHANGELOG 
  *
+ * Version 2.2
+ *
+ * Added mca_set_adapter_name().
+ *
  * Version 2.1
  *
  * Modularise the driver into a Board piece (this file) and a chip
@@ -86,7 +90,7 @@
  * disconnections and reselections are being processed correctly.
  * */
 
-#define NCR_D700_VERSION "2.1"
+#define NCR_D700_VERSION "2.2"
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -299,6 +303,7 @@
 				continue;
 			}
 			found++;
+			mca_set_adapter_name(slot, "NCR D700 SCSI Adapter (version " NCR_D700_VERSION ")");
 		}
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/README.st linux/drivers/scsi/README.st
--- linux.orig/drivers/scsi/README.st	Fri Nov  9 21:52:21 2001
+++ linux/drivers/scsi/README.st	Tue Feb  5 19:40:49 2002
@@ -1,8 +1,8 @@
 This file contains brief information about the SCSI tape driver.
-The driver is currently maintained by Kai M{kisara (email
+The driver is currently maintained by Kai Mäkisara (email
 Kai.Makisara@metla.fi)
 
-Last modified: Thu Nov  1 22:41:59 2001 by makisara@kai.makisara.local
+Last modified: Tue Feb  5 21:33:23 2002 by makisara
 
 
 BASICS
@@ -327,12 +327,19 @@
 	   0xffffff means that the default is not used any more.
 	MT_ST_DEF_DENSITY
 	MT_ST_DEF_DRVBUFFER
+	   Used to set or clear the density (8 bits), and drive buffer
+	   state (3 bits). If the value is MT_ST_CLEAR_DEFAULT
+	   (0xfffff) the default will not be used any more. Otherwise
+	   the lowermost bits of the value contain the new value of
+	   the parameter.
 	MT_ST_DEF_COMPRESSION
-	   Used to set or clear the density (8 bits), drive buffer
-	   state (3 bits), and compression (single bit). If the value is
-	   MT_ST_CLEAR_DEFAULT (0xfffff), the default will not be used
-	   any more. Otherwise the lower-most bits of the value contain
-	   the new value of the parameter.
+	   The compression default will not be used if the value of
+	   the lowermost byte is 0xff. Otherwise the lowermost bit
+	   contains the new default. If the bits 8-15 are set to a
+	   non-zero number, and this number is not 0xff, the number is
+	   used as the compression algorithm. The value
+	   MT_ST_CLEAR_DEFAULT can be used to clear the compression
+	   default.
 	MT_ST_SET_TIMEOUT
 	   Set the normal timeout in seconds for this device. The
 	   default is 900 seconds (15 minutes). The timeout should be
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/aacraid/README linux/drivers/scsi/aacraid/README
--- linux.orig/drivers/scsi/aacraid/README	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/aacraid/README	Mon Feb  4 17:38:23 2002
@@ -25,7 +25,7 @@
 People
 -------------------------
 Alan Cox <alan@redhat.com>
-Christoph Hellwig <hch@caldera.de>	(small cleanups/fixes)
+Christoph Hellwig <hch@infradead.org>	(small cleanups/fixes)
 Matt Domsch <matt_domsch@dell.com>	(revision ioctl, adapter messages)
 
 Original Driver
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/aacraid/rx.c linux/drivers/scsi/aacraid/rx.c
--- linux.orig/drivers/scsi/aacraid/rx.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/aacraid/rx.c	Mon Feb  4 19:23:45 2002
@@ -199,7 +199,7 @@
 	/*
 	 *	Wait up to 30 seconds
 	 */
-	while (time_before(start+30*HZ, jiffies)) 
+	while (time_before(jiffies, start+30*HZ)) 
 	{
 		udelay(5);	/* Delay 5 microseconds to let Mon960 get info. */
 		/*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux.orig/drivers/scsi/aic7xxx/aic7xxx_linux.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Jan  7 18:55:02 2002
@@ -1703,6 +1703,7 @@
 			       cmd->request_buffer,
 			       cmd->request_bufflen,
 			       scsi_to_pci_dma_dir(cmd->sc_data_direction));
+			scb->sg_count = 0;
 			scb->sg_count = ahc_linux_map_seg(ahc, scb,
 							  sg, addr,
 							  cmd->request_bufflen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/fdomain.c linux/drivers/scsi/fdomain.c
--- linux.orig/drivers/scsi/fdomain.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/fdomain.c	Mon Jan 14 18:53:53 2002
@@ -729,13 +729,13 @@
       switch (Quantum) {
       case 2:			/* ISA_200S */
       case 3:			/* ISA_250MG */
-	 base = readb(bios_base + 0x1fa2) + (readb(bios_base + 0x1fa3) << 8);
+	 base = isa_readb(bios_base + 0x1fa2) + (isa_readb(bios_base + 0x1fa3) << 8);
 	 break;
       case 4:			/* ISA_200S (another one) */
-	 base = readb(bios_base + 0x1fa3) + (readb(bios_base + 0x1fa4) << 8);
+	 base = isa_readb(bios_base + 0x1fa3) + (isa_readb(bios_base + 0x1fa4) << 8);
 	 break;
       default:
-	 base = readb(bios_base + 0x1fcc) + (readb(bios_base + 0x1fcd) << 8);
+	 base = isa_readb(bios_base + 0x1fcc) + (isa_readb(bios_base + 0x1fcd) << 8);
 	 break;
       }
    
@@ -1955,7 +1955,7 @@
 	 offset = bios_base + 0x1f31 + drive * 25;
 	 break;
       }
-      memcpy_fromio( &i, offset, sizeof( struct drive_info ) );
+      isa_memcpy_fromio( &i, offset, sizeof( struct drive_info ) );
       info_array[0] = i.heads;
       info_array[1] = i.sectors;
       info_array[2] = i.cylinders;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- linux.orig/drivers/scsi/hosts.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/hosts.c	Tue Jan  8 16:43:55 2002
@@ -275,6 +275,24 @@
     return 0;
 }
 
+void
+scsi_deregister_device(struct Scsi_Device_Template * tpnt)
+{
+    struct Scsi_Device_Template *spnt;
+    struct Scsi_Device_Template *prev_spnt;
+
+    spnt = scsi_devicelist;
+    prev_spnt = NULL;
+    while (spnt != tpnt) {
+	prev_spnt = spnt;
+	spnt = spnt->next;
+    }
+    if (prev_spnt == NULL)
+        scsi_devicelist = tpnt->next;
+    else
+        prev_spnt->next = spnt->next;
+}
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/hosts.h linux/drivers/scsi/hosts.h
--- linux.orig/drivers/scsi/hosts.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/hosts.h	Tue Jan  8 16:43:55 2002
@@ -526,6 +526,7 @@
 void  scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt);
 
 int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+void scsi_deregister_device(struct Scsi_Device_Template * tpnt);
 
 /* These are used by loadable modules */
 extern int scsi_register_module(int, void *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/i60uscsi.c linux/drivers/scsi/i60uscsi.c
--- linux.orig/drivers/scsi/i60uscsi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/i60uscsi.c	Mon Jan 14 18:53:53 2002
@@ -640,7 +640,6 @@
 	ULONG idx;
 	UCHAR index;
 	UCHAR i;
-	ULONG flags;
 
 	Ch = hcsp->HCS_Index;
 	for (i = 0; i < 8; i++) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/ips.c linux/drivers/scsi/ips.c
--- linux.orig/drivers/scsi/ips.c	Fri Nov  9 22:05:06 2001
+++ linux/drivers/scsi/ips.c	Mon Feb  4 17:38:23 2002
@@ -81,7 +81,7 @@
 /*            2.3.18 and later                                               */
 /*          - Sync with other changes from the 2.3 kernels                   */
 /* 4.00.06  - Fix timeout with initial FFDC command                          */
-/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@caldera.de>    */
+/* 4.00.06a - Port to 2.4 (trivial) -- Christoph Hellwig <hch@infradead.org> */
 /* 4.10.00  - Add support for ServeRAID 4M/4L                                */
 /* 4.10.13  - Fix for dynamic unload and proc file system                    */
 /* 4.20.03  - Rename version to coincide with new release schedules          */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/lasi700.c linux/drivers/scsi/lasi700.c
--- linux.orig/drivers/scsi/lasi700.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/lasi700.c	Mon Jan  7 15:35:16 2002
@@ -136,7 +136,6 @@
 lasi700_driver_callback(struct parisc_device *dev)
 {
 	unsigned long base = dev->hpa + LASI_SCSI_CORE_OFFSET;
-	int irq = busdevice_alloc_irq(dev);
 	char *driver_name;
 	struct Scsi_Host *host;
 	struct NCR_700_Host_Parameters *hostdata =
@@ -170,14 +169,15 @@
 		hostdata->chip710 = 1;
 		hostdata->dmode_extra = DMODE_FC2;
 	}
+	hostdata->pci_dev = ccio_get_fake(dev);
 	if((host = NCR_700_detect(host_tpnt, hostdata)) == NULL) {
 		kfree(hostdata);
 		release_mem_region(host->base, 64);
 		return 1;
 	}
-	host->irq = irq;
-	if(request_irq(irq, NCR_700_intr, SA_SHIRQ, driver_name, host)) {
-		printk(KERN_ERR "%s: irq problem, detatching\n",
+	host->irq = dev->irq;
+	if(request_irq(dev->irq, NCR_700_intr, SA_SHIRQ, driver_name, host)) {
+		printk(KERN_ERR "%s: irq problem, detaching\n",
 		       driver_name);
 		scsi_unregister(host);
 		NCR_700_release(host);
@@ -197,6 +197,7 @@
 	kfree(hostdata);
 	free_irq(host->irq, host);
 	release_mem_region(host->base, 64);
+	unregister_parisc_driver(&lasi700_driver);
 	return 1;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/mac_scsi.c linux/drivers/scsi/mac_scsi.c
--- linux.orig/drivers/scsi/mac_scsi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/mac_scsi.c	Mon Jan  7 17:36:23 2002
@@ -353,7 +353,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/megaraid.c linux/drivers/scsi/megaraid.c
--- linux.orig/drivers/scsi/megaraid.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/megaraid.c	Mon Feb  4 18:15:41 2002
@@ -1149,12 +1149,12 @@
 			if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU) {
 				memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14);
 			} else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) {
-				SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1);
+				SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1);
 				memcpy(
 					SCpnt->sense_buffer,
 					epthru->reqsensearea, 14
 				);
-				SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1);
+				SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1);
 				/*SCpnt->result =
 					(DRIVER_SENSE << 24) |
 					(DID_ERROR << 16) | status;*/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/megaraid.h linux/drivers/scsi/megaraid.h
--- linux.orig/drivers/scsi/megaraid.h	Thu Oct 25 20:53:51 2001
+++ linux/drivers/scsi/megaraid.h	Mon Feb  4 18:23:53 2002
@@ -30,7 +30,7 @@
 #define M_RD_IOCTL_CMD_NEW		0x81
 #define M_RD_DRIVER_IOCTL_INTERFACE	0x82
 
-#define MEGARAID_VERSION "v1.18 (Release Date: Thu Oct 11 15:02:53 EDT 2001\n)"
+#define MEGARAID_VERSION "v1.18 (Release Date: Thu Oct 11 15:02:53 EDT 2001)\n"
 
 #define MEGARAID_IOCTL_VERSION 	114
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/mesh.c linux/drivers/scsi/mesh.c
--- linux.orig/drivers/scsi/mesh.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/mesh.c	Tue Jan  8 18:08:44 2002
@@ -28,7 +28,8 @@
 #include <asm/irq.h>
 #include <asm/hydra.h>
 #include <asm/processor.h>
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #ifdef CONFIG_PMAC_PBOOK
 #include <linux/adb.h>
 #include <linux/pmu.h>
@@ -155,7 +156,6 @@
 	struct mesh_target tgts[8];
 	void	*dma_cmd_space;
 	struct device_node *ofnode;
-	u8*	mio_base;
 #ifndef MESH_NEW_STYLE_EH
 	Scsi_Cmnd *completed_q;
 	Scsi_Cmnd *completed_qtail;
@@ -258,8 +258,6 @@
 	if (mesh == 0)
 		mesh = find_compatible_devices("scsi", "chrp,mesh0");
 	for (; mesh != 0; mesh = mesh->next) {
-		struct device_node *mio;
-		
 		if (mesh->n_addrs != 2 || mesh->n_intrs != 2) {
 			printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
 			       " (got %d,%d)", mesh->n_addrs, mesh->n_intrs);
@@ -325,12 +323,6 @@
 		if (mesh_sync_period < minper)
 			mesh_sync_period = minper;
 
-		ms->mio_base = 0;
-		for (mio = ms->ofnode->parent; mio; mio = mio->parent)
-			if (strcmp(mio->name, "mac-io") == 0 && mio->n_addrs > 0)
-				break;
-		if (mio)
-			ms->mio_base = (u8 *) ioremap(mio->addrs[0].address, 0x40);
 		set_mesh_power(ms, 1);
 
 		mesh_init(ms);
@@ -363,11 +355,9 @@
 		iounmap((void *) ms->mesh);
 	if (ms->dma)
 		iounmap((void *) ms->dma);
-	if (ms->mio_base)
-		iounmap((void *) ms->mio_base);
 	kfree(ms->dma_cmd_space);
 	free_irq(ms->meshintr, ms);
-	feature_clear(ms->ofnode, FEATURE_MESH_enable);
+	pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 	return 0;
 }
 
@@ -377,16 +367,10 @@
 	if (_machine != _MACH_Pmac)
 		return;
 	if (state) {
-		feature_set(ms->ofnode, FEATURE_MESH_enable);
-		/* This seems to enable the termination power. strangely
-		   this doesn't fully agree with OF, but with MacOS */
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x70);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 1);
 		mdelay(200);
 	} else {
-		feature_clear(ms->ofnode, FEATURE_MESH_enable);
-		if (ms->mio_base)
-			out_8(ms->mio_base + 0x36, 0x34);
+		pmac_call_feature(PMAC_FTR_MESH_ENABLE, ms->ofnode, 0, 0);
 		mdelay(10);
 	}
 }			
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi.c linux/drivers/scsi/scsi.c
--- linux.orig/drivers/scsi/scsi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/scsi.c	Tue Jan  8 16:43:55 2002
@@ -2261,7 +2261,7 @@
 		for (SDpnt = shpnt->host_queue; SDpnt;
 		     SDpnt = SDpnt->next) {
 			if (tpnt->detect)
-				SDpnt->attached += (*tpnt->detect) (SDpnt);
+				SDpnt->detected = (*tpnt->detect) (SDpnt);
 		}
 	}
 
@@ -2269,9 +2269,19 @@
 	 * If any of the devices would match this driver, then perform the
 	 * init function.
 	 */
-	if (tpnt->init && tpnt->dev_noticed)
-		if ((*tpnt->init) ())
+	if (tpnt->init && tpnt->dev_noticed) {
+		if ((*tpnt->init) ()) {
+			for (shpnt = scsi_hostlist; shpnt;
+			     shpnt = shpnt->next) {
+				for (SDpnt = shpnt->host_queue; SDpnt;
+				     SDpnt = SDpnt->next) {
+					SDpnt->detected = 0;
+				}
+			}
+			scsi_deregister_device(tpnt);
 			return 1;
+		}
+	}
 
 	/*
 	 * Now actually connect the devices to the new driver.
@@ -2279,6 +2289,8 @@
 	for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) {
 		for (SDpnt = shpnt->host_queue; SDpnt;
 		     SDpnt = SDpnt->next) {
+			SDpnt->attached += SDpnt->detected;
+			SDpnt->detected = 0;
 			if (tpnt->attach)
 				(*tpnt->attach) (SDpnt);
 			/*
@@ -2314,9 +2326,7 @@
 {
 	Scsi_Device *SDpnt;
 	struct Scsi_Host *shpnt;
-	struct Scsi_Device_Template *spnt;
-	struct Scsi_Device_Template *prev_spnt;
-	
+
 	lock_kernel();
 	/*
 	 * If we are busy, this is not going to fly.
@@ -2347,16 +2357,7 @@
 	/*
 	 * Extract the template from the linked list.
 	 */
-	spnt = scsi_devicelist;
-	prev_spnt = NULL;
-	while (spnt != tpnt) {
-		prev_spnt = spnt;
-		spnt = spnt->next;
-	}
-	if (prev_spnt == NULL)
-		scsi_devicelist = tpnt->next;
-	else
-		prev_spnt->next = spnt->next;
+	scsi_deregister_device(tpnt);
 
 	MOD_DEC_USE_COUNT;
 	unlock_kernel();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi.h linux/drivers/scsi/scsi.h
--- linux.orig/drivers/scsi/scsi.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/scsi.h	Tue Jan  8 16:43:55 2002
@@ -575,8 +575,8 @@
 					 * vendor-specific cmd's */
 	unsigned sector_size;	/* size in bytes */
 
-	int attached;		/* # of high level drivers attached to 
-				 * this */
+	int attached;		/* # of high level drivers attached to this */
+	int detected;		/* Delta attached - don't use in drivers! */
 	int access_count;	/* Count of open channels/mounts */
 
 	void *hostdata;		/* available to low-level driver */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_debug.c linux/drivers/scsi/scsi_debug.c
--- linux.orig/drivers/scsi/scsi_debug.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/scsi_debug.c	Wed Dec 26 17:05:14 2001
@@ -3,8 +3,18 @@
  *
  *  Copyright (C) 1992  Eric Youngdale
  *  Simulate a host adapter with 2 disks attached.  Do a lot of checking
- *  to make sure that we are not getting blocks mixed up, and panic if
+ *  to make sure that we are not getting blocks mixed up, and PANIC if
  *  anything out of the ordinary is seen.
+ *
+ *  This version is more generic, simulating a variable number of disk 
+ *  (or disk like devices) sharing a common amount of RAM (default 8 MB
+ *  but can be set at driver/module load time).
+ *
+ *  For documentation see http://www.torque.net/sg/sdebug.html
+ *
+ *   D. Gilbert (dpg) work for MOD device test [20010421]
+ *   dpg, work for devfs large number of disks [20010809]
+ *   dpg, make more generic [20011123]
  */
 
 #include <linux/config.h>
@@ -18,7 +28,9 @@
 #include <linux/string.h>
 #include <linux/genhd.h>
 #include <linux/fs.h>
+#include <linux/init.h>
 #include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -27,151 +39,162 @@
 #include "scsi.h"
 #include "hosts.h"
 
-#include "sd.h"
-
 #include<linux/stat.h>
 
-/* A few options that we want selected */
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+static char scsi_debug_version_str[] = "Version: 0.57 (20011209)";
 
-#define NR_HOSTS_PRESENT 1
-#define NR_FAKE_DISKS   3
-#define N_HEAD          255
-#define N_SECTOR        63
-#define N_CYLINDER      524
+#ifndef SCSI_CMD_READ_16
+#define SCSI_CMD_READ_16 0x88
+#endif
+#ifndef SCSI_CMD_WRITE_16
+#define SCSI_CMD_WRITE_16 0x8a
+#endif
+
+/* A few options that we want selected */
+#define DEF_NR_FAKE_DEVS   1
+#define DEF_DEV_SIZE_MB   8
+#define DEF_FAKE_BLK0   0
+
+static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS;
+
+#define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1)
+#define N_HEAD          8
+#define N_SECTOR        32
 #define DISK_READONLY(TGT)      (0)
-#define DISK_REMOVEABLE(TGT)    (1)
-#define DEVICE_TYPE(TGT) (TGT == 2 ? TYPE_TAPE : TYPE_DISK);
+#define DISK_REMOVEABLE(TGT)    (0)
+#define DEVICE_TYPE(TGT) (TYPE_DISK);
+
+#define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1)
+
+static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB;
+#define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024)
+#define STORE_ELEM_ORDER 1
+#define STORE_ELEM_SIZE (PAGE_SIZE * (1 << STORE_ELEM_ORDER))
+#define STORE_ELEMENTS ((STORE_SIZE / STORE_ELEM_SIZE) + 1)
+
+/* default sector size is 512 bytes, 2**9 bytes */
+#define POW2_SECT_SIZE 9
+#define SECT_SIZE (1 << POW2_SECT_SIZE)
+
+#define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD))
+
+static int scsi_debug_fake_blk0 = DEF_FAKE_BLK0;
 
 /* Do not attempt to use a timer to simulate a real disk with latency */
 /* Only use this in the actual kernel, not in the simulator. */
 #define IMMEDIATE
 
-/* Skip some consistency checking.  Good for benchmarking */
-#define SPEEDY
-/* Read return zeros. Undefine for benchmarking */
-#define CLEAR
-
-/* Number of real scsi disks that will be detected ahead of time */
-static int NR_REAL = -1;
-
-#define NR_BLK_DEV  12
-#ifndef MAJOR_NR
-#define MAJOR_NR 8
-#endif
 #define START_PARTITION 4
 
 /* Time to wait before completing a command */
 #define DISK_SPEED     (HZ/10)	/* 100ms */
 #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER)
-#define SIZE(TGT) (TGT == 2 ? 2248 : 512)
+#define SECT_SIZE_PER(TGT) SECT_SIZE
+#define SECT_PER_ELEM (STORE_ELEM_SIZE / SECT_SIZE)
 
 static int starts[] =
 {N_SECTOR,
  N_HEAD * N_SECTOR,		/* Single cylinder */
  N_HEAD * N_SECTOR * 4,
- CAPACITY, 0};
+ 0 /* CAPACITY */, 0};
 static int npart = 0;
 
-#include "scsi_debug.h"
-#ifdef DEBUG
-#define DEB(x) x
-#else
-#define DEB(x)
-#endif
+typedef struct scsi_debug_store_elem {
+	unsigned char * p;
+} Sd_store_elem;
+
+static Sd_store_elem * store_arr = 0;
+
+typedef struct sdebug_dev_info {
+	Scsi_Device * sdp;
+	unsigned short host_no;
+	unsigned short id;
+	char reset;
+	char sb_index;
+} Sdebug_dev_info;
+static Sdebug_dev_info * devInfop;
+
+static int num_aborts = 0;
+static int num_dev_resets = 0;
+static int num_bus_resets = 0;
+static int num_host_resets = 0;
 
-#ifdef SPEEDY
-#define VERIFY1_DEBUG(RW)
-#define VERIFY_DEBUG(RW)
-#else
+static spinlock_t mailbox_lock = SPIN_LOCK_UNLOCKED;
+static rwlock_t sdebug_atomic_rw = RW_LOCK_UNLOCKED;
 
-#define VERIFY1_DEBUG(RW)                           \
-    if (bufflen != 1024) {printk("%d", bufflen); panic("(1)Bad bufflen");};         \
-    start = 0;                          \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1];        \
-    if (bh){                            \
-	if (bh->b_size != 1024) panic ("Wrong bh size");    \
-	if ((bh->b_blocknr << 1) + start != block)          \
-	{   printk("Wrong bh block# %d %d ",bh->b_blocknr, block);  \
-	    panic ("Wrong bh block#"); \
-	};  \
-	if (bh->b_dev != SCpnt->request.rq_dev)  \
-	    panic ("Bad bh target"); \
-    };
-
-#define VERIFY_DEBUG(RW)                            \
-    if (bufflen != 1024 && (!SCpnt->use_sg)) {printk("%x %d\n ",bufflen, SCpnt->use_sg); panic("Bad bufflen");};    \
-    start = 0;                          \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) > npart) panic ("Bad partition");    \
-    if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1];        \
-    if (SCpnt->request.cmd != RW) panic ("Wrong  operation");       \
-    if (SCpnt->request.sector + start != block) panic("Wrong block.");  \
-    if (SCpnt->request.current_nr_sectors != 2 && (!SCpnt->use_sg)) panic ("Wrong # blocks");   \
-    if (SCpnt->request.bh){                         \
-	if (SCpnt->request.bh->b_size != 1024) panic ("Wrong bh size"); \
-	if ((SCpnt->request.bh->b_blocknr << 1) + start != block)           \
-	{   printk("Wrong bh block# %d %d ",SCpnt->request.bh->b_blocknr, block);  \
-	    panic ("Wrong bh block#"); \
-	};  \
-	if (SCpnt->request.bh->b_dev != SCpnt->request.rq_dev) \
-	    panic ("Bad bh target");\
-    };
-#endif
+#include "scsi_debug.h"
 
 typedef void (*done_fct_t) (Scsi_Cmnd *);
 
-static volatile done_fct_t do_done[SCSI_DEBUG_MAILBOXES] =
-{NULL,};
+static volatile done_fct_t * do_done = 0;
 
 struct Scsi_Host * SHpnt = NULL;
 
+static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			   int num, int * errstsp, Sdebug_dev_info * devip);
+static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			    int num, int * errstsp, Sdebug_dev_info * devip);
 static void scsi_debug_send_self_command(struct Scsi_Host * shpnt);
 static void scsi_debug_intr_handle(unsigned long);
+static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp);
+static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, 
+			    int asc, int asq, int inbandLen);
+static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip);
 
-static struct timer_list timeout[SCSI_DEBUG_MAILBOXES];
-
-Scsi_Cmnd *SCint[SCSI_DEBUG_MAILBOXES] =
-{NULL,};
-static char SCrst[SCSI_DEBUG_MAILBOXES] =
-{0,};
+static struct timer_list * timeout = 0;
+static Scsi_Cmnd ** SCint = 0;
 
 /*
  * Semaphore used to simulate bus lockups.
  */
 static int scsi_debug_lockup = 0;
 
-static char sense_buffer[128] =
-{0,};
+#define NUM_SENSE_BUFFS 4
+#define SENSE_BUFF_LEN 32
+static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN];
 
+#ifdef SCSI_DUMP
 static void scsi_dump(Scsi_Cmnd * SCpnt, int flag)
 {
 	int i;
-#if 0
-	unsigned char *pnt;
-#endif
 	unsigned int *lpnt;
 	struct scatterlist *sgpnt = NULL;
 	printk("use_sg: %d", SCpnt->use_sg);
 	if (SCpnt->use_sg) {
 		sgpnt = (struct scatterlist *) SCpnt->buffer;
 		for (i = 0; i < SCpnt->use_sg; i++) {
-			printk(":%p %d\n", sgpnt[i].address, sgpnt[i].length);
-		};
+			lpnt = (int *) sgpnt[i].alt_address;
+			printk(":%p %p %d\n", sgpnt[i].alt_address, 
+			       sgpnt[i].address, sgpnt[i].length);
+			if (lpnt)
+				printk(" (Alt %x) ", lpnt[15]);
+		}
 	} else {
 		printk("nosg: %p %p %d\n", SCpnt->request.buffer, SCpnt->buffer,
 		       SCpnt->bufflen);
 		lpnt = (int *) SCpnt->request.buffer;
 		if (lpnt)
 			printk(" (Alt %x) ", lpnt[15]);
-	};
+	}
 	lpnt = (unsigned int *) SCpnt;
 	for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
 		if ((i & 7) == 0)
 			printk("\n");
 		printk("%x ", *lpnt++);
-	};
+	}
 	printk("\n");
 	if (flag == 0)
 		return;
+	lpnt = (unsigned int *) sgpnt[0].alt_address;
+	for (i = 0; i < sizeof(Scsi_Cmnd) / 4 + 1; i++) {
+		if ((i & 7) == 0)
+			printk("\n");
+		printk("%x ", *lpnt++);
+	}
 #if 0
 	printk("\n");
 	lpnt = (unsigned int *) sgpnt[0].address;
@@ -179,50 +202,85 @@
 		if ((i & 7) == 0)
 			printk("\n");
 		printk("%x ", *lpnt++);
-	};
+	}
 	printk("\n");
 #endif
 	printk("DMA free %d sectors.\n", scsi_dma_free_sectors);
 }
+#endif
+
+static int made_block0 = 0;
+
+static void scsi_debug_mkblock0(unsigned char * buff, int bufflen,
+				Scsi_Cmnd * SCpnt)
+{
+	int i;
+	struct partition *p;
+
+	memset(buff, 0, bufflen);
+	*((unsigned short *) (buff + 510)) = 0xAA55;
+	p = (struct partition *) (buff + 0x1be);
+	i = 0;
+	while (starts[i + 1]) {
+		int start_cyl, end_cyl;
+
+		start_cyl = starts[i] / N_HEAD / N_SECTOR;
+		end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR;
+		p->boot_ind = 0;
+
+		p->head = (i == 0 ? 1 : 0);
+		p->sector = 1 | ((start_cyl >> 8) << 6);
+		p->cyl = (start_cyl & 0xff);
+
+		p->end_head = N_HEAD - 1;
+		p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6);
+		p->end_cyl = (end_cyl & 0xff);
+
+		p->start_sect = starts[i];
+		p->nr_sects = starts[i + 1] - starts[i];
+		p->sys_ind = 0x83;	/* Linux ext2 partition */
+		p++;
+		i++;
+	}
+	if (!npart)
+		npart = i;
+	made_block0 = 1;
+	i = (bufflen > STORE_ELEM_SIZE) ? STORE_ELEM_SIZE : bufflen;
+	memcpy(store_arr[0].p, buff, i);
+}
 
 int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *))
 {
 	unchar *cmd = (unchar *) SCpnt->cmnd;
-	struct partition *p;
 	int block;
-	struct buffer_head *bh = NULL;
+	int upper_blk;
 	unsigned char *buff;
-	int nbytes, sgcount;
 	int scsi_debug_errsts;
-	struct scatterlist *sgpnt;
 	int target = SCpnt->target;
 	int bufflen = SCpnt->request_bufflen;
-	unsigned long flags;
-	int i;
-	sgcount = 0;
-	sgpnt = NULL;
+	unsigned long iflags;
+	int i, num, capac;
+	Sdebug_dev_info * devip = NULL;
+	char * sbuff;
 
 #ifdef CONFIG_SMP
         /*
          * The io_request_lock *must* be held at this point.
          */
-        if( io_request_lock.lock == 0 )
+        if(! spin_is_locked(&io_request_lock))
         {
-                printk("Warning - io_request_lock is not held in queuecommand\n");
+                printk("Warning - io_request_lock is not held in "
+		       "queuecommand\n");
         }
 #endif
 
 	/*
-	 * If we are being notified of the mid-level reposessing a command due to timeout,
-	 * just return.
+	 * If we are being notified of the mid-level reposessing a command
+	 * due to timeout, just return.
 	 */
 	if (done == NULL) {
 		return 0;
 	}
-	DEB(if (target >= NR_FAKE_DISKS) {
-	    SCpnt->result = DID_TIME_OUT << 16; done(SCpnt); return 0;
-	    }
-	);
 
 	buff = (unsigned char *) SCpnt->request_buffer;
 
@@ -230,249 +288,201 @@
          * If a command comes for the ID of the host itself, just print
          * a silly message and return.
          */
-        if( target == 7 ) {
+        if(target == 7) {
                 printk("How do you do!\n");
                 SCpnt->result = 0;
                 done(SCpnt);
                 return 0;
         }
 
-	if (target >= NR_FAKE_DISKS || SCpnt->lun != 0) {
+	if ((target > 7) || (SCpnt->lun != 0)) {
 		SCpnt->result = DID_NO_CONNECT << 16;
 		done(SCpnt);
 		return 0;
 	}
-	if (SCrst[target] != 0 && !scsi_debug_lockup) {
-		SCrst[target] = 0;
-		memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
-		SCpnt->sense_buffer[0] = 0x70;
-		SCpnt->sense_buffer[2] = UNIT_ATTENTION;
-		SCpnt->result = (CHECK_CONDITION << 1);
-		done(SCpnt);
+#if 0
+	printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n",
+	       (int)SCpnt->device->host->host_no, (int)SCpnt->device->id,
+	       SCpnt->device, (int)(unsigned char)*cmd);
+#endif
+	if (NULL == SCpnt->device->hostdata) {
+		devip = devInfoReg(SCpnt->device);
+		if (NULL == devip) {
+			SCpnt->result = DID_NO_CONNECT << 16;
+			done(SCpnt);
+			return 0;
+		}
+		SCpnt->device->hostdata = devip;
 	}
+	devip = SCpnt->device->hostdata;
+
 	switch (*cmd) {
-	case REQUEST_SENSE:
+	case REQUEST_SENSE:	/* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Request sense...\n"));
-#ifndef DEBUG
-		{
-			int i;
-			printk("scsi_debug: Requesting sense buffer (%p %p %p %d):", SCpnt, buff, done, bufflen);
-			for (i = 0; i < 12; i++)
-				printk("%d ", sense_buffer[i]);
-			printk("\n");
-		};
-#endif
-		memset(buff, 0, bufflen);
-		memcpy(buff, sense_buffer, bufflen);
-		memset(sense_buffer, 0, sizeof(sense_buffer));
+		if (devip) {
+			sbuff = &sense_buffers[(int)devip->sb_index][0];
+			devip->sb_index = 0; 
+		}
+		else
+		    sbuff = &sense_buffers[0][0]; 
+		memcpy(buff, sbuff, (bufflen < SENSE_BUFF_LEN) ? 
+				     bufflen : SENSE_BUFF_LEN);
+		memset(sbuff, 0, SENSE_BUFF_LEN);
+		sbuff[0] = 0x70;
 		SCpnt->result = 0;
 		done(SCpnt);
 		return 0;
 	case START_STOP:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		SCSI_LOG_LLQUEUE(3, printk("START_STOP\n"));
 		scsi_debug_errsts = 0;
 		break;
 	case ALLOW_MEDIUM_REMOVAL:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		if (cmd[4]) {
-			SCSI_LOG_LLQUEUE(2, printk("Medium removal inhibited..."));
+			SCSI_LOG_LLQUEUE(2, printk(
+					"Medium removal inhibited..."));
 		} else {
-			SCSI_LOG_LLQUEUE(2, printk("Medium removal enabled..."));
+			SCSI_LOG_LLQUEUE(2, 
+					printk("Medium removal enabled..."));
 		}
 		scsi_debug_errsts = 0;
 		break;
-	case INQUIRY:
+	case INQUIRY:     /* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen));
 		memset(buff, 0, bufflen);
 		buff[0] = DEVICE_TYPE(target);
-		buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0;	/* Removable disk */
-		buff[2] = 1;
-		buff[4] = 33 - 5;
-		memcpy(&buff[8], "Foo Inc", 7);
-		memcpy(&buff[16], "XYZZY", 5);
-		memcpy(&buff[32], "1", 1);
+		buff[1] = DISK_REMOVEABLE(target) ? 0x80 : 0;	
+				/* Removable disk */
+		buff[2] = 2;	/* claim SCSI 2 */
+		buff[4] = 36 - 5;
+		memcpy(&buff[8], "Linux   ", 8);
+		memcpy(&buff[16], "scsi_debug      ", 16);
+		memcpy(&buff[32], "0002", 4);
+		scsi_debug_errsts = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,10)
+		if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN)
+			SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN;
+#endif
+		break;
+	case SEND_DIAGNOSTIC:     /* mandatory */
+		SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n"));
+		if (buff)
+			memset(buff, 0, bufflen);
 		scsi_debug_errsts = 0;
 		break;
-	case TEST_UNIT_READY:
+	case TEST_UNIT_READY:     /* mandatory */
 		SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", buff, bufflen));
 		if (buff)
 			memset(buff, 0, bufflen);
 		scsi_debug_errsts = 0;
 		break;
 	case READ_CAPACITY:
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
 		SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n"));
                 SHpnt = SCpnt->host;
-		if (NR_REAL < 0)
-			NR_REAL = (MINOR(SCpnt->request.rq_dev) >> 4) & 0x0f;
 		memset(buff, 0, bufflen);
-		buff[0] = (CAPACITY >> 24);
-		buff[1] = (CAPACITY >> 16) & 0xff;
-		buff[2] = (CAPACITY >> 8) & 0xff;
-		buff[3] = CAPACITY & 0xff;
+		capac = CAPACITY - 1;
+		buff[0] = (capac >> 24);
+		buff[1] = (capac >> 16) & 0xff;
+		buff[2] = (capac >> 8) & 0xff;
+		buff[3] = capac & 0xff;
 		buff[4] = 0;
 		buff[5] = 0;
-		buff[6] = (SIZE(target) >> 8) & 0xff;	/* 512 byte sectors */
-		buff[7] = SIZE(target) & 0xff;
+		buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
+		buff[7] = SECT_SIZE_PER(target) & 0xff;
 
 		scsi_debug_errsts = 0;
 		break;
+	case SCSI_CMD_READ_16:	/* SBC-2 */
+	case READ_12:
 	case READ_10:
 	case READ_6:
-#ifdef DEBUG
-		printk("Read...");
-#endif
-		if ((*cmd) == READ_10)
-			block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
-		else
-			block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
-		VERIFY_DEBUG(READ);
-#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
-		{
-			int delay = SCSI_SETUP_LATENCY;
-
-			delay += SCpnt->request.nr_sectors * SCSI_DATARATE;
-			if (delay)
-				usleep(delay);
-		};
-#endif
-
-#ifdef DEBUG
-		printk("(r%d)", SCpnt->request.nr_sectors);
-#endif
-		nbytes = bufflen;
-		if (SCpnt->use_sg) {
-			sgcount = 0;
-			sgpnt = (struct scatterlist *) buff;
-			buff = sgpnt[sgcount].address;
-			bufflen = sgpnt[sgcount].length;
-			bh = SCpnt->request.bh;
-		};
-		scsi_debug_errsts = 0;
-		do {
-			VERIFY1_DEBUG(READ);
-			/* For the speedy test, we do not even want to fill the buffer with anything */
-#ifdef CLEAR
-			memset(buff, 0, bufflen);
-#endif
-			/* If this is block 0, then we want to read the partition table for this
-			 * device.  Let's make one up */
-			if (block == 0) {
-				int i;
-				memset(buff, 0, bufflen);
-				*((unsigned short *) (buff + 510)) = 0xAA55;
-				p = (struct partition *) (buff + 0x1be);
-				i = 0;
-				while (starts[i + 1]) {
-					int start_cyl, end_cyl;
-
-					start_cyl = starts[i] / N_HEAD / N_SECTOR;
-					end_cyl = (starts[i + 1] - 1) / N_HEAD / N_SECTOR;
-					p->boot_ind = 0;
-
-					p->head = (i == 0 ? 1 : 0);
-					p->sector = 1 | ((start_cyl >> 8) << 6);
-					p->cyl = (start_cyl & 0xff);
-
-					p->end_head = N_HEAD - 1;
-					p->end_sector = N_SECTOR | ((end_cyl >> 8) << 6);
-					p->end_cyl = (end_cyl & 0xff);
-
-					p->start_sect = starts[i];
-					p->nr_sects = starts[i + 1] - starts[i];
-					p->sys_ind = 0x81;	/* Linux partition */
-					p++;
-					i++;
-				};
-				if (!npart)
-					npart = i;
-				scsi_debug_errsts = 0;
-				break;
-			};
-#ifdef DEBUG
-			if (SCpnt->use_sg)
-				printk("Block %x (%d %d)\n", block, SCpnt->request.nr_sectors,
-				       SCpnt->request.current_nr_sectors);
-#endif
-
-#if 0
-			/* Simulate a disk change */
-			if (block == 0xfff0) {
-				sense_buffer[0] = 0x70;
-				sense_buffer[2] = UNIT_ATTENTION;
-				starts[0] += 10;
-				starts[1] += 10;
-				starts[2] += 10;
-
-#ifdef DEBUG
-				{
-					int i;
-					printk("scsi_debug: Filling sense buffer:");
-					for (i = 0; i < 12; i++)
-						printk("%d ", sense_buffer[i]);
-					printk("\n");
-				};
-#endif
-				scsi_debug_errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
-				break;
-			}	/* End phony disk change code */
-#endif
-
-#ifdef CLEAR
-			memcpy(buff, &target, sizeof(target));
-			memcpy(buff + sizeof(target), cmd, 24);
-			memcpy(buff + 60, &block, sizeof(block));
-			memcpy(buff + 64, SCpnt, sizeof(Scsi_Cmnd));
-#endif
-			nbytes -= bufflen;
-			if (SCpnt->use_sg) {
-#ifdef CLEAR
-				memcpy(buff + 128, bh, sizeof(struct buffer_head));
-#endif
-				block += bufflen >> 9;
-				bh = bh->b_reqnext;
-				sgcount++;
-				if (nbytes) {
-					if (!bh)
-						panic("Too few blocks for linked request.");
-					buff = sgpnt[sgcount].address;
-					bufflen = sgpnt[sgcount].length;
-				};
-			}
-		} while (nbytes);
-
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		upper_blk = 0;
+		if ((*cmd) == SCSI_CMD_READ_16) {
+			upper_blk = cmd[5] + (cmd[4] << 8) + 
+				    (cmd[3] << 16) + (cmd[2] << 24);
+			block = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+			num = cmd[13] + (cmd[12] << 8) + 
+				(cmd[11] << 16) + (cmd[10] << 24);
+		}
+		else if ((*cmd) == READ_12) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+		}
+		else if ((*cmd) == READ_10) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[8] + (cmd[7] << 8);
+		}
+		else {
+			block = cmd[3] + (cmd[2] << 8) + 
+				((cmd[1] & 0x1f) << 16);
+			num = cmd[4];
+		}
+		if (scsi_debug_read(SCpnt, upper_blk, block, num, 
+				    &scsi_debug_errsts, devip))
+			break;
 		SCpnt->result = 0;
-		(done) (SCpnt);
+/* calls bottom half in upper layers before return from scsi_do_...() */
+		(done) (SCpnt);	
 		return 0;
-
-		if (SCpnt->use_sg && !scsi_debug_errsts)
-			if (bh)
-				scsi_dump(SCpnt, 0);
-		break;
+	case SCSI_CMD_WRITE_16:	/* SBC-2 */
+	case WRITE_12:
 	case WRITE_10:
 	case WRITE_6:
-#ifdef DEBUG
-		printk("Write\n");
-#endif
-		if ((*cmd) == WRITE_10)
-			block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
-		else
-			block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
-		VERIFY_DEBUG(WRITE);
-		/*      printk("(w%d)",SCpnt->request.nr_sectors); */
-		if (SCpnt->use_sg) {
-			if ((bufflen >> 9) != SCpnt->request.nr_sectors)
-				panic("Trying to write wrong number of blocks\n");
-			sgpnt = (struct scatterlist *) buff;
-			buff = sgpnt[sgcount].address;
-		};
-#if 0
-		if (block != *((unsigned long *) (buff + 60))) {
-			printk("%x %x :", block, *((unsigned long *) (buff + 60)));
-			scsi_dump(SCpnt, 1);
-			panic("Bad block written.\n");
-		};
-#endif
-		scsi_debug_errsts = 0;
-		break;
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		upper_blk = 0;
+		if ((*cmd) == SCSI_CMD_WRITE_16) {
+			upper_blk = cmd[5] + (cmd[4] << 8) + 
+				    (cmd[3] << 16) + (cmd[2] << 24);
+			block = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+			num = cmd[13] + (cmd[12] << 8) + 
+				(cmd[11] << 16) + (cmd[10] << 24);
+		}
+		else if ((*cmd) == WRITE_12) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[9] + (cmd[8] << 8) + 
+				(cmd[7] << 16) + (cmd[6] << 24);
+		}
+		else if ((*cmd) == WRITE_10) {
+			block = cmd[5] + (cmd[4] << 8) + 
+				(cmd[3] << 16) + (cmd[2] << 24);
+			num = cmd[8] + (cmd[7] << 8);
+		}
+		else {
+			block = cmd[3] + (cmd[2] << 8) + 
+				((cmd[1] & 0x1f) << 16);
+			num = cmd[4];
+		}
+		if (scsi_debug_write(SCpnt, upper_blk, block, num, 
+				     &scsi_debug_errsts, devip))
+			break;
+		SCpnt->result = 0;
+/* calls bottom half in upper layers before return from scsi_do_...() */
+		(done) (SCpnt);	
+		return 0;
 	case MODE_SENSE:
 		/*
 		 * Used to detect write protected status.
@@ -481,26 +491,36 @@
 		memset(buff, 0, 6);
 		break;
 	default:
+#if 0
 		SCSI_LOG_LLQUEUE(3, printk("Unknown command %d\n", *cmd));
 		SCpnt->result = DID_NO_CONNECT << 16;
 		done(SCpnt);
 		return 0;
-	};
+#else
+		if (check_reset(SCpnt, devip)) {
+			done(SCpnt);
+			return 0;
+		}
+		scsi_debug_errsts = (COMMAND_COMPLETE << 8) |
+				    (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14);
+		break;
+#endif
+	}
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&mailbox_lock, iflags);
 	for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
 		if (timeout[i].function == NULL)
 			break;
-	};
+	}
 
 	/*
-	 * If all of the slots are full, just return 1.  The new error handling scheme
-	 * allows this, and the mid-level should queue things.
+	 * If all of the slots are full, just return 1.  The new error 
+	 * handling scheme allows this, and the mid-level should queue things.
 	 */
 	if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) {
 		SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n"));
-		restore_flags(flags);
+		spin_unlock_irqrestore(&mailbox_lock, iflags);
 		return 1;
 	}
 	SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i));
@@ -512,7 +532,7 @@
 		do_done[i] = done;
 		scsi_debug_intr_handle(i);	/* No timer - do this one right away */
 	}
-	restore_flags(flags);
+	spin_unlock_irqrestore(&mailbox_lock, iflags);
 #else
 
 	SCpnt->result = scsi_debug_errsts;
@@ -522,19 +542,190 @@
 	SCint[i] = SCpnt;
 	do_done[i] = done;
 
-	restore_flags(flags);
+	spin_unlock_irqrestore(&mailbox_lock, iflags);
 	add_timer(&timeout[i]);
 	if (!done)
-		panic("scsi_debug_queuecommand: done can't be NULL\n");
+		printk("scsi_debug_queuecommand: done can't be NULL\n");
 
 #if 0
-	printk("Sending command (%d %x %d %d)...", i, done, timeout[i].expires, jiffies);
+	printk("Sending command (%d %x %d %d)...", i, done, 
+	       timeout[i].expires, jiffies);
 #endif
 #endif
 
 	return 0;
 }
 
+static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip)
+{
+	if (devip->reset) {
+		devip->reset = 0;
+		mk_sense_buffer(devip, 3, UNIT_ATTENTION, 0x29, 0, 14);
+		SCpnt->result = (COMMAND_COMPLETE << 8) | 
+				(CHECK_CONDITION << 1);
+                return 1;
+	}
+	return 0;
+}
+
+static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			   int num, int * errstsp, Sdebug_dev_info * devip)
+{
+        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
+        int nbytes, sgcount;
+        struct scatterlist *sgpnt = NULL;
+        int bufflen = SCpnt->request_bufflen;
+	unsigned long iflags;
+
+	if (upper_blk || (block + num > CAPACITY)) {
+		*errstsp = (COMMAND_COMPLETE << 8) |
+			   (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
+		return 1;
+	}
+#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
+	{
+		int delay = SCSI_SETUP_LATENCY;
+
+		delay += SCpnt->request.nr_sectors * SCSI_DATARATE;
+		if (delay)
+			usleep(delay);
+	}
+#endif
+
+	read_lock_irqsave(&sdebug_atomic_rw, iflags);
+        sgcount = 0;
+	nbytes = bufflen;
+	/* printk("scsi_debug_read: block=%d, tot_bufflen=%d\n", 
+	       block, bufflen); */
+	if (SCpnt->use_sg) {
+		sgcount = 0;
+		sgpnt = (struct scatterlist *) buff;
+		buff = sgpnt[sgcount].address;
+		bufflen = sgpnt[sgcount].length;
+	}
+	*errstsp = 0;
+	do {
+		int resid, k, off, len, rem, blk;
+		unsigned char * bp;
+
+		/* If this is block 0, then we want to read the partition
+		 * table for this device.  Let's make one up */
+		if (scsi_debug_fake_blk0 && (block == 0) && (! made_block0)) {
+			scsi_debug_mkblock0(buff, bufflen, SCpnt);
+			*errstsp = 0;
+			break;
+		}
+		bp = buff;
+		blk = block;
+		for (resid = bufflen; resid > 0; resid -= len) {
+			k = blk / SECT_PER_ELEM; 
+			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
+			rem = STORE_ELEM_SIZE - off;
+			len = (resid > rem) ? rem : resid;
+/* printk("sdr: blk=%d k=%d off=%d rem=%d resid"
+			       "=%d len=%d  sgcount=%d\n", blk, k, 
+				off, rem, resid, len, sgcount); */
+			memcpy(bp, store_arr[k].p + off, len);
+			bp += len;
+			blk += len / SECT_SIZE;
+		}
+#if 0
+		/* Simulate a disk change */
+		if (block == 0xfff0) {
+			sense_buffer[0] = 0x70;
+			sense_buffer[2] = UNIT_ATTENTION;
+			starts[0] += 10;
+			starts[1] += 10;
+			starts[2] += 10;
+
+			*errstsp = (COMMAND_COMPLETE << 8) | 
+				   (CHECK_CONDITION << 1);
+			read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+			return 1;
+		}	/* End phony disk change code */
+#endif
+		nbytes -= bufflen;
+		if (SCpnt->use_sg) {
+			block += bufflen >> POW2_SECT_SIZE;
+			sgcount++;
+			if (nbytes) {
+				buff = sgpnt[sgcount].address;
+				bufflen = sgpnt[sgcount].length;
+			}
+		}
+		else if (nbytes > 0)
+			printk("sdebug_read: unexpected nbytes=%d\n", nbytes);
+	} while (nbytes);
+	read_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+	return 0;
+}
+
+static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, 
+			    int num, int * errstsp, Sdebug_dev_info * devip)
+{
+        unsigned char *buff = (unsigned char *) SCpnt->request_buffer;
+        int nbytes, sgcount;
+        struct scatterlist *sgpnt = NULL;
+        int bufflen = SCpnt->request_bufflen;
+	unsigned long iflags;
+
+	if (upper_blk || (block + num > CAPACITY)) {
+		*errstsp = (COMMAND_COMPLETE << 8) |
+			   (CHECK_CONDITION << 1);
+		mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14);
+		return 1;
+	}
+
+	write_lock_irqsave(&sdebug_atomic_rw, iflags);
+        sgcount = 0;
+#ifdef SCSI_DUMP
+	if (block != *((unsigned long *) (buff + 60))) {
+		printk("%x %x :", block, *((unsigned long *) (buff + 60)));
+		scsi_dump(SCpnt, 1);
+		printk("Bad block written.\n");
+	}
+#endif
+	nbytes = bufflen;
+	if (SCpnt->use_sg) {
+		sgcount = 0;
+		sgpnt = (struct scatterlist *) buff;
+		buff = sgpnt[sgcount].address;
+		bufflen = sgpnt[sgcount].length;
+	}
+	*errstsp = 0;
+	do {
+		int resid, k, off, len, rem, blk;
+		unsigned char * bp;
+
+		bp = buff;
+		blk = block;
+		for (resid = bufflen; resid > 0; resid -= len) {
+			k = blk / SECT_PER_ELEM; 
+			off = (blk % SECT_PER_ELEM) * SECT_SIZE;
+			rem = STORE_ELEM_SIZE - off;
+			len = (resid > rem) ? rem : resid;
+			memcpy(store_arr[k].p + off, bp, len);
+			bp += len;
+			blk += len / SECT_SIZE;
+		}
+
+		nbytes -= bufflen;
+		if (SCpnt->use_sg) {
+			block += bufflen >> POW2_SECT_SIZE;
+			sgcount++;
+			if (nbytes) {
+				buff = sgpnt[sgcount].address;
+				bufflen = sgpnt[sgcount].length;
+			}
+		}
+		else if (nbytes > 0)
+			printk("sdebug_write: unexpected nbytes=%d\n", nbytes);
+	} while (nbytes);
+	write_unlock_irqrestore(&sdebug_atomic_rw, iflags);
+	return 0;
+}
+
 static void scsi_debug_send_self_command(struct Scsi_Host * shpnt)
 {
 	static unsigned char cmd[6] =
@@ -582,10 +773,6 @@
 {
 	Scsi_Cmnd *SCtmp;
 	void (*my_done) (Scsi_Cmnd *);
-#ifdef DEBUG
-	int to;
-#endif
-
 #if 0
 	del_timer(&timeout[indx]);
 #endif
@@ -600,59 +787,213 @@
 		printk("scsi_debug_intr_handle: Unexpected interrupt\n");
 		return;
 	}
-#ifdef DEBUG
+#if 0
 	printk("In intr_handle...");
 	printk("...done %d %x %d %d\n", i, my_done, to, jiffies);
 	printk("In intr_handle: %d %x %x\n", i, SCtmp, my_done);
 #endif
 
 	my_done(SCtmp);
-#ifdef DEBUG
+#if 0
 	printk("Called done.\n");
 #endif
 }
 
+static int initialized = 0;
+
+static int do_init(void)
+{
+	int sz;
+
+	starts[3] = CAPACITY;
+	sz = sizeof(Sd_store_elem) * STORE_ELEMENTS;
+	store_arr = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == store_arr)
+		return 1;
+	memset(store_arr, 0, sz);
+	sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES;
+	do_done = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == do_done) {
+		kfree(store_arr);
+		return 1;
+	}
+	memset((void *)do_done, 0, sz);
+	sz = sizeof(struct timer_list) * SCSI_DEBUG_MAILBOXES;
+	timeout = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == timeout) {
+		kfree((void *)do_done);
+		kfree(store_arr);
+		return 1;
+	}
+	memset(timeout, 0, sz);
+	sz = sizeof(Scsi_Cmnd *) * SCSI_DEBUG_MAILBOXES;
+	SCint = kmalloc(sz, GFP_ATOMIC);
+	if (NULL == SCint) {
+		kfree(timeout);
+		kfree((void *)do_done);
+		kfree(store_arr);
+		return 1;
+	}
+	memset(SCint, 0, sz);
+	return 0;
+}
+
+static void do_end(void)
+{
+	kfree(SCint);
+	kfree(timeout);
+	kfree((void *)do_done);
+	kfree(store_arr);
+}
+
 
 int scsi_debug_detect(Scsi_Host_Template * tpnt)
 {
-	int i;
+	int k, num, sz;
+
+	if (0 == initialized) {
+		++initialized;
+		sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs;
+		devInfop = kmalloc(sz, GFP_ATOMIC);
+		if (NULL == devInfop) {
+			printk("scsi_debug_detect: out of memory, 0.5\n");
+			return 0;
+		}
+		memset(devInfop, 0, sz);
+		if (do_init()) {
+			printk("scsi_debug_detect: out of memory, 0\n");
+			return 0;
+		}
+		for (k = 0; k < STORE_ELEMENTS; ++k) {
+			store_arr[k].p = (unsigned char *)
+				__get_free_pages(GFP_ATOMIC, STORE_ELEM_ORDER);
+			if (0 == store_arr[k].p)
+				goto detect_err;
+			memset(store_arr[k].p, 0, STORE_ELEM_SIZE);
+		}
+		for (k = 0; k < NUM_SENSE_BUFFS; ++k)
+			sense_buffers[k][0] = 0x70;
+		for (k = 0; k < NR_HOSTS_PRESENT; k++) {
+			tpnt->proc_name = "scsi_debug";	/* In the loop??? */
+			scsi_register(tpnt, 0);
+		}
+		return NR_HOSTS_PRESENT;
+	}
+	else {
+		printk("scsi_debug_detect: called again\n");
+		return 0;
+	}
 
-	for (i = 0; i < NR_HOSTS_PRESENT; i++) {
-		tpnt->proc_name = "scsi_debug";	/* Huh? In the loop??? */
-		scsi_register(tpnt, 0);
+detect_err:
+	num = k;
+	for (k = 0; k < STORE_ELEMENTS; ++k) {
+		if (0 != store_arr[k].p) {
+			free_pages((unsigned long)store_arr[k].p, 
+				   STORE_ELEM_ORDER);
+			store_arr[k].p = NULL;
+		}
 	}
-	return NR_HOSTS_PRESENT;
+	printk("scsi_debug_detect: out of memory: %d out of %d bytes\n",
+	       (int)(num * STORE_ELEM_SIZE), 
+	       (int)(scsi_debug_dev_size_mb * 1024 * 1024));
+	return 0;
+}
+
+
+static int num_releases = 0;
+
+int scsi_debug_release(struct Scsi_Host * hpnt)
+{
+	int k;
+	
+	scsi_unregister(hpnt);
+	if (++num_releases != NR_HOSTS_PRESENT)
+		return 0;
+
+	for (k = 0; k < STORE_ELEMENTS; ++k) {
+		if (0 != store_arr[k].p) {
+			free_pages((unsigned long)store_arr[k].p, 
+				   STORE_ELEM_ORDER);
+			store_arr[k].p = NULL;
+		}
+	}
+	do_end();
+	kfree(devInfop);
+	return 0;
+}
+
+static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp)
+{
+	int k;
+	unsigned short host_no, id;
+	Sdebug_dev_info * devip;
+
+	host_no = sdp->host->host_no;
+	id = (unsigned short)sdp->id;
+	for (k = 0; k < scsi_debug_num_devs; ++k) {
+		devip = &devInfop[k];
+		if (devip->sdp && (host_no == devip->host_no) &&
+		    (id == devip->id)) {
+			devip->sdp = sdp; /* overwrite previous sdp */
+			return devip;
+		}
+		if (NULL == devip->sdp) {
+			devip->sdp = sdp;
+			devip->host_no = host_no;
+			devip->id = id;
+			devip->reset = 1;
+			devip->sb_index = 0;
+			return devip;
+		}
+	}
+	return NULL;
+}
+
+static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, 
+			    int asc, int asq, int inbandLen)
+{
+	char * sbuff;
+	if ((index < 0) || (index >= NUM_SENSE_BUFFS))
+		return;
+	if (devip)
+		devip->sb_index = index;
+	sbuff = &sense_buffers[index][0];
+	memset(sbuff, 0, SENSE_BUFF_LEN);
+	sbuff[0] = 0x70;
+	sbuff[2] = key;
+	sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0;
+	sbuff[12] = asc;
+	sbuff[13] = asq;
 }
 
 int scsi_debug_abort(Scsi_Cmnd * SCpnt)
 {
-#if 0
+#if 1
+	++num_aborts;
+	return SUCCESS;
+#else
 	int j;
 	void (*my_done) (Scsi_Cmnd *);
-	unsigned long flags;
-#endif
-
-	DEB(printk("scsi_debug_abort\n"));
-#if 0
+	unsigned long iflags;
 	SCpnt->result = SCpnt->abort_reason << 16;
 	for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) {
 		if (SCpnt == SCint[j]) {
 			my_done = do_done[j];
 			my_done(SCpnt);
-			save_flags(flags);
-			cli();
+			spin_lock_irqsave(&mailbox_lock, iflags);
 			timeout[j] = 0;
 			SCint[j] = NULL;
 			do_done[j] = NULL;
-			restore_flags(flags);
-		};
-	};
-#endif
+			spin_unlock_irqrestore(&mailbox_lock, iflags);
+		}
+	}
 	return SCSI_ABORT_SNOOZE;
+#endif
 }
 
 int scsi_debug_biosparam(Disk * disk, kdev_t dev, int *info)
 {
+	/* int size = disk->capacity; */
 	info[0] = N_HEAD;
 	info[1] = N_SECTOR;
 	info[2] = N_CYLINDER;
@@ -661,35 +1002,130 @@
 	return 0;
 }
 
+#if 0
 int scsi_debug_reset(Scsi_Cmnd * SCpnt, unsigned int why)
 {
 	int i;
-	unsigned long flags;
+	unsigned long iflags;
 
 	void (*my_done) (Scsi_Cmnd *);
 	printk("Bus unlocked by reset - %d\n", why);
 	scsi_debug_lockup = 0;
-	DEB(printk("scsi_debug_reset called\n"));
 	for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) {
 		if (SCint[i] == NULL)
 			continue;
 		SCint[i]->result = DID_RESET << 16;
 		my_done = do_done[i];
 		my_done(SCint[i]);
-		save_flags(flags);
-		cli();
+		spin_lock_irqsave(&mailbox_lock, iflags);
 		SCint[i] = NULL;
 		do_done[i] = NULL;
 		timeout[i].function = NULL;
-		restore_flags(flags);
+		spin_unlock_irqrestore(&mailbox_lock, iflags);
 	}
 	return SCSI_RESET_SUCCESS;
 }
+#endif
+
+int scsi_debug_device_reset(Scsi_Cmnd * SCpnt)
+{
+	Scsi_Device * sdp;
+	int k;
+
+	++num_dev_resets;
+	if (SCpnt && ((sdp = SCpnt->device))) {
+		for (k = 0; k < scsi_debug_num_devs; ++k) {
+			if (sdp->hostdata == (devInfop + k))
+				break;
+		}
+		if (k < scsi_debug_num_devs)
+			devInfop[k].reset = 1;
+	}
+	return SUCCESS;
+}
 
-const char *scsi_debug_info(void)
+int scsi_debug_bus_reset(Scsi_Cmnd * SCpnt)
 {
-	static char buffer[] = " ";	/* looks nicer without anything here */
-	return buffer;
+	Scsi_Device * sdp;
+	struct Scsi_Host * hp;
+	int k;
+
+	++num_bus_resets;
+	if (SCpnt && ((sdp = SCpnt->device)) && ((hp = sdp->host))) {
+		for (k = 0; k < scsi_debug_num_devs; ++k) {
+			if (hp == devInfop[k].sdp->host)
+				devInfop[k].reset = 1;
+		}
+	}
+	return SUCCESS;
+}
+
+int scsi_debug_host_reset(Scsi_Cmnd * SCpnt)
+{
+	int k;
+
+	++num_host_resets;
+	for (k = 0; k < scsi_debug_num_devs; ++k)
+		devInfop[k].reset = 1;
+
+	return SUCCESS;
+}
+
+#ifndef MODULE
+static int __init scsi_debug_num_devs_setup(char *str)
+{   
+    int tmp; 
+    
+    if (get_option(&str, &tmp) == 1) {
+        if (tmp > 0)
+            scsi_debug_num_devs = tmp;
+        return 1;
+    } else {
+        printk("scsi_debug_num_devs: usage scsi_debug_num_devs=<n> "
+               "(<n> can be from 1 to around 2000)\n");
+        return 0;
+    }
+}
+
+__setup("scsi_debug_num_devs=", scsi_debug_num_devs_setup);
+
+static int __init scsi_debug_dev_size_mb_setup(char *str)
+{   
+    int tmp; 
+    
+    if (get_option(&str, &tmp) == 1) {
+        if (tmp > 0)
+            scsi_debug_dev_size_mb = tmp;
+        return 1;
+    } else {
+        printk("scsi_debug_dev_size_mb: usage scsi_debug_dev_size_mb=<n>\n"
+               "    (<n> is number of MB ram shared by all devs\n");
+        return 0;
+    }
+}
+
+__setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup);
+#endif
+
+MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
+MODULE_DESCRIPTION("SCSI debug adapter driver");
+MODULE_PARM(scsi_debug_num_devs, "i");
+MODULE_PARM_DESC(scsi_debug_num_devs, "number of SCSI devices to simulate");
+MODULE_PARM(scsi_debug_dev_size_mb, "i");
+MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs");
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+
+static char sdebug_info[256];
+
+const char * scsi_debug_info(struct Scsi_Host * shp)
+{
+	sprintf(sdebug_info, "scsi_debug, %s, num_devs=%d, "
+		"dev_size_mb=%d\n", scsi_debug_version_str,
+		scsi_debug_num_devs, scsi_debug_dev_size_mb);
+	return sdebug_info;
 }
 
 /* scsi_debug_proc_info
@@ -739,10 +1175,20 @@
 
 	}
 	begin = 0;
-	pos = len = sprintf(buffer,
-	"This driver is not a real scsi driver, but it plays one on TV.\n"
+	pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n"
+	    "num_devs=%d, shared (ram) size=%d MB, sector_size=%d bytes\n" 
+	    "cylinders=%d, heads=%d, sectors=%d\n"
+	    "number of aborts=%d, device_reset=%d, bus_resets=%d, " 
+	    "host_resets=%d\n",
+	    scsi_debug_version_str, scsi_debug_num_devs, 
+	    scsi_debug_dev_size_mb, SECT_SIZE,
+	    N_CYLINDER, N_HEAD, N_SECTOR,
+	    num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
+#if 0
+	 "This driver is not a real scsi driver, but it plays one on TV.\n"
 	 "It is very handy for debugging specific problems because you\n"
 			 "can simulate a variety of error conditions\n");
+#endif
 	if (pos < offset) {
 		len = 0;
 		begin = pos;
@@ -755,44 +1201,8 @@
 	return (len);
 }
 
-#ifdef CONFIG_USER_DEBUG
-/*
- * This is a hack for the user space emulator.  It allows us to
- * "insert" arbitrary numbers of additional drivers.
- */
-void *scsi_debug_get_handle(void)
-{
-	static Scsi_Host_Template driver_copy = SCSI_DEBUG;
-	void *rtn;
-	rtn = kmalloc(sizeof(driver_copy), GFP_ATOMIC);
-	if(rtn==NULL)
-		return NULL;
-	memcpy(rtn, (void *) &driver_copy, sizeof(driver_copy));
-	return rtn;
-}
-#endif
-
 /* Eventually this will go into an include file, but this will be later */
-static Scsi_Host_Template driver_template = SCSI_DEBUG;
+static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE;
 
 #include "scsi_module.c"
 
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
-MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_debug.h linux/drivers/scsi/scsi_debug.h
--- linux.orig/drivers/scsi/scsi_debug.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/scsi_debug.h	Wed Dec 26 15:53:59 2001
@@ -8,35 +8,43 @@
 int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
 int scsi_debug_abort(Scsi_Cmnd *);
 int scsi_debug_biosparam(Disk *, kdev_t, int[]);
-int scsi_debug_reset(Scsi_Cmnd *, unsigned int);
+int scsi_debug_bus_reset(Scsi_Cmnd *);
+int scsi_debug_dev_reset(Scsi_Cmnd *);
+int scsi_debug_host_reset(Scsi_Cmnd *);
 int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+const char * scsi_debug_info(struct Scsi_Host *);
 
 #ifndef NULL
 #define NULL 0
 #endif
 
-
-#define SCSI_DEBUG_MAILBOXES 1
-
 /*
  * Allow the driver to reject commands.  Thus we accept only one, but
  * and the mid-level will queue the remainder.
  */
 #define SCSI_DEBUG_CANQUEUE  255
 
-#define SCSI_DEBUG {proc_info:         scsi_debug_proc_info,	\
+#define SCSI_DEBUG_MAX_CMD_LEN 16
+
+#define SCSI_DEBUG_TEMPLATE \
+		   {proc_info:         scsi_debug_proc_info,	\
 		    name:              "SCSI DEBUG",		\
+		    info:              scsi_debug_info,		\
 		    detect:            scsi_debug_detect,	\
+		    release:           scsi_debug_release,	\
 		    queuecommand:      scsi_debug_queuecommand, \
-		    abort:             scsi_debug_abort,	\
-		    reset:             scsi_debug_reset,	\
+		    eh_abort_handler:  scsi_debug_abort,	\
+		    eh_bus_reset_handler: scsi_debug_bus_reset,	\
+		    eh_device_reset_handler: scsi_debug_device_reset,	\
+		    eh_host_reset_handler: scsi_debug_host_reset,	\
 		    bios_param:        scsi_debug_biosparam,	\
 		    can_queue:         SCSI_DEBUG_CANQUEUE,	\
 		    this_id:           7,			\
-		    sg_tablesize:      16,			\
+		    sg_tablesize:      64,			\
 		    cmd_per_lun:       3,			\
 		    unchecked_isa_dma: 0,			\
 		    use_clustering:    ENABLE_CLUSTERING,	\
 		    use_new_eh_code:   1,			\
 }
+
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_dma.c linux/drivers/scsi/scsi_dma.c
--- linux.orig/drivers/scsi/scsi_dma.c	Tue Sep  5 21:08:55 2000
+++ linux/drivers/scsi/scsi_dma.c	Mon Feb  4 19:11:16 2002
@@ -259,6 +259,7 @@
 				if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
 					new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
 			} else if (SDpnt->type == TYPE_SCANNER ||
+				   SDpnt->type == TYPE_PRINTER ||
 				   SDpnt->type == TYPE_PROCESSOR ||
 				   SDpnt->type == TYPE_COMM ||
 				   SDpnt->type == TYPE_MEDIUM_CHANGER ||
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_merge.c linux/drivers/scsi/scsi_merge.c
--- linux.orig/drivers/scsi/scsi_merge.c	Thu Oct 25 21:05:31 2001
+++ linux/drivers/scsi/scsi_merge.c	Thu Feb  7 16:05:49 2002
@@ -150,14 +150,7 @@
 	panic("DMA pool exhausted");
 }
 
-/*
- * FIXME(eric) - the original disk code disabled clustering for MOD
- * devices.  I have no idea why we thought this was a good idea - my
- * guess is that it was an attempt to limit the size of requests to MOD
- * devices.
- */
-#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering && \
-				   SD->type != TYPE_MOD)
+#define CLUSTERABLE_DEVICE(SH,SD) (SH->use_clustering)
 
 /*
  * This entire source file deals with the new queueing code.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/scsi_scan.c linux/drivers/scsi/scsi_scan.c
--- linux.orig/drivers/scsi/scsi_scan.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/scsi/scsi_scan.c	Mon Feb  4 19:24:45 2002
@@ -163,6 +163,7 @@
 	{"HP", "NetRAID-4M", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN},
 	{"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN},
+	{"COMPAQ", "MSA1000", "*", BLIST_FORCELUN},
 
 	/*
 	 * Must be at end of list...
@@ -634,6 +635,7 @@
 	switch (type = (scsi_result[0] & 0x1f)) {
 	case TYPE_TAPE:
 	case TYPE_DISK:
+	case TYPE_PRINTER:
 	case TYPE_MOD:
 	case TYPE_PROCESSOR:
 	case TYPE_SCANNER:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- linux.orig/drivers/scsi/sd.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/sd.c	Tue Jan  8 16:43:55 2002
@@ -1078,6 +1078,7 @@
 		for (i = 0; i < N_USED_SD_MAJORS; i++) {
 			if (devfs_register_blkdev(SD_MAJOR(i), "sd", &sd_fops)) {
 				printk("Unable to get major %d for SCSI disk\n", SD_MAJOR(i));
+				sd_template.dev_noticed = 0;
 				return 1;
 			}
 		}
@@ -1175,7 +1176,8 @@
 		kfree(sd_gendisks[i].de_arr);
 		kfree(sd_gendisks[i].flags);
 	}
-	kfree(sd_gendisks);
+	if (sd_gendisks != &sd_gendisk)
+		kfree(sd_gendisks);
 cleanup_sd_gendisks:
 	kfree(sd);
 cleanup_sd:
@@ -1188,11 +1190,13 @@
 	kfree(sd_sizes);
 cleanup_disks:
 	kfree(rscsi_disks);
+	rscsi_disks = NULL;
 cleanup_devfs:
 	for (i = 0; i < N_USED_SD_MAJORS; i++) {
 		devfs_unregister_blkdev(SD_MAJOR(i), "sd");
 	}
 	sd_registered--;
+	sd_template.dev_noticed = 0;
 	return 1;
 }
 
@@ -1251,7 +1255,7 @@
 	if (SDp->type != TYPE_DISK && SDp->type != TYPE_MOD)
 		return 0;
 
-	if (sd_template.nr_dev >= sd_template.dev_max) {
+	if (sd_template.nr_dev >= sd_template.dev_max || rscsi_disks == NULL) {
 		SDp->attached--;
 		return 1;
 	}
@@ -1259,8 +1263,13 @@
 		if (!dpnt->device)
 			break;
 
-	if (i >= sd_template.dev_max)
-		panic("scsi_devices corrupt (sd)");
+	if (i >= sd_template.dev_max) {
+		printk(KERN_WARNING "scsi_devices corrupt (sd),"
+		    " nr_dev %d dev_max %d\n",
+		    sd_template.nr_dev, sd_template.dev_max);
+		SDp->attached--;
+		return 1;
+	}
 
 	rscsi_disks[i].device = SDp;
 	rscsi_disks[i].has_part_table = 0;
@@ -1347,6 +1356,9 @@
 	int i, j;
 	int max_p;
 	int start;
+
+	if (rscsi_disks == NULL)
+		return;
 
 	for (dpnt = rscsi_disks, i = 0; i < sd_template.dev_max; i++, dpnt++)
 		if (dpnt->device == SDp) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/sg.c linux/drivers/scsi/sg.c
--- linux.orig/drivers/scsi/sg.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/sg.c	Tue Jan  8 16:43:55 2002
@@ -1344,6 +1344,7 @@
             printk(KERN_ERR "Unable to get major %d for generic SCSI device\n",
                    SCSI_GENERIC_MAJOR);
 	    write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+            sg_template.dev_noticed = 0;
             return 1;
         }
         sg_registered++;
@@ -1356,6 +1357,7 @@
     if (NULL == sg_dev_arr) {
         printk(KERN_ERR "sg_init: no space for sg_dev_arr\n");
 	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+        sg_template.dev_noticed = 0;
         return 1;
     }
     memset(sg_dev_arr, 0, sg_template.dev_max * sizeof(Sg_device *));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/sr.c linux/drivers/scsi/sr.c
--- linux.orig/drivers/scsi/sr.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/sr.c	Tue Jan  8 16:43:55 2002
@@ -789,6 +789,7 @@
 	if (!sr_registered) {
 		if (devfs_register_blkdev(MAJOR_NR, "sr", &sr_bdops)) {
 			printk("Unable to get major %d for SCSI-CD\n", MAJOR_NR);
+			sr_template.dev_noticed = 0;
 			return 1;
 		}
 		sr_registered++;
@@ -830,8 +831,10 @@
 	kfree(sr_sizes);
 cleanup_cds:
 	kfree(scsi_CDs);
+	scsi_CDs = NULL;
 cleanup_devfs:
 	devfs_unregister_blkdev(MAJOR_NR, "sr");
+	sr_template.dev_noticed = 0;
 	sr_registered--;
 	return 1;
 }
@@ -905,6 +908,8 @@
 	Scsi_CD *cpnt;
 	int i;
 
+	if (scsi_CDs == NULL)
+		return;
 	for (cpnt = scsi_CDs, i = 0; i < sr_template.dev_max; i++, cpnt++)
 		if (cpnt->device == SDp) {
 			/*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/st.c linux/drivers/scsi/st.c
--- linux.orig/drivers/scsi/st.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/st.c	Tue Feb  5 19:40:49 2002
@@ -9,10 +9,10 @@
    Steve Hirsch, Andreas Koppenh"ofer, Michael Leodolter, Eyal Lebedinsky,
    Michael Schaefer, J"org Weule, and Eric Youngdale.
 
-   Copyright 1992 - 2001 Kai Makisara
+   Copyright 1992 - 2002 Kai Makisara
    email Kai.Makisara@metla.fi
 
-   Last modified: Sat Nov  3 19:30:55 2001 by makisara@kai.makisara.local
+   Last modified: Tue Feb  5 21:25:55 2002 by makisara
    Some small formal changes - aeb, 950809
 
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
@@ -21,7 +21,7 @@
    error handling will be discarded.
  */
 
-static char *verstr = "20011103";
+static char *verstr = "20020205";
 
 #include <linux/module.h>
 
@@ -1944,6 +1944,10 @@
 			STm->default_blksize = value;
 			printk(KERN_INFO "st%d: Default block size set to %d bytes.\n",
 			       dev, STm->default_blksize);
+			if (STp->ready == ST_READY) {
+				STp->blksize_changed = FALSE;
+				set_mode_densblk(STp, STm);
+			}
 		}
 	} else if (code == MT_ST_TIMEOUTS) {
 		value = (options & ~MT_ST_OPTIONS);
@@ -1979,6 +1983,10 @@
 				STm->default_density = value & 0xff;
 				printk(KERN_INFO "st%d: Density default set to %x\n",
 				       dev, STm->default_density);
+				if (STp->ready == ST_READY) {
+					STp->density_changed = FALSE;
+					set_mode_densblk(STp, STm);
+				}
 			}
 		} else if (code == MT_ST_DEF_DRVBUFFER) {
 			if (value == MT_ST_CLEAR_DEFAULT) {
@@ -1990,6 +1998,8 @@
 				printk(KERN_INFO
                                        "st%d: Drive buffer default set to %x\n",
 				       dev, STp->default_drvbuffer);
+				if (STp->ready == ST_READY)
+					st_int_ioctl(STp, MTSETDRVBUFFER, STp->default_drvbuffer);
 			}
 		} else if (code == MT_ST_DEF_COMPRESSION) {
 			if (value == MT_ST_CLEAR_DEFAULT) {
@@ -1997,9 +2007,20 @@
 				printk(KERN_INFO
                                        "st%d: Compression default disabled.\n", dev);
 			} else {
-				STm->default_compression = (value & 1 ? ST_YES : ST_NO);
-				printk(KERN_INFO "st%d: Compression default set to %x\n",
-				       dev, (value & 1));
+				if ((value & 0xff00) != 0) {
+					STp->c_algo = (value & 0xff00) >> 8;
+					printk(KERN_INFO "st%d: Compression algorithm set to 0x%x.\n",
+					       dev, STp->c_algo);
+				}
+				if ((value & 0xff) != 0xff) {
+					STm->default_compression = (value & 1 ? ST_YES : ST_NO);
+					printk(KERN_INFO "st%d: Compression default set to %x\n",
+					       dev, (value & 1));
+					if (STp->ready == ST_READY) {
+						STp->compression_changed = FALSE;
+						st_compression(STp, (STm->default_compression == ST_YES));
+					}
+				}
 			}
 		}
 	} else
@@ -2028,7 +2049,8 @@
 #define MODE_SELECT_PAGE_FORMAT 0x10
 
 /* Read a mode page into the tape buffer. The block descriptors are included
-   if incl_block_descs is true. */
+   if incl_block_descs is true. The page control is ored to the page number
+   parameter, if necessary. */
 static int read_mode_page(Scsi_Tape *STp, int page, int omit_block_descs)
 {
 	unsigned char cmd[MAX_COMMAND_SIZE];
@@ -2087,6 +2109,7 @@
 #define COMPRESSION_PAGE_LENGTH 16
 
 #define CP_OFF_DCE_DCC          2
+#define CP_OFF_C_ALGO           7
 
 #define DCE_MASK  0x80
 #define DCC_MASK  0x40
@@ -2122,16 +2145,22 @@
                     (b_data[mpoffs + CP_OFF_DCE_DCC] & DCE_MASK ? 1 : 0)));
 
 	/* Check if compression can be changed */
-	if ((b_data[mpoffs + 2] & DCC_MASK) == 0) {
+	if ((b_data[mpoffs + CP_OFF_DCE_DCC] & DCC_MASK) == 0) {
                 DEBC(printk(ST_DEB_MSG "st%d: Compression not supported.\n", dev));
 		return (-EIO);
 	}
 
 	/* Do the change */
-	if (state)
+	if (state) {
 		b_data[mpoffs + CP_OFF_DCE_DCC] |= DCE_MASK;
-	else
+		if (STp->c_algo != 0)
+			b_data[mpoffs + CP_OFF_C_ALGO] = STp->c_algo;
+	}
+	else {
 		b_data[mpoffs + CP_OFF_DCE_DCC] &= ~DCE_MASK;
+		if (STp->c_algo != 0)
+			b_data[mpoffs + CP_OFF_C_ALGO] = 0; /* no compression */
+	}
 
 	retval = write_mode_page(STp, COMPRESSION_PAGE);
 	if (retval) {
@@ -2171,7 +2200,7 @@
 	 */
 	if (load_code >= 1 + MT_ST_HPLOADER_OFFSET
 	    && load_code <= 6 + MT_ST_HPLOADER_OFFSET) {
-		DEBC(printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2ld.\n",
+		DEBC(printk(ST_DEB_MSG "st%d: Enhanced %sload slot %2d.\n",
 			    dev, (cmd[4]) ? "" : "un",
 			    load_code - MT_ST_HPLOADER_OFFSET));
 		cmd[3] = load_code - MT_ST_HPLOADER_OFFSET; /* MediaID field of C1553A */
@@ -3818,6 +3847,7 @@
 			write_unlock_irqrestore(&st_dev_arr_lock, flags);
 			printk(KERN_ERR "Unable to get major %d for SCSI tapes\n",
                                MAJOR_NR);
+			st_template.dev_noticed = 0;
 			return 1;
 		}
 		st_registered++;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/st.h linux/drivers/scsi/st.h
--- linux.orig/drivers/scsi/st.h	Fri Nov  9 21:52:21 2001
+++ linux/drivers/scsi/st.h	Tue Feb  5 19:40:49 2002
@@ -83,6 +83,7 @@
 	unsigned char cln_sense_value;
 	unsigned char cln_sense_mask;
 	unsigned char use_pf;			/* Set Page Format bit in all mode selects? */
+	unsigned char c_algo;	     /* compression algorithm */
 	int tape_type;
 	int write_threshold;
 	int timeout;		/* timeout for normal commands */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/scsi/sun3_scsi.c linux/drivers/scsi/sun3_scsi.c
--- linux.orig/drivers/scsi/sun3_scsi.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/scsi/sun3_scsi.c	Mon Jan  7 17:36:23 2002
@@ -357,7 +357,7 @@
 	NCR5380_write( INITIATOR_COMMAND_REG, ICR_BASE );
 	NCR5380_read( RESET_PARITY_INTERRUPT_REG );
 
-	for( end = jiffies + AFTER_RESET_DELAY; jiffies < end; )
+	for( end = jiffies + AFTER_RESET_DELAY; time_before(jiffies, end); )
 		barrier();
 
 	/* switch on SCSI IRQ again */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/ad1848.h linux/drivers/sound/ad1848.h
--- linux.orig/drivers/sound/ad1848.h	Fri Aug 11 15:26:43 2000
+++ linux/drivers/sound/ad1848.h	Mon Feb  4 17:38:23 2002
@@ -1,8 +1,3 @@
-/*
- * ad1848.c
- *
- * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
- */
 
 #define AD_F_CS4231     0x0001  /* Returned if a CS4232 (or compatible) detected */
 #define AD_F_CS4248     0x0001  /* Returned if a CS4248 (or compatible) detected */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/aedsp16.c linux/drivers/sound/aedsp16.c
--- linux.orig/drivers/sound/aedsp16.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/aedsp16.c	Mon Jan 14 18:53:53 2002
@@ -1,5 +1,5 @@
 /*
-   drivers/sound/lowlevel/aedsp16.c
+   drivers/sound/aedsp16.c
 
    Audio Excel DSP 16 software configuration routines
    Copyright (C) 1995,1996,1997,1998  Riccardo Facchetti (fizban@tin.it)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/btaudio.c linux/drivers/sound/btaudio.c
--- linux.orig/drivers/sound/btaudio.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/btaudio.c	Mon Jan 14 18:53:53 2002
@@ -885,6 +885,12 @@
 	}
 
 	bta = kmalloc(sizeof(*bta),GFP_ATOMIC);
+	if (!bta) {
+		printk(KERN_WARNING 
+		       "btaudio: not enough memory\n");
+		rc = -ENOMEM;
+		goto fail1;
+	}
 	memset(bta,0,sizeof(*bta));
 
 	bta->pci  = pci_dev;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- linux.orig/drivers/sound/cs4232.c	Sun Sep 30 19:26:08 2001
+++ linux/drivers/sound/cs4232.c	Wed Feb 13 17:37:40 2002
@@ -277,7 +277,7 @@
 	}
 }
 
-void __exit unload_cs4232(struct address_info *hw_config)
+void unload_cs4232(struct address_info *hw_config)
 {
 	int base = hw_config->io_base, irq = hw_config->irq;
 	int dma1 = hw_config->dma, dma2 = hw_config->dma2;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4232.h linux/drivers/sound/cs4232.h
--- linux.orig/drivers/sound/cs4232.h	Mon Jul  2 20:56:41 2001
+++ linux/drivers/sound/cs4232.h	Mon Feb  4 17:38:23 2002
@@ -1,9 +1,3 @@
-/*
- *	cs4232.h
- *
- * Copyright: Christoph Hellwig <chhellwig@gmx.net>
- *
- */
 
 int probe_cs4232 (struct address_info *hw_config,int useisapnp);
 void attach_cs4232 (struct address_info *hw_config);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4281/cs4281_wrapper-24.c linux/drivers/sound/cs4281/cs4281_wrapper-24.c
--- linux.orig/drivers/sound/cs4281/cs4281_wrapper-24.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/cs4281/cs4281_wrapper-24.c	Thu Jan  1 00:00:00 1970
@@ -1,42 +0,0 @@
-/*******************************************************************************
-*
-*      "cs4281_wrapper.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
-*
-*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
-*            -- tom woller (twoller@crystal.cirrus.com) or
-*               (audio@crystal.cirrus.com).
-*
-*      This program is free software; you can redistribute it and/or modify
-*      it under the terms of the GNU General Public License as published by
-*      the Free Software Foundation; either version 2 of the License, or
-*      (at your option) any later version.
-*
-*      This program is distributed in the hope that it will be useful,
-*      but WITHOUT ANY WARRANTY; without even the implied warranty of
-*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-*      GNU General Public License for more details.
-*
-*      You should have received a copy of the GNU General Public License
-*      along with this program; if not, write to the Free Software
-*      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*
-* 12/20/00 trw - new file. 
-*
-*******************************************************************************/
-
-#include <linux/spinlock.h>
-
-void cs4281_null(struct pci_dev *pcidev) { return; }
-#define cs4x_mem_map_reserve(page) mem_map_reserve(page)
-#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page)
-
-#define free_dmabuf(state, dmabuf) \
-	pci_free_consistent(state->pcidev, \
-			    PAGE_SIZE << (dmabuf)->buforder, \
-			    (dmabuf)->rawbuf, (dmabuf)->dmaaddr);
-#define free_dmabuf2(state, dmabuf) \
-	pci_free_consistent((state)->pcidev, \
-				    PAGE_SIZE << (state)->buforder_tmpbuff, \
-				    (state)->tmpbuff, (state)->dmaaddr_tmpbuff);
-#define cs4x_pgoff(vma) ((vma)->vm_pgoff)
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4281/cs4281_wrapper.h linux/drivers/sound/cs4281/cs4281_wrapper.h
--- linux.orig/drivers/sound/cs4281/cs4281_wrapper.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/cs4281/cs4281_wrapper.h	Wed Jan 16 21:05:19 2002
@@ -0,0 +1,54 @@
+/*******************************************************************************
+*
+*      "cs4281_wrapper.h" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      This program is free software; you can redistribute it and/or modify
+*      it under the terms of the GNU General Public License as published by
+*      the Free Software Foundation; either version 2 of the License, or
+*      (at your option) any later version.
+*
+*      This program is distributed in the hope that it will be useful,
+*      but WITHOUT ANY WARRANTY; without even the implied warranty of
+*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*      GNU General Public License for more details.
+*
+*      You should have received a copy of the GNU General Public License
+*      along with this program; if not, write to the Free Software
+*      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 12/22/00 trw - new file. 
+* 04/18/01 trw - rework entire wrapper logic.
+*
+*******************************************************************************/
+#ifndef __CS4281_WRAPPER_H
+#define __CS4281_WRAPPER_H
+
+/* 2.4.x wrapper */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,12)
+static int cs4281_null_suspend(struct pci_dev *pcidev, u32 unused) { return 0; }
+static int cs4281_null_resume(struct pci_dev *pcidev) { return 0; }
+#else
+#define no_llseek cs4281_llseek
+static loff_t cs4281_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+void cs4281_null_suspend(struct pci_dev *pcidev) { return; }
+void cs4281_null_resume(struct pci_dev *pcidev) { return; }
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3)
+/* Some versions of 2.4.2 resolve pci_set_dma_mask and some do not... 
+*  but 2.4.0 definitely does not 
+*/
+#define pci_set_dma_mask(dev,data) 0;
+#else
+#endif
+#define cs4x_mem_map_reserve(page) mem_map_reserve(page)
+#define cs4x_mem_map_unreserve(page) mem_map_unreserve(page)
+
+#endif /* #ifndef __CS4281_WRAPPER_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4281/cs4281m.c linux/drivers/sound/cs4281/cs4281m.c
--- linux.orig/drivers/sound/cs4281/cs4281m.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/cs4281/cs4281m.c	Mon Jan 14 18:53:53 2002
@@ -1,12 +1,12 @@
 /*******************************************************************************
 *
-*      "cs4281.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*      "cs4281m.c" --  Cirrus Logic-Crystal CS4281 linux audio driver.
 *
 *      Copyright (C) 2000,2001  Cirrus Logic Corp.  
 *            -- adapted from drivers by Thomas Sailer, 
 *            -- but don't bug him; Problems should go to:
 *            -- tom woller (twoller@crystal.cirrus.com) or
-*               (audio@crystal.cirrus.com).
+*               (pcaudio@crystal.cirrus.com).
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
@@ -51,6 +51,18 @@
 * 12/21/00 trw - added fractional "defaultorder" inputs. if >100 then use 
 *		 defaultorder-100 as power of 2 for the buffer size. example:
 *		 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
+* 04/02/01 trw - reworked the includes, so kgdb wouldn't get confused.
+* 04/17/01 trw - added ifdef CONFIG_PM for 2.4.x non-pm kernels. static pmprint.
+* 04/19/01 trw - reworked all of the wrapper macros to keep native 2.4.x code
+*		 predominate in the driver.
+* 07/01/01 trw - added ability to modify the record source mask to alleviate
+*		 problems with toshiba systems.  also, check for toshiba 
+*		 system to set default up properly.
+* 11/12/01 trw - removed cs4281_update_ptr() in the polling interface code.
+*		 returning with only a few bytes available in the write buffer
+*		 seems to cause some problems with some apps (xmms OSS plugin).
+*		 Also, fixed bug in cs4281_update_ptr() code to wakeup when
+*	 	 1/2 buffer is empty, not when completely full.
 *
 *******************************************************************************/
 
@@ -77,13 +89,17 @@
 #include <linux/wrapper.h>
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
-//#include "cs_dm.h"
 #include "cs4281_hwdefs.h"
-#include "cs4281pm.h"
 
-struct cs4281_state;
 EXPORT_NO_SYMBOLS;
 
+struct cs4281_state;
+int cs4281_suspend(struct cs4281_state *s);
+int cs4281_resume(struct cs4281_state *s);
+
+#include "cs4281_wrapper.h"
+#include "cs4281pm-24.h"
+
 static void stop_dac(struct cs4281_state *s);
 static void stop_adc(struct cs4281_state *s);
 static void start_dac(struct cs4281_state *s);
@@ -99,18 +115,40 @@
 #define PCI_DEVICE_ID_CRYSTAL_CS4281  0x6005
 #endif
 
+#ifndef SS_ID_TOSHIBA_1640CDT
+#define SS_ID_TOSHIBA_1640CDT	0xff00
+#endif
+
+#ifndef PCI_VENDOR_ID_TOSHIBA
+#define PCI_VENDOR_ID_TOSHIBA 	0x1179
+#endif
+
 #define CS4281_MAGIC  ((PCI_DEVICE_ID_CRYSTAL_CS4281<<16) | PCI_VENDOR_ID_CIRRUS)
 #define	CS4281_CFLR_DEFAULT	0x00000001  /* CFLR must be in AC97 link mode */
+#define CS4281_BA0_CPWR_DEFAULT 0x4281
 
-// buffer order determines the size of the dma buffer for the driver.
-// under Linux, a smaller buffer allows more responsiveness from many of the 
-// applications (e.g. games).  A larger buffer allows some of the apps (esound) 
-// to not underrun the dma buffer as easily.  As default, use 32k (order=3)
-// rather than 64k as some of the games work more responsively.
-// log base 2( buff sz = 32k).
+/* buffer order determines the size of the dma buffer for the driver.
+* under Linux, a smaller buffer allows more responsiveness from many of the 
+* applications (e.g. games).  A larger buffer allows some of the apps (esound) 
+* to not underrun the dma buffer as easily.  As default, use 32k (order=3)
+* rather than 64k as some of the games work more responsively.
+* (2^N) * PAGE_SIZE = allocated buffer size
+*
+* also added fractional "defaultorder" inputs. if >100 then use 
+* defaultorder-100 as power of 2 for the buffer size. example:
+* 106 = 2^(106-100) = 2^6 = 64 bytes for the buffer size.
+* games are even MORE responsive now, but prone to underruns.
+*/
 static unsigned long defaultorder = 3;
 MODULE_PARM(defaultorder, "i");
 
+/*
+* use this module parm to invalidate recording sources 
+* as on some machines (Toshiba Satellites... again) setting to LINE
+* causes an error and some of the mixers (gmix) to not load.
+*/
+static unsigned long recsrc_invalid = 0;
+MODULE_PARM(recsrc_invalid, "i");
 //
 // Turn on/off debugging compilation by commenting out "#define CSDEBUG"
 //
@@ -172,7 +210,7 @@
 #define FMODE_MIDI_WRITE (FMODE_WRITE << FMODE_MIDI_SHIFT)
 
 #define CS4281_MAJOR_VERSION 	1
-#define CS4281_MINOR_VERSION 	13
+#define CS4281_MINOR_VERSION 	30
 #ifdef __ia64__
 #define CS4281_ARCH	     	64	//architecture key
 #else
@@ -182,7 +220,6 @@
 #define CS_TYPE_ADC 0
 #define CS_TYPE_DAC 1
 
-
 static const char invalid_magic[] =
     KERN_CRIT "cs4281: invalid magic value\n";
 
@@ -194,16 +231,12 @@
         }                                         \
 })
 
-//LIST_HEAD(cs4281_devs);
 struct list_head cs4281_devs = { &cs4281_devs, &cs4281_devs };
 
-struct cs4281_state; 
-
-#include "cs4281_wrapper-24.c"
-
 struct cs4281_state {
 	// magic 
 	unsigned int magic;
+	u16 ss_id, ss_vendor;  /* subsystem and vendor IDs from pci space */
 
 	// we keep the cards in a linked list 
 	struct cs4281_state *next;
@@ -221,6 +254,7 @@
 	unsigned int pBA0phys, pBA1phys;
 	char *pBA0, *pBA1;
 	unsigned int irq;
+	unsigned recsrc;
 
 	// mixer registers 
 	struct {
@@ -291,12 +325,12 @@
 		unsigned char obuf[MIDIOUTBUF];
 	} midi;
 
+#ifndef NOT_CS4281_PM
 	struct cs4281_pm pm;
 	struct cs4281_pipeline pl[CS4281_NUMBER_OF_PIPELINES];
+#endif
 };
 
-#include "cs4281pm-24.c"
-
 #if CSDEBUG
 
 // DEBUG ROUTINES
@@ -527,7 +561,13 @@
 static void delayus(struct cs4281_state *s, u32 delay)
 {
 	u32 j;
-	if ((delay > 9999) && (s->pm.flags & CS4281_PM_IDLE)) {
+	if ((delay > 9999) 
+#ifndef NOT_CS4281_PM
+	&& (s->pm.flags & CS4281_PM_IDLE))
+#else
+	)
+#endif
+	{
 		j = (delay * HZ) / 1000000;	/* calculate delay in jiffies  */
 		if (j < 1)
 			j = 1;	/* minimum one jiffy. */
@@ -578,7 +618,7 @@
 	       card->pBA0 + BA0_ACCTL);
 
 	// Wait for the read to occur.
-	for (count = 0; count < 10; count++) {
+	for (count = 0; count < 100; count++) {
 		// First, we want to wait for a short time.
 		udelay(25);
 
@@ -593,7 +633,7 @@
 		return 1;
 
 	// Wait for the valid status bit to go active.
-	for (count = 0; count < 10; count++) {
+	for (count = 0; count < 100; count++) {
 		// Read the AC97 status register.
 		// ACSTS = Status Register = 464h
 		status = readl(card->pBA0 + BA0_ACSTS);
@@ -703,12 +743,26 @@
 		CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO 
 			"cs4281: cs4281_hw_init() CFLR invalid - resetting from 0x%x to 0x%x\n",
 				temp2,CS4281_CFLR_DEFAULT));
+		temp2 = readl(card->pBA0 + BA0_CWPR);
+		if(temp2 != CS4281_BA0_CPWR_DEFAULT)
+		{
+			writel(CS4281_BA0_CPWR_DEFAULT, card->pBA0 + BA0_CWPR);
+			temp2 = readl(card->pBA0 + BA0_CWPR);
+			if(temp2 != CS4281_BA0_CPWR_DEFAULT)
+			{
+			  CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO 
+				"cs4281: cs4281_hw_init() Invalid hardware - unable to configure CPWR (0x%x)\n",
+					temp2));
+			  return 1;
+			}
+		}
 		writel(CS4281_CFLR_DEFAULT, card->pBA0 + BA0_CFLR);
 		temp2 = readl(card->pBA0 + BA0_CFLR);
 		if(temp2 != CS4281_CFLR_DEFAULT)
 		{
 			CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_INFO 
-				"cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR\n"));
+				"cs4281: cs4281_hw_init() Invalid hardware - unable to configure CFLR (0x%x)\n",
+					temp2));
 			return 1;
 		}
 	}
@@ -1021,15 +1075,6 @@
 	int Count,i;
 
 	CS_DBGOUT(CS_PM, 9, printk("cs4281: cs4281_ac97_suspend()+\n"));
-/*
-* change the state, save the current hwptr, then stop the dac/adc
-*/
-	s->pm.flags &= ~CS4281_PM_IDLE;
-	s->pm.flags |= CS4281_PM_SUSPENDING;
-	s->pm.u32hwptr_playback = readl(s->pBA0 + BA0_DCA0);
-	s->pm.u32hwptr_capture = readl(s->pBA0 + BA0_DCA1);
-	stop_dac(s);
-	stop_adc(s);
 
 	for(Count = 0x2, i=0; (Count <= CS4281_AC97_HIGHESTREGTORESTORE)
 			&& (i < CS4281_AC97_NUMBER_RESTORE_REGS); 
@@ -1269,6 +1314,14 @@
 	pm->u32DacSR = readl(s->pBA0 + BA0_DACSR);
 	pm->u32AdcSR = readl(s->pBA0 + BA0_ADCSR);
 
+/*
+* save the current hwptr, then stop the dac/adc
+*/
+	pm->u32hwptr_playback = readl(s->pBA0 + BA0_DCA0);
+	pm->u32hwptr_capture = readl(s->pBA0 + BA0_DCA1);
+	stop_dac(s);
+	stop_adc(s);
+
 	//
 	// Loop through all of the PipeLines 
 	//
@@ -1582,9 +1635,9 @@
 #ifndef NOT_CS4281_PM
 	&& (s->pm.flags & CS4281_PM_IDLE))
 #else
-)
+	)
 #endif
- {
+	{
 		s->ena |= FMODE_WRITE;
 		temp1 = readl(s->pBA0 + BA0_DCR0) & ~DCRn_MSK;	// Clear DMA0 channel mask.
 		writel(temp1, s->pBA0 + BA0_DCR0);	// Start DMA'ing.
@@ -1640,7 +1693,7 @@
 #ifndef NOT_CS4281_PM
 	&& (s->pm.flags & CS4281_PM_IDLE))
 #else
-) 
+	) 
 #endif
 	{
 		if (s->prop_adc.fmt & AFMT_S8 || s->prop_adc.fmt & AFMT_U8) {
@@ -1689,7 +1742,7 @@
 
 // --------------------------------------------------------------------- 
 
-#define DMABUF_MINORDER 1	// ==> min buffer size = 8K.
+#define DMABUF_MINORDER 0	// ==> min buffer size = 8K.
 
 
 extern void dealloc_dmabuf(struct cs4281_state *s, struct dmabuf *db)
@@ -1698,21 +1751,21 @@
 
 	if (db->rawbuf) {
 		// Undo prog_dmabuf()'s marking the pages as reserved 
-		mapend =
-		    virt_to_page(db->rawbuf + (PAGE_SIZE << db->buforder) -
-				 1);
+		mapend = virt_to_page(db->rawbuf + 
+			(PAGE_SIZE << db->buforder) - 1);
 		for (map = virt_to_page(db->rawbuf); map <= mapend; map++)
 			cs4x_mem_map_unreserve(map);
-		free_dmabuf(s, db);
+		pci_free_consistent(s->pcidev, PAGE_SIZE << db->buforder, 
+			    db->rawbuf, db->dmaaddr);
 	}
 	if (s->tmpbuff && (db->type == CS_TYPE_ADC)) {
 		// Undo prog_dmabuf()'s marking the pages as reserved 
-		mapend =
-		    virt_to_page(s->tmpbuff +
-				 (PAGE_SIZE << s->buforder_tmpbuff) - 1);
+		mapend = virt_to_page(s->tmpbuff +
+			 (PAGE_SIZE << s->buforder_tmpbuff) - 1);
 		for (map = virt_to_page(s->tmpbuff); map <= mapend; map++)
 			cs4x_mem_map_unreserve(map);
-		free_dmabuf2(s, db);
+		pci_free_consistent(s->pcidev, PAGE_SIZE << s->buforder_tmpbuff,
+				    s->tmpbuff, s->dmaaddr_tmpbuff);
 	}
 	s->tmpbuff = NULL;
 	db->rawbuf = NULL;
@@ -1735,7 +1788,7 @@
 * check for order within limits, but do not overwrite value, check
 * later for a fractional defaultorder (i.e. 100+).
 */
-	if((defaultorder > 0) && (defaultorder < 12))
+	if((defaultorder >= 0) && (defaultorder < 12))
 		df = defaultorder;
 	else
 		df = 1;	
@@ -1744,7 +1797,7 @@
 		db->ready = db->mapped = 0;
 		for (order = df; order >= DMABUF_MINORDER; order--)
 			if ( (db->rawbuf = (void *) pci_alloc_consistent(
-				s->pcidev, PAGE_SIZE << order, &db-> dmaaddr)))
+				s->pcidev, PAGE_SIZE << order, &db->dmaaddr)))
 				    break;
 		if (!db->rawbuf) {
 			CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
@@ -1969,10 +2022,8 @@
 		if (s->dma_adc.count > s->dma_adc.dmasize)
 			s->dma_adc.count = s->dma_adc.dmasize;
 		if (s->dma_adc.mapped) {
-			if (s->dma_adc.count >=
-			    (signed) s->dma_adc.fragsize) wake_up(&s->
-								  dma_adc.
-								  wait);
+			if (s->dma_adc.count >= (signed) s->dma_adc.fragsize) 
+				wake_up(&s->dma_adc.wait);
 		} else {
 			if (s->dma_adc.count > 0)
 				wake_up(&s->dma_adc.wait);
@@ -2143,6 +2194,7 @@
 static int mixer_ioctl(struct cs4281_state *s, unsigned int cmd,
 		       unsigned long arg)
 {
+	int return_mask=0;
 	// Index to mixer_src[] is value of AC97 Input Mux Select Reg.
 	// Value of array member is recording source Device ID Mask.
 	static const unsigned int mixer_src[8] = {
@@ -2307,9 +2359,14 @@
 					SOUND_MASK_SPEAKER, (int *) arg);
 
 		case SOUND_MIXER_RECMASK:	// Arg contains a bit for each supported recording source 
-			return put_user(SOUND_MASK_LINE | SOUND_MASK_MIC |
+			return_mask = (((SOUND_MASK_LINE | SOUND_MASK_MIC |
 					SOUND_MASK_CD | SOUND_MASK_VOLUME |
-					SOUND_MASK_LINE1, (int *) arg);
+					SOUND_MASK_LINE1) ) & ~recsrc_invalid);
+
+			CS_DBGOUT(CS_PARMS, 6, printk(KERN_INFO
+				"cs4281: mixer_ioctl(): return_mask=0x%x recsrc_invalid=0x%x\n",
+					(unsigned)return_mask,(unsigned)recsrc_invalid));
+			return put_user(return_mask, (int *) arg);
 
 		case SOUND_MIXER_STEREODEVS:	// Mixer channels supporting stereo 
 			return put_user(SOUND_MASK_PCM | SOUND_MASK_SYNTH |
@@ -2346,6 +2403,13 @@
 		i = hweight32(val);	// i = # bits on in val.
 		if (i != 1)	// One & only 1 bit must be on.
 			return 0;
+		if(val & recsrc_invalid)
+		{
+			CS_DBGOUT(CS_ERROR, 2, printk(KERN_INFO
+				"cs4281: mixer_ioctl(): REC SOURCE select error - record source invalid on this system (0x%x)\n",
+					val));
+			return -EINVAL;
+		}
 		for (i = 0; i < sizeof(mixer_src) / sizeof(int); i++) {
 			if (val == mixer_src[i]) {
 				temp1 = (i << 8) | i;
@@ -2567,6 +2631,14 @@
 
 // --------------------------------------------------------------------- 
 
+static loff_t cs4281_llseek(struct file *file, loff_t offset, int origin)
+{
+	return -ESPIPE;
+}
+
+
+// --------------------------------------------------------------------- 
+
 static int cs4281_open_mixdev(struct inode *inode, struct file *file)
 {
 	int minor = MINOR(inode->i_rdev);
@@ -2622,7 +2694,7 @@
 //   Mixer file operations struct.
 // ******************************************************************************************
 static /*const */ struct file_operations cs4281_mixer_fops = {
-	llseek:no_llseek,
+	llseek:cs4281_llseek,
 	ioctl:cs4281_ioctl_mixdev,
 	open:cs4281_open_mixdev,
 	release:cs4281_release_mixdev,
@@ -2684,9 +2756,18 @@
 	int count;
 	unsigned tmo;
 
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: drain_dac()+\n"));
 	if (s->dma_dac.mapped)
+	{
+		CS_DBGOUT(CS_FUNCTION, 2,
+			  printk(KERN_INFO "cs4281: drain_dac()- (mmap) 0\n"));
+		return 0;
+	}
+	if (s->ena & FMODE_WRITE)
+		add_wait_queue(&s->dma_dac.wait, &wait);
+	else
 		return 0;
-	add_wait_queue(&s->dma_dac.wait, &wait);
 	for (;;) {
 		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock_irqsave(&s->lock, flags);
@@ -2699,6 +2780,8 @@
 		if (nonblock) {
 			remove_wait_queue(&s->dma_dac.wait, &wait);
 			current->state = TASK_RUNNING;
+			CS_DBGOUT(CS_FUNCTION, 2,
+				  printk(KERN_INFO "cs4281: drain_dac()- -EBUSY\n"));
 			return -EBUSY;
 		}
 		tmo =
@@ -2714,7 +2797,13 @@
 	remove_wait_queue(&s->dma_dac.wait, &wait);
 	current->state = TASK_RUNNING;
 	if (signal_pending(current))
+	{
+		CS_DBGOUT(CS_FUNCTION, 2,
+			  printk(KERN_INFO "cs4281: drain_dac()- -ERESTARTSYS\n"));
 		return -ERESTARTSYS;
+	}
+	CS_DBGOUT(CS_FUNCTION, 2,
+		  printk(KERN_INFO "cs4281: drain_dac()- 0\n"));
 	return 0;
 }
 
@@ -3051,6 +3140,9 @@
 	return ret;
 }
 
+/*
+* cs4281_poll(struct file *file, struct poll_table_struct *wait)
+*/
 
 static unsigned int cs4281_poll(struct file *file,
 				struct poll_table_struct *wait)
@@ -3061,7 +3153,7 @@
 	unsigned int mask = 0;
 
 	CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
-		  printk(KERN_INFO "cs4281: cs4281_poll()+\n"));
+		  printk(KERN_INFO "cs4281: cs4281_poll()+ wait=0x%x\n", (unsigned)wait));
 	VALIDATE_STATE(s);
 	if (file->f_mode & FMODE_WRITE) {
 		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
@@ -3070,17 +3162,17 @@
 		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
 			return 0;
 		poll_wait(file, &s->dma_dac.wait, wait);
-	}
-	if (file->f_mode & FMODE_READ) {
+
+	} else if (file->f_mode & FMODE_READ) {
 		CS_DBGOUT(CS_FUNCTION | CS_WAVE_WRITE | CS_WAVE_READ, 4,
 			  printk(KERN_INFO
 				 "cs4281: cs4281_poll() wait on FMODE_READ\n"));
-		if(!s->dma_dac.ready && prog_dmabuf_adc(s))
+		if(!s->dma_adc.ready && prog_dmabuf_adc(s))
 			return 0;
 		poll_wait(file, &s->dma_adc.wait, wait);
+
 	}
 	spin_lock_irqsave(&s->lock, flags);
-	cs4281_update_ptr(s,CS_FALSE);
 	if (file->f_mode & FMODE_WRITE) {
 		if (s->dma_dac.mapped) {
 			if (s->dma_dac.count >=
@@ -3139,7 +3231,7 @@
 //
 	db = &s->dma_dac;
 
-	if (cs4x_pgoff(vma) != 0)
+	if ((vma->vm_pgoff) != 0)
 		return -EINVAL;
 	size = vma->vm_end - vma->vm_start;
 	if (size > (PAGE_SIZE << db->buforder))
@@ -3408,7 +3500,7 @@
 		if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)))
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
-		cs4281_update_ptr(s,CS_FALSE);
+		cs4281_update_ptr(s,CS_TRUE);
 		abinfo.fragsize = s->dma_dac.fragsize;
 		if (s->dma_dac.mapped)
 			abinfo.bytes = s->dma_dac.dmasize;
@@ -3431,7 +3523,7 @@
 		if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)))
 			return val;
 		spin_lock_irqsave(&s->lock, flags);
-		cs4281_update_ptr(s,CS_FALSE);
+		cs4281_update_ptr(s,CS_TRUE);
 		if (s->conversion) {
 			abinfo.fragsize = s->dma_adc.fragsize / 2;
 			abinfo.bytes = s->dma_adc.count / 2;
@@ -3459,7 +3551,7 @@
 		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
 			return 0;
 		spin_lock_irqsave(&s->lock, flags);
-		cs4281_update_ptr(s,CS_FALSE);
+		cs4281_update_ptr(s,CS_TRUE);
 		val = s->dma_dac.count;
 		spin_unlock_irqrestore(&s->lock, flags);
 		return put_user(val, (int *) arg);
@@ -3470,7 +3562,7 @@
 		if(!s->dma_adc.ready && prog_dmabuf_adc(s))
 			return 0;
 		spin_lock_irqsave(&s->lock, flags);
-		cs4281_update_ptr(s,CS_FALSE);
+		cs4281_update_ptr(s,CS_TRUE);
 		cinfo.bytes = s->dma_adc.total_bytes;
 		if (s->dma_adc.mapped) {
 			cinfo.blocks =
@@ -3503,7 +3595,7 @@
 		if(!s->dma_dac.ready && prog_dmabuf_dac(s))
 			return 0;
 		spin_lock_irqsave(&s->lock, flags);
-		cs4281_update_ptr(s,CS_FALSE);
+		cs4281_update_ptr(s,CS_TRUE);
 		cinfo.bytes = s->dma_dac.total_bytes;
 		if (s->dma_dac.mapped) {
 			cinfo.blocks =
@@ -3538,6 +3630,10 @@
 	case SNDCTL_DSP_SETFRAGMENT:
 		if (get_user(val, (int *) arg))
 			return -EFAULT;
+
+		CS_DBGOUT(CS_PARMS, 4, printk(KERN_INFO
+			"cs4281: cs4281_ioctl(): Attempt to set fragsize=%d fragnum=%d\n",
+				1 << (val & 0xffff), (val >> 16) & 0xffff  ));
 		return 0;	// Say OK, but do nothing.
 
 	case SNDCTL_DSP_SUBDIVIDE:
@@ -3739,7 +3835,7 @@
 //   Wave (audio) file operations struct.
 // ******************************************************************************************
 static /*const */ struct file_operations cs4281_audio_fops = {
-	llseek:no_llseek,
+	llseek:cs4281_llseek,
 	read:cs4281_read,
 	write:cs4281_write,
 	poll:cs4281_poll,
@@ -4088,7 +4184,7 @@
 //   Midi file operations struct.
 // ******************************************************************************************
 static /*const */ struct file_operations cs4281_midi_fops = {
-	llseek:no_llseek,
+	llseek:cs4281_llseek,
 	read:cs4281_midi_read,
 	write:cs4281_midi_write,
 	poll:cs4281_midi_poll,
@@ -4263,6 +4359,35 @@
 }
 #endif
 
+/*
+* ss_vendor and ss_id must be setup prior to calling this routine.
+* setup the invalid recording source bitmask,
+* and also return a valid default initialization value as
+* the return value;
+*/
+static int cs4281_setup_record_src(struct cs4281_state *s)
+{
+	if(s->ss_vendor == PCI_VENDOR_ID_TOSHIBA)
+	{
+		if(s->ss_id == SS_ID_TOSHIBA_1640CDT)
+		{
+			CS_DBGOUT(CS_PARMS, 2, printk(KERN_INFO
+			  "cs4281: cs4281_setup_record_src(): setting LINE invalid\n"));
+			recsrc_invalid |= SOUND_MASK_LINE;
+		}
+	}
+/*
+* only return a valid recsrc value here, default to something useful.
+*/
+	if(!(recsrc_invalid & SOUND_MASK_MIC))
+		return(SOUND_MASK_MIC);
+	else if(!(recsrc_invalid & SOUND_MASK_LINE))
+		return(SOUND_MASK_LINE);
+	else if(!(recsrc_invalid & SOUND_MASK_LINE1))
+		return(SOUND_MASK_LINE1);
+	return 0;
+}
+
 static int __devinit cs4281_probe(struct pci_dev *pcidev,
 				  const struct pci_device_id *pciid)
 {
@@ -4285,22 +4410,22 @@
 	}
 	if (!(pci_resource_flags(pcidev, 0) & IORESOURCE_MEM) ||
 	    !(pci_resource_flags(pcidev, 1) & IORESOURCE_MEM)) {
-		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
-			 "cs4281: probe()- Memory region not assigned\n"));
+ 		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+ 			 "cs4281: probe()- Memory region not assigned\n"));
 		return -ENODEV;
-	}
-	if (pcidev->irq == 0) {
-		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
-			 "cs4281: probe() IRQ not assigned\n"));
+ 	}
+ 	if (pcidev->irq == 0) {
+ 		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+ 			 "cs4281: probe() IRQ not assigned\n"));
 		return -ENODEV;
-	}
+ 	}
 	dma_mask = 0xffffffff;	/* this enables playback and recording */
 	i = pci_set_dma_mask(pcidev, dma_mask);
 	if (i) {
-		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
-		      "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n"));
+ 		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
+ 		      "cs4281: probe() architecture does not support 32bit PCI busmaster DMA\n"));
 		return i;
-	}
+ 	}
 	if (!(s = kmalloc(sizeof(struct cs4281_state), GFP_KERNEL))) {
 		CS_DBGOUT(CS_ERROR, 1, printk(KERN_ERR
 		      "cs4281: probe() no memory for state struct.\n"));
@@ -4404,10 +4529,18 @@
 #endif
 
 	pci_set_master(pcidev);	// enable bus mastering 
+	pci_read_config_word(pcidev, PCI_SUBSYSTEM_VENDOR_ID, &s->ss_vendor);
+	pci_read_config_word(pcidev, PCI_SUBSYSTEM_ID, &s->ss_id);
+	printk(KERN_INFO "cs4281: Subsystem vendor/id (%04X:%04X) IRQ %d\n",
+		s->ss_vendor, s->ss_id, s->irq);
+	
+	if(!recsrc_invalid)
+		val = cs4281_setup_record_src(s);
+	else
+		val = SOUND_MASK_MIC;
 
 	fs = get_fs();
 	set_fs(KERNEL_DS);
-	val = SOUND_MASK_LINE;
 	mixer_ioctl(s, SOUND_MIXER_WRITE_RECSRC, (unsigned long) &val);
 	for (i = 0; i < sizeof(initvol) / sizeof(initvol[0]); i++) {
 		val = initvol[i].vol;
@@ -4455,7 +4588,7 @@
 	unregister_sound_midi(s->dev_midi);
 	iounmap(s->pBA1);
 	iounmap(s->pBA0);
-	pci_set_drvdata(pci_dev,NULL);
+	pci_set_drvdata(pci_dev, s);
 	list_del(&s->list);
 	kfree(s);
 	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2, printk(KERN_INFO
@@ -4494,6 +4627,12 @@
 	       CS4281_ARCH);
 	rtn = pci_module_init(&cs4281_pci_driver);
 
+	if(rtn == -ENODEV)
+	{
+		CS_DBGOUT(CS_ERROR | CS_INIT, 1, printk( 
+			"cs4281: Unable to locate any cs4281 device with valid IDs 0x%x-0x%x\n",
+				PCI_VENDOR_ID_CIRRUS, PCI_DEVICE_ID_CRYSTAL_CS4281));
+	}
 	CS_DBGOUT(CS_INIT | CS_FUNCTION, 2,
 		  printk(KERN_INFO "cs4281: cs4281_init_module()- (%d)\n",rtn));
 	return rtn;
@@ -4510,7 +4649,7 @@
 }
 // --------------------------------------------------------------------- 
 
-MODULE_AUTHOR("gw boynton, audio@crystal.cirrus.com");
+MODULE_AUTHOR("gw boynton, pcaudio@crystal.cirrus.com");
 MODULE_DESCRIPTION("Cirrus Logic CS4281 Driver");
 MODULE_LICENSE("GPL");
 
@@ -4525,3 +4664,4 @@
 	return cs4281_init_module();
 }
 #endif
+#include "cs4281pm-24.c"
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4281/cs4281pm-24.c linux/drivers/sound/cs4281/cs4281pm-24.c
--- linux.orig/drivers/sound/cs4281/cs4281pm-24.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/cs4281/cs4281pm-24.c	Mon Jan 14 18:53:53 2002
@@ -4,7 +4,7 @@
 *
 *      Copyright (C) 2000,2001  Cirrus Logic Corp.  
 *            -- tom woller (twoller@crystal.cirrus.com) or
-*               (audio@crystal.cirrus.com).
+*               (pcaudio@crystal.cirrus.com).
 *
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
@@ -25,28 +25,44 @@
 *******************************************************************************/
 
 #ifndef NOT_CS4281_PM
-#include <linux/pm.h>
 
-#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
-#define cs_pm_unregister_all(a) pm_unregister_all((a));
+#if CS4281_PCI_PM_SUPPORT_ENABLE
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,12)
+static int cs4281_suspend_tbl(struct pci_dev *pcidev, u32 unused)
+{
+	struct cs4281_state *s = pci_get_drvdata(pcidev);
+	cs4281_suspend(s);
+	return 0;
+}
 
-int cs4281_suspend(struct cs4281_state *s);
-int cs4281_resume(struct cs4281_state *s);
-/* 
-* for now (12/22/00) only enable the pm_register PM support.
-* allow these table entries to be null.
-#define CS4281_SUSPEND_TBL cs4281_suspend_tbl
-#define CS4281_RESUME_TBL cs4281_resume_tbl
-*/
-#define CS4281_SUSPEND_TBL cs4281_null
-#define CS4281_RESUME_TBL cs4281_null
+static int cs4281_resume_tbl(struct pci_dev *pcidev)
+{
+	struct cs4281_state *s = pci_get_drvdata(pcidev);
+	cs4281_resume(s);
+	return 0;
+}
+#else
+void cs4281_suspend_tbl(struct pci_dev *pcidev)
+{
+	struct cs4281_state *s = pci_get_drvdata(pcidev);
+	cs4281_suspend(s);
+	return;
+}
 
+void cs4281_resume_tbl(struct pci_dev *pcidev)
+{
+	struct cs4281_state *s = pci_get_drvdata(pcidev);
+	cs4281_resume(s);
+	return;
+}
+#endif
+#else
 int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
 {
 	struct cs4281_state *state;
 
-	CS_DBGOUT(CS_PM, 2, printk(KERN_INFO 
-		"cs4281: cs4281_pm_callback dev=0x%x rqst=0x%x state=%d\n",
+	CS_DBGOUT(CS_PM, 2, printk(
+		"cs4281: cs4281_pm_callback()+ dev=0x%x rqst=0x%x state=%d\n",
 			(unsigned)dev,(unsigned)rqst,(unsigned)data));
 	state = (struct cs4281_state *) dev->data;
 	if (state) {
@@ -73,12 +89,13 @@
 				break;
 		}
 	}
+	CS_DBGOUT(CS_PM, 2, printk("cs4281: cs4281_pm_callback()- 0\n"));
 
 	return 0;
 }
+#endif //#if CS4281_PCI_PM_SUPPORT_ENABLE
 
-#else /* CS4281_PM */
-#define CS4281_SUSPEND_TBL cs4281_null
-#define CS4281_RESUME_TBL cs4281_null
-#endif /* CS4281_PM */
-
+#else /* NOT_CS4281_PM */
+#define CS4281_SUSPEND_TBL cs4281_null_suspend
+#define CS4281_RESUME_TBL cs4281_null_resume
+#endif /* NOT_CS4281_PM */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs4281/cs4281pm-24.h linux/drivers/sound/cs4281/cs4281pm-24.h
--- linux.orig/drivers/sound/cs4281/cs4281pm-24.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/cs4281/cs4281pm-24.h	Mon Jan 14 18:54:19 2002
@@ -0,0 +1,67 @@
+/*******************************************************************************
+*
+*      "cs4281pm-24.h" --  Cirrus Logic-Crystal CS4281 linux audio driver.
+*
+*      Copyright (C) 2000,2001  Cirrus Logic Corp.  
+*            -- tom woller (twoller@crystal.cirrus.com) or
+*               (audio@crystal.cirrus.com).
+*
+*      This program is free software; you can redistribute it and/or modify
+*      it under the terms of the GNU General Public License as published by
+*      the Free Software Foundation; either version 2 of the License, or
+*      (at your option) any later version.
+*
+*      This program is distributed in the hope that it will be useful,
+*      but WITHOUT ANY WARRANTY; without even the implied warranty of
+*      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*      GNU General Public License for more details.
+*
+*      You should have received a copy of the GNU General Public License
+*      along with this program; if not, write to the Free Software
+*      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+* 2001.04.05 trw - new file. 
+*
+*******************************************************************************/
+#ifndef NOT_CS4281_PM
+#include <linux/pm.h>
+#include <linux/config.h>
+#include "cs4281pm.h"
+
+//#define CS4281_PCI_PM_SUPPORT_ENABLE 1
+#if CS4281_PCI_PM_SUPPORT_ENABLE
+#define cs_pm_register(a, b, c) 0;
+#define cs_pm_unregister_all(a)
+/* 
+* for now (12/22/00) only enable the pm_register PM support.
+* allow these table entries to be null.
+*/
+#define CS4281_SUSPEND_TBL cs4281_suspend_tbl
+#define CS4281_RESUME_TBL cs4281_resume_tbl
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,12)
+static int cs4281_suspend_tbl(struct pci_dev *pcidev, u32 unused);
+static int cs4281_resume_tbl(struct pci_dev *pcidev);
+#else 
+void cs4281_suspend_tbl(struct pci_dev *pcidev);
+void cs4281_resume_tbl(struct pci_dev *pcidev);
+#endif //LINUX_VERSION
+#else //CS4281_PCI_PM_SUPPORT_ENABLE
+#ifdef CONFIG_PM
+int cs4281_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data);
+#define cs_pm_register(a, b, c) pm_register((a), (b), (c));
+#define cs_pm_unregister_all(a) pm_unregister_all((a));
+#define CS4281_SUSPEND_TBL cs4281_null_suspend
+#define CS4281_RESUME_TBL cs4281_null_resume
+#else
+#define cs_pm_register(a, b, c) 0;
+#define cs_pm_unregister_all(a)
+#define CS4281_SUSPEND_TBL cs4281_null_suspend
+#define CS4281_RESUME_TBL cs4281_null_resume
+#endif //CONFIG_PM
+#endif //CS4281_PCI_PM_SUPPORT_ENABLE
+#else
+#define cs_pm_register(a, b, c) 0;
+#define cs_pm_unregister_all(a)
+#define CS4281_SUSPEND_TBL cs4281_null_suspend
+#define CS4281_RESUME_TBL cs4281_null_resume
+#endif //NOT_CS4281_PM
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/cs46xx.c linux/drivers/sound/cs46xx.c
--- linux.orig/drivers/sound/cs46xx.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/cs46xx.c	Wed Feb  6 20:47:54 2002
@@ -1910,11 +1910,8 @@
                                 break;
                         if (signal_pending(current))
                                 break;
-                        if (file->f_flags & O_NONBLOCK) {
-                                remove_wait_queue(&card->midi.owait, &wait);
-                                current->state = TASK_RUNNING;
-                                return -EBUSY;
-                        }                      
+                        if (file->f_flags & O_NONBLOCK)
+                        	break;
                         tmo = (count * HZ) / 3100;
                         if (!schedule_timeout(tmo ? : 1) && tmo)
                                 printk(KERN_DEBUG "cs46xx: midi timed out??\n");
@@ -2117,7 +2114,7 @@
 	
 	down(&state->sem);
 	if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
-		goto out;
+		goto out2;
 
 	add_wait_queue(&state->dmabuf.wait, &wait);
 	while (count > 0) {
@@ -2187,8 +2184,9 @@
                 start_adc(state);
 	}
 out:
-	up(&state->sem);
 	remove_wait_queue(&state->dmabuf.wait, &wait);
+out2:
+	up(&state->sem);
 	set_current_state(TASK_RUNNING);
 	CS_DBGOUT(CS_WAVE_READ | CS_FUNCTION, 4, 
 		printk("cs46xx: cs_read()- %d\n",ret) );
@@ -2213,6 +2211,8 @@
 	state = (struct cs_state *)card->states[1];
 	if(!state)
 		return -ENODEV;
+	if (!access_ok(VERIFY_READ, buffer, count))
+		return EFAULT;
 	dmabuf = &state->dmabuf;
 
 	if (ppos != &file->f_pos)
@@ -2227,11 +2227,6 @@
 
 	if (!dmabuf->ready && (ret = __prog_dmabuf(state)))
 		goto out;
-	if (!access_ok(VERIFY_READ, buffer, count))
-	{
-	ret = -EFAULT;
-	goto out;
-	}
 	add_wait_queue(&state->dmabuf.wait, &wait);
 	ret = 0;
 /*
@@ -5249,6 +5244,8 @@
 	{0x1681, 0x0052, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
 	{0x1681, 0x0053, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
 	{0x1681, 0x0054, "Hercules Game Theatre XP", amp_hercules, NULL, NULL},
+	{0x1681, 0xa010, "Hercules Fortissimo II", amp_none, NULL, NULL},
+	
 	/* Not sure if the 570 needs the clkrun hack */
 	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", amp_none, NULL, clkrun_hack},
 	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", amp_none, NULL, clkrun_hack},
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/Config.in linux/drivers/sound/dmasound/Config.in
--- linux.orig/drivers/sound/dmasound/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/Config.in	Wed Dec 26 16:47:48 2001
@@ -4,7 +4,7 @@
    dep_tristate '  Atari DMA sound support' CONFIG_DMASOUND_ATARI $CONFIG_SOUND
 fi
 if [ "$CONFIG_ALL_PPC" = "y" ]; then
-   dep_tristate '  PowerMac DMA sound support' CONFIG_DMASOUND_AWACS $CONFIG_SOUND
+   dep_tristate '  PowerMac DMA sound support' CONFIG_DMASOUND_PMAC $CONFIG_SOUND
 fi
 if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_APUS" = "y" ]; then
    dep_tristate '  Amiga DMA sound support' CONFIG_DMASOUND_PAULA $CONFIG_SOUND
@@ -13,15 +13,27 @@
    dep_tristate '  Q40 sound support' CONFIG_DMASOUND_Q40 $CONFIG_SOUND
 fi
 if [ "$CONFIG_DMASOUND_ATARI" = "y" -o \
-     "$CONFIG_DMASOUND_AWACS" = "y" -o \
+     "$CONFIG_DMASOUND_PMAC" = "y" -o \
      "$CONFIG_DMASOUND_PAULA" = "y" -o \
      "$CONFIG_DMASOUND_Q40" = "y" ]; then
    define_tristate CONFIG_DMASOUND y
 else
    if [ "$CONFIG_DMASOUND_ATARI" = "m" -o \
-	"$CONFIG_DMASOUND_AWACS" = "m" -o \
+	"$CONFIG_DMASOUND_PMAC" = "m" -o \
 	"$CONFIG_DMASOUND_PAULA" = "m" -o \
 	"$CONFIG_DMASOUND_Q40" = "m" ]; then
       define_tristate CONFIG_DMASOUND m
    fi
 fi
+
+# the new dmasound_pmac driver needs access to the i2c bus
+if [ "$CONFIG_DMASOUND_PMAC" = "y" ] ; then
+   define_tristate CONFIG_I2C y
+   define_tristate CONFIG_I2C_KEYWEST y
+else
+   if [ "$CONFIG_DMASOUND_PMAC" = "m" ] ; then
+   define_tristate CONFIG_I2C m
+   define_tristate CONFIG_I2C_KEYWEST m
+   fi
+fi
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/Makefile linux/drivers/sound/dmasound/Makefile
--- linux.orig/drivers/sound/dmasound/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/Makefile	Wed Dec 26 16:47:48 2001
@@ -7,15 +7,21 @@
 #
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
+O_TARGET = dmasound.o
+
 export-objs := dmasound_core.o
 
-obj-$(CONFIG_DMASOUND_ATARI)  += dmasound_core.o dmasound_atari.o
-obj-$(CONFIG_DMASOUND_AWACS)  += dmasound_core.o dmasound_awacs.o
-obj-$(CONFIG_DMASOUND_PAULA)  += dmasound_core.o dmasound_paula.o
-obj-$(CONFIG_DMASOUND_Q40)    += dmasound_core.o dmasound_q40.o
-
-ifeq ($(CONFIG_DMASOUND),y)
-  O_TARGET = dmasound.o
-endif
+list-multi := dmasound_pmac.o
+
+dmasound_pmac-objs := dmasound_awacs.o trans_16.o tas3001c.o dac3550a.o
+
+obj-$(CONFIG_DMASOUND)        += dmasound_core.o
+obj-$(CONFIG_DMASOUND_ATARI)  += dmasound_atari.o
+obj-$(CONFIG_DMASOUND_PMAC)   += dmasound_pmac.o
+obj-$(CONFIG_DMASOUND_PAULA)  += dmasound_paula.o
+obj-$(CONFIG_DMASOUND_Q40)    += dmasound_q40.o
 
 include $(TOPDIR)/Rules.make
+
+dmasound_pmac.o: $(dmasound_pmac-objs)
+	$(LD) $(EXTRA_LDFLAGS) -r -o $@ $(dmasound_pmac-objs)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/awacs_defs.h linux/drivers/sound/dmasound/awacs_defs.h
--- linux.orig/drivers/sound/dmasound/awacs_defs.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/awacs_defs.h	Wed Dec 26 16:47:48 2001
@@ -71,17 +71,20 @@
 /* ------- - --- ----- - ------ */
 #define MASK_GAINRIGHT	(0xf)		/* Gain Right Mask */
 #define MASK_GAINLEFT	(0xf << 4)	/* Gain Left Mask */
-#define MASK_GAINLINE	(0x1 << 8)	/* Change Gain for Line??? */
-#define MASK_GAINMIC	(0x0 << 8)	/* Change Gain for Mic??? */
+#define MASK_GAINLINE	(0x1 << 8)	/* Disable Mic preamp */
+#define MASK_GAINMIC	(0x0 << 8)	/* Enable Mic preamp */
 
 #define MASK_MUX_CD	(0x1 << 9)	/* Select CD in MUX */
-#define MASK_MUX_AUDIN	(0x1 << 10)	/* Select Audio In in MUX */
-#define MASK_MUX_MIC	(0x1 << 11)	/* Select Mic in MUX */
+#define MASK_MUX_MIC	(0x1 << 10)	/* Select Mic in MUX */
+#define MASK_MUX_AUDIN	(0x1 << 11)	/* Select Audio In in MUX */
 #define MASK_MUX_LINE	MASK_MUX_AUDIN
 
 #define GAINRIGHT(x)	((x) & MASK_GAINRIGHT)
 #define GAINLEFT(x)	(((x) << 4) & MASK_GAINLEFT)
 
+#define DEF_CD_GAIN 0x00bb
+#define DEF_MIC_GAIN 0x00cc
+
 /* Address 1 Bit Masks */
 /* ------- - --- ----- */
 #define MASK_ADDR1RES1	(0x3)		/* Reserved */
@@ -93,7 +96,10 @@
 #define MASK_ADDR1RES2	(0x1 << 8)	/* Reserved */
 #define MASK_AMUTE	(0x1 << 9)	/* Output A (Headphone) Mute when 1 */
 #define MASK_HDMUTE	MASK_AMUTE
-#define MASK_PAROUT	(0x3 << 10)	/* Parallel Out (???) */
+#define MASK_PAROUT0	(0x1 << 10)	/* Parallel Output 0 */
+#define MASK_PAROUT1	(0x2 << 10)	/* Parallel Output 1 */
+
+#define MASK_MIC_BOOST  (0x4)           /* screamer mic boost */
 
 #define SAMPLERATE_48000	(0x0 << 3)	/* 48 or 44.1 kHz */
 #define SAMPLERATE_32000	(0x1 << 3)	/* 32 or 29.4 kHz */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dac3550a.c linux/drivers/sound/dmasound/dac3550a.c
--- linux.orig/drivers/sound/dmasound/dac3550a.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/dac3550a.c	Mon Jan  7 13:00:34 2002
@@ -0,0 +1,269 @@
+/*
+ * Driver for the i2c/i2s based DAC3550a sound chip used
+ * on some Apple iBooks. Also known as "DACA".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+#include "dmasound.h"
+
+/* FYI: This code was derived from the tas3001c.c Texas/Tumbler mixer
+ * control code, as well as info derived from the AppleDACAAudio driver
+ * from Darwin CVS (main thing I derived being register numbers and 
+ * values, as well as when to make the calls). */
+
+#define I2C_DRIVERID_DACA (0xFDCB)
+
+#define DACA_VERSION	"0.1"
+#define DACA_DATE "20010930"
+
+static int cur_left_vol;
+static int cur_right_vol;
+static struct i2c_client * daca_client = NULL;
+
+static int daca_attach_adapter(struct i2c_adapter *adapter);
+
+static int daca_attach_adapter(struct i2c_adapter *adapter);
+static int daca_detect_client(struct i2c_adapter *adapter, int address);
+static int daca_detach_client(struct i2c_client *client);
+
+/* Unique ID allocation */
+static int daca_id;
+static int daca_initialized;
+
+struct daca_data
+{
+	int arf; /* place holder for furture use */
+};
+
+struct i2c_driver daca_driver = {  
+	name:		"DAC3550A driver  V " DACA_VERSION,
+	id:		I2C_DRIVERID_DACA,
+	flags:		I2C_DF_NOTIFY,
+	attach_adapter:	&daca_attach_adapter,
+	detach_client:	&daca_detach_client,
+	command:	NULL,
+	inc_use:	NULL, /* &daca_inc_use, */
+	dec_use:	NULL  /* &daca_dev_use  */
+  };
+
+
+#define VOL_MAX ((1<<20) - 1)
+
+void
+daca_get_volume(uint * left_vol, uint  *right_vol)
+{
+	*left_vol = cur_left_vol >> 5;
+	*right_vol = cur_right_vol >> 5;
+}
+
+int
+daca_set_volume(uint left_vol, uint right_vol)
+{
+	unsigned short voldata;
+  
+	if (!daca_client)
+		return -1;
+
+	/* Derived from experience, not from any specific values */
+	left_vol <<= 5;
+	right_vol <<= 5;
+
+	if (left_vol > VOL_MAX)
+		left_vol = VOL_MAX;
+	if (right_vol > VOL_MAX)
+		right_vol = VOL_MAX;
+
+	voldata = ((left_vol >> 14)  & 0x3f) << 8;
+	voldata |= (right_vol >> 14)  & 0x3f;
+  
+	if (i2c_smbus_write_word_data(daca_client, 2, voldata) < 0) {
+		printk("daca: failed to set volume \n");
+		return -1; 
+	}
+
+	cur_left_vol = left_vol;
+	cur_right_vol = right_vol;
+  
+	return 0;
+}
+
+int
+daca_leave_sleep(void)
+{
+	if (!daca_client)
+		return -1;
+  
+	/* Do a short sleep, just to make sure I2C bus is awake and paying
+	 * attention to us
+	 */
+	wait_ms(20);
+	/* Write the sample rate reg the value it needs */
+	i2c_smbus_write_byte_data(daca_client, 1, 8);
+	daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
+	/* Another short delay, just to make sure the other I2C bus writes
+	 * have taken...
+	 */
+	wait_ms(20);
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, use 5-volt mode */
+	i2c_smbus_write_byte_data(daca_client, 3, 0x45);
+
+	return 0;
+}
+
+int
+daca_enter_sleep(void)
+{
+	if (!daca_client)
+		return -1;
+
+	i2c_smbus_write_byte_data(daca_client, 1, 8);
+	daca_set_volume(cur_left_vol >> 5, cur_right_vol >> 5);
+
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, enter low-power mode, use 5-volt mode
+	 */
+	i2c_smbus_write_byte_data(daca_client, 3, 0x65);
+
+	return 0;
+}
+
+static int
+daca_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!strncmp(adapter->name, "mac-io", 6))
+		daca_detect_client(adapter, 0x4d);
+	return 0;
+}
+
+static int
+daca_init_client(struct i2c_client * new_client)
+{
+	/* 
+	 * Probe is not working with the current i2c-keywest
+	 * driver. We try to use addr 0x4d on each adapters
+	 * instead, by setting the format register.
+	 * 
+	 * FIXME: I'm sure that can be obtained from the
+	 * device-tree. --BenH.
+	 */
+  
+	/* Write the global config reg - invert right power amp,
+	 * DAC on, use 5-volt mode
+	 */
+	if (i2c_smbus_write_byte_data(new_client, 3, 0x45))
+		return -1;
+
+	i2c_smbus_write_byte_data(new_client, 1, 8);
+	daca_client = new_client;
+	daca_set_volume(15000, 15000);
+
+	return 0;
+}
+
+static int
+daca_detect_client(struct i2c_adapter *adapter, int address)
+{
+	int rc = 0;
+	struct i2c_client *new_client;
+
+	struct daca_data *data;
+	const char *client_name = "DAC 3550A Digital Equalizer";
+
+	new_client = kmalloc(
+			sizeof(struct i2c_client) + sizeof(struct daca_data),
+			GFP_KERNEL);
+	if (!new_client) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	/* This is tricky, but it will set the data to the right value. */
+	new_client->data = new_client + 1;
+	data = (struct daca_data *) (new_client->data);
+
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &daca_driver;
+	new_client->flags = 0;
+
+	strcpy(new_client->name,client_name);
+
+	new_client->id = daca_id++; /* Automatically unique */
+  
+	if (daca_init_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+
+	/* Tell the i2c layer a new client has arrived */
+	if (i2c_attach_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+bail:
+	if (rc && new_client)
+		kfree(new_client);
+	return rc;
+}
+
+
+static int
+daca_detach_client(struct i2c_client *client)
+{
+	if (client == daca_client)
+		daca_client = NULL;
+
+  	i2c_detach_client(client);
+	kfree(client);
+
+	return 0;
+}
+
+int
+daca_cleanup(void)
+{
+	if (!daca_initialized)
+		return -ENODEV;
+	i2c_del_driver(&daca_driver);
+	daca_initialized = 0;
+
+	return 0;
+}
+
+int
+daca_init(void)
+{
+	int rc;
+
+	if (daca_initialized)
+		return 0;
+		
+	printk("dac3550a driver version %s (%s)\n",DACA_VERSION,DACA_DATE);
+    
+	if ((rc = i2c_add_driver(&daca_driver))) {
+		printk("dac3550a: Driver registration failed, module not inserted.\n");
+		daca_cleanup();
+		return rc;
+	}
+	daca_initialized = 1;
+
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound.h linux/drivers/sound/dmasound/dmasound.h
--- linux.orig/drivers/sound/dmasound/dmasound.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound.h	Wed Dec 26 16:47:48 2001
@@ -1,4 +1,4 @@
-
+#ifndef _dmasound_h_
 /*
  *  linux/drivers/sound/dmasound/dmasound.h
  *
@@ -10,11 +10,11 @@
  *  device for true DSP processors but it will be called something else.
  *  In v3.0 it's /dev/sndproc but this could be a temporary solution.
  */
+#define _dmasound_h_
 
-
+#include <linux/types.h>
 #include <linux/config.h>
 
-
 #define SND_NDEVS	256	/* Number of supported devices */
 #define SND_DEV_CTL	0	/* Control port /dev/mixer */
 #define SND_DEV_SEQ	1	/* Sequencer output /dev/sequencer (FM
@@ -29,23 +29,16 @@
 #define SND_DEV_SNDPROC 9	/* /dev/sndproc for programmable devices */
 #define SND_DEV_PSS	SND_DEV_SNDPROC
 
-#define DSP_DEFAULT_SPEED	8000
-
-#define ON		1
-#define OFF		0
+/* switch on various prinks */
+#define DEBUG_DMASOUND 1
 
 #define MAX_AUDIO_DEV	5
-#define MAX_MIXER_DEV	2
+#define MAX_MIXER_DEV	4
 #define MAX_SYNTH_DEV	3
 #define MAX_MIDI_DEV	6
 #define MAX_TIMER_DEV	3
 
-
 #define MAX_CATCH_RADIUS	10
-#define MIN_BUFFERS		4
-#define MIN_BUFSIZE		4	/* in KB */
-#define MAX_BUFSIZE		128	/* Limit for Amiga in KB */
-
 
 #define le2be16(x)	(((x)<<8 & 0xff00) | ((x)>>8 & 0x00ff))
 #define le2be16dbl(x)	(((x)<<8 & 0xff00ff00) | ((x)>>8 & 0x00ff00ff))
@@ -67,21 +60,34 @@
      */
 
 #undef HAS_8BIT_TABLES
-#undef HAS_14BIT_TABLES
-#undef HAS_16BIT_TABLES
 #undef HAS_RECORD
 
 #if defined(CONFIG_DMASOUND_ATARI) || defined(CONFIG_DMASOUND_ATARI_MODULE) ||\
     defined(CONFIG_DMASOUND_PAULA) || defined(CONFIG_DMASOUND_PAULA_MODULE) ||\
     defined(CONFIG_DMASOUND_Q40) || defined(CONFIG_DMASOUND_Q40_MODULE)
 #define HAS_8BIT_TABLES
+#define MIN_BUFFERS	4
+#define MIN_BUFSIZE	(1<<12)	/* in bytes (- where does this come from ?) */
+#define MIN_FRAG_SIZE	8	/* not 100% sure about this */
+#define MAX_BUFSIZE	(1<<17)	/* Limit for Amiga is 128 kb */
+#define MAX_FRAG_SIZE	15	/* allow *4 for mono-8 => stereo-16 (for multi) */
+
+#else /* is pmac and multi is off */
+
+#define MIN_BUFFERS	2
+#define MIN_BUFSIZE	(1<<8)	/* in bytes */
+#define MIN_FRAG_SIZE	8
+#define MAX_BUFSIZE	(1<<18)	/* this is somewhat arbitrary for pmac */
+#define MAX_FRAG_SIZE	16	/* need to allow *4 for mono-8 => stereo-16 */
 #endif
-#if defined(CONFIG_DMASOUND_AWACS) || defined(CONFIG_DMASOUND_AWACS_MODULE)
-#define HAS_16BIT_TABLES
+
+#define DEFAULT_N_BUFFERS 4
+#define DEFAULT_BUFF_SIZE (1<<15)
+
+#if defined(CONFIG_DMASOUND_PMAC) || defined(CONFIG_DMASOUND_PMAC_MODULE)
 #define HAS_RECORD
 #endif
 
-
     /*
      *  Initialization
      */
@@ -93,6 +99,14 @@
 #define dmasound_deinit()	do { } while (0)
 #endif
 
+/* description of the set-up applies to either hard or soft settings */
+
+typedef struct {
+    int format;		/* AFMT_* */
+    int stereo;		/* 0 = mono, 1 = stereo */
+    int size;		/* 8/16 bit*/
+    int speed;		/* speed */
+} SETTINGS;
 
     /*
      *  Machine definitions
@@ -117,30 +131,29 @@
     int (*setTreble)(int);
     int (*setGain)(int);
     void (*play)(void);
-    void (*record)(void);			/* optional */
-    void (*mixer_init)(void);			/* optional */
-    int (*mixer_ioctl)(u_int, u_long);		/* optional */
-    void (*write_sq_setup)(void);		/* optional */
-    void (*read_sq_setup)(void);		/* optional */
-    void (*sq_open)(void);			/* optional */
-    int (*state_info)(char *);			/* optional */
-    void (*abort_read)(void);			/* optional */
+    void (*record)(void);		/* optional */
+    void (*mixer_init)(void);		/* optional */
+    int (*mixer_ioctl)(u_int, u_long);	/* optional */
+    int (*write_sq_setup)(void);	/* optional */
+    int (*read_sq_setup)(void);		/* optional */
+    int (*sq_open)(mode_t);		/* optional */
+    int (*state_info)(char *, size_t);	/* optional */
+    void (*abort_read)(void);		/* optional */
     int min_dsp_speed;
+    int max_dsp_speed;
+    int version ;
+    int hardware_afmts ;		/* OSS says we only return h'ware info */
+					/* when queried via SNDCTL_DSP_GETFMTS */
+    int capabilities ;		/* low-level reply to SNDCTL_DSP_GETCAPS */
+    SETTINGS default_hard ;	/* open() or init() should set something valid */
+    SETTINGS default_soft ;	/* you can make it look like old OSS, if you want to */
 } MACHINE;
 
-
     /*
      *  Low level stuff
      */
 
 typedef struct {
-    int format;		/* AFMT_* */
-    int stereo;		/* 0 = mono, 1 = stereo */
-    int size;		/* 8/16 bit*/
-    int speed;		/* speed */
-} SETTINGS;
-
-typedef struct {
     ssize_t (*ct_ulaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
     ssize_t (*ct_alaw)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
     ssize_t (*ct_s8)(const u_char *, size_t, u_char *, ssize_t *, ssize_t);
@@ -170,11 +183,10 @@
 
 extern struct sound_settings dmasound;
 
+#ifdef HAS_8BIT_TABLES
 extern char dmasound_ulaw2dma8[];
 extern char dmasound_alaw2dma8[];
-extern short dmasound_ulaw2dma16[];
-extern short dmasound_alaw2dma16[];
-
+#endif
 
     /*
      *  Mid level stuff
@@ -207,14 +219,17 @@
 
 struct sound_queue {
     /* buffers allocated for this queue */
-    int numBufs;
-    int bufSize;			/* in bytes */
+    int numBufs;		/* real limits on what the user can have */
+    int bufSize;		/* in bytes */
     char **buffers;
 
     /* current parameters */
-    int max_count;
-    int block_size;			/* in bytes */
-    int max_active;
+    int locked ;		/* params cannot be modified when != 0 */
+    int user_frags ;		/* user requests this many */
+    int user_frag_size ;	/* of this size */
+    int max_count;		/* actual # fragments <= numBufs */
+    int block_size;		/* internal block size in bytes */
+    int max_active;		/* in-use fragments <= max_count */
 
     /* it shouldn't be necessary to declare any of these volatile */
     int front, rear, count;
@@ -230,19 +245,35 @@
     int active;
     wait_queue_head_t action_queue, open_queue, sync_queue;
     int open_mode;
-    int busy, syncing;
+    int busy, syncing, xruns, died;
 };
 
 #define SLEEP(queue)		interruptible_sleep_on_timeout(&queue, HZ)
 #define WAKE_UP(queue)		(wake_up_interruptible(&queue))
 
 extern struct sound_queue dmasound_write_sq;
+#ifdef HAS_RECORD
 extern struct sound_queue dmasound_read_sq;
+#endif
 
 #define write_sq	dmasound_write_sq
+#ifdef HAS_RECORD
 #define read_sq		dmasound_read_sq
+#endif
 
 extern int dmasound_catchRadius;
 
 #define catchRadius	dmasound_catchRadius
 
+/* define the value to be put in the byte-swap reg in mac-io
+   when we want it to swap for us.
+*/
+#define BS_VAL 1
+
+static inline void wait_ms(unsigned int ms)
+{
+	current->state = TASK_UNINTERRUPTIBLE;
+	schedule_timeout(1 + ms * HZ / 1000);
+}
+
+#endif /* _dmasound_h_ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_atari.c linux/drivers/sound/dmasound/dmasound_atari.c
--- linux.orig/drivers/sound/dmasound/dmasound_atari.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound_atari.c	Wed Dec 26 16:47:48 2001
@@ -1,10 +1,16 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_atari.c
  *
  *  Atari TT and Falcon DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *  01/02/2001 [0.3] - put in default hard/soft settings.
  */
 
 
@@ -21,10 +27,11 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_ATARI_REVISION 0
+#define DMASOUND_ATARI_EDITION 3
 
 extern void atari_microwire_cmd(int cmd);
 
-
 static int is_falcon;
 static int write_sq_ignore_int = 0;	/* ++TeSche: used for Falcon */
 
@@ -137,9 +144,9 @@
 static int TTMixerIoctl(u_int cmd, u_long arg);
 static int FalconMixerIoctl(u_int cmd, u_long arg);
 static void AtaWriteSqSetup(void);
-static void AtaSqOpen(void);
-static int TTStateInfo(char *buffer);
-static int FalconStateInfo(char *buffer);
+static int AtaSqOpen(mode_t mode);
+static int TTStateInfo(char *buffer, size_t space);
+static int FalconStateInfo(char *buffer, size_t space);
 
 
 /*** Translations ************************************************************/
@@ -1438,43 +1445,73 @@
 	return AtaMixerIoctl(cmd, arg);
 }
 
-static void AtaWriteSqSetup(void)
+static int AtaWriteSqSetup(void)
 {
 	write_sq_ignore_int = 0;
+	return 0 ;
 }
 
-static void AtaSqOpen(void)
+static int AtaSqOpen(mode_t mode)
 {
 	write_sq_ignore_int = 1;
+	return 0 ;
 }
 
-static int TTStateInfo(char *buffer)
+static int TTStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
-	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-40...0]\n",
+	len += sprintf(buffer+len, "\tvol left  %ddB [-40...  0]\n",
 		       dmasound.volume_left);
-	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-40...0]\n",
+	len += sprintf(buffer+len, "\tvol right %ddB [-40...  0]\n",
 		       dmasound.volume_right);
-	len += sprintf(buffer+len, "\tsound.bass = %ddB [-12...+12]\n",
+	len += sprintf(buffer+len, "\tbass      %ddB [-12...+12]\n",
 		       dmasound.bass);
-	len += sprintf(buffer+len, "\tsound.treble = %ddB [-12...+12]\n",
+	len += sprintf(buffer+len, "\ttreble    %ddB [-12...+12]\n",
 		       dmasound.treble);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
-static int FalconStateInfo(char *buffer)
+static int FalconStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
-	len += sprintf(buffer+len, "\tsound.volume_left = %ddB [-22.5...0]\n",
+	len += sprintf(buffer+len, "\tvol left  %ddB [-22.5 ... 0]\n",
 		       dmasound.volume_left);
-	len += sprintf(buffer+len, "\tsound.volume_right = %ddB [-22.5...0]\n",
+	len += sprintf(buffer+len, "\tvol right %ddB [-22.5 ... 0]\n",
 		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_atari: overflowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard_falcon = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 8195
+} ;
+
+static SETTINGS def_hard_tt = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 12517
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machTT = {
 	name:		"Atari",
@@ -1501,6 +1538,9 @@
 	sq_open:	AtaSqOpen,
 	state_info:	TTStateInfo,
 	min_dsp_speed:	6258,
+	version:	((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	hardware_afmts:	AFMT_S8,  /* h'ware-supported formats *only* here */
+	capabilities:	 DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
 };
 
 static MACHINE machFalcon = {
@@ -1525,6 +1565,9 @@
 	sq_open:	AtaSqOpen,
 	state_info:	FalconStateInfo,
 	min_dsp_speed:	8195,
+	version:	((DMASOUND_ATARI_REVISION<<8) | DMASOUND_ATARI_EDITION),
+	hardware_afmts:	(AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+	capabilities:	 DSP_CAP_BATCH	/* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -1536,9 +1579,13 @@
 	if (MACH_IS_ATARI && ATARIHW_PRESENT(PCM_8BIT)) {
 	    if (ATARIHW_PRESENT(CODEC)) {
 		dmasound.mach = machFalcon;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_falcon ;
 		is_falcon = 1;
 	    } else if (ATARIHW_PRESENT(MICROWIRE)) {
 		dmasound.mach = machTT;
+		dmasound.mach.default_soft = def_soft ;
+		dmasound.mach.default_hard = def_hard_tt ;
 		is_falcon = 0;
 	    } else
 		return -ENODEV;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_awacs.c linux/drivers/sound/dmasound/dmasound_awacs.c
--- linux.orig/drivers/sound/dmasound/dmasound_awacs.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound_awacs.c	Mon Feb  4 18:47:24 2002
@@ -1,13 +1,60 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_awacs.c
  *
  *  PowerMac `AWACS' and `Burgundy' DMA Sound Driver
+ *  with some limited support for DACA & Tumbler
  *
- *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
- */
-
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
+ *  history prior to 2001/01/26.
+ *
+ *	26/01/2001 ed 0.1 Iain Sandoe
+ *		- added version info.
+ *		- moved dbdma command buffer allocation to PMacXXXSqSetup()
+ *		- fixed up beep dbdma cmd buffers
+ *
+ *	08/02/2001 [0.2]
+ *		- make SNDCTL_DSP_GETFMTS return the correct info for the h/w
+ *		- move soft format translations to a separate file
+ *		- [0.3] make SNDCTL_DSP_GETCAPS return correct info.
+ *		- [0.4] more informative machine name strings.
+ *		- [0.5]
+ *		- record changes.
+ *		- made the default_hard/soft entries.
+ *	04/04/2001 [0.6]
+ *		- minor correction to bit assignments in awacs_defs.h
+ *		- incorporate mixer changes from 2.2.x back-port.
+ *		- take out passthru as a rec input (it isn't).
+ *              - make Input Gain slider work the 'right way up'.
+ *              - try to make the mixer sliders more logical - so now the
+ *                input selectors are just two-state (>50% == ON) and the
+ *                Input Gain slider handles the rest of the gain issues.
+ *              - try to pick slider representations that most closely match
+ *                the actual use - e.g. IGain for input gain... 
+ *              - first stab at over/under-run detection.
+ *		- minor cosmetic changes to IRQ identification.
+ *		- fix bug where rates > max would be reported as supported.
+ *              - first stab at over/under-run detection.
+ *              - make use of i2c for mixer settings conditional on perch
+ *                rather than cuda (some machines without perch have cuda).
+ *              - fix bug where TX stops when dbdma status comes up "DEAD"
+ *		  so far only reported on PowerComputing clones ... but.
+ *		- put in AWACS/Screamer register write timeouts.
+ *		- part way to partitioning the init() stuff
+ *		- first pass at 'tumbler' stuff (not support - just an attempt
+ *		  to allow the driver to load on new G4s).
+ *      01/02/2002 [0.7] - BenH
+ *	        - all sort of minor bits went in since the latest update, I
+ *	          bumped the version number for that reason
+*/
+
+/* GENERAL FIXME/TODO: check that the assumptions about what is written to
+   mac-io is valid for DACA & Tumbler.
+
+   This driver is in bad need of a rewrite. The dbdma code has to be split,
+   some proper device-tree parsing code has to be written, etc...
+*/
 
+#include <linux/types.h>
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/slab.h>
@@ -18,6 +65,9 @@
 #include <linux/nvram.h>
 #include <linux/tty.h>
 #include <linux/vt_kern.h>
+#include <linux/irq.h>
+#include <linux/kmod.h>
+#include <asm/semaphore.h>
 #ifdef CONFIG_ADB_CUDA
 #include <linux/cuda.h>
 #endif
@@ -25,20 +75,30 @@
 #include <linux/pmu.h>
 #endif
 
+#include <linux/i2c-dev.h>
+
 #include <asm/uaccess.h>
 #include <asm/prom.h>
 #include <asm/machdep.h>
 #include <asm/io.h>
 #include <asm/dbdma.h>
-#include <asm/feature.h>
+#include <asm/pmac_feature.h>
 #include <asm/irq.h>
+#include <asm/nvram.h>
 
 #include "awacs_defs.h"
 #include "dmasound.h"
 
+#define DMASOUND_AWACS_REVISION	0
+#define DMASOUND_AWACS_EDITION	7
 
+#define AWACS_BURGUNDY	100	/* fake revision # for burgundy */
+#define AWACS_TUMBLER    90	/* fake revision # for tumbler */
+#define AWACS_DACA	 80	/* fake revision # for daca (ibook) */
+#define AWACS_AWACS       2     /* holding revision for AWACS */
+#define AWACS_SCREAMER    3     /* holding revision for Screamer */
 /*
- * Interrupt numbers and addresses, obtained from the device tree.
+ * Interrupt numbers and addresses, & info obtained from the device tree.
  */
 static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
 static volatile struct awacs_regs *awacs;
@@ -50,28 +110,58 @@
 
 static char awacs_name[64];
 static int awacs_revision;
-int awacs_is_screamer = 0;
-int awacs_device_id = 0;
-int awacs_has_iic = 0;
-#define AWACS_BURGUNDY	100		/* fake revision # for burgundy */
+static int awacs_sleeping;
+static DECLARE_MUTEX(dmasound_sem);
+
+static int sound_device_id;		/* exists after iMac revA */
+static int hw_can_byteswap = 1 ;	/* most pmac sound h/w can */
+
+/* model info */
+/* To be replaced with better interaction with pmac_feature.c */
+static int is_pbook_3X00;
+static int is_pbook_g3;
+
+/* expansion info */
+static int has_perch;
+static int has_ziva;
+
+/* for earlier powerbooks which need fiddling with mac-io to enable
+ * cd etc.
+*/
+static unsigned char *latch_base;
+static unsigned char *macio_base;
 
 /*
  * Space for the DBDMA command blocks.
  */
 static void *awacs_tx_cmd_space;
 static volatile struct dbdma_cmd *awacs_tx_cmds;
+static int number_of_tx_cmd_buffers = 0;
 
 static void *awacs_rx_cmd_space;
 static volatile struct dbdma_cmd *awacs_rx_cmds;
+static int number_of_rx_cmd_buffers = 0;
 
 /*
  * Cached values of AWACS registers (we can't read them).
- * Except on the burgundy. XXX
+ * Except on the burgundy (and screamer). XXX
  */
+
 int awacs_reg[8];
+int awacs_reg1_save;
+
+/* tracking values for the mixer contents
+*/
 
-#define HAS_16BIT_TABLES
-#undef HAS_8BIT_TABLES
+static int spk_vol = 0 ;
+static int line_vol = 0 ;
+static int passthru_vol = 0 ;
+
+static int ip_gain = 0 ; /* mic preamp settings */
+static int rec_lev = 0x4545 ; /* default CD gain 69 % */
+static int mic_lev = 0 ;
+static int cd_lev = 0x6363 ; /* 99 % */
+static int line_lev = 0 ;
 
 /*
  * Stuff for outputting a beep.  The values range from -327 to +327
@@ -113,20 +203,18 @@
 	-269,	-245,	-218,	-187,	-153,	-117,	-79,	-40,
 };
 
+/* beep support */
 #define BEEP_SRATE	22050	/* 22050 Hz sample rate */
 #define BEEP_BUFLEN	512
 #define BEEP_VOLUME	15	/* 0 - 100 */
 
-static int beep_volume = BEEP_VOLUME;
+static int beep_vol = BEEP_VOLUME;
 static int beep_playing = 0;
 static int awacs_beep_state = 0;
 static short *beep_buf;
+static void *beep_dbdma_cmd_space;
 static volatile struct dbdma_cmd *beep_dbdma_cmd;
 static void (*orig_mksound)(unsigned int, unsigned int);
-static int is_pbook_3400;
-static unsigned char *latch_base;
-static int is_pbook_G3;
-static unsigned char *macio_base;
 
 /* Burgundy functions */
 static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
@@ -136,6 +224,18 @@
 static void awacs_burgundy_write_mvolume(unsigned address, int volume);
 static int awacs_burgundy_read_mvolume(unsigned address);
 
+/* we will allocate a single 'emergency' dbdma cmd block to use if the
+   tx status comes up "DEAD".  This happens on some PowerComputing Pmac
+   clones, either owing to a bug in dbdma or some interaction between
+   IDE and sound.  However, this measure would deal with DEAD status if
+   if appeared elsewhere.
+
+   for the sake of memory efficiency we'll allocate this cmd as part of
+   the beep cmd stuff.
+*/
+
+static volatile struct dbdma_cmd *emergency_dbdma_cmd;
+
 #ifdef CONFIG_PMAC_PBOOK
 /*
  * Stuff for restoring after a sleep.
@@ -146,78 +246,11 @@
 };
 #endif /* CONFIG_PMAC_PBOOK */
 
-static int expand_bal;	/* Balance factor for expanding (not volume!) */
-static int expand_data;	/* Data for expanding */
-
-
-/*** Translations ************************************************************/
-
-
-/* ++TeSche: radically changed for new expanding purposes...
- *
- * These two routines now deal with copying/expanding/translating the samples
- * from user space into our buffer at the right frequency. They take care about
- * how much data there's actually to read, how much buffer space there is and
- * to convert samples into the right frequency/encoding. They will only work on
- * complete samples so it may happen they leave some bytes in the input stream
- * if the user didn't write a multiple of the current sample size. They both
- * return the number of bytes they've used from both streams so you may detect
- * such a situation. Luckily all programs should be able to cope with that.
- *
- * I think I've optimized anything as far as one can do in plain C, all
- * variables should fit in registers and the loops are really short. There's
- * one loop for every possible situation. Writing a more generalized and thus
- * parameterized loop would only produce slower code. Feel free to optimize
- * this in assembler if you like. :)
- *
- * I think these routines belong here because they're not yet really hardware
- * independent, especially the fact that the Falcon can play 16bit samples
- * only in stereo is hardcoded in both of them!
- *
- * ++geert: split in even more functions (one per format)
- */
-
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft);
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft);
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft);
-
+/* for (soft) sample rate translations */
+int expand_bal;		/* Balance factor for expanding (not volume!) */
 
 /*** Low level stuff *********************************************************/
 
-
 static void PMacOpen(void);
 static void PMacRelease(void);
 static void *PMacAlloc(unsigned int size, int flags);
@@ -244,565 +277,214 @@
 
 /*** Mid level stuff **********************************************************/
 
-
 static int PMacMixerIoctl(u_int cmd, u_long arg);
-static void PMacWriteSqSetup(void);
-static void PMacReadSqSetup(void);
+static int PMacWriteSqSetup(void);
+static int PMacReadSqSetup(void);
 static void PMacAbortRead(void);
 
+extern TRANS transAwacsNormal ;
+extern TRANS transAwacsExpand ;
+extern TRANS transAwacsNormalRead ;
+
+extern int daca_init(void);
+extern int daca_cleanup(void);
+extern int daca_set_volume(uint left_vol, uint right_vol);
+extern void daca_get_volume(uint * left_vol, uint  *right_vol);
+extern int daca_enter_sleep(void);
+extern int daca_leave_sleep(void);
+
+extern int tas_init(void);
+extern int tas_cleanup(void);
+extern int tumbler_set_volume(uint left_vol, uint right_vol);
+extern void tumbler_get_volume(uint * left_vol, uint  *right_vol);
+extern void tumbler_set_treble(int treble);
+extern void tumbler_get_treble(int *treble);
+extern void tumbler_set_bass(int bass);
+extern void tumbler_get_bass(int *bass);
+extern void tumbler_set_pcm_lvl(int pcm_lvl);
+extern void tumbler_get_pcm_lvl(int *pcm_lvl);
+extern int tumbler_enter_sleep(void);
+extern int tumbler_leave_sleep(void);
+
+#define TRY_LOCK()	\
+	if ((rc = down_interruptible(&dmasound_sem)) != 0)	\
+		return rc;
+#define LOCK()		down(&dmasound_sem);
+
+#define UNLOCK()	up(&dmasound_sem);
+
+/* We use different versions that the ones provided in dmasound.h
+ * 
+ * FIXME: Use different names ;)
+ */
+#undef IOCTL_IN
+#undef IOCTL_OUT
 
-/*** Translations ************************************************************/
-
-
-static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	short *table = dmasound.soft.format == AFMT_MU_LAW
-		? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = table[data];
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = table[data];
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = data << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = data << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-		if (get_user(data, userPtr++))
-			return -EFAULT;
-		val = (data ^ 0x80) << 8;
-		*p++ = val;
-		if (stereo) {
-			if (get_user(data, userPtr++))
-				return -EFAULT;
-			val = (data ^ 0x80) << 8;
-		}
-		*p++ = val;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			if (get_user(data, up++))
-				return -EFAULT;
-			*fp++ = data;
-			*fp++ = data;
-			count--;
-		}
-	} else {
-		if (copy_from_user(fp, userPtr, count * 4))
-			return -EFAULT;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
-}
+#define IOCTL_IN(arg, ret)	\
+	rc = get_user(ret, (int *)(arg)); \
+	if (rc) break;
+#define IOCTL_OUT(arg, ret)	\
+	ioctl_return2((int *)(arg), ret)
 
-static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		int data;
-		if (get_user(data, up++))
-			return -EFAULT;
-		data ^= mask;
-		*fp++ = data;
-		if (stereo) {
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-		}
-		*fp++ = data;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
+static inline int ioctl_return2(int *addr, int value)
+{
+	return value < 0 ? value : put_user(value, addr);
 }
 
 
-static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned short *table = (unsigned short *)
-		(dmasound.soft.format == AFMT_MU_LAW
-		 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
-	unsigned int data = expand_data;
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int utotal, ftotal;
-	int stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = table[c];
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + table[c];
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
+/*** AE - TUMBLER START *********************************************************/
 
 
-static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = c << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + (c << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
-}
+int gpio_audio_reset, gpio_audio_reset_pol;
+int gpio_amp_mute, gpio_amp_mute_pol;
+int gpio_headphone_mute, gpio_headphone_mute_pol;
+int gpio_headphone_detect, gpio_headphone_detect_pol;
+int gpio_headphone_irq;
 
+int
+setup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol)
+{
+	struct device_node *np;
+	u32* pp;
+	
+	np = find_devices("gpio");
+	if (!np)
+		return -ENODEV;
 
-static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		u_char c;
-		if (bal < 0) {
-			if (userCount == 0)
+	np = np->child;
+	while(np != 0) {
+		if (name) {
+			char *property = get_property(np,"audio-gpio",NULL);
+			if (property != 0 && strcmp(property,name) == 0)
 				break;
-			if (get_user(c, userPtr++))
-				return -EFAULT;
-			data = (c ^ 0x80) << 8;
-			if (stereo) {
-				if (get_user(c, userPtr++))
-					return -EFAULT;
-				data = (data << 16) + ((c ^ 0x80) << 8);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 2: utotal;
+		} else if (compatible && device_is_compatible(np, compatible))
+			break;
+		np = np->sibling;
+	}
+	if (!np)
+		return -ENODEV;
+	pp = (u32 *)get_property(np, "AAPL,address", NULL);
+	if (!pp)
+		return -ENODEV;
+	*gpio_addr = (*pp) & 0x0000ffff;
+	pp = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+	if (pp)
+		*gpio_pol = *pp;
+	else
+		*gpio_pol = 1;
+	if (np->n_intrs > 0)
+		return np->intrs[0].line;
+	
+	return 0;
 }
 
-
-static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + c;
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
+static inline void
+write_audio_gpio(int gpio_addr, int data)
+{
+	if (!gpio_addr)
+		return;
+	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_addr, data ? 0x05 : 0x04);
 }
 
+static inline int
+read_audio_gpio(int gpio_addr)
+{
+	if (!gpio_addr)
+		return 0;
+	return ((pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_addr, 0) & 0x02) !=0);
+}
 
-static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
-			    u_char frame[], ssize_t *frameUsed,
-			    ssize_t frameLeft)
-{
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	unsigned int *p = (unsigned int *) &frame[*frameUsed];
-	unsigned int data = expand_data;
-	unsigned short *up = (unsigned short *) userPtr;
-	int bal = expand_bal;
-	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
-	int stereo = dmasound.soft.stereo;
-	int utotal, ftotal;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	ftotal = frameLeft;
-	utotal = userCount;
-	while (frameLeft) {
-		unsigned short c;
-		if (bal < 0) {
-			if (userCount == 0)
-				break;
-			if (get_user(data, up++))
-				return -EFAULT;
-			data ^= mask;
-			if (stereo) {
-				if (get_user(c, up++))
-					return -EFAULT;
-				data = (data << 16) + (c ^ mask);
-			} else
-				data = (data << 16) + data;
-			userCount--;
-			bal += hSpeed;
-		}
-		*p++ = data;
-		frameLeft--;
-		bal -= sSpeed;
-	}
-	expand_bal = bal;
-	expand_data = data;
-	*frameUsed += (ftotal - frameLeft) * 4;
-	utotal -= userCount;
-	return stereo? utotal * 4: utotal * 2;
-}
-
-static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = val >> 8;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = val >> 8;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
-			  u_char frame[], ssize_t *frameUsed,
-			  ssize_t frameLeft)
-{
-	ssize_t count, used;
-	short *p = (short *) &frame[*frameUsed];
-	int val, stereo = dmasound.soft.stereo;
-
-	frameLeft >>= 2;
-	if (stereo)
-		userCount >>= 1;
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		u_char data;
-
-		val = *p++;
-		data = (val >> 8) ^ 0x80;
-		if (put_user(data, (u_char *)userPtr++))
-			return -EFAULT;
-		if (stereo) {
-			val = *p;
-			data = (val >> 8) ^ 0x80;
-			if (put_user(data, (u_char *)userPtr++))
-				return -EFAULT;
-		}
-		p++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 2: used;
-}
-
-
-static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	if (!stereo) {
-		short *up = (short *) userPtr;
-		while (count > 0) {
-			short data;
-			data = *fp;
-			if (put_user(data, up++))
-				return -EFAULT;
-			fp+=2;
-			count--;
-		}
+static void
+headphone_intr(int irq, void *devid, struct pt_regs *regs)
+{
+	if (read_audio_gpio(gpio_headphone_detect) == gpio_headphone_detect_pol) {
+		printk(KERN_INFO "Audio jack plugged, muting speakers.\n");
+		write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol);
+		write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
 	} else {
-		if (copy_to_user((u_char *)userPtr, fp, count * 4))
-			return -EFAULT;
+		printk(KERN_INFO "Audio jack unplugged, enabling speakers.\n");
+		write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
+		write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol);
 	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
 }
 
-static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
-			   u_char frame[], ssize_t *frameUsed,
-			   ssize_t frameLeft)
-{
-	ssize_t count, used;
-	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
-	int stereo = dmasound.soft.stereo;
-	short *fp = (short *) &frame[*frameUsed];
-	short *up = (short *) userPtr;
-
-	frameLeft >>= 2;
-	userCount >>= (stereo? 2: 1);
-	used = count = min_t(unsigned long, userCount, frameLeft);
-	while (count > 0) {
-		int data;
-
-		data = *fp++;
-		data ^= mask;
-		if (put_user(data, up++))
-			return -EFAULT;
-		if (stereo) {
-			data = *fp;
-			data ^= mask;
-			if (put_user(data, up++))
-				return -EFAULT;
-		}
-		fp++;
-		count--;
-	}
-	*frameUsed += used * 4;
-	return stereo? used * 4: used * 2;
+
+/* Initialize tumbler */
+
+static int
+awacs_tumbler_init(void)
+{
+	setup_audio_gpio(
+		"audio-hw-reset",
+		NULL,
+		&gpio_audio_reset,
+		&gpio_audio_reset_pol);
+	setup_audio_gpio(
+		"amp-mute",
+		NULL,
+		&gpio_amp_mute,
+		&gpio_amp_mute_pol);
+	setup_audio_gpio("headphone-mute",
+		NULL,
+		&gpio_headphone_mute,
+		&gpio_headphone_mute_pol);
+	gpio_headphone_irq = setup_audio_gpio(
+		"headphone-detect",
+		NULL,
+		&gpio_headphone_detect,
+		&gpio_headphone_detect_pol);
+	/* Fix some broken OF entries in desktop machines */
+	if (!gpio_headphone_irq)
+		gpio_headphone_irq = setup_audio_gpio(
+			NULL,
+			"keywest-gpio15",
+			&gpio_headphone_detect,
+			&gpio_headphone_detect_pol);
+
+	write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol);
+	wait_ms(100);
+	write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol);
+	wait_ms(100);
+  	if (gpio_headphone_irq) {
+		if (request_irq(gpio_headphone_irq,headphone_intr,0,"Headphone detect",0) < 0) {
+    			printk(KERN_ERR "tumbler: Can't request headphone interrupt\n");
+    			gpio_headphone_irq = 0;
+    		} else {
+			u8 val;
+			/* Activate headphone status interrupts */
+			val = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_headphone_detect, 0);
+			pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_headphone_detect, val | 0x80);
+			/* Trigger it */
+  			headphone_intr(0,0,0);
+  		}
+  	}
+  	if (!gpio_headphone_irq) {
+  		/* Some machine enter this case ? */
+  		printk(KERN_WARNING "tumbler: Headphone detect IRQ not found, enabling all outputs !\n");
+  		write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol);
+  		write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol);
+  	}
+	return 0;
 }
 
 
-static TRANS transAwacsNormal = {
-	ct_ulaw:	pmac_ct_law,
-	ct_alaw:	pmac_ct_law,
-	ct_s8:		pmac_ct_s8,
-	ct_u8:		pmac_ct_u8,
-	ct_s16be:	pmac_ct_s16,
-	ct_u16be:	pmac_ct_u16,
-	ct_s16le:	pmac_ct_s16,
-	ct_u16le:	pmac_ct_u16,
-};
+static int
+awacs_tumbler_cleanup(void)
+{
+	if (gpio_headphone_irq)
+		free_irq(gpio_headphone_irq, 0);
+	return 0;
+}
 
-static TRANS transAwacsExpand = {
-	ct_ulaw:	pmac_ctx_law,
-	ct_alaw:	pmac_ctx_law,
-	ct_s8:		pmac_ctx_s8,
-	ct_u8:		pmac_ctx_u8,
-	ct_s16be:	pmac_ctx_s16,
-	ct_u16be:	pmac_ctx_u16,
-	ct_s16le:	pmac_ctx_s16,
-	ct_u16le:	pmac_ctx_u16,
-};
 
-static TRANS transAwacsNormalRead = {
-	ct_s8:		pmac_ct_s8_read,
-	ct_u8:		pmac_ct_u8_read,
-	ct_s16be:	pmac_ct_s16_read,
-	ct_u16be:	pmac_ct_u16_read,
-	ct_s16le:	pmac_ct_s16_read,
-	ct_u16le:	pmac_ct_u16_read,
-};
+/*** AE - TUMBLER END *********************************************************/
 
-/*** Low level stuff *********************************************************/
 
 
+/*** Low level stuff *********************************************************/
 
 /*
- * PCI PowerMac, with AWACS and DBDMA.
+ * PCI PowerMac, with AWACS, Screamer, Burgundy, DACA or Tumbler and DBDMA.
  */
 
 static void PMacOpen(void)
@@ -827,9 +509,9 @@
 
 static int __init PMacIrqInit(void)
 {
-	if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
-	    || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
-	    || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
+	if (request_irq(awacs_irq, pmac_awacs_intr, 0, "Built-in Sound misc", 0)
+	    || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", 0)
+	    || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "Built-in Sound in", 0))
 		return 0;
 	return 1;
 }
@@ -837,25 +519,44 @@
 #ifdef MODULE
 static void PMacIrqCleanup(void)
 {
-	/* turn off output dma */
-	out_le32(&awacs_txdma->control, RUN<<16);
+	/* turn off input & output dma */
+	DBDMA_DO_STOP(awacs_txdma);
+	DBDMA_DO_STOP(awacs_rxdma);
+
 	/* disable interrupts from awacs interface */
 	out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
-#ifdef CONFIG_PMAC_PBOOK
-	if (is_pbook_G3) {
-		feature_clear(awacs_node, FEATURE_Sound_power);
-		feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
+
+	/* Switch off the sound clock */
+	pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
+	/* Make sure proper bits are set on pismo & tipb */
+	if (machine_is_compatible("PowerBook3,1") ||
+	    machine_is_compatible("PowerBook3,2")) {
+		awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		wait_ms(200);
 	}
-#endif
 	free_irq(awacs_irq, 0);
 	free_irq(awacs_tx_irq, 0);
 	free_irq(awacs_rx_irq, 0);
-	kfree(awacs_tx_cmd_space);
+	/* all OF versions I've seen use this value */
+	iounmap((void *)awacs);
+	iounmap((void *)awacs_txdma);
+	iounmap((void *)awacs_rxdma);
+
+	release_OF_resource(awacs_node, 0);
+	release_OF_resource(awacs_node, 1);
+	release_OF_resource(awacs_node, 2);
+
+	if (awacs_tx_cmd_space)
+		kfree(awacs_tx_cmd_space);
 	if (awacs_rx_cmd_space)
 		kfree(awacs_rx_cmd_space);
-	if (beep_buf)
+	if (beep_dbdma_cmd_space)
+		kfree(beep_dbdma_cmd_space);
+	if (beep_buf) {
 		kfree(beep_buf);
-	kd_mksound = orig_mksound;
+		kd_mksound = orig_mksound;
+	}
 #ifdef CONFIG_PMAC_PBOOK
 	pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
 #endif
@@ -865,7 +566,32 @@
 static void PMacSilence(void)
 {
 	/* turn off output dma */
-	out_le32(&awacs_txdma->control, RUN<<16);
+	DBDMA_DO_STOP(awacs_txdma);
+}
+
+static int tumbler_freqs[2] = { 48000, 44100 } ;
+static int tumbler_freqs_ok[2] = { 1, 1 } ;
+
+/* don't know what to do really - just have to leave it where
+ * OF left things
+*/
+
+static int tumbler_set_frame_rate(void)
+{
+	dmasound.hard.speed = 44100 ;
+	awacs_rate_index = 0 ;
+	return 44100 ;
+}
+
+/* don't know what to do really - just have to leave it where
+ * OF left things
+*/
+
+static int daca_set_frame_rate(void)
+{
+	dmasound.hard.speed = 44100 ;
+	awacs_rate_index = 0 ;
+	return 44100 ;
 }
 
 static int awacs_freqs[8] = {
@@ -873,59 +599,115 @@
 };
 static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
 
-static void PMacInit(void)
+static int awacs_set_frame_rate(int desired, int catch_r)
 {
-	int i, tolerance;
-
-	switch (dmasound.soft.format) {
-	case AFMT_S16_LE:
-	case AFMT_U16_LE:
-		dmasound.hard.format = AFMT_S16_LE;
-		break;
-	default:
-		dmasound.hard.format = AFMT_S16_BE;
-		break;
-	}
-	dmasound.hard.stereo = 1;
-	dmasound.hard.size = 16;
-
+	int tolerance, i = 8 ;
 	/*
 	 * If we have a sample rate which is within catchRadius percent
 	 * of the requested value, we don't have to expand the samples.
 	 * Otherwise choose the next higher rate.
-	 * N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
+	 * N.B.: burgundy awacs only works at 44100 Hz.
 	 */
-	i = 8;
 	do {
-		tolerance = catchRadius * awacs_freqs[--i] / 100;
+		tolerance = catch_r * awacs_freqs[--i] / 100;
 		if (awacs_freqs_ok[i]
 		    && dmasound.soft.speed <= awacs_freqs[i] + tolerance)
 			break;
 	} while (i > 0);
-	if (dmasound.soft.speed >= awacs_freqs[i] - tolerance)
-		dmasound.trans_write = &transAwacsNormal;
-	else
-		dmasound.trans_write = &transAwacsExpand;
-	dmasound.trans_read = &transAwacsNormalRead;
 	dmasound.hard.speed = awacs_freqs[i];
 	awacs_rate_index = i;
 
-	/* XXX disable error interrupt on burgundy for now */
-	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
-		 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
+	out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 );
 	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
 	awacs_write(awacs_reg[1] | MASK_ADDR1);
-	out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+	return dmasound.hard.speed;
+}
+
+static int burgundy_frame_rates = 1 ;
+static int burgundy_set_frame_rate(void)
+{
+#ifdef DEBUG_DMASOUND
+if (burgundy_frame_rates > 1)
+	printk("dmasound_pmac: warning Burgundy had more than one frame rate\n");
+#endif
+	awacs_rate_index = 0 ;
+	awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) ;
+	/* XXX disable error interrupt on burgundy for now */
+	out_le32(&awacs->control, MASK_IEPC | 0 | 0x11 | MASK_IEE);
+	return 44100 ;
+}
+
+static int set_frame_rate(int desired, int catch_r)
+{
+	switch (awacs_revision) {
+		case AWACS_BURGUNDY:
+			dmasound.hard.speed =
+			  burgundy_set_frame_rate();
+			break ;
+		case AWACS_TUMBLER:
+			dmasound.hard.speed =
+			  tumbler_set_frame_rate();
+			break ;
+		case AWACS_DACA:
+			dmasound.hard.speed =
+			  daca_set_frame_rate();
+			break ;
+		default:
+			dmasound.hard.speed =
+			  awacs_set_frame_rate(desired, catch_r);
+			break ;
+	}
+	return dmasound.hard.speed ;
+}
 
-	/* We really want to execute a DMA stop command, after the AWACS
-	 * is initialized.
-	 * For reasons I don't understand, it stops the hissing noise
-	 * common to many PowerBook G3 systems (like mine :-).
+static void
+awacs_recalibrate(void)
+{
+	/* Sorry for the horrible delays... I hope to get that improved
+	 * by making the whole PM process asynchronous in a future version
 	 */
-	out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
-	st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
-	out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
-	out_le32(&awacs_txdma->control, RUN | (RUN << 16));
+	wait_ms(750);
+	awacs_reg[1] |= MASK_CMUTE | MASK_AMUTE;
+	awacs_write(awacs_reg[1] | MASK_RECALIBRATE | MASK_ADDR1);
+	wait_ms(1000);
+	awacs_write(awacs_reg[1] | MASK_ADDR1);
+}
+
+static void PMacInit(void)
+{
+	int tolerance;
+
+	switch (dmasound.soft.format) {
+	    case AFMT_S16_LE:
+	    case AFMT_U16_LE:
+		if (hw_can_byteswap)
+			dmasound.hard.format = AFMT_S16_LE;
+		else
+			dmasound.hard.format = AFMT_S16_BE;
+		break;
+	default:
+		dmasound.hard.format = AFMT_S16_BE;
+		break;
+	}
+	dmasound.hard.stereo = 1;
+	dmasound.hard.size = 16;
+
+	/* set dmasound.hard.speed - on the basis of what we want (soft)
+	 * and the tolerance we'll allow.
+	*/
+	set_frame_rate(dmasound.soft.speed, catchRadius) ;
+
+	tolerance = (catchRadius * dmasound.hard.speed) / 100;
+	if (dmasound.soft.speed >= dmasound.hard.speed - tolerance)
+		dmasound.trans_write = &transAwacsNormal;
+	else
+		dmasound.trans_write = &transAwacsExpand;
+	dmasound.trans_read = &transAwacsNormalRead;
+
+	if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+		out_le32(&awacs->byteswap, BS_VAL);
+	else
+		out_le32(&awacs->byteswap, 0);
 
 	expand_bal = -dmasound.soft.speed;
 }
@@ -933,7 +715,8 @@
 static int PMacSetFormat(int format)
 {
 	int size;
-
+	int req_format = format;
+		
 	switch (format) {
 	case AFMT_QUERY:
 		return dmasound.soft.format;
@@ -943,10 +726,16 @@
 	case AFMT_S8:
 		size = 8;
 		break;
-	case AFMT_S16_BE:
-	case AFMT_U16_BE:
 	case AFMT_S16_LE:
+		if(!hw_can_byteswap)
+			format = AFMT_S16_BE;
+	case AFMT_S16_BE:
+		size = 16;
+		break;
 	case AFMT_U16_LE:
+		if(!hw_can_byteswap)
+			format = AFMT_U16_BE;
+	case AFMT_U16_BE:
 		size = 16;
 		break;
 	default: /* :-) */
@@ -955,16 +744,16 @@
 		size = 8;
 		format = AFMT_U8;
 	}
-
-	dmasound.soft.format = format;
-	dmasound.soft.size = size;
-	if (dmasound.minDev == SND_DEV_DSP) {
-		dmasound.dsp.format = format;
-		dmasound.dsp.size = size;
+	
+	if (req_format == format) {
+		dmasound.soft.format = format;
+		dmasound.soft.size = size;
+		if (dmasound.minDev == SND_DEV_DSP) {
+			dmasound.dsp.format = format;
+			dmasound.dsp.size = size;
+		}
 	}
 
-	PMacInit();
-
 	return format;
 }
 
@@ -1007,48 +796,91 @@
 	return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
 }
 
-static void PMacPlay(void)
+static void __PMacPlay(void)
 {
 	volatile struct dbdma_cmd *cp;
-	int i, count;
+	int next_frg, count;
 	unsigned long flags;
 
+	/* CHECK: how much of this *really* needs IRQs masked? */
+
 	save_flags(flags); cli();
+	count = 300 ; /* > two cycles at the lowest sample rate */
+
+	/* what we want to send next */
+	next_frg = (write_sq.front + write_sq.active) % write_sq.max_count;
+
 	if (awacs_beep_state) {
 		/* sound takes precedence over beeps */
+		/* stop the dma channel */
 		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		while ( (in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1);
+		/* FIXME: check that this is OK for other chip sets */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (awacs_rate_index << 8));
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
-		out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count])));
+
+		if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+			out_le32(&awacs->byteswap, BS_VAL);
+		else
+			out_le32(&awacs->byteswap, 0);
+		out_le32(&awacs_txdma->cmdptr,
+			 virt_to_bus(&(awacs_tx_cmds[next_frg])));
 
 		beep_playing = 0;
 		awacs_beep_state = 0;
 	}
-	i = write_sq.front + write_sq.active;
-	if (i >= write_sq.max_count)
-		i -= write_sq.max_count;
+	/* this won't allow more than two frags to be in the output queue at
+	   once. (or one, if the max frags is 2 - because count can't exceed
+	   2 in that case)
+	*/
 	while (write_sq.active < 2 && write_sq.active < write_sq.count) {
-		count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size;
-		if (count < write_sq.block_size && !write_sq.syncing)
-			/* last block not yet filled, and we're not syncing. */
-			break;
-		cp = &awacs_tx_cmds[i];
+		count = (write_sq.count == write_sq.active + 1) ?
+				write_sq.rear_size:write_sq.block_size ;
+		if (count < write_sq.block_size) {
+			if (!write_sq.syncing) /* last block not yet filled,*/
+				break; 	/* and we're not syncing or POST-ed */
+			else {
+				/* pretend the block is full to force a new
+				   block to be started on the next write */
+				write_sq.rear_size = write_sq.block_size ;
+				write_sq.syncing &= ~2 ; /* clear POST */
+			}
+		}
+		cp = &awacs_tx_cmds[next_frg];
 		st_le16(&cp->req_count, count);
 		st_le16(&cp->xfer_status, 0);
-		if (++i >= write_sq.max_count)
-			i = 0;
-		out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
-		out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+		st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+		/* put a STOP at the end of the queue - but only if we have
+		   space for it.  This means that, if we under-run and we only
+		   have two fragments, we might re-play sound from an existing
+		   queued frag.  I guess the solution to that is not to set two
+		   frags if you are likely to under-run...
+		*/
+		if (write_sq.count < write_sq.max_count) {
+			if (++next_frg >= write_sq.max_count)
+				next_frg = 0 ; /* wrap */
+			/* if we get here then we've underrun so we will stop*/
+			st_le16(&awacs_tx_cmds[next_frg].command, DBDMA_STOP);
+		}
+		/* set the dbdma controller going, if it is not already */
 		if (write_sq.active == 0)
 			out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
+		(void)in_le32(&awacs_txdma->status);
 		out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
 		++write_sq.active;
 	}
 	restore_flags(flags);
 }
 
+static void PMacPlay(void)
+{
+	LOCK();
+	if (!awacs_sleeping)
+		__PMacPlay();
+	UNLOCK();
+}
 
 static void PMacRecord(void)
 {
@@ -1067,6 +899,26 @@
 	restore_flags(flags);
 }
 
+/* if the TX status comes up "DEAD" - reported on some Power Computing machines
+   we need to re-start the dbdma - but from a different physical start address
+   and with a different transfer length.  It would get very messy to do this
+   with the normal dbdma_cmd blocks - we would have to re-write the buffer start
+   addresses each time.  So, we will keep a single dbdma_cmd block which can be
+   fiddled with.
+   When DEAD status is first reported the content of the faulted dbdma block is
+   copied into the emergency buffer and we note that the buffer is in use.
+   we then bump the start physical address by the amount that was successfully
+   output before it died.
+   On any subsequent DEAD result we just do the bump-ups (we know that we are
+   already using the emergency dbdma_cmd).
+   CHECK: this just tries to "do it".  It is possible that we should abandon
+   xfers when the number of residual bytes gets below a certain value - I can
+   see that this might cause a loop-forever if too small a transfer causes
+   DEAD status.  However this is a TODO for now - we'll see what gets reported.
+   When we get a successful transfer result with the emergency buffer we just
+   pretend that it completed using the original dmdma_cmd and carry on.  The
+   'next_cmd' field will already point back to the original loop of blocks.
+*/
 
 static void
 pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
@@ -1074,35 +926,92 @@
 	int i = write_sq.front;
 	int stat;
 	volatile struct dbdma_cmd *cp;
+	/* != 0 when we are dealing with a DEAD xfer */
+	static int emergency_in_use = 0 ;
 
-	while (write_sq.active > 0) {
-		cp = &awacs_tx_cmds[i];
+	while (write_sq.active > 0) { /* we expect to have done something*/
+		if (emergency_in_use) /* we are dealing with DEAD xfer */
+			cp = emergency_dbdma_cmd ;
+		else
+			cp = &awacs_tx_cmds[i];
 		stat = ld_le16(&cp->xfer_status);
+		if (stat & DEAD) {
+			unsigned short req, res ;
+			unsigned int phy ;
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: tx-irq: xfer died - patching it up...\n") ;
+#endif
+			/* to clear DEAD status we must first clear RUN
+			   set it to quiescent to be on the safe side */
+			(void)in_le32(&awacs_txdma->status);
+			out_le32(&awacs_txdma->control,
+				(RUN|PAUSE|FLUSH|WAKE) << 16);
+			write_sq.died++ ;
+			if (!emergency_in_use) { /* new problem */
+				memcpy((void *)emergency_dbdma_cmd, (void *)cp,
+					sizeof(struct dbdma_cmd));
+				emergency_in_use = 1;
+				cp = emergency_dbdma_cmd;
+			}
+			/* now bump the values to reflect the amount
+			   we haven't yet shifted */
+			req = ld_le16(&cp->req_count);
+			res = ld_le16(&cp->res_count);
+			phy = ld_le32(&cp->phy_addr);
+			phy += (req - res);
+			st_le16(&cp->req_count, res);
+			st_le16(&cp->res_count, 0);
+			st_le16(&cp->xfer_status, 0);
+			st_le32(&cp->phy_addr, phy);
+			st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
+			/* point at our patched up command block */
+			out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
+			/* we must re-start the controller */
+			(void)in_le32(&awacs_txdma->status);
+			/* should complete clearing the DEAD status */
+			out_le32(&awacs_txdma->control,
+				((RUN|WAKE) << 16) + (RUN|WAKE));
+			break; /* this block is still going */
+		}
 		if ((stat & ACTIVE) == 0)
 			break;	/* this frame is still going */
+		if (emergency_in_use)
+			emergency_in_use = 0 ; /* done that */
 		--write_sq.count;
 		--write_sq.active;
 		if (++i >= write_sq.max_count)
 			i = 0;
 	}
+
+	/* if we stopped and we were not sync-ing - then we under-ran */
+	if( write_sq.syncing == 0 ){
+		stat = in_le32(&awacs_txdma->status) ;
+		/* we hit the dbdma_stop */
+		if( (stat & ACTIVE) == 0 ) write_sq.xruns++ ;
+	}
+
+	/* if we used some data up then wake the writer to supply some more*/
 	if (i != write_sq.front)
 		WAKE_UP(write_sq.action_queue);
 	write_sq.front = i;
 
-	PMacPlay();
-
-	if (!write_sq.active)
-		WAKE_UP(write_sq.sync_queue);
+	/* but make sure we funnel what we've already got */\
+	 if (!awacs_sleeping)
+		__PMacPlay();
+
+	/* make the wake-on-empty conditional on syncing */
+	if (!write_sq.active && (write_sq.syncing & 1))
+		WAKE_UP(write_sq.sync_queue); /* any time we're empty */
 }
 
 
 static void
 pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
 {
-
+	int stat ;
 	/* For some reason on my PowerBook G3, I get one interrupt
 	 * when the interrupt vector is installed (like something is
-	 * pending).  This happens before the dbdma is initialize by
+	 * pending).  This happens before the dbdma is initialized by
 	 * us, so I just check the command pointer and if it is zero,
 	 * just blow it off.
 	 */
@@ -1118,8 +1027,35 @@
 	 * interrupt processing for a long time.  Geeze, I really hope
 	 * this doesn't happen.
 	 */
-	while (awacs_rx_cmds[read_sq.rear].xfer_status) {
+	while ((stat=awacs_rx_cmds[read_sq.rear].xfer_status)) {
 
+		/* if we got a "DEAD" status then just log it for now.
+		   and try to restart dma.
+		   TODO: figure out how best to fix it up
+		*/
+		if (stat & DEAD){
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: rx-irq: DIED - attempting resurection\n");
+#endif
+			/* to clear DEAD status we must first clear RUN
+			   set it to quiescent to be on the safe side */
+			(void)in_le32(&awacs_txdma->status);
+			out_le32(&awacs_txdma->control,
+				(RUN|PAUSE|FLUSH|WAKE) << 16);
+			awacs_rx_cmds[read_sq.rear].xfer_status = 0;
+			awacs_rx_cmds[read_sq.rear].res_count = 0;
+			read_sq.died++ ;
+			(void)in_le32(&awacs_txdma->status);
+			/* re-start the same block */
+			out_le32(&awacs_rxdma->cmdptr,
+				virt_to_bus(&awacs_rx_cmds[read_sq.rear]));
+			/* we must re-start the controller */
+			(void)in_le32(&awacs_rxdma->status);
+			/* should complete clearing the DEAD status */
+			out_le32(&awacs_rxdma->control,
+				((RUN|WAKE) << 16) + (RUN|WAKE));
+			return; /* try this block again */
+		}
 		/* Clear status and move on to next buffer.
 		*/
 		awacs_rx_cmds[read_sq.rear].xfer_status = 0;
@@ -1138,6 +1074,7 @@
 		 */
 		if (read_sq.rear == read_sq.front) {
 			read_sq.front++;
+			read_sq.xruns++ ; /* we overan */
 			if (read_sq.front >= read_sq.max_active)
 				read_sq.front = 0;
 		}
@@ -1157,8 +1094,9 @@
 	}
 	if (ctrl & MASK_CNTLERR) {
 		int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
-		if (err != 0 && awacs_revision < AWACS_BURGUNDY)
-			printk(KERN_ERR "AWACS: error %x\n", err);
+		/* CHECK: we just swallow burgundy errors at the moment..*/
+		if (err != 0 && awacs_revision != AWACS_BURGUNDY)
+			printk(KERN_ERR "dmasound_pmac: error %x\n", err);
 	}
 	/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
 	out_le32(&awacs->control, ctrl);
@@ -1167,25 +1105,38 @@
 static void
 awacs_write(int val)
 {
-	if (awacs_revision >= AWACS_BURGUNDY)
-		return;
-	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
-		;	/* XXX should have timeout */
+	int count = 300 ;
+	if (awacs_revision >= AWACS_DACA)
+		return ;
+
+	while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
+		udelay(1) ;	/* timeout is > 2 samples at lowest rate */
 	out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
+	(void)in_le32(&awacs->byteswap);
 }
 
+/* this is called when the beep timer expires... it will be called even
+   if the beep has been overidden by other sound output.
+*/
 static void awacs_nosound(unsigned long xx)
 {
 	unsigned long flags;
+	int count = 600 ; /* > four samples at lowest rate */
 
 	save_flags(flags); cli();
 	if (beep_playing) {
 		st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
 		out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+		while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1);
+		/* FIXME: check this is OK for DACA, Tumbler */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (awacs_rate_index << 8));
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+			out_le32(&awacs->byteswap, BS_VAL);
+		else
+			out_le32(&awacs->byteswap, 0);
 		beep_playing = 0;
 	}
 	restore_flags(flags);
@@ -1195,6 +1146,11 @@
 	function: awacs_nosound
 };
 
+/* we generate the beep with a single dbdma command that loops a buffer
+   forever - without generating interrupts.
+   So, to stop it you have to stop dma output as per awacs_nosound.
+*/
+
 static void awacs_mksound(unsigned int hz, unsigned int ticks)
 {
 	unsigned long flags;
@@ -1207,11 +1163,19 @@
 	static int beep_nsamples_cache;
 	static int beep_volume_cache;
 
-	for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
-		if (awacs_freqs_ok[i])
-			beep_speed = i;
-	srate = awacs_freqs[beep_speed];
+	if (beep_buf == NULL)
+		return;
+
+	/* quick-hack fix for DACA, Burgundy & Tumbler */
 
+	if (awacs_revision >= AWACS_DACA){
+		srate = 44100 ;
+	} else {
+		for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
+			if (awacs_freqs_ok[i])
+				beep_speed = i;
+		srate = awacs_freqs[beep_speed];
+	}
 	if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
 #if 1
 		/* this is a hack for broken X server code */
@@ -1237,7 +1201,7 @@
 	st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
 	restore_flags(flags);
 
-	if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
+	if (hz == beep_hz_cache && beep_vol == beep_volume_cache) {
 		nsamples = beep_nsamples_cache;
 	} else {
 		period = srate * 256 / hz;	/* fixed point */
@@ -1247,11 +1211,11 @@
 		j = 0;
 		p = beep_buf;
 		for (i = 0; i < nsamples; ++i, p += 2) {
-			p[0] = p[1] = beep_wform[j >> 8] * beep_volume;
+			p[0] = p[1] = beep_wform[j >> 8] * beep_vol;
 			j = (j + f) & 0xffff;
 		}
 		beep_hz_cache = hz;
-		beep_volume_cache = beep_volume;
+		beep_volume_cache = beep_vol;
 		beep_nsamples_cache = nsamples;
 	}
 
@@ -1263,78 +1227,159 @@
 
 	save_flags(flags); cli();
 	if (beep_playing) {	/* i.e. haven't been terminated already */
+		int count = 300 ;
 		out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
+		while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+			udelay(1); /* timeout > 2 samples at lowest rate*/
+		/* FIXME: check this is OK on DACA, Tumbler */
 		out_le32(&awacs->control,
 			 (in_le32(&awacs->control) & ~0x1f00)
 			 | (beep_speed << 8));
-		out_le32(&awacs->byteswap, 0);
+		out_le32(&awacs->byteswap, 0); /* force BE */
 		out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+		(void)in_le32(&awacs_txdma->status);
 		out_le32(&awacs_txdma->control, RUN | (RUN << 16));
 	}
 	restore_flags(flags);
 }
 
+/* used in init and for wake-up */
+
+static void
+load_awacs(void)
+{
+	awacs_write(awacs_reg[0] + MASK_ADDR0);
+	awacs_write(awacs_reg[1] + MASK_ADDR1);
+	awacs_write(awacs_reg[2] + MASK_ADDR2);
+	awacs_write(awacs_reg[4] + MASK_ADDR4);
+
+	if (awacs_revision == AWACS_SCREAMER) {
+		awacs_write(awacs_reg[5] + MASK_ADDR5);
+		wait_ms(100);
+		awacs_write(awacs_reg[6] + MASK_ADDR6);
+		wait_ms(2);
+		awacs_write(awacs_reg[1] + MASK_ADDR1);
+		awacs_write(awacs_reg[7] + MASK_ADDR7);
+	}
+	if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
+		out_le32(&awacs->byteswap, BS_VAL);
+	else
+		out_le32(&awacs->byteswap, 0);
+}
+
 #ifdef CONFIG_PMAC_PBOOK
 /*
  * Save state when going to sleep, restore it afterwards.
  */
+/* FIXME: sort out disabling/re-enabling of read stuff as well */
 static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
 {
 	switch (when) {
-	case PBOOK_SLEEP_NOW:
-		/* XXX we should stop any dma in progress when going to sleep
-		   and restart it when we wake. */
+	case PBOOK_SLEEP_NOW:		
+		LOCK();
+		awacs_sleeping = 1;
+		/* Tell the rest of the driver we are now going to sleep */
+		mb();
+		if (awacs_revision == AWACS_SCREAMER ||
+		    awacs_revision == AWACS_AWACS) {
+			awacs_reg1_save = awacs_reg[1];
+			awacs_reg[1] |= MASK_AMUTE | MASK_CMUTE;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+
 		PMacSilence();
+		/* stop rx - if going - a bit of a daft user... but */
+		out_le32(&awacs_rxdma->control, (RUN|WAKE|FLUSH << 16));
+		/* deny interrupts */
+		switch (awacs_revision) {
+			case AWACS_TUMBLER:
+				tumbler_enter_sleep(); /* Stub for now */
+				break ;
+			case AWACS_DACA:
+				daca_enter_sleep();
+				break ;
+			case AWACS_BURGUNDY:
+				break ;
+			case AWACS_SCREAMER:
+			case AWACS_AWACS:
+			default:
+				out_le32(&awacs->control, 0x11) ;
+				break ;
+		}
 		disable_irq(awacs_irq);
 		disable_irq(awacs_tx_irq);
-		if (is_pbook_G3) {
-			feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
-			feature_clear(awacs_node, FEATURE_Sound_power);
+		disable_irq(awacs_rx_irq);
+		/* Disable sound clock */
+		pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
+		/* According to Darwin, we do that after turning off the sound
+		 * chip clock. All this will have to be cleaned up once we properly
+		 * parse the OF sound-objects
+		 */
+		if (machine_is_compatible("PowerBook3,1") ||
+		    machine_is_compatible("PowerBook3,2")) {
+			awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+			wait_ms(200);
 		}
 		break;
 	case PBOOK_WAKE:
-		/* There is still a problem on wake. Sound seems to work fine
-		   if I launch mpg123 and resumes fine if mpg123 was playing,
-		   but the console beep is dead until I do something with the
-		   mixer. Probably yet another timing issue */
-		if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable)
-		    || !feature_test(awacs_node, FEATURE_Sound_power)) {
-			/* these aren't present on the 3400 AFAIK -- paulus */
-			feature_set(awacs_node, FEATURE_Sound_CLK_enable);
-			feature_set(awacs_node, FEATURE_Sound_power);
-			mdelay(1000);
-		}
-		out_le32(&awacs->control, MASK_IEPC
-			 | (awacs_rate_index << 8) | 0x11
-			 | (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
-		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		awacs_write(awacs_reg[1] | MASK_ADDR1);
-		awacs_write(awacs_reg[2] | MASK_ADDR2);
-		awacs_write(awacs_reg[4] | MASK_ADDR4);
-		if (awacs_is_screamer) {
-			awacs_write(awacs_reg[5] + MASK_ADDR5);
-			awacs_write(awacs_reg[6] + MASK_ADDR6);
-			awacs_write(awacs_reg[7] + MASK_ADDR7);
-		}
-		out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
+		/* Enable sound clock */
+		pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 1);
+		if (machine_is_compatible("PowerBook3,1") ||
+		    machine_is_compatible("PowerBook3,2")) {
+			wait_ms(100);
+			awacs_reg[1] &= ~(MASK_PAROUT0 | MASK_PAROUT1);
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+			wait_ms(300);
+		} else
+			wait_ms(1000);
+ 		/* restore settings */
+		switch (awacs_revision) {
+			case AWACS_TUMBLER:
+				headphone_intr(0,0,0);
+				tumbler_leave_sleep(); /* Stub for now */
+				break;
+			case AWACS_DACA:
+				wait_ms(10); /* Check this !!! */
+				daca_leave_sleep();
+				break ;		/* dont know how yet */
+			case AWACS_BURGUNDY:
+				break ;
+			case AWACS_SCREAMER:
+			case AWACS_AWACS:
+			default:
+		 		load_awacs() ;
+				break ;
+		}
+		/* Recalibrate chip */
+		if (awacs_revision == AWACS_SCREAMER)
+			awacs_recalibrate();
+		/* Make sure dma is stopped */
+		PMacSilence();
 		enable_irq(awacs_irq);
 		enable_irq(awacs_tx_irq);
-		if (awacs_revision == 3) {
-			mdelay(100);
-			awacs_write(0x6000);
-			mdelay(2);
-			awacs_write(awacs_reg[1] | MASK_ADDR1);
-		}
-		/* enable CD sound input */
-		if (macio_base && is_pbook_G3) {
+ 		enable_irq(awacs_rx_irq);
+ 		/* OK, allow ints back again */
+ 		out_le32(&awacs->control, MASK_IEPC
+ 		 	| (awacs_rate_index << 8) | 0x11
+ 			 | (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
+ 		if (macio_base && is_pbook_g3) {
+			/* FIXME: should restore the setup we had...*/
 			out_8(macio_base + 0x37, 3);
-		} else if (is_pbook_3400) {
-			feature_set(awacs_node, FEATURE_IOBUS_enable);
-			udelay(10);
+ 		} else if (is_pbook_3X00) {
 			in_8(latch_base + 0x190);
 		}
+		/* Remove mute */
+		if (awacs_revision == AWACS_SCREAMER ||
+		    awacs_revision == AWACS_AWACS) {
+			awacs_reg[1] = awacs_reg1_save;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+ 		awacs_sleeping = 0;
 		/* Resume pending sounds. */
-		PMacPlay();
+		/* we don't try to restart input... */
+		__PMacPlay();
+		UNLOCK();
 	}
 	return PBOOK_SLEEP_OK;
 }
@@ -1347,17 +1392,20 @@
 inline static void
 awacs_burgundy_busy_wait(void)
 {
-	while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
-		;
+	int count = 50; /* > 2 samples at 44k1 */
+	while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
+		udelay(1) ;
 }
 
 inline static void
 awacs_burgundy_extend_wait(void)
 {
-	while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
-		;
-	while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
-		;
+	int count = 50 ; /* > 2 samples at 44k1 */
+	while ((!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) && count--)
+		udelay(1) ;
+	count = 50;
+	while ((in_le32(&awacs->codec_stat) & MASK_EXTEND) && count--)
+		udelay(1);
 }
 
 static void
@@ -1447,7 +1495,7 @@
 awacs_burgundy_init(void)
 {
 	if (awacs_burgundy_check()) {
-		printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
+		printk(KERN_WARNING "dmasound_pmac: burgundy not working :-(\n");
 		return 1;
 	}
 
@@ -1515,9 +1563,6 @@
 	return softvolume > 0 ? softvolume : 0;
 }
 
-
-
-
 static int
 awacs_burgundy_read_mvolume(unsigned address)
 {
@@ -1533,7 +1578,6 @@
 	return lvolume + (rvolume << 8);
 }
 
-
 static void
 awacs_burgundy_write_mvolume(unsigned address, int volume)
 {
@@ -1550,21 +1594,23 @@
 
 /* End burgundy functions */
 
+/* Set up output volumes on machines with the 'perch/whisper' extension card.
+ * this has an SGS i2c chip (7433) which is accessed using the cuda.
+ *
+ * TODO: split this out and make use of the other parts of the SGS chip to
+ * do Bass, Treble etc.
+ */
 
-
-
-
-/* Turn on sound output, needed on G3 desktop powermacs */
 static void
 awacs_enable_amp(int spkr_vol)
 {
+#ifdef CONFIG_ADB_CUDA
 	struct adb_request req;
 
 	awacs_spkr_vol = spkr_vol;
 	if (sys_ctrler != SYS_CTRLER_CUDA)
 		return;
 
-#ifdef CONFIG_ADB_CUDA
 	/* turn on headphones */
 	cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
 		     0x8a, 4, 0);
@@ -1595,146 +1641,229 @@
  * /dev/mixer abstraction
  */
 
-static int awacs_mixer_ioctl(u_int cmd, u_long arg)
+static void do_line_lev(int data)
 {
-	int data;
-
-	switch (cmd) {
-	case SOUND_MIXER_READ_DEVMASK:
-		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-			| SOUND_MASK_LINE | SOUND_MASK_MIC
-			| SOUND_MASK_CD | SOUND_MASK_RECLEV
-			| SOUND_MASK_ALTPCM
-			| SOUND_MASK_MONITOR;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_RECMASK:
-		data = SOUND_MASK_LINE | SOUND_MASK_MIC
-			| SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_RECSRC:
-		data = 0;
-		if (awacs_reg[0] & MASK_MUX_AUDIN)
-			data |= SOUND_MASK_LINE;
-		if (awacs_reg[0] & MASK_MUX_MIC)
-			data |= SOUND_MASK_MIC;
-		if (awacs_reg[0] & MASK_MUX_CD)
-			data |= SOUND_MASK_CD;
-		if (awacs_reg[1] & MASK_LOOPTHRU)
-			data |= SOUND_MASK_MONITOR;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_WRITE_RECSRC:
-		IOCTL_IN(arg, data);
-		data &= (SOUND_MASK_LINE
-			 | SOUND_MASK_MIC | SOUND_MASK_CD
-			 | SOUND_MASK_MONITOR);
-		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
-				  | MASK_MUX_AUDIN);
-		awacs_reg[1] &= ~MASK_LOOPTHRU;
-		if (data & SOUND_MASK_LINE)
+		line_lev = data ;
+		awacs_reg[0] &= ~MASK_MUX_AUDIN;
+		if ((data & 0xff) >= 50)
 			awacs_reg[0] |= MASK_MUX_AUDIN;
-		if (data & SOUND_MASK_MIC)
-			awacs_reg[0] |= MASK_MUX_MIC;
-		if (data & SOUND_MASK_CD)
-			awacs_reg[0] |= MASK_MUX_CD;
-		if (data & SOUND_MASK_MONITOR)
+		awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_ip_gain(int data)
+{
+	ip_gain = data ;
+	data &= 0xff;
+	awacs_reg[0] &= ~MASK_GAINLINE;
+	if (awacs_revision == AWACS_SCREAMER) {
+		awacs_reg[6] &= ~MASK_MIC_BOOST ;
+		if (data >= 33) {
+			awacs_reg[0] |= MASK_GAINLINE;
+			if( data >= 66)
+				awacs_reg[6] |= MASK_MIC_BOOST ;
+		}
+		awacs_write(MASK_ADDR6 | awacs_reg[6]) ;
+	} else {
+		if (data >= 50)
+			awacs_reg[0] |= MASK_GAINLINE;
+	}
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_mic_lev(int data)
+{
+	mic_lev = data ;
+	data &= 0xff;
+	awacs_reg[0] &= ~MASK_MUX_MIC;
+	if (data >= 50)
+		awacs_reg[0] |= MASK_MUX_MIC;
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_cd_lev(int data)
+{
+	cd_lev = data ;
+	awacs_reg[0] &= ~MASK_MUX_CD;
+	if ((data & 0xff) >= 50)
+		awacs_reg[0] |= MASK_MUX_CD;
+	awacs_write(MASK_ADDR0 | awacs_reg[0]);
+}
+
+static void do_rec_lev(int data)
+{
+	int left, right ;
+	rec_lev = data ;
+	/* need to fudge this to use the volume setter routine */
+	left = 100 - (data & 0xff) ; if( left < 0 ) left = 0 ;
+	right = 100 - ((data >> 8) & 0xff) ; if( right < 0 ) right = 0 ;
+	left |= (right << 8 );
+	left = awacs_volume_setter(left, 0, 0, 4);
+}
+
+static void do_passthru_vol(int data)
+{
+	passthru_vol = data ;
+	awacs_reg[1] &= ~MASK_LOOPTHRU;
+	if (awacs_revision == AWACS_SCREAMER) {
+		if( data ) { /* switch it on for non-zero */
 			awacs_reg[1] |= MASK_LOOPTHRU;
+			awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		}
+		data = awacs_volume_setter(data, 5, 0, 6) ;
+	} else {
+		if ((data & 0xff) >= 50)
+			awacs_reg[1] |= MASK_LOOPTHRU;
+		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
+	}
+}
+
+static int awacs_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	switch (cmd) {
+	case SOUND_MIXER_READ_CAPS:
+		/* say we will allow multiple inputs?  prob. wrong
+			so I'm switching it to single */
+		return IOCTL_OUT(arg, 1);
+	case SOUND_MIXER_READ_DEVMASK:
+		data  = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
+			| SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD
+			| SOUND_MASK_IGAIN | SOUND_MASK_RECLEV
+			| SOUND_MASK_ALTPCM
+			| SOUND_MASK_MONITOR;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		if (awacs_reg[0] & MASK_MUX_AUDIN)
+			data |= SOUND_MASK_LINE;
+		if (awacs_reg[0] & MASK_MUX_MIC)
+			data |= SOUND_MASK_MIC;
+		if (awacs_reg[0] & MASK_MUX_CD)
+			data |= SOUND_MASK_CD;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD);
+		awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
+				  | MASK_MUX_AUDIN);
+		if (data & SOUND_MASK_LINE)
+			awacs_reg[0] |= MASK_MUX_AUDIN;
+		if (data & SOUND_MASK_MIC)
+			awacs_reg[0] |= MASK_MUX_MIC;
+		if (data & SOUND_MASK_CD)
+			awacs_reg[0] |= MASK_MUX_CD;
 		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		awacs_write(awacs_reg[1] | MASK_ADDR1);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_STEREODEVS:
-		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
-			| SOUND_MASK_RECLEV;
-		return IOCTL_OUT(arg, data);
-	case SOUND_MIXER_READ_CAPS:
-		return IOCTL_OUT(arg, 0);
-	case SOUND_MIXER_READ_VOLUME:
-		data = (awacs_reg[1] & MASK_AMUTE)? 0:
-			awacs_get_volume(awacs_reg[2], 6);
-		return IOCTL_OUT(arg, data);
+		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER| SOUND_MASK_RECLEV  ;
+		if (awacs_revision == AWACS_SCREAMER)
+			data |= SOUND_MASK_MONITOR ;
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_VOLUME:
 		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, PMacSetVolume(data));
-	case SOUND_MIXER_READ_SPEAKER:
-		if (awacs_revision == 3
-		    && sys_ctrler == SYS_CTRLER_CUDA)
-			data = awacs_spkr_vol;
-		else
-			data = (awacs_reg[1] & MASK_CMUTE)? 0:
-				awacs_get_volume(awacs_reg[4], 6);
-		return IOCTL_OUT(arg, data);
+		line_vol = data ;
+		awacs_volume_setter(data, 2, 0, 6);
+		/* fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		rc = IOCTL_OUT(arg, line_vol);
+		break;
 	case SOUND_MIXER_WRITE_SPEAKER:
 		IOCTL_IN(arg, data);
-		if (awacs_revision == 3
-		    && sys_ctrler == SYS_CTRLER_CUDA)
+		spk_vol = data ;
+		if (has_perch)
 			awacs_enable_amp(data);
 		else
-			data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
-		return IOCTL_OUT(arg, data);
+			(void)awacs_volume_setter(data, 4, MASK_CMUTE, 6);
+		/* fall though */
+	case SOUND_MIXER_READ_SPEAKER:
+		rc = IOCTL_OUT(arg, spk_vol);
+		break;
 	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
 		IOCTL_IN(arg, data);
-		beep_volume = data & 0xff;
-				/* fall through */
+		beep_vol = data & 0xff;
+		/* fall through */
 	case SOUND_MIXER_READ_ALTPCM:
-		return IOCTL_OUT(arg, beep_volume);
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
 	case SOUND_MIXER_WRITE_LINE:
 		IOCTL_IN(arg, data);
-		awacs_reg[0] &= ~MASK_MUX_AUDIN;
-		if ((data & 0xff) >= 50)
-			awacs_reg[0] |= MASK_MUX_AUDIN;
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_line_lev(data) ;
+		/* fall through */
 	case SOUND_MIXER_READ_LINE:
-		data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, line_lev);
+		break;
+	case SOUND_MIXER_WRITE_IGAIN:
+		IOCTL_IN(arg, data);
+		do_ip_gain(data) ;
+		/* fall through */
+	case SOUND_MIXER_READ_IGAIN:
+		rc = IOCTL_OUT(arg, ip_gain);
+		break;
 	case SOUND_MIXER_WRITE_MIC:
 		IOCTL_IN(arg, data);
-		data &= 0xff;
-		awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
-		if (data >= 25) {
-			awacs_reg[0] |= MASK_MUX_MIC;
-			if (data >= 75)
-				awacs_reg[0] |= MASK_GAINLINE;
-		}
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_mic_lev(data);
+		/* fall through */
 	case SOUND_MIXER_READ_MIC:
-		data = (awacs_reg[0] & MASK_MUX_MIC)?
-			(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, mic_lev);
+		break;
 	case SOUND_MIXER_WRITE_CD:
 		IOCTL_IN(arg, data);
-		awacs_reg[0] &= ~MASK_MUX_CD;
-		if ((data & 0xff) >= 50)
-			awacs_reg[0] |= MASK_MUX_CD;
-		awacs_write(MASK_ADDR0 | awacs_reg[0]);
-				/* fall through */
+		do_cd_lev(data);
+		/* fall through */
 	case SOUND_MIXER_READ_CD:
-		data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, cd_lev);
+		break;
 	case SOUND_MIXER_WRITE_RECLEV:
 		IOCTL_IN(arg, data);
-		data = awacs_volume_setter(data, 0, 0, 4);
-		return IOCTL_OUT(arg, data);
+		do_rec_lev(data) ;
+		/* fall through */
 	case SOUND_MIXER_READ_RECLEV:
-		data = awacs_get_volume(awacs_reg[0], 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, rec_lev);
+		break;
 	case MIXER_WRITE(SOUND_MIXER_MONITOR):
 		IOCTL_IN(arg, data);
-		awacs_reg[1] &= ~MASK_LOOPTHRU;
-		if ((data & 0xff) >= 50)
-			awacs_reg[1] |= MASK_LOOPTHRU;
-		awacs_write(MASK_ADDR1 | awacs_reg[1]);
+		do_passthru_vol(data) ;
 		/* fall through */
 	case MIXER_READ(SOUND_MIXER_MONITOR):
-		data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, passthru_vol);
+		break;
+	default:
+		rc = -EINVAL;
 	}
-	return -EINVAL;
+	
+	return rc;
+}
+
+static void awacs_mixer_init(void)
+{
+	awacs_volume_setter(line_vol, 2, 0, 6);
+	if (has_perch)
+		awacs_enable_amp(spk_vol);
+	else
+		(void)awacs_volume_setter(spk_vol, 4, MASK_CMUTE, 6);
+	do_line_lev(line_lev) ;
+	do_ip_gain(ip_gain) ;
+	do_mic_lev(mic_lev) ;
+	do_cd_lev(cd_lev) ;
+	do_rec_lev(rec_lev) ;
+	do_passthru_vol(passthru_vol) ;
 }
 
 static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
 {
 	int data;
+	int rc;
 
 	/* We are, we are, we are... Burgundy or better */
 	switch(cmd) {
@@ -1742,11 +1871,13 @@
 		data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
 			SOUND_MASK_LINE | SOUND_MASK_MIC |
 			SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECMASK:
 		data = SOUND_MASK_LINE | SOUND_MASK_MIC
 			| SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECSRC:
 		data = 0;
 		if (awacs_reg[0] & MASK_MUX_AUDIN)
@@ -1755,7 +1886,8 @@
 			data |= SOUND_MASK_MIC;
 		if (awacs_reg[0] & MASK_MUX_CD)
 			data |= SOUND_MASK_CD;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_RECSRC:
 		IOCTL_IN(arg, data);
 		data &= (SOUND_MASK_LINE
@@ -1769,23 +1901,26 @@
 		if (data & SOUND_MASK_CD)
 			awacs_reg[0] |= MASK_MUX_CD;
 		awacs_write(awacs_reg[0] | MASK_ADDR0);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_STEREODEVS:
 		data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
 			| SOUND_MASK_RECLEV | SOUND_MASK_CD
 			| SOUND_MASK_LINE;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_CAPS:
-		return IOCTL_OUT(arg, 0);
+		rc = IOCTL_OUT(arg, 0);
+		break;
 	case SOUND_MIXER_WRITE_VOLUME:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
 				/* Fall through */
 	case SOUND_MIXER_READ_VOLUME:
-		return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+		rc = IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
+		break;
 	case SOUND_MIXER_WRITE_SPEAKER:
 		IOCTL_IN(arg, data);
-
 		if (!(data & 0xff)) {
 			/* Mute the left speaker */
 			awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
@@ -1806,7 +1941,7 @@
 		}
 
 		data = (((data&0xff)*16)/100 > 0xf ? 0xf :
-			(((data&0xff)*16)/100)) + 
+			(((data&0xff)*16)/100)) +
 			((((data>>8)*16)/100 > 0xf ? 0xf :
 			  ((((data>>8)*16)/100)))<<4);
 
@@ -1815,21 +1950,24 @@
 	case SOUND_MIXER_READ_SPEAKER:
 		data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
 		data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
-		return IOCTL_OUT(arg, ~data);
+		rc = IOCTL_OUT(arg, ~data);
+		break;
 	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
 		IOCTL_IN(arg, data);
-		beep_volume = data & 0xff;
+		beep_vol = data & 0xff;
 				/* fall through */
 	case SOUND_MIXER_READ_ALTPCM:
-		return IOCTL_OUT(arg, beep_volume);
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
 	case SOUND_MIXER_WRITE_LINE:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
 
 				/* fall through */
 	case SOUND_MIXER_READ_LINE:
-		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);				
-		return IOCTL_OUT(arg, data);
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_MIC:
 		IOCTL_IN(arg, data);
 				/* Mic is mono device */
@@ -1837,66 +1975,331 @@
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
 				/* fall through */
 	case SOUND_MIXER_READ_MIC:
-		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);				
+		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);
 		data <<= 24;
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_CD:
 		IOCTL_IN(arg, data);
 		awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
 				/* fall through */
 	case SOUND_MIXER_READ_CD:
 		data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_WRITE_RECLEV:
 		IOCTL_IN(arg, data);
 		data = awacs_volume_setter(data, 0, 0, 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_READ_RECLEV:
 		data = awacs_get_volume(awacs_reg[0], 4);
-		return IOCTL_OUT(arg, data);
+		rc = IOCTL_OUT(arg, data);
+		break;
 	case SOUND_MIXER_OUTMASK:
+	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
+	}
+	
+	return rc;
+}
+
+static int tumbler_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	/* We are, we are, we are... Tumbler (and very dumb) */
+	/* Ok, we're not THAT dumb anymore, but still pretty dumb :-) */
+
+	switch(cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data =  SOUND_MASK_VOLUME | SOUND_MASK_ALTPCM |
+			SOUND_MASK_BASS | SOUND_MASK_TREBLE |
+			SOUND_MASK_PCM;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data =0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME | SOUND_MASK_PCM;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_CAPS:
+		rc = IOCTL_OUT(arg, 0);
+		break;
+	case SOUND_MIXER_WRITE_BASS:
+		IOCTL_IN(arg, data);
+		tumbler_set_bass(data);
+		/* Fall through */
+	case SOUND_MIXER_READ_BASS:
+		tumbler_get_bass(&data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_TREBLE:
+		IOCTL_IN(arg, data);
+		tumbler_set_treble(data);
+		/* Fall through */
+	case SOUND_MIXER_READ_TREBLE:
+		tumbler_get_treble(&data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_PCM:
+		IOCTL_IN(arg, data);
+		tumbler_set_pcm_lvl(data);
+		/* Fall through */
+	case SOUND_MIXER_READ_PCM:
+		tumbler_get_pcm_lvl(&data);
+		IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		tumbler_set_volume(data, data);
+		/* Fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		tumbler_get_volume(& data, &data);
+		rc = IOCTL_OUT(arg, data);
 		break;
+	case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */
+		IOCTL_IN(arg, data);
+		beep_vol = data & 0xff;
+		/* fall through */
+	case SOUND_MIXER_READ_ALTPCM:
+		rc = IOCTL_OUT(arg, beep_vol);
+		break;
+	case SOUND_MIXER_OUTMASK:
 	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int daca_mixer_ioctl(u_int cmd, u_long arg)
+{
+	int data;
+	int rc;
+
+	/* And the DACA's no genius either! */
+
+	switch(cmd) {
+	case SOUND_MIXER_READ_DEVMASK:
+		data = SOUND_MASK_VOLUME;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECMASK:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_RECSRC:
+		data = 0;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_WRITE_RECSRC:
+		IOCTL_IN(arg, data);
+		data =0;
+		rc = IOCTL_OUT(arg, data);
 		break;
+	case SOUND_MIXER_READ_STEREODEVS:
+		data = SOUND_MASK_VOLUME;
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_READ_CAPS:
+		rc = IOCTL_OUT(arg, 0);
+		break;
+	case SOUND_MIXER_WRITE_VOLUME:
+		IOCTL_IN(arg, data);
+		daca_set_volume(data, data);
+		/* Fall through */
+	case SOUND_MIXER_READ_VOLUME:
+		daca_get_volume(& data, &data);
+		rc = IOCTL_OUT(arg, data);
+		break;
+	case SOUND_MIXER_OUTMASK:
+	case SOUND_MIXER_OUTSRC:
+	default:
+		rc = -EINVAL;
 	}
-	return -EINVAL;
+	return rc;
 }
 
 static int PMacMixerIoctl(u_int cmd, u_long arg)
 {
-	/* Different IOCTLS for burgundy*/
-	if (awacs_revision >= AWACS_BURGUNDY)
-		return burgundy_mixer_ioctl(cmd, arg);
-	return awacs_mixer_ioctl(cmd, arg);
+	int rc;
+	
+	/* Different IOCTLS for burgundy and, eventually, DACA & Tumbler */
+
+	TRY_LOCK();
+	
+	switch (awacs_revision){
+		case AWACS_BURGUNDY:
+			rc = burgundy_mixer_ioctl(cmd, arg);
+			break ;
+		case AWACS_DACA:
+			rc = daca_mixer_ioctl(cmd, arg);
+			break;
+		case AWACS_TUMBLER:
+			rc = tumbler_mixer_ioctl(cmd, arg);
+			break ;
+		default: /* ;-)) */
+			rc = awacs_mixer_ioctl(cmd, arg);
+	}
+
+	UNLOCK();
+	
+	return rc;
+}
+
+static void PMacMixerInit(void)
+{
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+		  printk("AE-Init tumbler mixer\n");
+		  break ;
+		  
+		case AWACS_DACA:
+		case AWACS_BURGUNDY:
+			break ;	/* don't know yet */
+		case AWACS_AWACS:
+		case AWACS_SCREAMER:
+		default:
+			awacs_mixer_init() ;
+			break ;
+	}
 }
 
+/* Write/Read sq setup functions:
+   Check to see if we have enough (or any) dbdma cmd buffers for the
+   user's fragment settings.  If not, allocate some. If this fails we will
+   point at the beep buffer - as an emergency provision - to stop dma tromping
+   on some random bit of memory (if someone lets it go anyway).
+   The command buffers are then set up to point to the fragment buffers
+   (allocated elsewhere).  We need n+1 commands the last of which holds
+   a NOP + loop to start.
+*/
 
-static void PMacWriteSqSetup(void)
+static int PMacWriteSqSetup(void)
 {
-	int i;
+	int i, count = 600 ;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	
+	/* stop the controller from doing any output - if it isn't already.
+	   it _should_ be before this is called anyway */
+
+	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	while ((in_le32(&awacs_txdma->status) & RUN) && count--)
+		udelay(1);
+#ifdef DEBUG_DMASOUND
+if (count <= 0)
+	printk("dmasound_pmac: write sq setup: timeout waiting for dma to stop\n");
+#endif
+
+	if ((write_sq.max_count + 1) > number_of_tx_cmd_buffers) {
+		if (awacs_tx_cmd_space)
+			kfree(awacs_tx_cmd_space);
+		number_of_tx_cmd_buffers = 0;
+
+		/* we need nbufs + 1 (for the loop) and we should request + 1
+		   again because the DBDMA_ALIGN might pull the start up by up
+		   to sizeof(struct dbdma_cmd) - 4.
+		*/
+
+		awacs_tx_cmd_space = kmalloc
+			((write_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
+			 GFP_KERNEL);
+		if (awacs_tx_cmd_space == NULL) {
+			/* don't leave it dangling - nasty but better than a
+			   random address */
+			out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+			printk(KERN_ERR
+			   "dmasound_pmac: can't allocate dbdma cmd buffers"
+			   ", driver disabled\n");
+			UNLOCK();
+			return -ENOMEM;
+		}
+		awacs_tx_cmds = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(awacs_tx_cmd_space);
+		number_of_tx_cmd_buffers = write_sq.max_count + 1;
+	}
+
 	cp = awacs_tx_cmds;
-	memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd));
-	for (i = 0; i < write_sq.numBufs; ++i, ++cp) {
+	memset((void *)cp, 0, (write_sq.max_count+1) * sizeof(struct dbdma_cmd));
+	for (i = 0; i < write_sq.max_count; ++i, ++cp) {
 		st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
 	}
 	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
 	st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
-	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	/* point the controller at the command stack - ready to go */
 	out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
+	UNLOCK();
+	return 0;
 }
 
-static void PMacReadSqSetup(void)
+static int PMacReadSqSetup(void)
 {
-	int i;
+	int i, count = 600;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	
+	/* stop the controller from doing any input - if it isn't already.
+	   it _should_ be before this is called anyway */
+	
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	while ((in_le32(&awacs_rxdma->status) & RUN) && count--)
+		udelay(1);
+#ifdef DEBUG_DMASOUND
+if (count <= 0)
+	printk("dmasound_pmac: read sq setup: timeout waiting for dma to stop\n");
+#endif
+
+	if ((read_sq.max_count+1) > number_of_rx_cmd_buffers ) {
+		if (awacs_rx_cmd_space)
+			kfree(awacs_rx_cmd_space);
+		number_of_rx_cmd_buffers = 0;
+
+		/* we need nbufs + 1 (for the loop) and we should request + 1 again
+		   because the DBDMA_ALIGN might pull the start up by up to
+		   sizeof(struct dbdma_cmd) - 4 (assuming kmalloc aligns 32 bits).
+		*/
+
+		awacs_rx_cmd_space = kmalloc
+			((read_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
+			 GFP_KERNEL);
+		if (awacs_rx_cmd_space == NULL) {
+			/* don't leave it dangling - nasty but better than a
+			   random address */
+			out_le32(&awacs_rxdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
+			printk(KERN_ERR
+			   "dmasound_pmac: can't allocate dbdma cmd buffers"
+			   ", driver disabled\n");
+			UNLOCK();
+			return -ENOMEM;
+		}
+		awacs_rx_cmds = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(awacs_rx_cmd_space);
+		number_of_rx_cmd_buffers = read_sq.max_count + 1 ;
+	}
 	cp = awacs_rx_cmds;
-	memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd));
+	memset((void *)cp, 0, (read_sq.max_count+1) * sizeof(struct dbdma_cmd));
 
 	/* Set dma buffers up in a loop */
-	for (i = 0; i < read_sq.numBufs; i++,cp++) {
+	for (i = 0; i < read_sq.max_count; i++,cp++) {
 		st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
 		st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
 		st_le16(&cp->req_count, read_sq.block_size);
@@ -1907,37 +2310,105 @@
 	*/
 	st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
 	st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
-
-	/* Don't start until the first read is done.
-	 * This will also abort any operations in progress if the DMA
-	 * happens to be running (and it shouldn't).
-	 */
-	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	/* point the controller at the command stack - ready to go */
 	out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
 
+	UNLOCK();
+	return 0;
 }
 
+/* TODO: this needs work to guarantee that when it returns DMA has stopped
+   but in a more elegant way than is done here....
+*/
+
 static void PMacAbortRead(void)
 {
 	int i;
 	volatile struct dbdma_cmd *cp;
 
+	LOCK();
+	/* give it a chance to update the output and provide the IRQ
+	   that is expected.
+	*/
+
+	out_le32(&awacs_rxdma->control, ((FLUSH) << 16) + FLUSH );
+
 	cp = awacs_rx_cmds;
-	for (i = 0; i < read_sq.numBufs; i++,cp++)
+	for (i = 0; i < read_sq.max_count; i++,cp++)
 		st_le16(&cp->command, DBDMA_STOP);
 	/*
 	 * We should probably wait for the thing to stop before we
-	 * release the memory
+	 * release the memory.
 	 */
+
+	wait_ms(100) ; /* give it a (small) chance to act */
+
+	/* apply the sledgehammer approach - just stop it now */
+
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
+	UNLOCK();
 }
 
+extern char *get_afmt_string(int);
+static int PMacStateInfo(char *b, size_t sp)
+{
+	int i, len = 0;
+	len = sprintf(b,"HW rates: ");
+	switch (awacs_revision){
+		case AWACS_DACA:
+		case AWACS_BURGUNDY:
+			len += sprintf(b,"44100 ") ;
+			break ;
+		case AWACS_TUMBLER:
+			for (i=0; i<2; i++){
+				if (tumbler_freqs_ok[i])
+					len += sprintf(b+len,"%d ", tumbler_freqs[i]) ;
+			}
+			break ;
+
+		case AWACS_AWACS:
+		case AWACS_SCREAMER:
+		default:
+			for (i=0; i<8; i++){
+				if (awacs_freqs_ok[i])
+					len += sprintf(b+len,"%d ", awacs_freqs[i]) ;
+			}
+			break ;
+	}
+	len += sprintf(b+len,"s/sec\n") ;
+	if (len < sp) {
+		len += sprintf(b+len,"HW AFMTS: ");
+		i = AFMT_U16_BE ;
+		while (i) {
+			if (i & dmasound.mach.hardware_afmts)
+				len += sprintf(b+len,"%s ",
+					get_afmt_string(i & dmasound.mach.hardware_afmts));
+			i >>= 1 ;
+		}
+		len += sprintf(b+len,"\n") ;
+	}
+	return len ;
+}
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_S16_BE,
+	stereo: 1,
+	size: 16,
+	speed: 44100
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_S16_BE,
+	stereo: 1,
+	size: 16,
+	speed: 44100
+} ;
 
 static MACHINE machPMac = {
 	name:		awacs_name,
-	name2:		"AWACS",
+	name2:		"PowerMac Built-in Sound",
 	open:		PMacOpen,
 	release:	PMacRelease,
 	dma_alloc:	PMacAlloc,
@@ -1951,233 +2422,660 @@
 	setFormat:	PMacSetFormat,
 	setVolume:	PMacSetVolume,
 	play:		PMacPlay,
-	record:		PMacRecord,
+	record:		NULL,		/* default to no record */
+	mixer_init:	PMacMixerInit,
 	mixer_ioctl:	PMacMixerIoctl,
 	write_sq_setup:	PMacWriteSqSetup,
 	read_sq_setup:	PMacReadSqSetup,
+	state_info:	PMacStateInfo,
 	abort_read:	PMacAbortRead,
-	min_dsp_speed:	8000
+	min_dsp_speed:	7350,
+	max_dsp_speed:	44100,
+	version:	((DMASOUND_AWACS_REVISION<<8) + DMASOUND_AWACS_EDITION)
 };
 
 
 /*** Config & Setup **********************************************************/
 
+/* Check for pmac models that we care about in terms of special actions.
+*/
 
-int __init dmasound_awacs_init(void)
+void __init
+set_model(void)
 {
-	struct device_node *np;
+	/* portables/lap-tops */
 
-	if (_machine != _MACH_Pmac)
-		return -ENODEV;
+	if (machine_is_compatible("AAPL,3400/2400") ||
+	    machine_is_compatible("AAPL,3500"))	{
+		is_pbook_3X00 = 1 ;
+	}
+	if (machine_is_compatible("PowerBook1,1")  || /* lombard */
+	    machine_is_compatible("AAPL,PowerBook1998")){ /* wallstreet */
+		is_pbook_g3 = 1 ;
+		return ;
+	}
+}
 
-	awacs_subframe = 0;
-	awacs_revision = 0;
-	np = find_devices("awacs");
-	if (np == 0) {
-		/*
-		 * powermac G3 models have a node called "davbus"
-		 * with a child called "sound".
-		 */
-		struct device_node *sound;
-		np = find_devices("davbus");
-		sound = find_devices("sound");
-		if (sound != 0 && sound->parent == np) {
-			unsigned int *prop, l, i;
-			prop = (unsigned int *)
-				get_property(sound, "sub-frame", 0);
-			if (prop != 0 && *prop >= 0 && *prop < 16)
-				awacs_subframe = *prop;
-			if (device_is_compatible(sound, "burgundy"))
-				awacs_revision = AWACS_BURGUNDY;
-			/* This should be verified on older screamers */
-			if (device_is_compatible(sound, "screamer"))
-				awacs_is_screamer = 1;
-			prop = (unsigned int *)get_property(sound, "device-id", 0);
-			if (prop != 0)
-				awacs_device_id = *prop;
-			awacs_has_iic = (find_devices("perch") != NULL);
-
-			/* look for a property saying what sample rates
-			   are available */
-			for (i = 0; i < 8; ++i)
-				awacs_freqs_ok[i] = 0;
-			prop = (unsigned int *) get_property
-				(sound, "sample-rates", &l);
-			if (prop == 0)
-				prop = (unsigned int *) get_property
-					(sound, "output-frame-rates", &l);
-			if (prop != 0) {
-				for (l /= sizeof(int); l > 0; --l) {
-					/* sometimes the rate is in the
-					   high-order 16 bits (?) */
-					unsigned int r = *prop++;
-					if (r >= 0x10000)
-						r >>= 16;
-					for (i = 0; i < 8; ++i) {
-						if (r == awacs_freqs[i]) {
-							awacs_freqs_ok[i] = 1;
-							break;
-						}
-					}
+/* Get the OF node that tells us about the registers, interrupts etc. to use
+   for sound IO.
+
+   On most machines the sound IO OF node is the 'davbus' node.  On newer pmacs
+   with DACA (& Tumbler) the node to use is i2s-a.  On much older machines i.e.
+   before 9500 there is no davbus node and we have to use the 'awacs' property.
+
+  In the latter case we signal this by setting the codec value - so that the
+  code that looks for chip properties knows how to go about it.
+*/
+
+static struct device_node
+__init *get_snd_io_node(void)
+{
+	struct device_node *np = NULL;
+
+	/* set up awacs_node for early OF which doesn't have a full set of
+	 * properties on davbus
+	*/
+
+	awacs_node = find_devices("awacs");
+	if (awacs_node)
+		awacs_revision = AWACS_AWACS;
+
+	/* powermac models after 9500 (other than those which use DACA or
+	 * Tumbler) have a node called "davbus".
+	 */
+	np = find_devices("davbus");
+	/*
+	 * if we didn't find a davbus device, try 'i2s-a' since
+	 * this seems to be what iBooks (& Tumbler) have.
+	 */
+	if (np == NULL)
+		np = find_devices("i2s-a");
+
+	/* if we didn't find this - perhaps we are on an early model
+	 * which _only_ has an 'awacs' node
+	*/
+	if (np == NULL && awacs_node)
+		np = awacs_node ;
+
+	/* if we failed all these return null - this will cause the
+	 * driver to give up...
+	*/
+	return np ;
+}
+
+/* Get the OF node that contains the info about the sound chip, inputs s-rates
+   etc.
+   This node does not exist (or contains much reduced info) on earlier machines
+   we have to deduce the info other ways for these.
+*/
+
+static struct device_node
+__init *get_snd_info_node(struct device_node *io)
+{
+	struct device_node *info;
+
+	info = find_devices("sound");
+	while (info != 0 && info->parent != io)
+		info = info->next;
+
+	return info ;
+}
+
+/* Find out what type of codec we have.
+*/
+
+static int
+__init get_codec_type(struct device_node *info)
+{
+	/* already set if pre-davbus model and info will be NULL */
+	int codec = awacs_revision ;
+
+	if (info) {
+		/* must do awacs first to allow screamer to overide it */
+		if (device_is_compatible(info, "awacs"))
+			codec = AWACS_AWACS ;
+		if (device_is_compatible(info, "screamer"))
+			codec = AWACS_SCREAMER;
+		if (device_is_compatible(info, "burgundy"))
+			codec = AWACS_BURGUNDY ;
+		if (device_is_compatible(info, "daca"))
+			codec = AWACS_DACA;
+		if (device_is_compatible(info, "tumbler"))
+			codec = AWACS_TUMBLER;
+	}
+	return codec ;
+}
+
+/* find out what type, if any, of expansion card we have
+*/
+static void
+__init get_expansion_type(void)
+{
+	if (find_devices("perch") != NULL)
+		has_perch = 1;
+
+	if (find_devices("pb-ziva-pc") != NULL)
+		has_ziva = 1;
+	/* need to work out how we deal with iMac SRS module */
+}
+
+/* set up frame rates.
+ * I suspect that these routines don't quite go about it the right way:
+ * - where there is more than one rate - I think that the first property
+ * value is the number of rates.
+ * TODO: check some more device trees and modify accordingly
+ *       Set dmasound.mach.max_dsp_rate on the basis of these routines.
+*/
+
+static void
+__init init_awacs_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int i ;
+	if (prop) {
+		for (i=0; i<8; i++)
+			awacs_freqs_ok[i] = 0 ;
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			for (i = 0; i < 8; ++i) {
+				if (r == awacs_freqs[i]) {
+					awacs_freqs_ok[i] = 1;
+					break;
 				}
-			} else {
-				/* assume just 44.1k is OK */
-				awacs_freqs_ok[0] = 1;
 			}
 		}
 	}
-	if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
-		int vol;
-		dmasound.mach = machPMac;
-
-		awacs = (volatile struct awacs_regs *)
-			ioremap(np->addrs[0].address, 0x80);
-		awacs_txdma = (volatile struct dbdma_regs *)
-			ioremap(np->addrs[1].address, 0x100);
-		awacs_rxdma = (volatile struct dbdma_regs *)
-			ioremap(np->addrs[2].address, 0x100);
-
-		awacs_irq = np->intrs[0].line;
-		awacs_tx_irq = np->intrs[1].line;
-		awacs_rx_irq = np->intrs[2].line;
+	/* else we assume that all the rates are available */
+}
 
-		awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
-					     GFP_KERNEL);
-		if (awacs_tx_cmd_space == NULL) {
-			printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
-			return -ENOMEM;
+static void
+__init init_tumbler_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int i ;
+	if (prop) {
+		for (i=0; i<2; i++)
+			tumbler_freqs_ok[i] = 0;
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			for (i = 0; i < 2; ++i) {
+				if (r == tumbler_freqs[i]) {
+					tumbler_freqs_ok[i] = 1;
+					break;
+				}
+			}
 		}
-		awacs_node = np;
-#ifdef CONFIG_PMAC_PBOOK
-		if (machine_is_compatible("PowerBook1,1")
-		    || machine_is_compatible("AAPL,PowerBook1998")) {
-			pmu_suspend();
-			feature_set(np, FEATURE_Sound_CLK_enable);
-			feature_set(np, FEATURE_Sound_power);
-			/* Shorter delay will not work */
-			mdelay(1000);
-			pmu_resume();
+	}
+	/* else we assume that all the rates are available */
+}
+
+static void
+__init init_burgundy_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int temp[9] ;
+	int i = 0 ;
+	if (prop) {
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			temp[i] = r ;
+			i++ ; if(i>=9) i=8;
+		}
+	}
+#ifdef DEBUG_DMASOUND
+if (i > 1){
+	int j;
+	printk("dmasound_pmac: burgundy with multiple frame rates\n");
+	for(j=0; j<i; j++)
+		printk("%d ", temp[j]) ;
+	printk("\n") ;
+}
+#endif
+}
+
+static void
+__init init_daca_frame_rates(unsigned int *prop, unsigned int l)
+{
+	int temp[9] ;
+	int i = 0 ;
+	if (prop) {
+		for (l /= sizeof(int); l > 0; --l) {
+			unsigned int r = *prop++;
+			/* Apple 'Fixed' format */
+			if (r >= 0x10000)
+				r >>= 16;
+			temp[i] = r ;
+			i++ ; if(i>=9) i=8;
+
 		}
+	}
+#ifdef DEBUG_DMASOUND
+if (i > 1){
+	int j;
+	printk("dmasound_pmac: DACA with multiple frame rates\n");
+	for(j=0; j<i; j++)
+		printk("%d ", temp[j]) ;
+	printk("\n") ;
+}
 #endif
-		awacs_tx_cmds = (volatile struct dbdma_cmd *)
-			DBDMA_ALIGN(awacs_tx_cmd_space);
+}
+
+static void
+__init init_frame_rates(unsigned int *prop, unsigned int l)
+{
+	switch (awacs_revision){
+		case AWACS_TUMBLER:
+			init_tumbler_frame_rates(prop, l);
+			break ;
+		case AWACS_DACA:
+			init_daca_frame_rates(prop, l);
+			break ;
+		case AWACS_BURGUNDY:
+			init_burgundy_frame_rates(prop, l);
+			break ;
+		default: /* ;-))) */
+			init_awacs_frame_rates(prop, l);
+			break ;
+	}
+}
 
+/* find things/machines that can't do mac-io byteswap
+*/
 
-		awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
-					     GFP_KERNEL);
-		if (awacs_rx_cmd_space == NULL) {
-		  printk("DMA sound driver: No memory for input");
+static void
+__init set_hw_byteswap(struct device_node *io)
+{
+	struct device_node *mio ;
+	unsigned int *p, kl = 0 ;
+
+	/* if seems that Keylargo can't byte-swap  */
+
+	for (mio = io->parent; mio ; mio = mio->parent) {
+		if (strcmp(mio->name, "mac-io") == 0) {
+			if (device_is_compatible(mio, "Keylargo"))
+				kl = 1;
+			break;
 		}
-		awacs_rx_cmds = (volatile struct dbdma_cmd *)
-		  DBDMA_ALIGN(awacs_rx_cmd_space);
+	}
+	hw_can_byteswap = !kl;
+}
 
+/* Allocate the resources necessary for beep generation.  This cannot be (quite)
+   done statically (yet) because we cannot do virt_to_bus() on static vars when
+   the code is loaded as a module.
 
+   for the sake of saving the possibility that two allocations will incur the
+   overhead of two pull-ups in DBDMA_ALIGN() we allocate the 'emergency' dmdma
+   command here as well... even tho' it is not part of the beep process.
+*/
 
-		awacs_reg[0] = MASK_MUX_CD;
-		/* FIXME: Only machines with external SRS module need MASK_PAROUT */
-		awacs_reg[1] = MASK_LOOPTHRU;
-		if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8
-			|| */awacs_device_id == 0xb)
-			awacs_reg[1] |= MASK_PAROUT;
-		/* get default volume from nvram */
-		vol = (~nvram_read_byte(0x1308) & 7) << 1;
-		awacs_reg[2] = vol + (vol << 6);
-		awacs_reg[4] = vol + (vol << 6);
-		awacs_reg[5] = 0;
-		awacs_reg[6] = 0;
-		awacs_reg[7] = 0;
-		out_le32(&awacs->control, 0x11);
-		awacs_write(awacs_reg[0] + MASK_ADDR0);
-		awacs_write(awacs_reg[1] + MASK_ADDR1);
-		awacs_write(awacs_reg[2] + MASK_ADDR2);
-		awacs_write(awacs_reg[4] + MASK_ADDR4);
-		if (awacs_is_screamer) {
-			awacs_write(awacs_reg[5] + MASK_ADDR5);
-			awacs_write(awacs_reg[6] + MASK_ADDR6);
-			awacs_write(awacs_reg[7] + MASK_ADDR7);
-		}
-
-		/* Initialize recent versions of the awacs */
-		if (awacs_revision == 0) {
-			awacs_revision =
-				(in_le32(&awacs->codec_stat) >> 12) & 0xf;
-			if (awacs_revision == 3) {
-				mdelay(100);
-				awacs_write(0x6000);
-				mdelay(2);
-				awacs_write(awacs_reg[1] + MASK_ADDR1);
-				awacs_enable_amp(100 * 0x101);
+int32_t
+__init setup_beep(void)
+{
+	/* Initialize beep stuff */
+	/* want one cmd buffer for beeps, and a second one for emergencies
+	   - i.e. dbdma error conditions.
+	   ask for three to allow for pull up in DBDMA_ALIGN().
+	*/
+	beep_dbdma_cmd_space =
+		kmalloc((2 + 1) * sizeof(struct dbdma_cmd), GFP_KERNEL);
+	if(beep_dbdma_cmd_space == NULL) {
+		printk(KERN_ERR "dmasound_pmac: no beep dbdma cmd space\n") ;
+		return -ENOMEM ;
+	}
+	beep_dbdma_cmd = (volatile struct dbdma_cmd *)
+			DBDMA_ALIGN(beep_dbdma_cmd_space);
+	/* set up emergency dbdma cmd */
+	emergency_dbdma_cmd = beep_dbdma_cmd+1 ;
+	beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
+	if (beep_buf == NULL) {
+		printk(KERN_ERR "dmasound_pmac: no memory for beep buffer\n");
+		if( beep_dbdma_cmd_space ) kfree(beep_dbdma_cmd_space) ;
+		return -ENOMEM ;
+	}
+	/* OK, we should be safe to claim the mksound vector now */
+	orig_mksound = kd_mksound;
+	kd_mksound = awacs_mksound;
+	return 0 ;
+}
+
+int __init dmasound_awacs_init(void)
+{
+	struct device_node *io = NULL, *info = NULL;
+	int vol, res;
+
+	if (_machine != _MACH_Pmac)
+		return -ENODEV;
+
+	awacs_subframe = 0;
+	awacs_revision = 0;
+	hw_can_byteswap = 1 ; /* most can */
+
+	/* look for models we need to handle specially */
+	set_model() ;
+
+	/* find the OF node that tells us about the dbdma stuff
+	*/
+	io = get_snd_io_node();
+	if (io == NULL) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find sound io OF node\n");
+#endif
+		return -ENODEV ;
+	}
+
+	/* find the OF node that tells us about the sound sub-system
+	 * this doesn't exist on pre-davbus machines (earlier than 9500)
+	*/
+	if (awacs_revision != AWACS_AWACS) { /* set for pre-davbus */
+		info = get_snd_info_node(io) ;
+		if (info == NULL){
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find 'sound' OF node\n");
+#endif
+			return -ENODEV ;
+		}
+	}
+
+	awacs_revision = get_codec_type(info) ;
+	if (awacs_revision == 0) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: couldn't find a Codec we can handle\n");
+#endif
+		return -ENODEV ; /* we don't know this type of h/w */
+	}
+
+	/* set up perch, ziva, SRS or whatever else we have as sound
+	 *  expansion.
+	*/
+	get_expansion_type();
+
+	/* we've now got enough information to make up the audio topology.
+	 * we will map the sound part of mac-io now so that we can probe for
+	 * other info if necessary (early AWACS we want to read chip ids)
+	 */
+
+	if (io->n_addrs < 3 || io->n_intrs < 3) {
+		/* OK - maybe we need to use the 'awacs' node (on earlier
+		 * machines).
+		*/
+		if (awacs_node) {
+			io = awacs_node ;
+			if (io->n_addrs < 3 || io->n_intrs < 3) {
+				printk("dmasound_pmac: can't use %s"
+					" (%d addrs, %d intrs)\n",
+		      		 io->full_name, io->n_addrs, io->n_intrs);
+				return -ENODEV;
 			}
+		} else {
+			printk("dmasound_pmac: can't use %s (%d addrs, %d intrs)\n",
+		 	      io->full_name, io->n_addrs, io->n_intrs);
 		}
-		if (awacs_revision >= AWACS_BURGUNDY)
+	}
+
+	if (!request_OF_resource(io, 0, NULL)) {
+		printk(KERN_ERR "dmasound: can't request IO resource !\n");
+		return -ENODEV;
+	}
+	if (!request_OF_resource(io, 1, " (tx dma)")) {
+		release_OF_resource(io, 0);
+		printk(KERN_ERR "dmasound: can't request TX DMA resource !\n");
+		return -ENODEV;
+	}
+
+	if (!request_OF_resource(io, 2, " (rx dma)")) {
+		release_OF_resource(io, 0);
+		release_OF_resource(io, 1);
+		printk(KERN_ERR "dmasound: can't request RX DMA resource !\n");
+		return -ENODEV;
+	}
+
+	/* all OF versions I've seen use this value */
+	awacs = (volatile struct awacs_regs *)
+		ioremap(io->addrs[0].address, 0x1000);
+	awacs_txdma = (volatile struct dbdma_regs *)
+		ioremap(io->addrs[1].address, 0x100);
+	awacs_rxdma = (volatile struct dbdma_regs *)
+		ioremap(io->addrs[2].address, 0x100);
+
+#ifdef CONFIG_PMAC_PBOOK
+	/* first of all make sure that the chip is powered up....*/
+	pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1);
+	if (awacs_revision == AWACS_SCREAMER)
+		awacs_recalibrate();
+#endif
+	awacs_irq = io->intrs[0].line;
+	awacs_tx_irq = io->intrs[1].line;
+	awacs_rx_irq = io->intrs[2].line;
+
+	awacs_node = io;
+
+	/* if we have an awacs or screamer - probe the chip to make
+	 * sure we have the right revision.
+	*/
+
+	if (awacs_revision <= AWACS_SCREAMER){
+		uint32_t temp, rev, mfg ;
+		/* find out the awacs revision from the chip */
+		temp = in_le32(&awacs->codec_stat);
+		rev = (temp >> 12) & 0xf;
+		mfg = (temp >>  8) & 0xf;
+#ifdef DEBUG_DMASOUND
+printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %d\n", mfg, rev);
+#endif
+		if (rev >= AWACS_SCREAMER)
+			awacs_revision = AWACS_SCREAMER ;
+		else
+			awacs_revision = rev ;
+	}
+
+	dmasound.mach = machPMac;
+
+	/* find out other bits & pieces from OF, these may be present
+	   only on some models ... so be careful.
+	*/
+
+	/* in the absence of a frame rates property we will use the defaults
+	*/
+
+	if (info) {
+		unsigned int *prop, l;
+
+		sound_device_id = 0;
+		/* device ID appears post g3 b&w */
+		prop = (unsigned int *)get_property(info, "device-id", 0);
+		if (prop != 0)
+			sound_device_id = *prop;
+
+		/* look for a property saying what sample rates
+		   are available */
+
+		prop = (unsigned int *)get_property(info, "sample-rates", &l);
+		if (prop == 0)
+			prop = (unsigned int *) get_property
+				(info, "output-frame-rates", &l);
+
+		/* if it's there use it to set up frame rates */
+		init_frame_rates(prop, l) ;
+	}
+	
+	out_le32(&awacs->control, 0x11); /* set everything quiesent */
+
+	set_hw_byteswap(io) ; /* figure out if the h/w can do it */
+
+	/* get default volume from nvram
+	 * vol = (~nvram_read_byte(0x1308) & 7) << 1;
+	*/
+	vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
+	/* set up tracking values */
+	spk_vol = vol * 100 ;
+	spk_vol /= 7 ; /* get set value to a percentage */
+	spk_vol |= (spk_vol << 8) ; /* equal left & right */
+ 	line_vol = passthru_vol = spk_vol ;
+
+	/* fill regs that are shared between AWACS & Burgundy */
+
+	awacs_reg[2] = vol + (vol << 6);
+	awacs_reg[4] = vol + (vol << 6);
+	awacs_reg[5] = vol + (vol << 6); /* screamer has loopthru vol control */
+	awacs_reg[6] = 0; /* maybe should be vol << 3 for PCMCIA speaker */
+	awacs_reg[7] = 0;
+
+	awacs_reg[0] = MASK_MUX_CD;
+	awacs_reg[1] = MASK_LOOPTHRU;
+
+	/* FIXME: Only machines with external SRS module need MASK_PAROUT */
+	if (has_perch || sound_device_id == 0x5
+	    || /*sound_device_id == 0x8 ||*/ sound_device_id == 0xb)
+		awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
+
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+#ifdef CONFIG_KMOD
+			request_module("i2c-keywest");
+#endif /* CONFIG_KMOD */	
+			awacs_tumbler_init();
+			tas_init();
+			break ;
+		case AWACS_DACA:
+#ifdef CONFIG_KMOD
+			request_module("i2c-keywest");
+#endif /* CONFIG_KMOD */
+			daca_init();
+			break ;		/* dont know how yet */
+		case AWACS_BURGUNDY:
 			awacs_burgundy_init();
+			break ;
+		case AWACS_SCREAMER:
+		case AWACS_AWACS:
+		default:
+			load_awacs() ;
+			break ;
+	}
+
+	/* enable/set-up external modules - when we know how */
+
+	if (has_perch)
+		awacs_enable_amp(100 * 0x101);
+
+	/* Reset dbdma channels */
+	out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	while (in_le32(&awacs_txdma->status) & RUN)
+		udelay(1);
+	out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
+	while (in_le32(&awacs_rxdma->status) & RUN)
+		udelay(1);
+
+	/* Initialize beep stuff */
+	if ((res=setup_beep()))
+		return res ;
 
-		/* Initialize beep stuff */
-		beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1);
-		orig_mksound = kd_mksound;
-		kd_mksound = awacs_mksound;
-		beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
-		if (beep_buf == NULL)
-			printk(KERN_WARNING "dmasound: no memory for "
-			       "beep buffer\n");
 #ifdef CONFIG_PMAC_PBOOK
-		pmu_register_sleep_notifier(&awacs_sleep_notifier);
+	pmu_register_sleep_notifier(&awacs_sleep_notifier);
 #endif /* CONFIG_PMAC_PBOOK */
 
-		/* Powerbooks have odd ways of enabling inputs such as
-		   an expansion-bay CD or sound from an internal modem
-		   or a PC-card modem. */
-		if (machine_is_compatible("AAPL,3400/2400")
-			|| machine_is_compatible("AAPL,3500")) {
-			is_pbook_3400 = 1;
-			/*
-			 * Enable CD and PC-card sound inputs.
-			 * This is done by reading from address
-			 * f301a000, + 0x10 to enable the expansion-bay
-			 * CD sound input, + 0x80 to enable the PC-card
-			 * sound input.  The 0x100 enables the SCSI bus
-			 * terminator power.
-			 */
-			latch_base = (unsigned char *) ioremap
-				(0xf301a000, 0x1000);
-			in_8(latch_base + 0x190);
-		} else if (machine_is_compatible("PowerBook1,1")
-			   || machine_is_compatible("AAPL,PowerBook1998")) {
-			struct device_node* mio;
-			macio_base = 0;
-			is_pbook_G3 = 1;
-			for (mio = np->parent; mio; mio = mio->parent) {
-				if (strcmp(mio->name, "mac-io") == 0
-				    && mio->n_addrs > 0) {
-					macio_base = (unsigned char *) ioremap
-						(mio->addrs[0].address, 0x40);
-					break;
-				}
+	/* Powerbooks have odd ways of enabling inputs such as
+	   an expansion-bay CD or sound from an internal modem
+	   or a PC-card modem. */
+	if (is_pbook_3X00) {
+		/*
+		 * Enable CD and PC-card sound inputs.
+		 * This is done by reading from address
+		 * f301a000, + 0x10 to enable the expansion-bay
+		 * CD sound input, + 0x80 to enable the PC-card
+		 * sound input.  The 0x100 enables the SCSI bus
+		 * terminator power.
+		 */
+		latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000);
+		in_8(latch_base + 0x190);
+
+	} else if (is_pbook_g3) {
+		struct device_node* mio;
+		macio_base = 0;
+		for (mio = io->parent; mio; mio = mio->parent) {
+			if (strcmp(mio->name, "mac-io") == 0
+			    && mio->n_addrs > 0) {
+				macio_base = (unsigned char *) ioremap
+					(mio->addrs[0].address, 0x40);
+				break;
 			}
-			/*
-			 * Enable CD sound input.
-			 * The relevant bits for writing to this byte are 0x8f.
-			 * I haven't found out what the 0x80 bit does.
-			 * For the 0xf bits, writing 3 or 7 enables the CD
-			 * input, any other value disables it.  Values
-			 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
-			 * 4, 6, 8 - f enable the input from the modem.
-			 */
-			if (macio_base)
-				out_8(macio_base + 0x37, 3);
-		}
-		sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
-			awacs_revision);
-		return dmasound_init();
+		}
+		/*
+		 * Enable CD sound input.
+		 * The relevant bits for writing to this byte are 0x8f.
+		 * I haven't found out what the 0x80 bit does.
+		 * For the 0xf bits, writing 3 or 7 enables the CD
+		 * input, any other value disables it.  Values
+		 * 1, 3, 5, 7 enable the microphone.  Values 0, 2,
+		 * 4, 6, 8 - f enable the input from the modem.
+		 *  -- paulus.
+		 */
+		if (macio_base)
+			out_8(macio_base + 0x37, 3);
+	}
+
+	if (hw_can_byteswap)
+ 		dmasound.mach.hardware_afmts = (AFMT_S16_BE | AFMT_S16_LE) ;
+ 	else
+		dmasound.mach.hardware_afmts = AFMT_S16_BE ;
+
+	/* shut out chips that do output only.
+	   may need to extend this to machines which have no inputs - even tho'
+	   they use screamer - IIRC one of the powerbooks is like this.
+	*/
+
+	if (awacs_revision != AWACS_TUMBLER && awacs_revision != AWACS_DACA) {
+		dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
+		dmasound.mach.record = PMacRecord ;
+	}
+
+	dmasound.mach.default_hard = def_hard ;
+	dmasound.mach.default_soft = def_soft ;
+
+	switch (awacs_revision) {
+		case AWACS_BURGUNDY:
+			sprintf(awacs_name, "PowerMac Burgundy ") ;
+			break ;
+		case AWACS_DACA:
+			sprintf(awacs_name, "PowerMac DACA ") ;
+			break ;
+		case AWACS_TUMBLER:
+			sprintf(awacs_name, "PowerMac Tumbler ") ;
+			break ;
+		case AWACS_SCREAMER:
+			sprintf(awacs_name, "PowerMac Screamer ") ;
+			break ;
+		case AWACS_AWACS:
+		default:
+			sprintf(awacs_name, "PowerMac AWACS rev %d ", awacs_revision) ;
+			break ;
 	}
-	return -ENODEV;
+
+	return dmasound_init();
 }
 
 static void __exit dmasound_awacs_cleanup(void)
 {
+	switch (awacs_revision) {
+		case AWACS_TUMBLER:
+			awacs_tumbler_cleanup();
+			tas_cleanup();
+			break ;
+		case AWACS_DACA:
+			daca_cleanup();
+			break;
+	}
 	dmasound_deinit();
 }
 
+MODULE_DESCRIPTION("PowerMac built-in audio driver.");
+MODULE_LICENSE("GPL");
+
 module_init(dmasound_awacs_init);
 module_exit(dmasound_awacs_cleanup);
-MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_core.c linux/drivers/sound/dmasound/dmasound_core.c
--- linux.orig/drivers/sound/dmasound/dmasound_core.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound_core.c	Mon Feb  4 18:47:24 2002
@@ -1,4 +1,3 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_core.c
  *
@@ -103,38 +102,113 @@
  *	2000/3/25	Geert Uytterhoeven:
  *			  - Integration of dmasound_q40
  *			  - Small clean ups
+ *
+ *	2001/01/26 [1.0] Iain Sandoe
+ *			  - make /dev/sndstat show revision & edition info.
+ *			  - since dmasound.mach.sq_setup() can fail on pmac
+ *			    its type has been changed to int and the returns
+ *			    are checked.
+ *		   [1.1]  - stop missing translations from being called.
+ *	2001/02/08 [1.2]  - remove unused translation tables & move machine-
+ *			    specific tables to low-level.
+ *			  - return correct info. for SNDCTL_DSP_GETFMTS.
+ *		   [1.3]  - implement SNDCTL_DSP_GETCAPS fully.
+ *		   [1.4]  - make /dev/sndstat text length usage deterministic.
+ *			  - make /dev/sndstat call to low-level
+ *			    dmasound.mach.state_info() pass max space to ll driver.
+ *			  - tidy startup banners and output info.
+ *		   [1.5]  - tidy up a little (removed some unused #defines in
+ *			    dmasound.h)
+ *			  - fix up HAS_RECORD conditionalisation.
+ *			  - add record code in places it is missing...
+ *			  - change buf-sizes to bytes to allow < 1kb for pmac
+ *			    if user param entry is < 256 the value is taken to
+ *			    be in kb > 256 is taken to be in bytes.
+ *			  - make default buff/frag params conditional on
+ *			    machine to allow smaller values for pmac.
+ *			  - made the ioctls, read & write comply with the OSS
+ *			    rules on setting params.
+ *			  - added parsing of _setup() params for record.
+ *	2001/04/04 [1.6]  - fix bug where sample rates higher than maximum were
+ *			    being reported as OK.
+ *			  - fix open() to return -EBUSY as per OSS doc. when
+ *			    audio is in use - this is independent of O_NOBLOCK.
+ *			  - fix bug where SNDCTL_DSP_POST was blocking.
  */
 
+ /* Record capability notes 30/01/2001:
+  * At present these observations apply only to pmac LL driver (the only one
+  * that can do record, at present).  However, if other LL drivers for machines
+  * with record are added they may apply.
+  *
+  * The fragment parameters for the record and play channels are separate.
+  * However, if the driver is opened O_RDWR there is no way (in the current OSS
+  * API) to specify their values independently for the record and playback
+  * channels.  Since the only common factor between the input & output is the
+  * sample rate (on pmac) it should be possible to open /dev/dspX O_WRONLY and
+  * /dev/dspY O_RDONLY.  The input & output channels could then have different
+  * characteristics (other than the first that sets sample rate claiming the
+  * right to set it for ever).  As it stands, the format, channels, number of
+  * bits & sample rate are assumed to be common.  In the future perhaps these
+  * should be the responsibility of the LL driver - and then if a card really
+  * does not share items between record & playback they can be specified
+  * separately.
+*/
+
+/* Thread-safeness of shared_resources notes: 31/01/2001
+ * If the user opens O_RDWR and then splits record & play between two threads
+ * both of which inherit the fd - and then starts changing things from both
+ * - we will have difficulty telling.
+ *
+ * It's bad application coding - but ...
+ * TODO: think about how to sort this out... without bogging everything down in
+ * semaphores.
+ *
+ * Similarly, the OSS spec says "all changes to parameters must be between
+ * open() and the first read() or write(). - and a bit later on (by
+ * implication) "between SNDCTL_DSP_RESET and the first read() or write() after
+ * it".  If the app is multi-threaded and this rule is broken between threads
+ * we will have trouble spotting it - and the fault will be rather obscure :-(
+ *
+ * We will try and put out at least a kmsg if we see it happen... but I think
+ * it will be quite hard to trap it with an -EXXX return... because we can't
+ * see the fault until after the damage is done.
+*/
 
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sound.h>
 #include <linux/init.h>
 #include <linux/soundcard.h>
+#include <linux/poll.h>
 #include <linux/smp_lock.h>
 
 #include <asm/uaccess.h>
 
 #include "dmasound.h"
 
+#define DMASOUND_CORE_REVISION 1
+#define DMASOUND_CORE_EDITION 6
 
     /*
      *  Declarations
      */
 
 int dmasound_catchRadius = 0;
-static unsigned int numWriteBufs = 4;
-static unsigned int writeBufSize = 32;	/* in KB! */
+static unsigned int numWriteBufs = DEFAULT_N_BUFFERS;
+static unsigned int writeBufSize = DEFAULT_BUFF_SIZE ;	/* in bytes */
 #ifdef HAS_RECORD
-static unsigned int numReadBufs = 4;
-static unsigned int readBufSize = 32;	/* in KB! */
+static unsigned int numReadBufs = DEFAULT_N_BUFFERS;
+static unsigned int readBufSize = DEFAULT_BUFF_SIZE;	/* in bytes */
 #endif
 
 MODULE_PARM(dmasound_catchRadius, "i");
 MODULE_PARM(numWriteBufs, "i");
 MODULE_PARM(writeBufSize, "i");
+#ifdef HAS_RECORD
 MODULE_PARM(numReadBufs, "i");
 MODULE_PARM(readBufSize, "i");
+#endif
 MODULE_LICENSE("GPL");
 
 #ifdef MODULE
@@ -144,246 +218,9 @@
 static int irq_installed = 0;
 #endif /* MODULE */
 
-
-    /*
-     *  Conversion tables
-     */
-
-#ifdef HAS_8BIT_TABLES
-/* 8 bit mu-law */
-
-char dmasound_ulaw2dma8[] = {
-	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
-	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
-	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
-	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
-	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
-	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
-	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
-	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
-	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
-	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
-	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
-	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
-	125,	121,	117,	113,	109,	105,	101,	97,
-	93,	89,	85,	81,	77,	73,	69,	65,
-	62,	60,	58,	56,	54,	52,	50,	48,
-	46,	44,	42,	40,	38,	36,	34,	32,
-	30,	29,	28,	27,	26,	25,	24,	23,
-	22,	21,	20,	19,	18,	17,	16,	15,
-	15,	14,	14,	13,	13,	12,	12,	11,
-	11,	10,	10,	9,	9,	8,	8,	7,
-	7,	7,	6,	6,	6,	6,	5,	5,
-	5,	5,	4,	4,	4,	4,	3,	3,
-	3,	3,	3,	3,	2,	2,	2,	2,
-	2,	2,	2,	2,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0
-};
-
-/* 8 bit A-law */
-
-char dmasound_alaw2dma8[] = {
-	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
-	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
-	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
-	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
-	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
-	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
-	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
-	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
-	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
-	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
-	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
-	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
-	21,	20,	23,	22,	17,	16,	19,	18,
-	29,	28,	31,	30,	25,	24,	27,	26,
-	10,	10,	11,	11,	8,	8,	9,	9,
-	14,	14,	15,	15,	12,	12,	13,	13,
-	86,	82,	94,	90,	70,	66,	78,	74,
-	118,	114,	126,	122,	102,	98,	110,	106,
-	43,	41,	47,	45,	35,	33,	39,	37,
-	59,	57,	63,	61,	51,	49,	55,	53,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	5,	5,	5,	5,	4,	4,	4,	4,
-	7,	7,	7,	7,	6,	6,	6,	6,
-	2,	2,	2,	2,	2,	2,	2,	2,
-	3,	3,	3,	3,	3,	3,	3,	3
-};
-#endif /* HAS_8BIT_TABLES */
-
-#ifdef HAS_16BIT_TABLES
-
-/* 16 bit mu-law */
-
-short dmasound_ulaw2dma16[] = {
-	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
-	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
-	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
-	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
-	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
-	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
-	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
-	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
-	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
-	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
-	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
-	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
-	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
-	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
-	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
-	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
-	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
-	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
-	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
-	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
-	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
-	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
-	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
-	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
-	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
-	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
-	876,	844,	812,	780,	748,	716,	684,	652,
-	620,	588,	556,	524,	492,	460,	428,	396,
-	372,	356,	340,	324,	308,	292,	276,	260,
-	244,	228,	212,	196,	180,	164,	148,	132,
-	120,	112,	104,	96,	88,	80,	72,	64,
-	56,	48,	40,	32,	24,	16,	8,	0,
-};
-
-/* 16 bit A-law */
-
-short dmasound_alaw2dma16[] = {
-	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
-	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
-	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
-	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
-	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
-	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
-	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
-	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
-	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
-	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
-	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
-	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
-	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
-	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
-	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
-	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
-	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
-	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
-	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
-	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
-	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
-	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
-	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
-	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
-	344,	328,	376,	360,	280,	264,	312,	296,
-	472,	456,	504,	488,	408,	392,	440,	424,
-	88,	72,	120,	104,	24,	8,	56,	40,
-	216,	200,	248,	232,	152,	136,	184,	168,
-	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
-	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
-	688,	656,	752,	720,	560,	528,	624,	592,
-	944,	912,	1008,	976,	816,	784,	880,	848,
-};
-#endif /* HAS_16BIT_TABLES */
-
-
-#ifdef HAS_14BIT_TABLES
-
-    /*
-     *  Unused for now. Where are the MSB parts anyway??
-     */
-
-/* 14 bit mu-law (LSB) */
-
-char dmasound_ulaw2dma14l[] = {
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	33,	33,	33,	33,	33,	33,	33,	33,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	1,	1,	1,	1,	1,	1,	1,	1,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	49,	17,	49,	17,	49,	17,	49,	17,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	41,	57,	9,	25,	41,	57,	9,	25,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	37,	45,	53,	61,	5,	13,	21,	29,
-	35,	39,	43,	47,	51,	55,	59,	63,
-	3,	7,	11,	15,	19,	23,	27,	31,
-	34,	36,	38,	40,	42,	44,	46,	48,
-	50,	52,	54,	56,	58,	60,	62,	0,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	31,	31,	31,	31,	31,	31,	31,	31,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	63,	63,	63,	63,	63,	63,	63,	63,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	15,	47,	15,	47,	15,	47,	15,	47,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	23,	7,	55,	39,	23,	7,	55,	39,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	27,	19,	11,	3,	59,	51,	43,	35,
-	29,	25,	21,	17,	13,	9,	5,	1,
-	61,	57,	53,	49,	45,	41,	37,	33,
-	30,	28,	26,	24,	22,	20,	18,	16,
-	14,	12,	10,	8,	6,	4,	2,	0
-};
-
-/* 14 bit A-law (LSB) */
-
-char dmasound_alaw2dma14l[] = {
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	16,	48,	16,	48,	16,	48,	16,	48,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	42,	46,	34,	38,	58,	62,	50,	54,
-	10,	14,	2,	6,	26,	30,	18,	22,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	40,	56,	8,	24,	40,	56,	8,	24,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	20,	28,	4,	12,	52,	60,	36,	44,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	32,	32,	32,	32,	32,	32,	32,	32,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	48,	16,	48,	16,	48,	16,	48,	16,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	0,	0,	0,	0,	0,	0,	0,	0,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	22,	18,	30,	26,	6,	2,	14,	10,
-	54,	50,	62,	58,	38,	34,	46,	42,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	24,	8,	56,	40,	24,	8,	56,	40,
-	44,	36,	60,	52,	12,	4,	28,	20,
-	44,	36,	60,	52,	12,	4,	28,	20
-};
-#endif /* HAS_14BIT_TABLES */
-
+/* control over who can modify resources shared between play/record */
+static mode_t shared_resource_owner = 0 ;
+static int shared_resources_initialised = 0 ;
 
     /*
      *  Mid level stuff
@@ -393,15 +230,7 @@
 
 static inline void sound_silence(void)
 {
-	/* update hardware settings one more */
-	dmasound.mach.init();
-
-	dmasound.mach.silence();
-}
-
-static inline void sound_init(void)
-{
-	dmasound.mach.init();
+	dmasound.mach.silence(); /* _MUST_ stop DMA */
 }
 
 static inline int sound_set_format(int format)
@@ -414,8 +243,17 @@
 	if (speed < 0)
 		return dmasound.soft.speed;
 
+	/* trap out-of-range speed settings.
+	   at present we allow (arbitrarily) low rates - using soft
+	   up-conversion - but we can't allow > max because there is
+	   no soft down-conversion.
+	*/
+	if (dmasound.mach.max_dsp_speed &&
+	   (speed > dmasound.mach.max_dsp_speed))
+		speed = dmasound.mach.max_dsp_speed ;
+
 	dmasound.soft.speed = speed;
-	dmasound.mach.init();
+
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.speed = dmasound.soft.speed;
 
@@ -432,7 +270,6 @@
 	dmasound.soft.stereo = stereo;
 	if (dmasound.minDev == SND_DEV_DSP)
 		dmasound.dsp.stereo = stereo;
-	dmasound.mach.init();
 
 	return stereo;
 }
@@ -471,10 +308,14 @@
 	    default:
 		return 0;
 	}
-	return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	/* if the user has requested a non-existent translation don't try
+	   to call it but just return 0 bytes moved
+	*/
+	if (ct_func)
+		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);
+	return 0;
 }
 
-
     /*
      *  /dev/mixer abstraction
      */
@@ -559,6 +400,11 @@
 struct sound_queue dmasound_read_sq;
 #endif
 
+static void sq_reset_output(void) ;
+#ifdef HAS_RECORD
+static void sq_reset_input(void) ;
+#endif
+
 static int sq_allocate_buffers(struct sound_queue *sq, int num, int size)
 {
 	int i;
@@ -588,8 +434,6 @@
 	int i;
 
 	if (sq->buffers) {
-		if (sq != &write_sq && dmasound.mach.abort_read)
-			dmasound.mach.abort_read();
 		for (i = 0; i < sq->numBufs; i++)
 			dmasound.mach.dma_free(sq->buffers[i], sq->bufSize);
 		kfree(sq->buffers);
@@ -597,15 +441,84 @@
 	}
 }
 
-static void sq_setup(struct sound_queue *sq, int max_count, int max_active,
-		     int block_size)
+
+static int sq_setup(struct sound_queue *sq)
 {
-	void (*setup_func)(void);
+	int (*setup_func)(void);
+	int hard_frame ;
+
+	if (sq->locked) { /* are we already set? - and not changeable */
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to sq_setup a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+	sq->locked = 1 ; /* don't think we have a race prob. here _check_ */
+
+	/* make sure that the parameters are set up
+	   This should have been done already...
+	*/
 
-	sq->max_count = max_count;
-	sq->max_active = max_active;
-	sq->block_size = block_size;
+	dmasound.mach.init();
+
+	/* OK.  If the user has set fragment parameters explicitly, then we
+	   should leave them alone... as long as they are valid.
+	   Invalid user fragment params can occur if we allow the whole buffer
+	   to be used when the user requests the fragments sizes (with no soft
+	   x-lation) and then the user subsequently sets a soft x-lation that
+	   requires increased internal buffering.
+
+	   Othwerwise (if the user did not set them) OSS says that we should
+	   select frag params on the basis of 0.5 s output & 0.1 s input
+	   latency. (TODO.  For now we will copy in the defaults.)
+	*/
 
+	if (sq->user_frags <= 0) {
+		sq->max_count = sq->numBufs ;
+		sq->max_active = sq->numBufs ;
+		sq->block_size = sq->bufSize;
+		/* set up the user info */
+		sq->user_frags = sq->numBufs ;
+		sq->user_frag_size = sq->bufSize ;
+		sq->user_frag_size *=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		sq->user_frag_size /=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+	} else {
+		/* work out requested block size */
+		sq->block_size = sq->user_frag_size ;
+		sq->block_size *=
+			(dmasound.hard.size * (dmasound.hard.stereo+1) ) ;
+		sq->block_size /=
+			(dmasound.soft.size * (dmasound.soft.stereo+1) ) ;
+		/* the user wants to write frag-size chunks */
+		sq->block_size *= dmasound.hard.speed ;
+		sq->block_size /= dmasound.soft.speed ;
+		/* this only works for size values which are powers of 2 */
+		hard_frame =
+			(dmasound.hard.size * (dmasound.hard.stereo+1))/8 ;
+		sq->block_size +=  (hard_frame - 1) ;
+		sq->block_size &= ~(hard_frame - 1) ; /* make sure we are aligned */
+		/* let's just check for obvious mistakes */
+		if ( sq->block_size <= 0 || sq->block_size > sq->bufSize) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag size (user set %d)\n", sq->user_frag_size) ;
+#endif
+			sq->block_size = sq->bufSize ;
+		}
+		if ( sq->user_frags <= sq->numBufs ) {
+			sq->max_count = sq->user_frags ;
+			/* if user has set max_active - then use it */
+			sq->max_active = (sq->max_active <= sq->max_count) ?
+				sq->max_active : sq->max_count ;
+		} else {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: invalid frag count (user set %d)\n", sq->user_frags) ;
+#endif
+			sq->max_count =
+			sq->max_active = sq->numBufs ;
+		}
+	}
 	sq->front = sq->count = sq->rear_size = 0;
 	sq->syncing = 0;
 	sq->active = 0;
@@ -613,12 +526,16 @@
 	if (sq == &write_sq) {
 	    sq->rear = -1;
 	    setup_func = dmasound.mach.write_sq_setup;
-	} else {
+	}
+#ifdef HAS_RECORD
+	else {
 	    sq->rear = 0;
 	    setup_func = dmasound.mach.read_sq_setup;
 	}
+#endif
 	if (setup_func)
-	    setup_func();
+	    return setup_func();
+	return 0 ;
 }
 
 static inline void sq_play(void)
@@ -632,6 +549,7 @@
 	ssize_t uWritten = 0;
 	u_char *dest;
 	ssize_t uUsed, bUsed, bLeft;
+	unsigned long flags ;
 
 	/* ++TeSche: Is something like this necessary?
 	 * Hey, that's an honest question! Or does any other part of the
@@ -640,11 +558,52 @@
 	if (uLeft == 0)
 		return 0;
 
+	/* implement any changes we have made to the soft/hard params.
+	   this is not satisfactory really, all we have done up to now is to
+	   say what we would like - there hasn't been any real checking of capability
+	*/
+
+	if (shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. This may seem a dumb place
+	   to do it - but it is what OSS requires.  It means that write() can
+	   return memory allocation errors.  To avoid this possibility use the
+	   GETBLKSIZE or GETOSPACE ioctls (after you've fiddled with all the
+	   params you want to change) - these ioctls also force the setup.
+	*/
+
+	if (write_sq.locked == 0) {
+		if ((uWritten = sq_setup(&write_sq)) < 0) return uWritten ;
+		uWritten = 0 ;
+	}
+
+/* FIXME: I think that this may be the wrong behaviour when we get strapped
+	for time and the cpu is close to being (or actually) behind in sending data.
+	- because we've lost the time that the N samples, already in the buffer,
+	would have given us to get here with the next lot from the user.
+*/
 	/* The interrupt doesn't start to play the last, incomplete frame.
 	 * Thus we can append to it without disabling the interrupts! (Note
 	 * also that write_sq.rear isn't affected by the interrupt.)
 	 */
 
+	/* as of 1.6 this behaviour changes if SNDCTL_DSP_POST has been issued:
+	   this will mimic the behaviour of syncing and allow the sq_play() to
+	   queue a partial fragment.  Since sq_play() may/will be called from
+	   the IRQ handler - at least on Pmac we have to deal with it.
+	   The strategy - possibly not optimum - is to kill _POST status if we
+	   get here.  This seems, at least, reasonable - in the sense that POST
+	   is supposed to indicate that we might not write before the queue
+	   is drained - and if we get here in time then it does not apply.
+	*/
+
+	save_flags(flags) ; cli() ;
+	write_sq.syncing &= ~2 ; /* take out POST status */
+	restore_flags(flags) ;
+
 	if (write_sq.count > 0 &&
 	    (bLeft = write_sq.block_size-write_sq.rear_size) > 0) {
 		dest = write_sq.buffers[write_sq.rear];
@@ -655,12 +614,12 @@
 			return uUsed;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		write_sq.rear_size = bUsed;
 	}
 
-	do {
-		while (write_sq.count == write_sq.max_active) {
+	while (uLeft) {
+		while (write_sq.count >= write_sq.max_active) {
 			sq_play();
 			if (write_sq.open_mode & O_NONBLOCK)
 				return uWritten > 0 ? uWritten : -EAGAIN;
@@ -685,19 +644,45 @@
 			break;
 		src += uUsed;
 		uWritten += uUsed;
-		uLeft -= uUsed;
+		uLeft = (uUsed <= uLeft) ? (uLeft - uUsed) : 0 ; /* paranoia */
 		if (bUsed) {
 			write_sq.rear = (write_sq.rear+1) % write_sq.max_count;
 			write_sq.rear_size = bUsed;
 			write_sq.count++;
 		}
-	} while (bUsed);   /* uUsed may have been 0 */
+	} /* uUsed may have been 0 */
 
 	sq_play();
 
 	return uUsed < 0? uUsed: uWritten;
 }
 
+static unsigned int sq_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned int mask = 0;
+	int retVal;
+	
+	if (write_sq.locked == 0) {
+		if ((retVal = sq_setup(&write_sq)) < 0)
+			return retVal;
+		return 0;
+	}
+	if (file->f_mode & FMODE_WRITE )
+		poll_wait(file, &write_sq.action_queue, wait);
+#ifdef HAS_RECORD
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &read_sq.action_queue, wait);
+	if (file->f_mode & FMODE_READ)
+		if (read_sq.block_size - read_sq.rear_size > 0)
+			mask |= POLLIN | POLLRDNORM;
+#endif
+	if (file->f_mode & FMODE_WRITE)
+		if (write_sq.count < write_sq.max_active || write_sq.block_size - write_sq.rear_size > 0)
+			mask |= POLLOUT | POLLWRNORM;
+	return mask;
+
+}
+
 #ifdef HAS_RECORD
     /*
      *  Here is how the values are used for reading.
@@ -707,9 +692,12 @@
      *  The value 'rear' indicates the buffer the DMA is currently filling.
      *  When 'front' == 'rear' the buffer "ring" is empty (we always have an
      *  empty available).  The 'rear_size' is used to track partial offsets
-     *  into the current buffer.  Right now, I just keep the DMA running.  If
-     *  the reader can't keep up, the interrupt tosses the oldest buffer.  We
-     *  could also shut down the DMA in this case.
+     *  into the buffer we are currently returning to the user.
+
+     *  This level (> [1.5]) doesn't care what strategy the LL driver uses with
+     *  DMA on over-run.  It can leave it running (and keep active == 1) or it
+     *  can kill it and set active == 0 in which case this routine will spot
+     *  it and restart the DMA.
      */
 
 static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,
@@ -721,8 +709,25 @@
 	if (uLeft == 0)
 		return 0;
 
-	if (!read_sq.active && dmasound.mach.record)
-		dmasound.mach.record();	/* Kick off the record process. */
+	/* cater for the compatibility mode - record compiled in but no LL */
+	if (dmasound.mach.record == NULL)
+		return -EINVAL ;
+
+	/* see comment in sq_write()
+	*/
+
+	if( shared_resources_initialised == 0) {
+		dmasound.mach.init() ;
+		shared_resources_initialised = 1 ;
+	}
+
+	/* set up the sq if it is not already done. see comments in sq_write().
+	*/
+
+	if (read_sq.locked == 0) {
+		if ((uRead = sq_setup(&read_sq)) < 0)
+			return uRead ;
+	}
 
 	uRead = 0;
 
@@ -730,6 +735,13 @@
 	*/
 	while (uLeft > 0) {
 
+		/* we happened to get behind and the LL driver killed DMA
+		   then we should set it going again.  This also sets it
+		   going the first time through.
+		*/
+		if ( !read_sq.active )
+			dmasound.mach.record();
+
 		/* When front == rear, the DMA is not done yet.
 		*/
 		while (read_sq.front == read_sq.rear) {
@@ -774,14 +786,16 @@
 	sq->busy = 0;
 }
 
+#if 0 /* blocking open() */
 static inline void sq_wake_up(struct sound_queue *sq, struct file *file,
 			      mode_t mode)
 {
 	if (file->f_mode & mode) {
-		sq->busy = 0;
+		sq->busy = 0; /* CHECK: IS THIS OK??? */
 		WAKE_UP(sq->open_queue);
 	}
 }
+#endif
 
 static int sq_open2(struct sound_queue *sq, struct file *file, mode_t mode,
 		    int numbufs, int bufsize)
@@ -790,6 +804,7 @@
 
 	if (file->f_mode & mode) {
 		if (sq->busy) {
+#if 0 /* blocking open() */
 			rc = -EBUSY;
 			if (file->f_flags & O_NONBLOCK)
 				return rc;
@@ -800,84 +815,188 @@
 					return rc;
 			}
 			rc = 0;
+#else
+			/* OSS manual says we will return EBUSY regardless
+			   of O_NOBLOCK.
+			*/
+			return -EBUSY ;
+#endif
 		}
 		sq->busy = 1; /* Let's play spot-the-race-condition */
 
-		if (sq_allocate_buffers(sq, numbufs, bufsize)) {
+		/* allocate the default number & size of buffers.
+		   (i.e. specified in _setup() or as module params)
+		   can't be changed at the moment - but _could_ be perhaps
+		   in the setfragments ioctl.
+		*/
+		if (( rc = sq_allocate_buffers(sq, numbufs, bufsize))) {
+#if 0 /* blocking open() */
 			sq_wake_up(sq, file, mode);
+#else
+			sq->busy = 0 ;
+#endif
 			return rc;
 		}
 
-		sq_setup(sq, numbufs, numbufs, bufsize);
 		sq->open_mode = file->f_mode;
 	}
 	return rc;
 }
 
 #define write_sq_init_waitqueue()	sq_init_waitqueue(&write_sq)
+#if 0 /* blocking open() */
 #define write_sq_wake_up(file)		sq_wake_up(&write_sq, file, FMODE_WRITE)
+#endif
 #define write_sq_release_buffers()	sq_release_buffers(&write_sq)
 #define write_sq_open(file)	\
-	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize << 10)
+	sq_open2(&write_sq, file, FMODE_WRITE, numWriteBufs, writeBufSize )
 
 #ifdef HAS_RECORD
 #define read_sq_init_waitqueue()	sq_init_waitqueue(&read_sq)
+#if 0 /* blocking open() */
 #define read_sq_wake_up(file)		sq_wake_up(&read_sq, file, FMODE_READ)
+#endif
 #define read_sq_release_buffers()	sq_release_buffers(&read_sq)
 #define read_sq_open(file)	\
-	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize << 10)
-#else /* !HAS_RECORD */
-#define read_sq_init_waitqueue()	do {} while (0)
-#define read_sq_wake_up(file)		do {} while (0)
-#define read_sq_release_buffers()	do {} while (0)
-#define read_sq_open(file)		(0)
-#endif /* !HAS_RECORD */
+	sq_open2(&read_sq, file, FMODE_READ, numReadBufs, readBufSize )
+#endif
 
 static int sq_open(struct inode *inode, struct file *file)
 {
 	int rc;
 
 	dmasound.mach.open();
-	if ((rc = write_sq_open(file)) || (rc = read_sq_open(file))) {
+
+	if ((rc = write_sq_open(file))) { /* checks the f_mode */
 		dmasound.mach.release();
 		return rc;
 	}
+#ifdef HAS_RECORD
+	if (dmasound.mach.record) {
+		if ((rc = read_sq_open(file))) { /* checks the f_mode */
+			dmasound.mach.release();
+			return rc;
+		}
+	} else { /* no record function installed; in compat mode */
+		if (file->f_mode & FMODE_READ) {
+			/* TODO: if O_RDWR, release any resources grabbed by write part */
+			dmasound.mach.release() ;
+			/* I think this is what is required by open(2) */
+			return -ENXIO ;
+		}
+	}
+#else /* !HAS_RECORD */
+	if (file->f_mode & FMODE_READ) {
+		/* TODO: if O_RDWR, release any resources grabbed by write part */
+		dmasound.mach.release() ;
+		return -ENXIO ; /* I think this is what is required by open(2) */
+	}
+#endif /* HAS_RECORD */
 
 	if (dmasound.mach.sq_open)
-	    dmasound.mach.sq_open();
+	    dmasound.mach.sq_open(file->f_mode);
+
+	/* CHECK whether this is sensible - in the case that dsp0 could be opened
+	  O_RDONLY and dsp1 could be opened O_WRONLY
+	*/
+
 	dmasound.minDev = MINOR(inode->i_rdev) & 0x0f;
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_init();
-	if ((MINOR(inode->i_rdev) & 0x0f) == SND_DEV_AUDIO) {
+
+	/* OK. - we should make some attempt at consistency. At least the H'ware
+	   options should be set with a valid mode.  We will make it that the LL
+	   driver must supply defaults for hard & soft params.
+	*/
+
+	if (shared_resource_owner == 0) {
+		/* you can make this AFMT_U8/mono/8K if you want to mimic old
+		   OSS behaviour - while we still have soft translations ;-) */
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+#ifndef DMASOUND_STRICT_OSS_COMPLIANCE
+	/* none of the current LL drivers can actually do this "native" at the moment
+	   OSS does not really require us to supply /dev/audio if we can't do it.
+	*/
+	if (dmasound.minDev == SND_DEV_AUDIO) {
 		sound_set_speed(8000);
 		sound_set_stereo(0);
 		sound_set_format(AFMT_MU_LAW);
 	}
-
-#if 0
-	if (file->f_mode == FMODE_READ && dmasound.mach.record) {
-		/* Start dma'ing straight away */
-		dmasound.mach.record();
-	}
 #endif
 
 	return 0;
 }
 
-static void sq_reset(void)
+static void sq_reset_output(void)
 {
-	sound_silence();
+	sound_silence(); /* this _must_ stop DMA, we might be about to lose the buffers */
 	write_sq.active = 0;
 	write_sq.count = 0;
-	write_sq.front = (write_sq.rear+1) % write_sq.max_count;
+	write_sq.rear_size = 0;
+	/* write_sq.front = (write_sq.rear+1) % write_sq.max_count;*/
+	write_sq.front = 0 ;
+	write_sq.rear = -1 ; /* same as for set-up */
+
+	/* OK - we can unlock the parameters and fragment settings */
+	write_sq.locked = 0 ;
+	write_sq.user_frags = 0 ;
+	write_sq.user_frag_size = 0 ;
+}
+
+#ifdef HAS_RECORD
+
+static void sq_reset_input(void)
+{
+	if (dmasound.mach.record && read_sq.active) {
+		if (dmasound.mach.abort_read) { /* this routine must really be present */
+			read_sq.syncing = 1 ;
+			/* this can use the read_sq.sync_queue to sleep if
+			   necessary - it should not return until DMA
+			   is really stopped - because we might deallocate
+			   the buffers as the next action...
+			*/
+			dmasound.mach.abort_read() ;
+		} else {
+			printk(KERN_ERR
+			"dmasound_core: %s has no abort_read()!! all bets are off\n",
+				dmasound.mach.name) ;
+		}
+	}
+	read_sq.syncing =
+	read_sq.active =
+	read_sq.front =
+	read_sq.count =
+	read_sq.rear = 0 ;
+
+	/* OK - we can unlock the parameters and fragment settings */
+	read_sq.locked = 0 ;
+	read_sq.user_frags = 0 ;
+	read_sq.user_frag_size = 0 ;
+}
+
+#endif
+
+static void sq_reset(void)
+{
+	sq_reset_output() ;
+#ifdef HAS_RECORD
+	sq_reset_input() ;
+#endif
+	/* we could consider resetting the shared_resources_owner here... but I
+	   think it is probably still rather non-obvious to application writer
+	*/
+
+	/* we release everything else though */
+	shared_resources_initialised = 0 ;
 }
 
 static int sq_fsync(struct file *filp, struct dentry *dentry)
 {
 	int rc = 0;
 
-	write_sq.syncing = 1;
+	write_sq.syncing |= 1;
 	sq_play();	/* there may be an incomplete frame waiting */
 
 	while (write_sq.active) {
@@ -886,13 +1005,14 @@
 			/* While waiting for audio output to drain, an
 			 * interrupt occurred.  Stop audio output immediately
 			 * and clear the queue. */
-			sq_reset();
+			sq_reset_output();
 			rc = -EINTR;
 			break;
 		}
 	}
 
-	write_sq.syncing = 0;
+	/* flag no sync regardless of whether we had a DSP_POST or not */
+	write_sq.syncing = 0 ;
 	return rc;
 }
 
@@ -901,33 +1021,132 @@
 	int rc = 0;
 
 	lock_kernel();
-	if (write_sq.busy)
-		rc = sq_fsync(file, file->f_dentry);
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
-	sound_silence();
 
-	write_sq_release_buffers();
-	read_sq_release_buffers();
-	dmasound.mach.release();
+#ifdef HAS_RECORD
+	/* probably best to do the read side first - so that time taken to do it
+	   overlaps with playing any remaining output samples.
+	*/
+	if (file->f_mode & FMODE_READ) {
+		sq_reset_input() ; /* make sure dma is stopped and all is quiet */
+		read_sq_release_buffers();
+		read_sq.busy = 0;
+	}
+#endif
 
-	/* There is probably a DOS atack here. They change the mode flag. */
-	/* XXX add check here */
-	read_sq_wake_up(file);
-	write_sq_wake_up(file);
+	if (file->f_mode & FMODE_WRITE) {
+		if (write_sq.busy)
+			rc = sq_fsync(file, file->f_dentry);
+
+		sq_reset_output() ; /* make sure dma is stopped and all is quiet */
+		write_sq_release_buffers();
+		write_sq.busy = 0;
+	}
 
+	if (file->f_mode & shared_resource_owner) { /* it's us that has them */
+		shared_resource_owner = 0 ;
+		shared_resources_initialised = 0 ;
+		dmasound.hard = dmasound.mach.default_hard ;
+	}
+
+	dmasound.mach.release();
+
+#if 0 /* blocking open() */
 	/* Wake up a process waiting for the queue being released.
 	 * Note: There may be several processes waiting for a call
 	 * to open() returning. */
+
+	/* Iain: hmm I don't understand this next comment ... */
+	/* There is probably a DOS atack here. They change the mode flag. */
+	/* XXX add check here,*/
+#ifdef HAS_RECORD
+	read_sq_wake_up(file); /* checks f_mode */
+#endif
+	write_sq_wake_up(file); /* checks f_mode */
+#endif /* blocking open() */
+
 	unlock_kernel();
 
 	return rc;
 }
 
+/* here we see if we have a right to modify format, channels, size and so on
+   if no-one else has claimed it already then we do...
+
+   TODO: We might change this to mask O_RDWR such that only one or the other channel
+   is the owner - if we have problems.
+*/
+
+static int shared_resources_are_mine(mode_t md)
+{
+	if (shared_resource_owner)
+		return (shared_resource_owner & md ) ;
+	else {
+		shared_resource_owner = md ;
+		return 1 ;
+	}
+}
+
+/* if either queue is locked we must deny the right to change shared params
+*/
+
+static int queues_are_quiescent(void)
+{
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		if (read_sq.locked)
+			return 0 ;
+#endif
+	if (write_sq.locked)
+		return 0 ;
+	return 1 ;
+}
+
+/* check and set a queue's fragments per user's wishes...
+   we will check against the pre-defined literals and the actual sizes.
+   This is a bit fraught - because soft translations can mess with our
+   buffer requirements *after* this call - OSS says "call setfrags first"
+*/
+
+/* It is possible to replace all the -EINVAL returns with an override that
+   just puts the allowable value in.  This may be what many OSS apps require
+*/
+
+static int set_queue_frags(struct sound_queue *sq, int bufs, int size)
+{
+	if (sq->locked) {
+#ifdef DEBUG_DMASOUND
+printk("dmasound_core: tried to set_queue_frags on a locked queue\n") ;
+#endif
+		return -EINVAL ;
+	}
+
+	if ((size < MIN_FRAG_SIZE) || (size > MAX_FRAG_SIZE))
+		return -EINVAL ;
+	size = (1<<size) ; /* now in bytes */
+	if (size > sq->bufSize)
+		return -EINVAL ; /* this might still not work */
+
+	if (bufs <= 0)
+		return -EINVAL ;
+	if (bufs > sq->numBufs) /* the user is allowed say "don't care" with 0x7fff */
+		bufs = sq->numBufs ;
+
+	/* there is, currently, no way to specify max_active separately
+	   from max_count.  This could be a LL driver issue - I guess
+	   if there is a requirement for these values to be different then
+	  we will have to pass that info. up to this level.
+	*/
+	sq->user_frags =
+	sq->max_active = bufs ;
+	sq->user_frag_size = size ;
+
+	return 0 ;
+}
+
 static int sq_ioctl(struct inode *inode, struct file *file, u_int cmd,
 		    u_long arg)
 {
-	int val;
+	int val, result;
 	u_long fmt;
 	int data;
 	int size, nbufs;
@@ -937,85 +1156,167 @@
 	case SNDCTL_DSP_RESET:
 		sq_reset();
 		return 0;
+		break ;
+	case SNDCTL_DSP_GETFMTS:
+		fmt = dmasound.mach.hardware_afmts ; /* this is what OSS says.. */
+		return IOCTL_OUT(arg, fmt);
+		break ;
+	case SNDCTL_DSP_GETBLKSIZE:
+		/* this should tell the caller about bytes that the app can
+		   read/write - the app doesn't care about our internal buffers.
+		   We force sq_setup() here as per OSS 1.1 (which should
+		   compute the values necessary).
+		   Since there is no mechanism to specify read/write separately, for
+		   fds opened O_RDWR, the write_sq values will, arbitrarily, overwrite
+		   the read_sq ones.
+		*/
+		size = 0 ;
+#ifdef HAS_RECORD
+		if (dmasound.mach.record && (file->f_mode & FMODE_READ)) {
+			if ( !read_sq.locked )
+				sq_setup(&read_sq) ; /* set params */
+			size = read_sq.user_frag_size ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			size = write_sq.user_frag_size ;
+		}
+		return IOCTL_OUT(arg, size);
+		break ;
 	case SNDCTL_DSP_POST:
+		/* all we are going to do is to tell the LL that any
+		   partial frags can be queued for output.
+		   The LL will have to clear this flag when last output
+		   is queued.
+		*/
+		write_sq.syncing |= 0x2 ;
+		sq_play() ;
+		return 0 ;
 	case SNDCTL_DSP_SYNC:
-		return sq_fsync(file, file->f_dentry);
-
-		/* ++TeSche: before changing any of these it's
-		 * probably wise to wait until sound playing has
-		 * settled down. */
+		/* This call, effectively, has the same behaviour as SNDCTL_DSP_RESET
+		   except that it waits for output to finish before resetting
+		   everything - read, however, is killed imediately.
+		*/
+		result = 0 ;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record)
+			sq_reset_input() ;
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = sq_fsync(file, file->f_dentry);
+			sq_reset_output() ;
+		}
+		/* if we are the shared resource owner then release them */
+		if (file->f_mode & shared_resource_owner)
+			shared_resources_initialised = 0 ;
+		return result ;
+		break ;
 	case SNDCTL_DSP_SPEED:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_speed(data));
+		/* changing this on the fly will have wierd effects on the sound.
+		   Where there are rate conversions implemented in soft form - it
+		   will cause the _ctx_xxx() functions to be substituted.
+		   However, there doesn't appear to be any reason to dis-allow it from
+		   a driver pov.
+		*/
+		if (shared_resources_are_mine(file->f_mode)) {
+			IOCTL_IN(arg, data);
+			data = sound_set_speed(data) ;
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, data);
+		} else
+			return -EINVAL ;
+		break ;
+	/* OSS says these next 4 actions are undefined when the device is
+	   busy/active - we will just return -EINVAL.
+	   To be allowed to change one - (a) you have to own the right
+	    (b) the queue(s) must be quiescent
+	*/
 	case SNDCTL_DSP_STEREO:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data));
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data));
+		} else
+			return -EINVAL ;
+		break ;
 	case SOUND_PCM_WRITE_CHANNELS:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+			IOCTL_IN(arg, data);
+			/* the user might ask for 20 channels, we will return 1 or 2 */
+			shared_resources_initialised = 0 ;
+			return IOCTL_OUT(arg, sound_set_stereo(data-1)+1);
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SETFMT:
-		sq_fsync(file, file->f_dentry);
-		IOCTL_IN(arg, data);
-		return IOCTL_OUT(arg, sound_set_format(data));
-	case SNDCTL_DSP_GETFMTS:
-		fmt = 0;
-		if (dmasound.trans_write) {
-			if (dmasound.trans_write->ct_ulaw)
-				fmt |= AFMT_MU_LAW;
-			if (dmasound.trans_write->ct_alaw)
-				fmt |= AFMT_A_LAW;
-			if (dmasound.trans_write->ct_s8)
-				fmt |= AFMT_S8;
-			if (dmasound.trans_write->ct_u8)
-				fmt |= AFMT_U8;
-			if (dmasound.trans_write->ct_s16be)
-				fmt |= AFMT_S16_BE;
-			if (dmasound.trans_write->ct_u16be)
-				fmt |= AFMT_U16_BE;
-			if (dmasound.trans_write->ct_s16le)
-				fmt |= AFMT_S16_LE;
-			if (dmasound.trans_write->ct_u16le)
-				fmt |= AFMT_U16_LE;
-		}
-		return IOCTL_OUT(arg, fmt);
-	case SNDCTL_DSP_GETBLKSIZE:
-		size = write_sq.block_size
-			* dmasound.soft.size * (dmasound.soft.stereo + 1)
-			/ (dmasound.hard.size * (dmasound.hard.stereo + 1));
-		return IOCTL_OUT(arg, size);
+		if (shared_resources_are_mine(file->f_mode) &&
+		    queues_are_quiescent()) {
+		    	int format;
+			IOCTL_IN(arg, data);
+			shared_resources_initialised = 0 ;
+			format = sound_set_format(data);
+			result = IOCTL_OUT(arg, format);
+			if (result < 0)
+				return result;
+			if (format != data)
+				return -EINVAL;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_SUBDIVIDE:
+		return -EINVAL ;
 		break;
 	case SNDCTL_DSP_SETFRAGMENT:
-		if (write_sq.count || write_sq.active || write_sq.syncing)
-			return -EINVAL;
-		IOCTL_IN(arg, size);
-		nbufs = size >> 16;
-		if (nbufs < 2 || nbufs > write_sq.numBufs)
-			nbufs = write_sq.numBufs;
-		size &= 0xffff;
-		if (size >= 8 && size <= 29) {
-			size = 1 << size;
-			size *= dmasound.hard.size * (dmasound.hard.stereo + 1);
-			size /= dmasound.soft.size * (dmasound.soft.stereo + 1);
-			if (size > write_sq.bufSize)
-				size = write_sq.bufSize;
-		} else
-			size = write_sq.bufSize;
-		sq_setup(&write_sq, write_sq.numBufs, nbufs, size);
-		return IOCTL_OUT(arg,write_sq.bufSize | write_sq.numBufs << 16);
+		/* we can do this independently for the two queues - with the
+		   proviso that for fds opened O_RDWR we cannot separate the
+		   actions and both queues will be set per the last call.
+		   NOTE: this does *NOT* actually set the queue up - merely
+		   registers our intentions.
+		*/
+		IOCTL_IN(arg, data);
+		result = 0 ;
+		nbufs = (data >> 16) & 0x7fff ; /* 0x7fff is 'use maximum' */
+		size = data & 0xffff;
+#ifdef HAS_RECORD
+		if ((file->f_mode & FMODE_READ) && dmasound.mach.record) {
+			result = set_queue_frags(&read_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+#endif
+		if (file->f_mode & FMODE_WRITE) {
+			result = set_queue_frags(&write_sq, nbufs, size) ;
+			if (result)
+				return result ;
+		}
+		/* NOTE: this return value is irrelevant - OSS specifically says that
+		   the value is 'random' and that the user _must_ check the actual
+		   frags values using SNDCTL_DSP_GETBLKSIZE or similar */
+		return IOCTL_OUT(arg, data);
+		break ;
 	case SNDCTL_DSP_GETOSPACE:
-		info.fragments = write_sq.max_active - write_sq.count;
-		info.fragstotal = write_sq.max_active;
-		info.fragsize = write_sq.block_size;
-		info.bytes = info.fragments * info.fragsize;
-		if (copy_to_user((void *)arg, &info, sizeof(info)))
-			return -EFAULT;
-		return 0;
+		/*
+		*/
+		if (file->f_mode & FMODE_WRITE) {
+			if ( !write_sq.locked )
+				sq_setup(&write_sq) ;
+			info.fragments = write_sq.max_active - write_sq.count;
+			info.fragstotal = write_sq.max_active;
+			info.fragsize = write_sq.user_frag_size;
+			info.bytes = info.fragments * info.fragsize;
+			if (copy_to_user((void *)arg, &info, sizeof(info)))
+				return -EFAULT;
+			return 0;
+		} else
+			return -EINVAL ;
+		break ;
 	case SNDCTL_DSP_GETCAPS:
-		val = 1;        /* Revision level of this ioctl() */
+		val = dmasound.mach.capabilities & 0xffffff00;
 		return IOCTL_OUT(arg,val);
 
 	default:
@@ -1029,42 +1330,48 @@
 	owner:		THIS_MODULE,
 	llseek:		no_llseek,
 	write:		sq_write,
+	poll:		sq_poll,
 	ioctl:		sq_ioctl,
 	open:		sq_open,
 	release:	sq_release,
 #ifdef HAS_RECORD
-	read:		sq_read,
+	read:		NULL	/* default to no read for compat mode */
 #endif
 };
 
-static void __init sq_init(void)
+static int __init sq_init(void)
 {
 #ifndef MODULE
 	int sq_unit;
 #endif
+
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		sq_fops.read = sq_read ;
+#endif
 	sq_unit = register_sound_dsp(&sq_fops, -1);
-	if (sq_unit < 0)
-		return;
+	if (sq_unit < 0) {
+		printk(KERN_ERR "dmasound_core: couldn't register fops\n") ;
+		return sq_unit ;
+	}
 
 	write_sq_init_waitqueue();
+#ifdef HAS_RECORD
 	read_sq_init_waitqueue();
+#endif
 
-	/* whatever you like as startup mode for /dev/dsp,
-	 * (/dev/audio hasn't got a startup mode). note that
-	 * once changed a new open() will *not* restore these!
+	/* These parameters will be restored for every clean open()
+	 * in the case of multiple open()s (e.g. dsp0 & dsp1) they
+	 * will be set so long as the shared resources have no owner.
 	 */
-	dmasound.dsp.format = AFMT_U8;
-	dmasound.dsp.stereo = 0;
-	dmasound.dsp.size = 8;
-
-	/* set minimum rate possible without expanding */
-	dmasound.dsp.speed = dmasound.mach.min_dsp_speed;
-
-	/* before the first open to /dev/dsp this wouldn't be set */
-	dmasound.soft = dmasound.dsp;
-	dmasound.hard = dmasound.dsp;
 
-	sound_silence();
+	if (shared_resource_owner == 0) {
+		dmasound.soft = dmasound.mach.default_soft ;
+		dmasound.hard = dmasound.mach.default_hard ;
+		dmasound.dsp = dmasound.mach.default_soft ;
+		shared_resources_initialised = 0 ;
+	}
+	return 0 ;
 }
 
 
@@ -1072,12 +1379,68 @@
      *  /dev/sndstat
      */
 
+/* we allow more space for record-enabled because there are extra output lines.
+   the number here must include the amount we are prepared to give to the low-level
+   driver.
+*/
+
+#ifdef HAS_RECORD
+#define STAT_BUFF_LEN 1024
+#else
+#define STAT_BUFF_LEN 768
+#endif
+
+/* this is how much space we will allow the low-level driver to use
+   in the stat buffer.  Currently, 2 * (80 character line + <NL>).
+   We do not police this (it is up to the ll driver to be honest).
+*/
+
+#define LOW_LEVEL_STAT_ALLOC 162
+
 static struct {
     int busy;
-    char buf[512];	/* state.buf should not overflow! */
+    char buf[STAT_BUFF_LEN];	/* state.buf should not overflow! */
     int len, ptr;
 } state;
 
+/* publish this function for use by low-level code, if required */
+
+char *get_afmt_string(int afmt)
+{
+        switch(afmt) {
+            case AFMT_MU_LAW:
+                return "mu-law";
+                break;
+            case AFMT_A_LAW:
+                return "A-law";
+                break;
+            case AFMT_U8:
+                return "unsigned 8 bit";
+                break;
+            case AFMT_S8:
+                return "signed 8 bit";
+                break;
+            case AFMT_S16_BE:
+                return "signed 16 bit BE";
+                break;
+            case AFMT_U16_BE:
+                return "unsigned 16 bit BE";
+                break;
+            case AFMT_S16_LE:
+                return "signed 16 bit LE";
+                break;
+            case AFMT_U16_LE:
+                return "unsigned 16 bit LE";
+                break;
+	    case 0:
+		return "format not set" ;
+		break ;
+            default:
+                break ;
+        }
+        return "ERROR: Unsupported AFMT_XXXX code" ;
+}
+
 static int state_open(struct inode *inode, struct file *file)
 {
 	char *buffer = state.buf;
@@ -1090,52 +1453,76 @@
 	state.ptr = 0;
 	state.busy = 1;
 
-	len += sprintf(buffer+len, "%sDMA sound driver:\n", dmasound.mach.name);
+	len += sprintf(buffer+len, "%sDMA sound driver rev %03d :\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	len += sprintf(buffer+len,
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+
+	/* call the low-level module to fill in any stat info. that it has
+	   if present.  Maximum buffer usage is specified.
+	*/
 
-	len += sprintf(buffer+len, "\tsound.format = 0x%x",
-		       dmasound.soft.format);
-	switch (dmasound.soft.format) {
-	    case AFMT_MU_LAW:
-		len += sprintf(buffer+len, " (mu-law)");
-		break;
-	    case AFMT_A_LAW:
-		len += sprintf(buffer+len, " (A-law)");
-		break;
-	    case AFMT_U8:
-		len += sprintf(buffer+len, " (unsigned 8 bit)");
-		break;
-	    case AFMT_S8:
-		len += sprintf(buffer+len, " (signed 8 bit)");
-		break;
-	    case AFMT_S16_BE:
-		len += sprintf(buffer+len, " (signed 16 bit big)");
-		break;
-	    case AFMT_U16_BE:
-		len += sprintf(buffer+len, " (unsigned 16 bit big)");
-		break;
-	    case AFMT_S16_LE:
-		len += sprintf(buffer+len, " (signed 16 bit little)");
-		break;
-	    case AFMT_U16_LE:
-		len += sprintf(buffer+len, " (unsigned 16 bit little)");
-		break;
-	}
-	len += sprintf(buffer+len, "\n");
-	len += sprintf(buffer+len, "\tsound.speed = %dHz (phys. %dHz)\n",
-		       dmasound.soft.speed, dmasound.hard.speed);
-	len += sprintf(buffer+len, "\tsound.stereo = 0x%x (%s)\n",
-		       dmasound.soft.stereo,
-		       dmasound.soft.stereo ? "stereo" : "mono");
 	if (dmasound.mach.state_info)
-	    len += dmasound.mach.state_info(buffer);
-	len += sprintf(buffer+len, "\tsq.block_size = %d sq.max_count = %d"
-		       " sq.max_active = %d\n",
-		       write_sq.block_size, write_sq.max_count,
-		       write_sq.max_active);
-	len += sprintf(buffer+len, "\tsq.count = %d sq.rear_size = %d\n",
-		       write_sq.count, write_sq.rear_size);
-	len += sprintf(buffer+len, "\tsq.active = %d sq.syncing = %d\n",
-		       write_sq.active, write_sq.syncing);
+		len += dmasound.mach.state_info(buffer+len,
+			(size_t) LOW_LEVEL_STAT_ALLOC) ;
+
+	/* make usage of the state buffer as deterministic as poss.
+	   exceptional conditions could cause overrun - and this is flagged as
+	   a kernel error.
+	*/
+
+	/* formats and settings */
+
+	len += sprintf(buffer+len,"\t\t === Formats & settings ===\n") ;
+	len += sprintf(buffer+len,"Parameter %20s%20s\n","soft","hard") ;
+	len += sprintf(buffer+len,"Format   :%20s%20s\n",
+		get_afmt_string(dmasound.soft.format),
+		get_afmt_string(dmasound.hard.format));
+
+	len += sprintf(buffer+len,"Samp Rate:%14d s/sec%14d s/sec\n",
+		       dmasound.soft.speed, dmasound.hard.speed);
+
+	len += sprintf(buffer+len,"Channels :%20s%20s\n",
+		       dmasound.soft.stereo ? "stereo" : "mono",
+		       dmasound.hard.stereo ? "stereo" : "mono" );
+
+	/* sound queue status */
+
+	len += sprintf(buffer+len,"\t\t === Sound Queue status ===\n");
+	len += sprintf(buffer+len,"Allocated:%8s%6s\n","Buffers","Size") ;
+	len += sprintf(buffer+len,"%9s:%8d%6d\n",
+		"write", write_sq.numBufs, write_sq.bufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%8d%6d\n",
+			"read", read_sq.numBufs, read_sq.bufSize) ;
+#endif
+	len += sprintf(buffer+len,
+		"Current  : MaxFrg FragSiz MaxAct Frnt Rear "
+		"Cnt RrSize A B S L  xruns\n") ;
+	len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+		"write", write_sq.max_count, write_sq.block_size,
+		write_sq.max_active, write_sq.front, write_sq.rear,
+		write_sq.count, write_sq.rear_size, write_sq.active,
+		write_sq.busy, write_sq.syncing, write_sq.locked, write_sq.xruns) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		len += sprintf(buffer+len,"%9s:%7d%8d%7d%5d%5d%4d%7d%2d%2d%2d%2d%7d\n",
+			"read", read_sq.max_count, read_sq.block_size,
+			read_sq.max_active, read_sq.front, read_sq.rear,
+			read_sq.count, read_sq.rear_size, read_sq.active,
+			read_sq.busy, read_sq.syncing, read_sq.locked, read_sq.xruns) ;
+#endif
+#ifdef DEBUG_DMASOUND
+printk("dmasound: stat buffer used %d bytes\n", len) ;
+#endif
+
+	if (len >= STAT_BUFF_LEN)
+		printk(KERN_ERR "dmasound_core: stat buffer overflowed!\n");
+
 	state.len = len;
 	return 0;
 }
@@ -1171,15 +1558,16 @@
 	release:	state_release,
 };
 
-static void __init state_init(void)
+static int __init state_init(void)
 {
 #ifndef MODULE
 	int state_unit;
 #endif
 	state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
 	if (state_unit < 0)
-		return;
+		return state_unit ;
 	state.busy = 0;
+	return 0 ;
 }
 
 
@@ -1191,6 +1579,7 @@
 
 int __init dmasound_init(void)
 {
+	int res ;
 #ifdef MODULE
 	if (irq_installed)
 		return -EBUSY;
@@ -1199,10 +1588,12 @@
 	/* Set up sound queue, /dev/audio and /dev/dsp. */
 
 	/* Set default settings. */
-	sq_init();
+	if ((res = sq_init()) < 0)
+		return res ;
 
 	/* Set up /dev/sndstat. */
-	state_init();
+	if ((res = state_init()) < 0)
+		return res ;
 
 	/* Set up /dev/mixer. */
 	mixer_init();
@@ -1215,8 +1606,21 @@
 	irq_installed = 1;
 #endif
 
-	printk(KERN_INFO "DMA sound driver installed, using %d buffers of %dk.\n",
-	       numWriteBufs, writeBufSize);
+	printk(KERN_INFO "%s DMA sound driver rev %03d installed\n",
+		dmasound.mach.name, (DMASOUND_CORE_REVISION<<4) +
+		((dmasound.mach.version>>8) & 0x0f));
+	printk(KERN_INFO
+		"Core driver edition %02d.%02d : %s driver edition %02d.%02d\n",
+		DMASOUND_CORE_REVISION, DMASOUND_CORE_EDITION, dmasound.mach.name2,
+		(dmasound.mach.version >> 8), (dmasound.mach.version & 0xff)) ;
+	printk(KERN_INFO "Write will use %4d fragments of %7d bytes as default\n",
+		numWriteBufs, writeBufSize) ;
+#ifdef HAS_RECORD
+	if (dmasound.mach.record)
+		printk(KERN_INFO
+			"Read  will use %4d fragments of %7d bytes as default\n",
+			numReadBufs, readBufSize) ;
+#endif
 
 	return 0;
 }
@@ -1228,6 +1632,7 @@
 	if (irq_installed) {
 		sound_silence();
 		dmasound.mach.irqcleanup();
+		irq_installed = 0;
 	}
 
 	write_sq_release_buffers();
@@ -1245,29 +1650,60 @@
 
 static int __init dmasound_setup(char *str)
 {
-	int ints[6];
+	int ints[6], size;
 
 	str = get_options(str, ARRAY_SIZE(ints), ints);
 
 	/* check the bootstrap parameter for "dmasound=" */
 
+	/* FIXME: other than in the most naive of cases there is no sense in these
+	 *	  buffers being other than powers of two.  This is not checked yet.
+	 */
+
 	switch (ints[0]) {
+#ifdef HAS_RECORD
+        case 5:
+                if ((ints[5] < 0) || (ints[5] > MAX_CATCH_RADIUS))
+                        printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
+                else
+                        catchRadius = ints[5];
+                /* fall through */
+        case 4:
+                if (ints[4] < MIN_BUFFERS)
+                        printk("dmasound_setup: illegal number of read buffers, using default = %d\n",
+                                 numReadBufs);
+                else
+                        numReadBufs = ints[4];
+                /* fall through */
+        case 3:
+		if ((size = ints[3]) < 256)  /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal read buffer size, using default = %d\n", readBufSize);
+                else
+                        readBufSize = size;
+                /* fall through */
+#else
 	case 3:
 		if ((ints[3] < 0) || (ints[3] > MAX_CATCH_RADIUS))
 			printk("dmasound_setup: illegal catch radius, using default = %d\n", catchRadius);
 		else
 			catchRadius = ints[3];
 		/* fall through */
+#endif
 	case 2:
 		if (ints[1] < MIN_BUFFERS)
 			printk("dmasound_setup: illegal number of buffers, using default = %d\n", numWriteBufs);
 		else
 			numWriteBufs = ints[1];
-		if (ints[2] < MIN_BUFSIZE || ints[2] > MAX_BUFSIZE)
-			printk("dmasound_setup: illegal buffer size, using default = %dK\n", writeBufSize);
-		else
-			writeBufSize = ints[2];
-		break;
+		/* fall through */
+	case 1:
+		if ((size = ints[2]) < 256) /* check for small buffer specs */
+			size <<= 10 ;
+                if (size < MIN_BUFSIZE || size > MAX_BUFSIZE)
+                        printk("dmasound_setup: illegal write buffer size, using default = %d\n", writeBufSize);
+                else
+                        writeBufSize = size;
 	case 0:
 		break;
 	default:
@@ -1281,6 +1717,85 @@
 
 #endif /* !MODULE */
 
+    /*
+     *  Conversion tables
+     */
+
+#ifdef HAS_8BIT_TABLES
+/* 8 bit mu-law */
+
+char dmasound_ulaw2dma8[] = {
+	-126,	-122,	-118,	-114,	-110,	-106,	-102,	-98,
+	-94,	-90,	-86,	-82,	-78,	-74,	-70,	-66,
+	-63,	-61,	-59,	-57,	-55,	-53,	-51,	-49,
+	-47,	-45,	-43,	-41,	-39,	-37,	-35,	-33,
+	-31,	-30,	-29,	-28,	-27,	-26,	-25,	-24,
+	-23,	-22,	-21,	-20,	-19,	-18,	-17,	-16,
+	-16,	-15,	-15,	-14,	-14,	-13,	-13,	-12,
+	-12,	-11,	-11,	-10,	-10,	-9,	-9,	-8,
+	-8,	-8,	-7,	-7,	-7,	-7,	-6,	-6,
+	-6,	-6,	-5,	-5,	-5,	-5,	-4,	-4,
+	-4,	-4,	-4,	-4,	-3,	-3,	-3,	-3,
+	-3,	-3,	-3,	-3,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	0,
+	125,	121,	117,	113,	109,	105,	101,	97,
+	93,	89,	85,	81,	77,	73,	69,	65,
+	62,	60,	58,	56,	54,	52,	50,	48,
+	46,	44,	42,	40,	38,	36,	34,	32,
+	30,	29,	28,	27,	26,	25,	24,	23,
+	22,	21,	20,	19,	18,	17,	16,	15,
+	15,	14,	14,	13,	13,	12,	12,	11,
+	11,	10,	10,	9,	9,	8,	8,	7,
+	7,	7,	6,	6,	6,	6,	5,	5,
+	5,	5,	4,	4,	4,	4,	3,	3,
+	3,	3,	3,	3,	2,	2,	2,	2,
+	2,	2,	2,	2,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0
+};
+
+/* 8 bit A-law */
+
+char dmasound_alaw2dma8[] = {
+	-22,	-21,	-24,	-23,	-18,	-17,	-20,	-19,
+	-30,	-29,	-32,	-31,	-26,	-25,	-28,	-27,
+	-11,	-11,	-12,	-12,	-9,	-9,	-10,	-10,
+	-15,	-15,	-16,	-16,	-13,	-13,	-14,	-14,
+	-86,	-82,	-94,	-90,	-70,	-66,	-78,	-74,
+	-118,	-114,	-126,	-122,	-102,	-98,	-110,	-106,
+	-43,	-41,	-47,	-45,	-35,	-33,	-39,	-37,
+	-59,	-57,	-63,	-61,	-51,	-49,	-55,	-53,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-2,	-2,	-2,	-2,	-2,	-2,	-2,	-2,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-1,	-1,	-1,	-1,	-1,	-1,	-1,	-1,
+	-6,	-6,	-6,	-6,	-5,	-5,	-5,	-5,
+	-8,	-8,	-8,	-8,	-7,	-7,	-7,	-7,
+	-3,	-3,	-3,	-3,	-3,	-3,	-3,	-3,
+	-4,	-4,	-4,	-4,	-4,	-4,	-4,	-4,
+	21,	20,	23,	22,	17,	16,	19,	18,
+	29,	28,	31,	30,	25,	24,	27,	26,
+	10,	10,	11,	11,	8,	8,	9,	9,
+	14,	14,	15,	15,	12,	12,	13,	13,
+	86,	82,	94,	90,	70,	66,	78,	74,
+	118,	114,	126,	122,	102,	98,	110,	106,
+	43,	41,	47,	45,	35,	33,	39,	37,
+	59,	57,	63,	61,	51,	49,	55,	53,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	1,	1,	1,	1,	1,	1,	1,	1,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	0,	0,	0,	0,	0,	0,	0,	0,
+	5,	5,	5,	5,	4,	4,	4,	4,
+	7,	7,	7,	7,	6,	6,	6,	6,
+	2,	2,	2,	2,	2,	2,	2,	2,
+	3,	3,	3,	3,	3,	3,	3,	3
+};
+#endif /* HAS_8BIT_TABLES */
 
     /*
      *  Visible symbols for modules
@@ -1300,14 +1815,4 @@
 EXPORT_SYMBOL(dmasound_ulaw2dma8);
 EXPORT_SYMBOL(dmasound_alaw2dma8);
 #endif
-#ifdef HAS_16BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma16);
-EXPORT_SYMBOL(dmasound_alaw2dma16);
-#endif
-#ifdef HAS_14BIT_TABLES
-EXPORT_SYMBOL(dmasound_ulaw2dma14l);
-EXPORT_SYMBOL(dmasound_ulaw2dma14h);
-EXPORT_SYMBOL(dmasound_alaw2dma14l);
-EXPORT_SYMBOL(dmasound_alaw2dma14h);
-#endif
-
+EXPORT_SYMBOL(get_afmt_string) ;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_paula.c linux/drivers/sound/dmasound/dmasound_paula.c
--- linux.orig/drivers/sound/dmasound/dmasound_paula.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound_paula.c	Wed Dec 26 16:47:48 2001
@@ -1,11 +1,18 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_paula.c
  *
  *  Amiga `Paula' DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
- */
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in constraint on state buffer usage.
+ *	       [0.4] - put in default hard/soft settings
+*/
 
 
 #include <linux/module.h>
@@ -23,6 +30,8 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_PAULA_REVISION 0
+#define DMASOUND_PAULA_EDITION 4
 
    /*
     *	The minimum period for audio depends on htotal (for OCS/ECS/AGA)
@@ -114,7 +123,7 @@
 static void AmiMixerInit(void);
 static int AmiMixerIoctl(u_int cmd, u_long arg);
 static void AmiWriteSqSetup(void);
-static int AmiStateInfo(char *buffer);
+static int AmiStateInfo(char *buffer, size_t space);
 
 
 /*** Translations ************************************************************/
@@ -653,19 +662,36 @@
 }
 
 
-static int AmiStateInfo(char *buffer)
+static int AmiStateInfo(char *buffer, size_t space)
 {
 	int len = 0;
 	len += sprintf(buffer+len, "\tsound.volume_left = %d [0...64]\n",
 		       dmasound.volume_left);
 	len += sprintf(buffer+len, "\tsound.volume_right = %d [0...64]\n",
 		       dmasound.volume_right);
+	if (len >= space) {
+		printk(KERN_ERR "dmasound_paula: overlowed state buffer alloc.\n") ;
+		len = space ;
+	}
 	return len;
 }
 
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_S8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machAmiga = {
 	name:		"Amiga",
@@ -688,7 +714,10 @@
 	mixer_ioctl:	AmiMixerIoctl,
 	write_sq_setup:	AmiWriteSqSetup,
 	state_info:	AmiStateInfo,
-	min_dsp_speed:	8000
+	min_dsp_speed:	8000,
+	version:	((DMASOUND_PAULA_REVISION<<8) | DMASOUND_PAULA_EDITION),
+	hardware_afmts:	(AFMT_S8 | AFMT_S16_BE), /* h'ware-supported formats *only* here */
+        capabilities:   DSP_CAP_BATCH          /* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -704,6 +733,8 @@
 				    "dmasound [Paula]"))
 		return -EBUSY;
 	    dmasound.mach = machAmiga;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
 	    err = dmasound_init();
 	    if (err)
 		release_mem_region(CUSTOM_PHYSADDR+0xa0, 0x40);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/dmasound_q40.c linux/drivers/sound/dmasound/dmasound_q40.c
--- linux.orig/drivers/sound/dmasound/dmasound_q40.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/dmasound/dmasound_q40.c	Wed Dec 26 16:47:48 2001
@@ -1,10 +1,16 @@
-
 /*
  *  linux/drivers/sound/dmasound/dmasound_q40.c
  *
  *  Q40 DMA Sound Driver
  *
  *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
+ *  prior to 28/01/2001
+ *
+ *  28/01/2001 [0.1] Iain Sandoe
+ *		     - added versioning
+ *		     - put in and populated the hardware_afmts field.
+ *             [0.2] - put in SNDCTL_DSP_GETCAPS value.
+ *	       [0.3] - put in default hard/soft settings.
  */
 
 
@@ -18,6 +24,8 @@
 
 #include "dmasound.h"
 
+#define DMASOUND_Q40_REVISION 0
+#define DMASOUND_Q40_EDITION 3
 
 static int expand_bal;	/* Balance factor for expanding (not volume!) */
 static int expand_data;	/* Data for expanding */
@@ -216,7 +224,7 @@
 	int bal = expand_bal;
 	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
 	int utotal, ftotal;
- 
+
 	ftotal = frameLeft;
 	utotal = userCount;
 	while (frameLeft) {
@@ -390,7 +398,7 @@
 
 	q40_pp=start;
 	q40_sc=size;
-		
+
 	write_sq.front = (write_sq.front+1) % write_sq.max_count;
 	write_sq.active++;
 
@@ -446,7 +454,7 @@
             *DAC_LEFT=*q40_pp;
 	    *DAC_RIGHT=*q40_pp++;
 	    q40_sc --;
-	    master_outb(1,SAMPLE_CLEAR_REG);	    
+	    master_outb(1,SAMPLE_CLEAR_REG);
 	}else Q40Interrupt();
 }
 static void Q40Interrupt(void)
@@ -546,6 +554,19 @@
 
 /*** Machine definitions *****************************************************/
 
+static SETTINGS def_hard = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 10000
+} ;
+
+static SETTINGS def_soft = {
+	format: AFMT_U8,
+	stereo: 0,
+	size: 8,
+	speed: 8000
+} ;
 
 static MACHINE machQ40 = {
 	name:		"Q40",
@@ -559,10 +580,13 @@
 	irqcleanup:	Q40IrqCleanUp,
 #endif /* MODULE */
 	init:		Q40Init,
-	silence:	Q40Silence, 
-	setFormat:	Q40SetFormat, 
+	silence:	Q40Silence,
+	setFormat:	Q40SetFormat,
 	setVolume:	Q40SetVolume,
-	play:		Q40Play
+	play:		Q40Play,
+	version:	((DMASOUND_Q40_REVISION<<8) | DMASOUND_Q40_EDITION),
+	hardware_afmts:	AFMT_U8, /* h'ware-supported formats *only* here */
+        capabilities:	DSP_CAP_BATCH  /* As per SNDCTL_DSP_GETCAPS */
 };
 
 
@@ -573,6 +597,8 @@
 {
 	if (MACH_IS_Q40) {
 	    dmasound.mach = machQ40;
+	    dmasound.mach.default_hard = def_hard ;
+	    dmasound.mach.default_soft = def_soft ;
 	    return dmasound_init();
 	} else
 	    return -ENODEV;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/tas3001c.c linux/drivers/sound/dmasound/tas3001c.c
--- linux.orig/drivers/sound/dmasound/tas3001c.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/tas3001c.c	Mon Feb  4 18:47:24 2002
@@ -0,0 +1,447 @@
+/*
+ * Driver for the i2c/i2s based TA3001C sound chip used
+ * on some Apple hardware. Also known as "tumbler".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ * Modified by Christopher C. Chimelis <chris@debian.org>:
+ *
+ *   TODO:
+ *   -----
+ *   * Enable DRC since the TiBook speakers are less than good
+ *   * Enable control over input line 2 (is this connected?)
+ *   * Play with the dual six-stage cascading biquad filtering to see how
+ *     we can use it to our advantage (currently not implemented)
+ *   * Reorganise driver a bit to make it cleaner and easier to work with
+ *     (read: use the header file more :-P)
+ *   * Implement sleep support
+ *
+ *   Version 0.4:
+ *   ------------
+ *   * Balance control finally works (can someone document OSS better please?)
+ *   * Moved to a struct for common values referenced in the driver
+ *   * Put stubs in for sleep/wake-up support for now.  This will take some
+ *     experimentation to make sure that the timing is right, since the
+ *     TAS hardware requires specific timing while enabling low-power mode.
+ *     I may cheat for now and just reset the chip on wake-up, but I'd rather
+ *     not if I don't have to.
+ *
+ *   Version 0.3:
+ *   ------------
+ *   * Fixed volume control
+ *   * Added bass and treble control
+ *   * Added PCM line level control (mixer 1 in the TAS manual)
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/ioport.h>
+#include <linux/sysctl.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+
+#include "dmasound.h"
+#include "tas3001c.h"
+
+#define I2C_DRIVERID_TAS (0xFEBA)
+
+#define TAS_VERSION	"0.3"
+#define TAS_DATE	"20011214"
+
+#define TAS_SETTING_MAX	100
+
+#define VOL_DEFAULT	(((((TAS_SETTING_MAX*4)/5)<<0)<<8) | (((TAS_SETTING_MAX*4)/5)<<0))
+#define INPUT_DEFAULT	(((TAS_SETTING_MAX*4)/5)<<0)
+#define BASS_DEFAULT	((TAS_SETTING_MAX/2)<<0)
+#define TREBLE_DEFAULT	((TAS_SETTING_MAX/2)<<0)
+
+static struct i2c_client * tumbler_client = NULL;
+
+static int tumbler_enter_sleep(void);
+static int tumbler_leave_sleep(void);
+
+static int tas_attach_adapter(struct i2c_adapter *adapter);
+static int tas_detect_client(struct i2c_adapter *adapter, int address);
+static int tas_detach_client(struct i2c_client *client);
+
+/* Unique ID allocation */
+static int tas_id;
+static int tas_initialized;
+
+static struct device_node* tas_node;
+static u8 tas_i2c_address = 0x34;
+
+struct tas_data_t {
+	uint left_vol;		/* left volume */
+	uint right_vol;		/* right volume */
+	uint treble;		/* treble */
+	uint bass;		/* bass */
+	uint pcm_level;		/* pcm level */
+};
+
+struct i2c_driver tas_driver = {  
+	name:		"TAS3001C driver  V 0.3",
+	id:		I2C_DRIVERID_TAS,
+	flags:		I2C_DF_NOTIFY,
+	attach_adapter:	&tas_attach_adapter,
+	detach_client:	&tas_detach_client,
+	command:	NULL,
+	inc_use:	NULL, /* &tas_inc_use, */
+	dec_use:	NULL  /* &tas_dev_use  */
+};
+
+int
+tumbler_get_volume(uint * left_vol, uint  *right_vol)
+{
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+	*left_vol = data->left_vol;
+	*right_vol = data->right_vol;
+	
+	return 0;
+}
+
+int
+tumbler_set_register(uint reg, uint size, char *block)
+{
+	if (i2c_smbus_write_block_data(tumbler_client, reg, size, block) < 0) {
+		printk("tas3001c: I2C write failed \n");  
+		return -1; 
+	}
+	return 0;
+}
+
+int
+tumbler_get_pcm_lvl(uint *pcm_lvl)
+{
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+	*pcm_lvl = data->pcm_level;
+
+	return 0;
+}
+
+int
+tumbler_get_treble(uint *treble)
+{
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+	*treble = data->treble;
+	
+	return 0;
+}
+
+int
+tumbler_get_bass(uint *bass)
+{
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+	*bass = data->bass;
+
+	return 0;
+}
+
+int
+tumbler_set_bass(uint bass)
+{
+	uint cur_bass_pers = bass;
+	char block;
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+
+	bass &= 0xff;
+	if (bass > TAS_SETTING_MAX)
+		bass = TAS_SETTING_MAX;
+	bass = ((bass * 72) / TAS_SETTING_MAX) << 0;
+	bass = tas_bass_table[bass];
+	block = (bass >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_BASS, &block) < 0) {
+		printk("tas3001c: failed to set bass \n");  
+		return -1; 
+	}
+	data->bass = cur_bass_pers;
+	return 0;
+}
+
+int
+tumbler_set_treble(uint treble)
+{
+	uint cur_treble_pers = treble;
+	char block;
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+
+	treble &= 0xff;
+	if (treble > TAS_SETTING_MAX)
+		treble = TAS_SETTING_MAX;
+	treble = ((treble * 72) / TAS_SETTING_MAX) << 0;
+	treble = tas_treble_table[treble];
+	block = (treble >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_TREBLE, &block) < 0) {
+		printk("tas3001c: failed to set treble \n");  
+		return -1; 
+	}
+	data->treble = cur_treble_pers;
+	return 0;
+}
+
+int
+tumbler_set_pcm_lvl(uint pcm_lvl)
+{
+	uint pcm_lvl_pers = pcm_lvl;
+	unsigned char block[3];
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+
+	pcm_lvl &= 0xff;
+	if (pcm_lvl > TAS_SETTING_MAX)
+		pcm_lvl = TAS_SETTING_MAX;
+	pcm_lvl = ((pcm_lvl * 176) / TAS_SETTING_MAX) << 0;
+
+	pcm_lvl = tas_input_table[pcm_lvl];
+
+	block[0] = (pcm_lvl >> 16) & 0xff;
+	block[1] = (pcm_lvl >> 8)  & 0xff;
+	block[2] = (pcm_lvl >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_MIXER1, block) < 0) {
+		printk("tas3001c: failed to set input level \n");  
+		return -1; 
+	}
+	data->pcm_level = pcm_lvl_pers;
+
+	return 0;
+}
+
+int
+tumbler_set_volume(uint left_vol, uint right_vol)
+{
+	uint left_vol_pers = left_vol;
+	uint right_vol_pers = right_vol;
+	unsigned char block[6];
+	struct tas_data_t *data;
+
+	if (!tumbler_client)
+		return -1;
+
+	data = (struct tas_data_t *) (tumbler_client->data);
+
+	left_vol &= 0xff;
+	if (left_vol > TAS_SETTING_MAX)
+		left_vol = TAS_SETTING_MAX;
+
+	right_vol = (right_vol >> 8) & 0xff;
+	if (right_vol > TAS_SETTING_MAX)
+		right_vol = TAS_SETTING_MAX;
+
+	left_vol = ((left_vol * 176) / TAS_SETTING_MAX) << 0;
+	right_vol = ((right_vol * 176) / TAS_SETTING_MAX) << 0;
+
+	left_vol = tas_volume_table[left_vol];
+	right_vol = tas_volume_table[right_vol];
+
+	block[0] = (left_vol >> 16) & 0xff;
+	block[1] = (left_vol >> 8)  & 0xff;
+	block[2] = (left_vol >> 0)  & 0xff;
+
+	block[3] = (right_vol >> 16) & 0xff;
+	block[4] = (right_vol >> 8)  & 0xff;
+	block[5] = (right_vol >> 0)  & 0xff;
+
+	if (tumbler_set_register(TAS_SET_VOLUME, block) < 0) {
+		printk("tas3001c: failed to set volume \n");  
+		return -1; 
+	}
+	data->left_vol = left_vol_pers;
+	data->right_vol = right_vol_pers;
+
+	return 0;
+}
+
+static int
+tumbler_leave_sleep(void)
+{
+	/* Stub for now, but I have the details on low-power mode */
+	if (!tumbler_client)
+		return -1;
+
+	return 0;
+}
+
+static int
+tumbler_enter_sleep(void)
+{
+	/* Stub for now, but I have the details on low-power mode */
+	if (!tumbler_client)
+		return -1;
+
+	return 0;
+}
+
+static int
+tas_attach_adapter(struct i2c_adapter *adapter)
+{
+	if (!strncmp(adapter->name, "mac-io", 6))
+		tas_detect_client(adapter, tas_i2c_address);
+
+	return 0;
+}
+
+static int
+tas_init_client(struct i2c_client * new_client)
+{
+	/* Make sure something answers on the i2c bus
+	*/
+
+	if (i2c_smbus_write_byte_data(new_client, 1, (1<<6)+(2<<4)+(2<<2)+0) < 0)
+		return -1;
+
+	tumbler_client = new_client;
+
+	tumbler_set_volume(VOL_DEFAULT, VOL_DEFAULT);
+	tumbler_set_pcm_lvl(INPUT_DEFAULT);
+	tumbler_set_bass(BASS_DEFAULT);
+	tumbler_set_treble(TREBLE_DEFAULT);
+
+	return 0;
+}
+
+static int
+tas_detect_client(struct i2c_adapter *adapter, int address)
+{
+	int rc = 0;
+	struct i2c_client *new_client;
+	struct tas_data_t *data;
+	const char *client_name = "tas 3001c Digital Equalizer";
+
+	new_client = kmalloc(
+			     sizeof(struct i2c_client) + sizeof(struct tas_data_t),
+			     GFP_KERNEL);
+	if (!new_client) {
+		rc = -ENOMEM;
+		goto bail;
+	}
+
+	/* This is tricky, but it will set the data to the right value. */
+	new_client->data = new_client + 1;
+	data = (struct tas_data_t *) (new_client->data);
+
+	new_client->addr = address;
+	new_client->data = data;
+	new_client->adapter = adapter;
+	new_client->driver = &tas_driver;
+	new_client->flags = 0;
+
+	strcpy(new_client->name,client_name);
+
+	new_client->id = tas_id++; /* Automatically unique */
+
+	if (tas_init_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+
+	/* Tell the i2c layer a new client has arrived */
+	if (i2c_attach_client(new_client)) {
+		rc = -ENODEV;
+		goto bail;
+	}
+bail:
+	if (rc && new_client)
+		kfree(new_client);
+	return rc;
+}
+
+static int
+tas_detach_client(struct i2c_client *client)
+{
+	if (client == tumbler_client)
+		tumbler_client = NULL;
+
+	i2c_detach_client(client);
+	kfree(client);
+
+	return 0;
+}
+
+int
+tas_cleanup(void)
+{
+	if (!tas_initialized)
+		return -ENODEV;
+	i2c_del_driver(&tas_driver);
+	tas_initialized = 0;
+
+	return 0;
+}
+
+int
+tas_init(void)
+{
+	int rc;
+	u32* paddr;
+
+	if (tas_initialized)
+		return 0;
+
+	tas_node = find_devices("deq");
+	if (tas_node == NULL)
+		return -ENODEV;
+
+	printk(KERN_INFO "tas3001c driver version %s (%s)\n",TAS_VERSION,TAS_DATE);
+	paddr = (u32 *)get_property(tas_node, "i2c-address", NULL);
+	if (paddr) {
+		tas_i2c_address = (*paddr) >> 1;
+		printk(KERN_INFO "using i2c address: 0x%x from device-tree\n",
+		       tas_i2c_address);
+	} else    
+		printk(KERN_INFO "using i2c address: 0x%x (default)\n", tas_i2c_address);
+
+	if ((rc = i2c_add_driver(&tas_driver))) {
+		printk("tas3001c: Driver registration failed, module not inserted.\n");
+		tas_cleanup();
+		return rc;
+	}
+	tas_initialized = 1;
+	return 0;
+}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/tas3001c.h linux/drivers/sound/dmasound/tas3001c.h
--- linux.orig/drivers/sound/dmasound/tas3001c.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/tas3001c.h	Wed Dec 26 16:47:48 2001
@@ -0,0 +1,248 @@
+/*
+ * Header file for the i2c/i2s based TA3001C sound chip used
+ * on some Apple hardware. Also known as "tumbler".
+ *
+ *  This file is subject to the terms and conditions of the GNU General Public
+ *  License.  See the file COPYING in the main directory of this archive
+ *  for more details.
+ *
+ * Written by Christopher C. Chimelis <chris@debian.org>
+ */
+
+#ifndef _tas3001c_h_
+#define _tas3001c_h_
+
+/*
+ * Macros that correspond to the registers that we write to
+ * when setting the various values.
+ */
+#define TAS_DRC			0x02	/* DRC		*/
+#define TAS_VOLUME		0x04	/* Volume	*/
+#define TAS_TREBLE		0x05	/* Treble	*/
+#define TAS_BASS		0x06	/* Bass		*/
+#define TAS_MIXER1		0x07	/* PCM line	*/
+#define TAS_MIXER2		0x08	/* Input (Unk)	*/
+
+/*
+ * Macros that define various arguments to tas_set_register()
+ */
+#define TAS_SET_DRC		TAS_DRC, 2
+#define TAS_SET_VOLUME		TAS_VOLUME, 6
+#define TAS_SET_TREBLE		TAS_TREBLE, 1
+#define TAS_SET_BASS		TAS_BASS, 1
+#define TAS_SET_MIXER1		TAS_MIXER1, 3
+#define TAS_SET_MIXER2		TAS_MIXER2, 3
+
+
+/*
+ * tas_volume_table contains lookup values for the volume settings
+ * for tumbler.  This is straight from the programming manual
+ * for the chip, however, it's zero-sourced for your shopping pleasure
+ * (meaning, you'll have to compute the difference between the desired
+ * dB and the index value of the proper setting.
+ *
+ * This table should've been replaced by the formula:
+ * 	dB = 20 log(x)
+ * but, since there's no log() or supporting functions like exp(),
+ * my implementation of the above won't work. Yeah, I could do it
+ * the hard way, but this table is just easier :-)
+ *
+ * For reference, -70 dB = tas_volume_table[0]
+ */
+
+static unsigned int tas_volume_table[] = {
+	0x00000015,	0x00000016,	0x00000017,	/* -70.0, -69.5, -69.0 */
+	0x00000019,	0x0000001a,	0x0000001c,	/* -68.5, -68.0, -67.5 */
+	0x0000001d,	0x0000001f,	0x00000021,	/* -67.0, -66.5, -66.0 */
+	0x00000023,	0x00000025,	0x00000027,	/* -65.5, -65.0, -64.5 */
+	0x00000029,	0x0000002c,	0x0000002e,	/* -64.0, -63.5, -63.0 */
+	0x00000031,	0x00000034,	0x00000037,	/* -62.5, -62.0, -61.5 */
+	0x0000003a,	0x0000003e,	0x00000042,	/* -61.0, -60.5, -60.0 */
+	0x00000045,	0x0000004a,	0x0000004e,	/* -59.5, -59.0, -58.5 */
+	0x00000053,	0x00000057,	0x0000005d,	/* -58.0, -57.5, -57.0 */
+	0x00000062,	0x00000068,	0x0000006e,	/* -56.5, -56.0, -55.5 */
+	0x00000075,	0x0000007b,	0x00000083,	/* -55.0, -54.5, -54.0 */
+	0x0000008b,	0x00000093,	0x0000009b,	/* -53.5, -53.0, -52.5 */
+	0x000000a5,	0x000000ae,	0x000000b9,	/* -52.0, -51.5, -51.0 */
+	0x000000c4,	0x000000cf,	0x000000dc,	/* -50.5, -50.0, -49.5 */
+	0x000000e9,	0x000000f6,	0x00000105,	/* -49.0, -48.5, -48.0 */
+	0x00000114,	0x00000125,	0x00000136,	/* -47.5, -47.0, -46.5 */
+	0x00000148,	0x0000015c,	0x00000171,	/* -46.0, -45.5, -45.0 */
+	0x00000186,	0x0000019e,	0x000001b6,	/* -44.5, -44.0, -43.5 */
+	0x000001d0,	0x000001eb,	0x00000209,	/* -43.0, -42.5, -42.0 */
+	0x00000227,	0x00000248,	0x0000026b,	/* -41.5, -41.0, -40.5 */
+	0x0000028f,	0x000002b6,	0x000002df,	/* -40.0, -39.5, -39.0 */
+	0x0000030b,	0x00000339,	0x0000036a,	/* -38.5, -38.0, -37.5 */
+	0x0000039e,	0x000003d5,	0x0000040f,	/* -37.0, -36.5, -36.0 */
+	0x0000044c,	0x0000048d,	0x000004d2,	/* -35.5, -35.0, -34.5 */
+	0x0000051c,	0x00000569,	0x000005bb,	/* -34.0, -33.5, -33.0 */
+	0x00000612,	0x0000066e,	0x000006d0,	/* -32.5, -32.0, -31.5 */
+	0x00000737,	0x000007a5,	0x00000818,	/* -31.0, -30.5, -30.0 */
+	0x00000893,	0x00000915,	0x0000099f,	/* -29.5, -29.0, -28.5 */
+	0x00000a31,	0x00000acc,	0x00000b6f,	/* -28.0, -27.5, -27.0 */
+	0x00000c1d,	0x00000cd5,	0x00000d97,	/* -26.5, -26.0, -25.5 */
+	0x00000e65,	0x00000f40,	0x00001027,	/* -25.0, -24.5, -24.0 */
+	0x0000111c,	0x00001220,	0x00001333,	/* -23.5, -23.0, -22.5 */
+	0x00001456,	0x0000158a,	0x000016d1,	/* -22.0, -21.5, -21.0 */
+	0x0000182b,	0x0000199a,	0x00001b1e,	/* -20.5, -20.0, -19.5 */
+	0x00001cb9,	0x00001e6d,	0x0000203a,	/* -19.0, -18.5, -18.0 */
+	0x00002223,	0x00002429,	0x0000264e,	/* -17.5, -17.0, -16.5 */
+	0x00002893,	0x00002afa,	0x00002d86,	/* -16.0, -15.5, -15.0 */
+	0x00003039,	0x00003314,	0x0000361b,	/* -14.5, -14.0, -13.5 */
+	0x00003950,	0x00003cb5,	0x0000404e,	/* -13.0, -12.5, -12.0 */
+	0x0000441d,	0x00004827,	0x00004c6d,	/* -11.5, -11.0, -10.5 */
+	0x000050f4,	0x000055c0,	0x00005ad5,	/* -10.0, -09.5, -09.0 */
+	0x00006037,	0x000065ea,	0x00006bf4,	/* -08.5, -08.0, -07.5 */
+	0x0000725a,	0x00007920,	0x0000804e,	/* -07.0, -06.5, -06.0 */
+	0x000087e8,	0x00008ff6,	0x0000987d,	/* -05.5, -05.0, -04.5 */
+	0x0000a186,	0x0000ab19,	0x0000b53c,	/* -04.0, -03.5, -03.0 */
+	0x0000bff9,	0x0000cb59,	0x0000d766,	/* -02.5, -02.0, -01.5 */
+	0x0000e429,	0x0000f1ae,	0x00010000,	/* -01.0, -00.5,  00.0 */
+	0x00010f2b,	0x00011f3d,	0x00013042,	/* +00.5, +01.0, +01.5 */
+	0x00014249,	0x00015562,	0x0001699c,	/* +02.0, +02.5, +03.0 */
+	0x00017f09,	0x000195bc,	0x0001adc6,	/* +03.5, +04.0, +04.5 */
+	0x0001c73d,	0x0001e237,	0x0001feca,	/* +05.0, +05.5, +06.0 */
+	0x00021d0e,	0x00023d1d,	0x00025f12,	/* +06.5, +07.0, +07.5 */
+	0x0002830b,	0x0002a925,	0x0002d182,	/* +08.0, +08.5, +09.0 */
+	0x0002fc42,	0x0003298b,	0x00035983,	/* +09.5, +10.0, +10.5 */
+	0x00038c53,	0x0003c225,	0x0003fb28,	/* +11.0, +11.5, +12.0 */
+	0x0004378b,	0x00047783,	0x0004bb44,	/* +12.5, +13.0, +13.5 */
+	0x0005030a,	0x00054f10,	0x00059f98,	/* +14.0, +14.5, +15.0 */
+	0x0005f4e5,	0x00064f40,	0x0006aef6,	/* +15.5, +16.0, +16.5 */
+	0x00071457,	0x00077fbb,	0x0007f17b	/* +17.0, +17.5, +18.0 */
+};
+
+/* tas_treble_table[] is a lookup table that holds the values to drop into
+ * the treble setting register on the TAS.  Again, there is a formula for
+ * this one, but we use this instead due to lack of real math functions
+ * in the kernel.
+ */
+static char tas_treble_table[] = {
+	0x96,		0x95,		0x94,	/* -18.0, -17.5, -17.0 */
+	0x93,		0x92,		0x91,	/* -16.5, -16.0, -15.5 */
+	0x90,		0x8f,		0x8e,	/* -15.0, -14.5, -14.0 */
+	0x8d,		0x8c,		0x8b,	/* -13.5, -13.0, -12.5 */
+	0x8a,		0x89,		0x88,	/* -12.0, -11.5, -11.0 */
+	0x87,		0x86,		0x85,	/* -10.5, -10.0, -09.5 */
+	0x84,		0x83,		0x82,	/* -09.0, -08.5, -08.0 */
+	0x81,		0x80,		0x7f,	/* -07.5, -07.0, -06.5 */
+	0x7e,		0x7d,		0x7c,	/* -06.0, -05.5, -05.0 */
+	0x7b,		0x7a,		0x79,	/* -04.5, -04.0, -03.5 */
+	0x78,		0x77,		0x76,	/* -03.0, -02.5, -02.0 */
+	0x75,		0x74,		0x73,	/* -01.5, -01.0, -00.5 */
+	0x72,		0x71,		0x70,	/*  00.0, +00.5, +01.0 */
+	0x6e,		0x6d,		0x6c,	/* +01.5, +02.0, +02.5 */
+	0x6b,		0x69,		0x68,	/* +03.0, +03.5, +04.0 */
+	0x66,		0x65,		0x63,	/* +04.5, +05.0, +05.5 */
+	0x62,		0x60,		0x5e,	/* +06.0, +06.5, +07.0 */
+	0x5c,		0x5a,		0x57,	/* +07.5, +08.0, +08.5 */
+	0x55,		0x52,		0x4f,	/* +09.0, +09.5, +10.0 */
+	0x4c,		0x49,		0x45,	/* +10.5, +11.0, +11.5 */
+	0x42,		0x3e,		0x3a,	/* +12.0, +12.5, +13.0 */
+	0x36,		0x32,		0x2d,	/* +13.5, +14.0, +14.5 */
+	0x28,		0x22,		0x1c,	/* +15.0, +15.5, +16.0 */
+	0x16,		0x10,		0x09,	/* +16.5, +17.0, +17.5 */
+	0x01					/* +18.0	       */
+};
+
+/* tas_bass_table[] is a lookup table that holds the values to drop into
+ * the bass setting register on the TAS.  Again, there is a formula for
+ * this one, but we use this instead due to lack of real math functions
+ * in the kernel.
+ */
+static char tas_bass_table[] = {
+	0x86,		0x82,		0x7f,	/* -18.0, -17.5, -17.0 */
+	0x7d,		0x7a,		0x78,	/* -16.5, -16.0, -15.5 */
+	0x76,		0x74,		0x72,	/* -15.0, -14.5, -14.0 */
+	0x70,		0x6e,		0x6d,   /* -13.5, -13.0, -12.5 */
+	0x6b,		0x69,		0x66,	/* -12.0, -11.5, -11.0 */
+	0x64,		0x61,		0x5f,	/* -10.5, -10.0, -09.5 */
+	0x5d,		0x5c,		0x5a,	/* -09.0, -08.5, -08.0 */
+	0x59,		0x58,		0x56,	/* -07.5, -07.0, -06.5 */
+	0x55,		0x54,		0x53,	/* -06.0, -05.5, -05.0 */
+	0x51,		0x4f,		0x4d,	/* -04.5, -04.0, -03.5 */
+	0x4b,		0x49,		0x46,	/* -03.0, -02.5, -02.0 */
+	0x44,		0x42,		0x40,	/* -01.5, -01.0, -00.5 */
+	0x3e,		0x3c,		0x3b,	/*  00.0, +00.5, +01.0 */
+	0x39,		0x38,		0x36,	/* +01.5, +02.0, +02.5 */
+	0x35,		0x33,		0x31,	/* +03.0, +03.5, +04.0 */
+	0x30,		0x2e,		0x2c,	/* +04.5, +05.0, +05.5 */
+	0x2b,		0x29,		0x28,	/* +06.0, +06.5, +07.0 */
+	0x26,		0x25,		0x23,	/* +07.5, +08.0, +08.5 */
+	0x21,		0x1f,		0x1c,	/* +09.0, +09.5, +10.0 */
+	0x19,		0x18,		0x17,	/* +10.5, +11.0, +11.5 */
+	0x16,		0x14,		0x13,	/* +12.0, +12.5, +13.0 */
+	0x12,		0x10,		0x0f,	/* +13.5, +14.0, +14.5 */
+	0x0d,		0x0b,		0x0a,	/* +15.0, +15.5, +16.0 */
+	0x08,		0x06,		0x03,	/* +16.5, +17.0, +17.5 */
+	0x01					/* +18.0	       */
+};
+
+/* tas_input_table[] is a lookup table that holds the values to drop into
+ * the setting registers on the TAS for "mixers 1 & 2" (which are the input
+ * lines).  Again, there is a formula for these, but we use this instead
+ * due to lack of real math functions in the kernel.
+ */
+static unsigned int tas_input_table[] = {
+	0x00014b,	0x00015f,	0x000174,	/* -70.0, -69.5, -69.0 */
+	0x00018a,	0x0001a1,	0x0001ba,	/* -68.5, -68.0, -67.5 */
+	0x0001d4,	0x0001f0,	0x00020d,	/* -67.0, -66.5, -66.0 */
+	0x00022c,	0x00024d,	0x000270,	/* -65.5, -65.0, -64.5 */
+	0x000295,	0x0002bc,	0x0002e6,	/* -64.0, -63.5, -63.0 */
+	0x000312,	0x000340,	0x000372,	/* -62.5, -62.0, -61.5 */
+	0x0003a6,	0x0003dd,	0x000418,	/* -61.0, -60.5, -60.0 */
+	0x000456,	0x000498,	0x0004de,	/* -59.5, -59.0, -58.5 */
+	0x000528,	0x000576,	0x0005c9,	/* -58.0, -57.5, -57.0 */
+	0x000620,	0x00067d,	0x0006e0,	/* -56.5, -56.0, -55.5 */
+	0x000748,	0x0007b7,	0x00082c,	/* -55.0, -54.5, -54.0 */
+	0x0008a8,	0x00092b,	0x0009b6,	/* -53.5, -53.0, -52.5 */
+	0x000a49,	0x000ae5,	0x000b8b,	/* -52.0, -51.5, -51.0 */
+	0x000c3a,	0x000cf3,	0x000db8,	/* -50.5, -50.0, -49.5 */
+	0x000e88,	0x000f64,	0x00104e,	/* -49.0, -48.5, -48.0 */
+	0x001145,	0x00124b,	0x001361,	/* -47.5, -47.0, -46.5 */
+	0x001487,	0x0015be,	0x001708,	/* -46.0, -45.5, -45.0 */
+	0x001865,	0x0019d8,	0x001b60,	/* -44.5, -44.0, -43.5 */
+	0x001cff,	0x001eb7,	0x002089,	/* -43.0, -42.5, -42.0 */
+	0x002276,	0x002481,	0x0026ab,	/* -41.5, -41.0, -40.5 */
+	0x0028f5,	0x002b63,	0x002df5,	/* -40.0, -39.5, -39.0 */
+	0x0030ae,	0x003390,	0x00369e,	/* -38.5, -38.0, -37.5 */
+	0x0039db,	0x003d49,	0x0040ea,	/* -37.0, -36.5, -36.0 */
+	0x0044c3,	0x0048d6,	0x004d27,	/* -35.5, -35.0, -34.5 */
+	0x0051b9,	0x005691,	0x005bb2,	/* -34.0, -33.5, -33.0 */
+	0x006121,	0x0066e3,	0x006cfb,	/* -32.5, -32.0, -31.5 */
+	0x007370,	0x007a48,	0x008186,	/* -31.0, -30.5, -30.0 */
+	0x008933,	0x009154,	0x0099f1,	/* -29.5, -29.0, -28.5 */
+	0x00a310,	0x00acba,	0x00b6f6,	/* -28.0, -27.5, -27.0 */
+	0x00c1cd,	0x00cd49,	0x00d973,	/* -26.5, -26.0, -25.5 */
+	0x00e655,	0x00f3fb,	0x010270,	/* -25.0, -24.5, -24.0 */
+	0x0111c0,	0x0121f9,	0x013328,	/* -23.5, -23.0, -22.5 */
+	0x01455b,	0x0158a2,	0x016d0e,	/* -22.0, -21.5, -21.0 */
+	0x0182af,	0x019999,	0x01b1de,	/* -20.5, -20.0, -19.5 */
+	0x01cb94,	0x01e6cf,	0x0203a7,	/* -19.0, -18.5, -18.0 */
+	0x022235,	0x024293,	0x0264db,	/* -17.5, -17.0, -16.5 */
+	0x02892c,	0x02afa3,	0x02d862,	/* -16.0, -15.5, -15.0 */
+	0x03038a,	0x033142,	0x0361af,	/* -14.5, -14.0, -13.5 */
+	0x0394fa,	0x03cb50,	0x0404de,	/* -13.0, -12.5, -12.0 */
+	0x0441d5,	0x048268,	0x04c6d0,	/* -11.5, -11.0, -10.5 */
+	0x050f44,	0x055c04,	0x05ad50,	/* -10.0, -09.5, -09.0 */
+	0x06036e,	0x065ea5,	0x06bf44,	/* -08.5, -08.0, -07.5 */
+	0x07259d,	0x079207,	0x0804dc,	/* -07.0, -06.5, -06.0 */
+	0x087e80,	0x08ff59,	0x0987d5,	/* -05.5, -05.0, -04.5 */
+	0x0a1866,	0x0ab189,	0x0b53be,	/* -04.0, -03.5, -03.0 */
+	0x0bff91,	0x0cb591,	0x0d765a,	/* -02.5, -02.0, -01.5 */
+	0x0e4290,	0x0f1adf,	0x100000,	/* -01.0, -00.5,  00.0 */
+	0x10f2b4,	0x11f3c9,	0x13041a,	/* +00.5, +01.0, +01.5 */
+	0x14248e,	0x15561a,	0x1699c0,	/* +02.0, +02.5, +03.0 */
+	0x17f094,	0x195bb8,	0x1adc61,	/* +03.5, +04.0, +04.5 */
+	0x1c73d5,	0x1e236d,	0x1fec98,	/* +05.0, +05.5, +06.0 */
+	0x21d0d9,	0x23d1cd,	0x25f125,	/* +06.5, +07.0, +07.5 */
+	0x2830af,	0x2a9254,	0x2d1818,	/* +08.0, +08.5, +09.0 */
+	0x2fc420,	0x3298b0,	0x35982f,	/* +09.5, +10.0, +10.5 */
+	0x38c528,	0x3c224c,	0x3fb278,	/* +11.0, +11.5, +12.0 */
+	0x437880,	0x477828,	0x4bb446,	/* +12.5, +13.0, +13.5 */
+	0x5030a1,	0x54f106,	0x59f980,	/* +14.0, +14.5, +15.0 */
+	0x5f4e52,	0x64f403,	0x6aef5d,	/* +15.5, +16.0, +16.5 */
+	0x714575,	0x77fbaa,	0x7f17af	/* +17.0, +17.5, +18.0 */
+};
+
+#endif /* _tas3001c_h_ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/dmasound/trans_16.c linux/drivers/sound/dmasound/trans_16.c
--- linux.orig/drivers/sound/dmasound/trans_16.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/sound/dmasound/trans_16.c	Wed Dec 26 16:47:48 2001
@@ -0,0 +1,679 @@
+/*
+ *  linux/drivers/sound/dmasound/trans_16.c
+ *
+ *  16 bit translation routines.  Only used by Power mac at present.
+ *
+ *  See linux/drivers/sound/dmasound/dmasound_core.c for copyright and
+ *  history prior to 08/02/2001.
+ *
+ *  08/02/2001 Iain Sandoe
+ *		split from dmasound_awacs.c
+ */
+
+#include <linux/soundcard.h>
+#include <asm/uaccess.h>
+#include "dmasound.h"
+
+static short dmasound_alaw2dma16[] ;
+static short dmasound_ulaw2dma16[] ;
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft);
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft);
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft);
+
+/*** Translations ************************************************************/
+
+extern int expand_bal;	/* Balance factor for expanding (not volume!) */
+static int expand_data;	/* Data for expanding */
+
+static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	short *table = dmasound.soft.format == AFMT_MU_LAW
+		? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = table[data];
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = table[data];
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = data << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = data << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+		if (get_user(data, userPtr++))
+			return -EFAULT;
+		val = (data ^ 0x80) << 8;
+		*p++ = val;
+		if (stereo) {
+			if (get_user(data, userPtr++))
+				return -EFAULT;
+			val = (data ^ 0x80) << 8;
+		}
+		*p++ = val;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			if (get_user(data, up++))
+				return -EFAULT;
+			*fp++ = data;
+			*fp++ = data;
+			count--;
+		}
+	} else {
+		if (copy_from_user(fp, userPtr, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		short data;
+		if (get_user(data, up++))
+			return -EFAULT;
+		data ^= mask;
+		*fp++ = data;
+		if (stereo) {
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+		}
+		*fp++ = data;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+
+static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned short *table = (unsigned short *)
+		(dmasound.soft.format == AFMT_MU_LAW
+		 ? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
+	unsigned int data = expand_data;
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int utotal, ftotal;
+	int stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = table[c];
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + table[c];
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = c << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + (c << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		u_char c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(c, userPtr++))
+				return -EFAULT;
+			data = (c ^ 0x80) << 8;
+			if (stereo) {
+				if (get_user(c, userPtr++))
+					return -EFAULT;
+				data = (data << 16) + ((c ^ 0x80) << 8);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 2: utotal;
+}
+
+
+static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + c;
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+
+static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
+			    u_char frame[], ssize_t *frameUsed,
+			    ssize_t frameLeft)
+{
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	unsigned int *p = (unsigned int *) &frame[*frameUsed];
+	unsigned int data = expand_data;
+	unsigned short *up = (unsigned short *) userPtr;
+	int bal = expand_bal;
+	int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
+	int stereo = dmasound.soft.stereo;
+	int utotal, ftotal;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	ftotal = frameLeft;
+	utotal = userCount;
+	while (frameLeft) {
+		unsigned short c;
+		if (bal < 0) {
+			if (userCount == 0)
+				break;
+			if (get_user(data, up++))
+				return -EFAULT;
+			data ^= mask;
+			if (stereo) {
+				if (get_user(c, up++))
+					return -EFAULT;
+				data = (data << 16) + (c ^ mask);
+			} else
+				data = (data << 16) + data;
+			userCount--;
+			bal += hSpeed;
+		}
+		*p++ = data;
+		frameLeft--;
+		bal -= sSpeed;
+	}
+	expand_bal = bal;
+	expand_data = data;
+	*frameUsed += (ftotal - frameLeft) * 4;
+	utotal -= userCount;
+	return stereo? utotal * 4: utotal * 2;
+}
+
+/* data in routines... */
+
+static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = val >> 8;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = val >> 8;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+
+static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
+			  u_char frame[], ssize_t *frameUsed,
+			  ssize_t frameLeft)
+{
+	ssize_t count, used;
+	short *p = (short *) &frame[*frameUsed];
+	int val, stereo = dmasound.soft.stereo;
+
+	frameLeft >>= 2;
+	if (stereo)
+		userCount >>= 1;
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		u_char data;
+
+		val = *p++;
+		data = (val >> 8) ^ 0x80;
+		if (put_user(data, (u_char *)userPtr++))
+			return -EFAULT;
+		if (stereo) {
+			val = *p;
+			data = (val >> 8) ^ 0x80;
+			if (put_user(data, (u_char *)userPtr++))
+				return -EFAULT;
+		}
+		p++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 2: used;
+}
+
+static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	if (!stereo) {
+		short *up = (short *) userPtr;
+		while (count > 0) {
+			short data;
+			data = *fp;
+			if (put_user(data, up++))
+				return -EFAULT;
+			fp+=2;
+			count--;
+		}
+	} else {
+		if (copy_to_user((u_char *)userPtr, fp, count * 4))
+			return -EFAULT;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
+			   u_char frame[], ssize_t *frameUsed,
+			   ssize_t frameLeft)
+{
+	ssize_t count, used;
+	int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
+	int stereo = dmasound.soft.stereo;
+	short *fp = (short *) &frame[*frameUsed];
+	short *up = (short *) userPtr;
+
+	frameLeft >>= 2;
+	userCount >>= (stereo? 2: 1);
+	used = count = min_t(unsigned long, userCount, frameLeft);
+	while (count > 0) {
+		int data;
+
+		data = *fp++;
+		data ^= mask;
+		if (put_user(data, up++))
+			return -EFAULT;
+		if (stereo) {
+			data = *fp;
+			data ^= mask;
+			if (put_user(data, up++))
+				return -EFAULT;
+		}
+		fp++;
+		count--;
+	}
+	*frameUsed += used * 4;
+	return stereo? used * 4: used * 2;
+}
+
+TRANS transAwacsNormal = {
+	ct_ulaw:	pmac_ct_law,
+	ct_alaw:	pmac_ct_law,
+	ct_s8:		pmac_ct_s8,
+	ct_u8:		pmac_ct_u8,
+	ct_s16be:	pmac_ct_s16,
+	ct_u16be:	pmac_ct_u16,
+	ct_s16le:	pmac_ct_s16,
+	ct_u16le:	pmac_ct_u16,
+};
+
+TRANS transAwacsExpand = {
+	ct_ulaw:	pmac_ctx_law,
+	ct_alaw:	pmac_ctx_law,
+	ct_s8:		pmac_ctx_s8,
+	ct_u8:		pmac_ctx_u8,
+	ct_s16be:	pmac_ctx_s16,
+	ct_u16be:	pmac_ctx_u16,
+	ct_s16le:	pmac_ctx_s16,
+	ct_u16le:	pmac_ctx_u16,
+};
+
+TRANS transAwacsNormalRead = {
+	ct_s8:		pmac_ct_s8_read,
+	ct_u8:		pmac_ct_u8_read,
+	ct_s16be:	pmac_ct_s16_read,
+	ct_u16be:	pmac_ct_u16_read,
+	ct_s16le:	pmac_ct_s16_read,
+	ct_u16le:	pmac_ct_u16_read,
+};
+
+/* translation tables */
+/* 16 bit mu-law */
+
+static short dmasound_ulaw2dma16[] = {
+	-32124,	-31100,	-30076,	-29052,	-28028,	-27004,	-25980,	-24956,
+	-23932,	-22908,	-21884,	-20860,	-19836,	-18812,	-17788,	-16764,
+	-15996,	-15484,	-14972,	-14460,	-13948,	-13436,	-12924,	-12412,
+	-11900,	-11388,	-10876,	-10364,	-9852,	-9340,	-8828,	-8316,
+	-7932,	-7676,	-7420,	-7164,	-6908,	-6652,	-6396,	-6140,
+	-5884,	-5628,	-5372,	-5116,	-4860,	-4604,	-4348,	-4092,
+	-3900,	-3772,	-3644,	-3516,	-3388,	-3260,	-3132,	-3004,
+	-2876,	-2748,	-2620,	-2492,	-2364,	-2236,	-2108,	-1980,
+	-1884,	-1820,	-1756,	-1692,	-1628,	-1564,	-1500,	-1436,
+	-1372,	-1308,	-1244,	-1180,	-1116,	-1052,	-988,	-924,
+	-876,	-844,	-812,	-780,	-748,	-716,	-684,	-652,
+	-620,	-588,	-556,	-524,	-492,	-460,	-428,	-396,
+	-372,	-356,	-340,	-324,	-308,	-292,	-276,	-260,
+	-244,	-228,	-212,	-196,	-180,	-164,	-148,	-132,
+	-120,	-112,	-104,	-96,	-88,	-80,	-72,	-64,
+	-56,	-48,	-40,	-32,	-24,	-16,	-8,	0,
+	32124,	31100,	30076,	29052,	28028,	27004,	25980,	24956,
+	23932,	22908,	21884,	20860,	19836,	18812,	17788,	16764,
+	15996,	15484,	14972,	14460,	13948,	13436,	12924,	12412,
+	11900,	11388,	10876,	10364,	9852,	9340,	8828,	8316,
+	7932,	7676,	7420,	7164,	6908,	6652,	6396,	6140,
+	5884,	5628,	5372,	5116,	4860,	4604,	4348,	4092,
+	3900,	3772,	3644,	3516,	3388,	3260,	3132,	3004,
+	2876,	2748,	2620,	2492,	2364,	2236,	2108,	1980,
+	1884,	1820,	1756,	1692,	1628,	1564,	1500,	1436,
+	1372,	1308,	1244,	1180,	1116,	1052,	988,	924,
+	876,	844,	812,	780,	748,	716,	684,	652,
+	620,	588,	556,	524,	492,	460,	428,	396,
+	372,	356,	340,	324,	308,	292,	276,	260,
+	244,	228,	212,	196,	180,	164,	148,	132,
+	120,	112,	104,	96,	88,	80,	72,	64,
+	56,	48,	40,	32,	24,	16,	8,	0,
+};
+
+/* 16 bit A-law */
+
+static short dmasound_alaw2dma16[] = {
+	-5504,	-5248,	-6016,	-5760,	-4480,	-4224,	-4992,	-4736,
+	-7552,	-7296,	-8064,	-7808,	-6528,	-6272,	-7040,	-6784,
+	-2752,	-2624,	-3008,	-2880,	-2240,	-2112,	-2496,	-2368,
+	-3776,	-3648,	-4032,	-3904,	-3264,	-3136,	-3520,	-3392,
+	-22016,	-20992,	-24064,	-23040,	-17920,	-16896,	-19968,	-18944,
+	-30208,	-29184,	-32256,	-31232,	-26112,	-25088,	-28160,	-27136,
+	-11008,	-10496,	-12032,	-11520,	-8960,	-8448,	-9984,	-9472,
+	-15104,	-14592,	-16128,	-15616,	-13056,	-12544,	-14080,	-13568,
+	-344,	-328,	-376,	-360,	-280,	-264,	-312,	-296,
+	-472,	-456,	-504,	-488,	-408,	-392,	-440,	-424,
+	-88,	-72,	-120,	-104,	-24,	-8,	-56,	-40,
+	-216,	-200,	-248,	-232,	-152,	-136,	-184,	-168,
+	-1376,	-1312,	-1504,	-1440,	-1120,	-1056,	-1248,	-1184,
+	-1888,	-1824,	-2016,	-1952,	-1632,	-1568,	-1760,	-1696,
+	-688,	-656,	-752,	-720,	-560,	-528,	-624,	-592,
+	-944,	-912,	-1008,	-976,	-816,	-784,	-880,	-848,
+	5504,	5248,	6016,	5760,	4480,	4224,	4992,	4736,
+	7552,	7296,	8064,	7808,	6528,	6272,	7040,	6784,
+	2752,	2624,	3008,	2880,	2240,	2112,	2496,	2368,
+	3776,	3648,	4032,	3904,	3264,	3136,	3520,	3392,
+	22016,	20992,	24064,	23040,	17920,	16896,	19968,	18944,
+	30208,	29184,	32256,	31232,	26112,	25088,	28160,	27136,
+	11008,	10496,	12032,	11520,	8960,	8448,	9984,	9472,
+	15104,	14592,	16128,	15616,	13056,	12544,	14080,	13568,
+	344,	328,	376,	360,	280,	264,	312,	296,
+	472,	456,	504,	488,	408,	392,	440,	424,
+	88,	72,	120,	104,	24,	8,	56,	40,
+	216,	200,	248,	232,	152,	136,	184,	168,
+	1376,	1312,	1504,	1440,	1120,	1056,	1248,	1184,
+	1888,	1824,	2016,	1952,	1632,	1568,	1760,	1696,
+	688,	656,	752,	720,	560,	528,	624,	592,
+	944,	912,	1008,	976,	816,	784,	880,	848,
+};
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/audio.c linux/drivers/sound/emu10k1/audio.c
--- linux.orig/drivers/sound/emu10k1/audio.c	Tue Oct  9 17:53:17 2001
+++ linux/drivers/sound/emu10k1/audio.c	Mon Jan 21 18:48:29 2002
@@ -983,11 +983,11 @@
 	unsigned long pgoff;
 	int rd, wr;
 
-	DPF(4, "emu10k1_mm_nopage()\n");
-	DPD(4, "addr: %#lx\n", address);
+	DPF(3, "emu10k1_mm_nopage()\n");
+	DPD(3, "addr: %#lx\n", address);
 
 	if (address > vma->vm_end) {
-		DPF(2, "EXIT, returning NOPAGE_SIGBUS\n");
+		DPF(1, "EXIT, returning NOPAGE_SIGBUS\n");
 		return NOPAGE_SIGBUS; /* Disallow mremap */
 	}
 
@@ -1009,14 +1009,14 @@
 			pgoff -= woinst->buffer.pages;
 			dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
 		} else
-			dmapage = virt_to_page (woinst->buffer.mem[0].addr[pgoff]);
+			dmapage = virt_to_page (woinst->voice[0].mem.addr[pgoff]);
 	} else {
 		dmapage = virt_to_page ((u8 *) wiinst->buffer.addr + pgoff * PAGE_SIZE);
 	}
 
 	get_page (dmapage);
 
-	DPD(4, "page: %#lx\n", dmapage);
+	DPD(3, "page: %#lx\n", (unsigned long) dmapage);
 	return dmapage;
 }
 
@@ -1083,8 +1083,8 @@
 	n_pages = ((vma->vm_end - vma->vm_start) + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	pgoffset = vma->vm_pgoff;
 
-	DPD(3, "vma_start: %#lx, vma_end: %#lx, vma_offset: %d\n", vma->vm_start, vma->vm_end, pgoffset);
-	DPD(3, "n_pages: %d, max_pages: %d\n", n_pages, max_pages);
+	DPD(2, "vma_start: %#lx, vma_end: %#lx, vma_offset: %ld\n", vma->vm_start, vma->vm_end, pgoffset);
+	DPD(2, "n_pages: %ld, max_pages: %ld\n", n_pages, max_pages);
 
 	if (pgoffset + n_pages > max_pages)
 		return -EINVAL;
@@ -1092,7 +1092,6 @@
 	vma->vm_flags |= VM_RESERVED;
 	vma->vm_ops = &emu10k1_mm_ops;
 	vma->vm_private_data = wave_dev;
-
 	return 0;
 }
 
@@ -1211,7 +1210,7 @@
 		woinst->num_voices = 1;
 		for (i = 0; i < WAVEOUT_MAXVOICES; i++) {
 			woinst->voice[i].usage = VOICE_USAGE_FREE;
-			woinst->buffer.mem[i].emupageindex = -1;
+			woinst->voice[i].mem.emupageindex = -1;
 		}
 
 		init_waitqueue_head(&woinst->wait_queue);
@@ -1330,23 +1329,13 @@
 	if (file->f_mode & FMODE_READ) {
 		spin_lock_irqsave(&wiinst->lock, flags);
 
-		if (wiinst->state == WAVE_STATE_CLOSED) {
-			calculate_ifrag(wiinst);
-			if (emu10k1_wavein_open(wave_dev) < 0) {
-				spin_unlock_irqrestore(&wiinst->lock, flags);
-				return (mask |= POLLERR);
-			}
-		}
+		if (wiinst->state & WAVE_STATE_OPEN) {
+			emu10k1_wavein_update(wave_dev->card, wiinst);
+			emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
 
-		if (!(wiinst->state & WAVE_STATE_STARTED)) {
-			wave_dev->enablebits |= PCM_ENABLE_INPUT;
-			emu10k1_wavein_start(wave_dev);
+			if (bytestocopy >= wiinst->buffer.fragment_size)
+				mask |= POLLIN | POLLRDNORM;
 		}
-		emu10k1_wavein_update(wave_dev->card, wiinst);
-		emu10k1_wavein_getxfersize(wiinst, &bytestocopy);
-
-		if (bytestocopy >= wiinst->buffer.fragment_size)
-			mask |= POLLIN | POLLRDNORM;
 
 		spin_unlock_irqrestore(&wiinst->lock, flags);
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/audio.h linux/drivers/sound/emu10k1/audio.h
--- linux.orig/drivers/sound/emu10k1/audio.h	Mon Aug 14 15:32:48 2000
+++ linux/drivers/sound/emu10k1/audio.h	Mon Jan 21 18:48:29 2002
@@ -33,7 +33,7 @@
 #ifndef _AUDIO_H
 #define _AUDIO_H
 
-#define MINFRAGS	2	/* _don't_ got bellow 2 */
+#define MINFRAGS	3	/* _don't_ go bellow 3 */
 
 struct emu10k1_wavedevice
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/cardwi.c linux/drivers/sound/emu10k1/cardwi.c
--- linux.orig/drivers/sound/emu10k1/cardwi.c	Mon Sep 10 16:06:10 2001
+++ linux/drivers/sound/emu10k1/cardwi.c	Mon Jan 21 18:48:29 2002
@@ -96,6 +96,7 @@
 	wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
 	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
 	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
+	wave_fmt->bytespervoicesample = wave_fmt->bytespersample;
 }
 
 static int alloc_buffer(struct emu10k1_card *card, struct wavein_buffer *buffer)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/cardwo.c linux/drivers/sound/emu10k1/cardwo.c
--- linux.orig/drivers/sound/emu10k1/cardwo.c	Mon Sep 10 16:06:10 2001
+++ linux/drivers/sound/emu10k1/cardwo.c	Mon Jan 21 18:48:29 2002
@@ -108,95 +108,20 @@
 	}
 
 	wave_fmt->bytesperchannel = wave_fmt->bitsperchannel >> 3;
+	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
+	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
 
 	if (wave_fmt->channels == 2)
 		wave_fmt->bytespervoicesample = wave_fmt->channels * wave_fmt->bytesperchannel;
 	else
 		wave_fmt->bytespervoicesample = wave_fmt->bytesperchannel;
-
-	wave_fmt->bytespersample = wave_fmt->channels * wave_fmt->bytesperchannel;
-	wave_fmt->bytespersec = wave_fmt->bytespersample * wave_fmt->samplingrate;
-}
-
-/**
- * alloc_buffer -
- *
- * allocates the memory buffer for a voice. Two page tables are kept for each buffer.
- * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable)
- * is passed to the device so that it can do DMA to host memory. 
- *
- */
-static int alloc_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum)
-{
-	u32 pageindex, pagecount;
-	unsigned long busaddx;
-	int i;
-
-	DPD(2, "requested pages is: %d\n", buffer->pages);
-
-	if ((buffer->mem[voicenum].emupageindex =
-	 emu10k1_addxmgr_alloc(buffer->pages * PAGE_SIZE, card)) < 0)
-		return -1;
-
-	/* Fill in virtual memory table */
-	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
-		if ((buffer->mem[voicenum].addr[pagecount] =
-		 pci_alloc_consistent(card->pci_dev, PAGE_SIZE,
-		 &buffer->mem[voicenum].dma_handle[pagecount])) == NULL) {
-			buffer->pages = pagecount;
-			return -1;
-		}
-
-		DPD(2, "Virtual Addx: %p\n", buffer->mem[voicenum].addr[pagecount]);
-
-		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
-			busaddx = buffer->mem[voicenum].dma_handle[pagecount] + i * EMUPAGESIZE;
-
-			DPD(3, "Bus Addx: %#lx\n", busaddx);
-
-			pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
-
-			((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex);
-		}
-	}
-
-	return 0;
-}
-
-/**
- * free_buffer -
- *
- * frees the memory buffer for a voice.
- */
-static void free_buffer(struct emu10k1_card *card, struct waveout_buffer *buffer, unsigned int voicenum)
-{
-	u32 pagecount, pageindex;
-	int i;
-
-	if (buffer->mem[voicenum].emupageindex < 0)
-		return;
-
-	for (pagecount = 0; pagecount < buffer->pages; pagecount++) {
-		pci_free_consistent(card->pci_dev, PAGE_SIZE,
-		 buffer->mem[voicenum].addr[pagecount],
-		 buffer->mem[voicenum].dma_handle[pagecount]);
-
-		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
-			pageindex = buffer->mem[voicenum].emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
-			((u32 *) card->virtualpagetable.addr)[pageindex] =
-			 cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex);
-		}
-	}
-
-	emu10k1_addxmgr_free(card, buffer->mem[voicenum].emupageindex);
-	buffer->mem[voicenum].emupageindex = -1;
 }
 
 static int get_voice(struct emu10k1_card *card, struct woinst *woinst, unsigned int voicenum)
 {
 	struct emu_voice *voice = &woinst->voice[voicenum];
-	/* Allocate voices here, if no voices available, return error.
-	 * Init voice_allocdesc first.*/
+
+	/* Allocate voices here, if no voices available, return error. */
 
 	voice->usage = VOICE_USAGE_PLAYBACK;
 
@@ -219,7 +144,7 @@
 
 	DPD(2, "Initial pitch --> %#x\n", voice->initial_pitch);
 
-	voice->startloop = (woinst->buffer.mem[voicenum].emupageindex << 12) /
+	voice->startloop = (voice->mem.emupageindex << 12) /
 	 woinst->format.bytespervoicesample;
 	voice->endloop = voice->startloop + woinst->buffer.size / woinst->format.bytespervoicesample;
 	voice->start = voice->startloop;
@@ -302,7 +227,7 @@
 	DPF(2, "emu10k1_waveout_open()\n");
 
 	for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
-		if (alloc_buffer(card, buffer, voicenum) < 0) {
+		if (emu10k1_voice_alloc_buffer(card, &woinst->voice[voicenum].mem, woinst->buffer.pages) < 0) {
 			ERROR();
 			emu10k1_waveout_close(wave_dev);
 			return -1;
@@ -324,7 +249,7 @@
 	delay = (48000 * woinst->buffer.fragment_size) /
 		 (woinst->format.samplingrate * woinst->format.bytespervoicesample);
 
-	emu10k1_timer_install(card, &woinst->timer, delay / 2);
+	emu10k1_timer_install(card, &woinst->timer, delay);
 
 	woinst->state = WAVE_STATE_OPEN;
 
@@ -345,7 +270,7 @@
 
 	for (voicenum = 0; voicenum < woinst->num_voices; voicenum++) {
 		emu10k1_voice_free(&woinst->voice[voicenum]);
-		free_buffer(card, &woinst->buffer, voicenum);
+		emu10k1_voice_free_buffer(card, &woinst->voice[voicenum].mem);
 	}
 
 	woinst->state = WAVE_STATE_CLOSED;
@@ -404,7 +329,7 @@
 		delay = (48000 * woinst->buffer.fragment_size) /
 			 (woinst->format.samplingrate * woinst->format.bytespervoicesample);
 
-		emu10k1_timer_install(card, &woinst->timer, delay / 2);
+		emu10k1_timer_install(card, &woinst->timer, delay);
 	}
 
 	return 0;
@@ -449,7 +374,7 @@
 
 	pending_bytes = buffer->size - buffer->free_bytes;
 
-	buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size) ? 1 : 0;
+	buffer->fill_silence = (pending_bytes < (signed) buffer->fragment_size * 2) ? 1 : 0;
 
 	if (pending_bytes > (signed) buffer->silence_bytes) {
 		*total_free_bytes = (buffer->free_bytes + buffer->silence_bytes);
@@ -508,14 +433,14 @@
         unsigned int pg;
 	unsigned int pgoff;
 	unsigned int voice_num;
-	struct waveout_mem *mem = woinst->buffer.mem;
+	struct emu_voice *voice = woinst->voice;
 
 	pg = str / PAGE_SIZE;
 	pgoff = str % PAGE_SIZE;
 
 	while (len) { 
 		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++) {
-			__copy_from_user((u8 *)(mem[voice_num].addr[pg]) + pgoff, src, woinst->format.bytespervoicesample);
+			__copy_from_user((u8 *)(voice[voice_num].mem.addr[pg]) + pgoff, src, woinst->format.bytespervoicesample);
 			src += woinst->format.bytespervoicesample;
 		}
 
@@ -540,7 +465,7 @@
 	unsigned int pg;
 	unsigned int pgoff;
 	unsigned int voice_num;
-        struct waveout_mem *mem = woinst->buffer.mem;
+        struct emu_voice *voice = woinst->voice;
 	unsigned int  k;
 
 	pg = str / PAGE_SIZE;
@@ -549,22 +474,22 @@
 	if (len > PAGE_SIZE - pgoff) {
 		k = PAGE_SIZE - pgoff;
 		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
-			memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, k);
+			memset((u8 *)voice[voice_num].mem.addr[pg] + pgoff, data, k);
 		len -= k;
 		while (len > PAGE_SIZE) {
 			pg++;
 			for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
-				memset(mem[voice_num].addr[pg], data, PAGE_SIZE);
+				memset(voice[voice_num].mem.addr[pg], data, PAGE_SIZE);
 
 			len -= PAGE_SIZE;
 		}
 		pg++;
 		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
-			memset(mem[voice_num].addr[pg], data, len);
+			memset(voice[voice_num].mem.addr[pg], data, len);
 
 	} else {
 		for (voice_num = 0; voice_num < woinst->num_voices; voice_num++)
-			memset((u8 *)mem[voice_num].addr[pg] + pgoff, data, len);
+			memset((u8 *)voice[voice_num].mem.addr[pg] + pgoff, data, len);
 	}
 }
 
@@ -578,6 +503,7 @@
 void emu10k1_waveout_xferdata(struct woinst *woinst, u8 *data, u32 *size)
 {
 	struct waveout_buffer *buffer = &woinst->buffer;
+	struct voice_mem *mem = &woinst->voice[0].mem;
 	u32 sizetocopy, sizetocopy_now, start;
 	unsigned long flags;
 
@@ -606,14 +532,14 @@
 			copy_ilv_block(woinst, start, data, sizetocopy_now);
 			copy_ilv_block(woinst, 0, data + sizetocopy_now * woinst->num_voices, sizetocopy);
 		} else {
-			copy_block(buffer->mem[0].addr, start, data, sizetocopy_now);
-			copy_block(buffer->mem[0].addr, 0, data + sizetocopy_now, sizetocopy);
+			copy_block(mem->addr, start, data, sizetocopy_now);
+			copy_block(mem->addr, 0, data + sizetocopy_now, sizetocopy);
 		}
 	} else {
 		if (woinst->num_voices > 1)
 			copy_ilv_block(woinst, start, data, sizetocopy);
 		else
-			copy_block(buffer->mem[0].addr, start, data, sizetocopy);
+			copy_block(mem->addr, start, data, sizetocopy);
 	}
 }
 
@@ -670,7 +596,7 @@
 {
 	u32 hw_pos;
 	u32 diff;
-	
+
 	/* There is no actual start yet */
 	if (!(woinst->state & WAVE_STATE_STARTED)) {
 		hw_pos = woinst->buffer.hw_pos;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/cardwo.h linux/drivers/sound/emu10k1/cardwo.h
--- linux.orig/drivers/sound/emu10k1/cardwo.h	Sat Aug 11 04:02:18 2001
+++ linux/drivers/sound/emu10k1/cardwo.h	Mon Jan 21 18:48:29 2002
@@ -47,20 +47,12 @@
 #define WAVEOUT_MINFRAGSHIFT	6
 #define WAVEOUT_MAXVOICES 6
 
-/* waveout_mem is cardwo internal */
-struct waveout_mem {
-	int emupageindex;
-	void *addr[BUFMAXPAGES];
-	dma_addr_t dma_handle[BUFMAXPAGES];
-};
-
 struct waveout_buffer {
 	u16 ossfragshift;
 	u32 numfrags;
 	u32 fragment_size;	/* in bytes units */
 	u32 size;		/* in bytes units */
 	u32 pages;		/* buffer size in page units*/
-	struct waveout_mem mem[WAVEOUT_MAXVOICES];
 	u32 silence_pos;	/* software cursor position (including silence bytes) */
 	u32 hw_pos;		/* hardware cursor position */
 	u32 free_bytes;		/* free bytes available on the buffer (not including silence bytes) */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/efxmgr.c linux/drivers/sound/emu10k1/efxmgr.c
--- linux.orig/drivers/sound/emu10k1/efxmgr.c	Tue Oct  9 17:53:18 2001
+++ linux/drivers/sound/emu10k1/efxmgr.c	Mon Jan 21 18:48:29 2002
@@ -173,7 +173,7 @@
 	unsigned long flags;
 	int muting;
 
-	const s32 log2lin[5] ={                  //  attenuation (dB)
+	static const s32 log2lin[4] ={           //  attenuation (dB)
 		0x7fffffff,                      //       0.0         
 		0x7fffffff * 0.840896415253715 , //       1.5          
 		0x7fffffff * 0.707106781186548,  //       3.0
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/hwaccess.h linux/drivers/sound/emu10k1/hwaccess.h
--- linux.orig/drivers/sound/emu10k1/hwaccess.h	Thu Oct 25 21:01:57 2001
+++ linux/drivers/sound/emu10k1/hwaccess.h	Mon Jan 21 18:48:29 2002
@@ -126,6 +126,7 @@
 #define CMD_SETMCH_FX		_IOW('D', 17, struct mixer_private_ioctl)
 #define CMD_SETPASSTHROUGH	_IOW('D', 18, struct mixer_private_ioctl)
 #define CMD_PRIVATE3_VERSION	_IOW('D', 19, struct mixer_private_ioctl)
+#define CMD_AC97_BOOST		_IOW('D', 20, struct mixer_private_ioctl)
 
 //up this number when breaking compatibility
 #define PRIVATE3_VERSION 1
@@ -216,7 +217,7 @@
 
 void sblive_writeptr(struct emu10k1_card *, u32 , u32 , u32 );
 void sblive_writeptr_tag(struct emu10k1_card *card, u32 channel, ...);
-#define TAGLIST_END 0
+#define TAGLIST_END	0
 
 u32 sblive_readptr(struct emu10k1_card *, u32 , u32 );
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/irqmgr.c linux/drivers/sound/emu10k1/irqmgr.c
--- linux.orig/drivers/sound/emu10k1/irqmgr.c	Sat Aug 11 04:02:18 2001
+++ linux/drivers/sound/emu10k1/irqmgr.c	Mon Jan 21 18:48:29 2002
@@ -1,4 +1,3 @@
-
 /*
  **********************************************************************
  *     irqmgr.c - IRQ manager for emu10k1 driver
@@ -41,7 +40,7 @@
 void emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	struct emu10k1_card *card = (struct emu10k1_card *) dev_id;
-	u32 irqstatus;
+	u32 irqstatus, irqstatus_tmp;
 
 	DPD(4, "emu10k1_interrupt called, irq =  %u\n", irq);
 
@@ -60,8 +59,7 @@
 	while ((irqstatus = inl(card->iobase + IPR))) {
 		DPD(4, "irq status %#x\n", irqstatus);
 
-		/* acknowledge interrupt */
-		outl(irqstatus, card->iobase + IPR);
+		irqstatus_tmp = irqstatus;
 
 		if (irqstatus & IRQTYPE_TIMER) {
 			emu10k1_timer_irqhandler(card);
@@ -98,7 +96,15 @@
 			irqstatus &=~IPR_VOLDECR;
 		}
 
-		if (irqstatus)
-			emu10k1_irq_disable(card, irqstatus);
+		if (irqstatus){
+			printk(KERN_ERR "emu10k1: Warning, unhandled interrupt: %#08x\n", irqstatus);
+			//make sure any interrupts we don't handle are disabled:
+			emu10k1_irq_disable(card, ~(INTE_MIDIRXENABLE | INTE_MIDITXENABLE | INTE_INTERVALTIMERENB |
+						INTE_VOLDECRENABLE | INTE_VOLINCRENABLE | INTE_MUTEENABLE |
+						INTE_FXDSPENABLE));
+		}
+
+		/* acknowledge interrupt */
+                outl(irqstatus_tmp, card->iobase + IPR);
 	}
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/main.c linux/drivers/sound/emu10k1/main.c
--- linux.orig/drivers/sound/emu10k1/main.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/sound/emu10k1/main.c	Mon Jan 21 18:48:29 2002
@@ -1,4 +1,4 @@
-/*
+ /*
  **********************************************************************
  *     main.c - Creative EMU10K1 audio driver
  *     Copyright 1999, 2000 Creative Labs, Inc.
@@ -69,6 +69,15 @@
  *     0.16 Mixer improvements, added old treble/bass support (Daniel Bertrand)
  *          Small code format cleanup.
  *          Deadlock bug fix for emu10k1_volxxx_irqhandler().
+ *     0.17 Fix for mixer SOUND_MIXER_INFO ioctl.
+ *	    Fix for HIGHMEM machines (emu10k1 can only do 31 bit bus master) 
+ *	    midi poll initial implementation.
+ *	    Small mixer fixes/cleanups.
+ *	    Improved support for 5.1 cards.
+ *     0.18 Fix for possible leak in pci_alloc_consistent()
+ *          Cleaned up poll() functions (audio and midi). Don't start input.
+ *	    Restrict DMA pages used to 512Mib range.
+ *	    New AC97_BOOST mixer ioctl.
  *
  *********************************************************************/
 
@@ -102,11 +111,10 @@
 #define SNDCARD_EMU10K1 46
 #endif
  
-#define DRIVER_VERSION "0.16"
+#define DRIVER_VERSION "0.18"
 
-/* FIXME: is this right? */
-/* does the card support 32 bit bus master?*/
-#define EMU10K1_DMA_MASK                0xffffffff	/* DMA buffer mask for pci_alloc_consist */
+/* the emu10k1 _seems_ to only supports 29 bit (512MiB) bit bus master */
+#define EMU10K1_DMA_MASK                0x1fffffff	/* DMA buffer mask for pci_alloc_consist */
 
 #ifndef PCI_VENDOR_ID_CREATIVE
 #define PCI_VENDOR_ID_CREATIVE 0x1102
@@ -211,6 +219,8 @@
 static int __devinit emu10k1_mixer_init(struct emu10k1_card *card)
 {
 	char s[32];
+
+	struct ac97_codec *codec = &card->ac97;
 	card->ac97.dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1);
 	if (card->ac97.dev_mixer < 0) {
 		printk(KERN_ERR "emu10k1: cannot register mixer device\n");
@@ -228,11 +238,14 @@
 			printk(KERN_ERR "emu10k1: unable to probe AC97 codec\n");
 			goto err_out;
 		}
-		/* 5.1: Enable the additional AC97 Slots. If the emu10k1 version
-			does not support this, it shouldn't do any harm */
-		sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE);
+		/* 5.1: Enable the additional AC97 Slots and unmute extra channels on AC97 codec */
+		if (codec->codec_read(codec, AC97_EXTENDED_ID) & 0x0080){
+			printk(KERN_INFO "emu10k1: SBLive! 5.1 card detected\n"); 
+			sblive_writeptr(card, AC97SLOT, 0, AC97SLOT_CNTR | AC97SLOT_LFE);
+			codec->codec_write(codec, AC97_SURROUND_MASTER, 0x0);
+		}
 
-		// Force 5bit
+		// Force 5bit:		    
 		//card->ac97.bit_resolution=5;
 
 		if (!proc_mkdir ("driver/emu10k1", 0)) {
@@ -586,15 +599,15 @@
 	CONNECT(PCM1_IN_R, ANALOG_REAR_R);
 
 	/* Digital In + PCM + AC97 In + MULTI_FRONT --> Digital out */
-	OP(6, 0x10b, 0x100, 0x102, 0x10c);
-	OP(6, 0x10b, 0x10b, 0x113, 0x40);
+	OP(6, 0x10a, 0x100, 0x102, 0x10c);
+	OP(6, 0x10a, 0x10a, 0x113, 0x40);
 
 	CONNECT(MULTI_FRONT_L, DIGITAL_OUT_L);
 	CONNECT(PCM_IN_L, DIGITAL_OUT_L);
 	CONNECT(AC97_IN_L, DIGITAL_OUT_L);
 	CONNECT(SPDIF_CD_L, DIGITAL_OUT_L);
 
-	OP(6, 0x10a, 0x101, 0x103, 0x10e);
+	OP(6, 0x10b, 0x101, 0x103, 0x10e);
 	OP(6, 0x10b, 0x10b, 0x114, 0x40);
 
 	CONNECT(MULTI_FRONT_R, DIGITAL_OUT_R);
@@ -768,7 +781,7 @@
 				    VTFT, 0xffff,
 				    CVCF, 0xffff,
 				    PTRX, 0,
-				    CPF, 0,
+				    //CPF, 0,
 				    CCR, 0,
 
 				    PSST, 0,
@@ -794,7 +807,9 @@
 				    ENVVOL, 0,
 				    ENVVAL, 0,
                                     TAGLIST_END);
+		sblive_writeptr(card, CPF, nCh, 0);
 	}
+	
 
 	/*
 	 ** Init to 0x02109204 :
@@ -951,8 +966,9 @@
 				    VTFT, 0,
 				    CVCF, 0,
 				    PTRX, 0,
-				    CPF, 0,
+				    //CPF, 0,
 				    TAGLIST_END);
+		sblive_writeptr(card, CPF, ch, 0);
 	}
 
 	/* Disable audio and lock cache */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/midi.c linux/drivers/sound/emu10k1/midi.c
--- linux.orig/drivers/sound/emu10k1/midi.c	Tue Oct  9 17:53:18 2001
+++ linux/drivers/sound/emu10k1/midi.c	Mon Jan 21 18:48:29 2002
@@ -31,6 +31,7 @@
 
 #define __NO_VERSION__
 #include <linux/module.h>
+#include <linux/poll.h>
 #include <linux/slab.h>
 #include <linux/version.h>
 #include <linux/sched.h>
@@ -371,8 +372,32 @@
 
 static unsigned int emu10k1_midi_poll(struct file *file, struct poll_table_struct *wait)
 {
+	struct emu10k1_mididevice *midi_dev = (struct emu10k1_mididevice *) file->private_data;
+	unsigned long flags;
+	unsigned int mask = 0;
+
 	DPF(4, "emu10k1_midi_poll() called\n");
-	return 0;
+
+	if (file->f_mode & FMODE_WRITE)
+		poll_wait(file, &midi_dev->oWait, wait);
+
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &midi_dev->iWait, wait);
+
+	spin_lock_irqsave(&midi_spinlock, flags);
+
+	if (file->f_mode & FMODE_WRITE)
+		mask |= POLLOUT | POLLWRNORM;
+
+	if (file->f_mode & FMODE_READ) {
+		if (midi_dev->mistate == MIDIIN_STATE_STARTED)
+			if (midi_dev->icnt > 0)
+				mask |= POLLIN | POLLRDNORM;
+	}
+
+	spin_unlock_irqrestore(&midi_spinlock, flags);
+
+	return mask;
 }
 
 int emu10k1_midi_callback(unsigned long msg, unsigned long refdata, unsigned long *pmsg)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/mixer.c linux/drivers/sound/emu10k1/mixer.c
--- linux.orig/drivers/sound/emu10k1/mixer.c	Tue Oct  9 17:53:18 2001
+++ linux/drivers/sound/emu10k1/mixer.c	Mon Jan 21 18:48:29 2002
@@ -444,6 +444,7 @@
 
 		case CMD_SETGPR2OSS:
 			id = ctl->val[0];
+			/* 0 == left, 1 == right */
 			ch = ctl->val[1];
 			addr = ctl->val[2];
 
@@ -460,13 +461,13 @@
 			if (addr >= 0) {
 				unsigned int state = card->ac97.mixer_state[id];
 
-				if (ch) {
+				if (ch == 1) {
 					state >>= 8;
 					card->ac97.stereo_mixers |= (1 << id);
-				} else {
-					card->ac97.supported_mixers |= (1 << id);
 				}
 
+				card->ac97.supported_mixers |= (1 << id);
+
 				if (id == SOUND_MIXER_TREBLE) {
 					set_treble(card, card->ac97.mixer_state[id] & 0xff, (card->ac97.mixer_state[id] >> 8) & 0xff);
 				} else if (id == SOUND_MIXER_BASS) {
@@ -475,10 +476,10 @@
 					emu10k1_set_volume_gpr(card, addr, state & 0xff,
 							       volume_params[id]);
 			} else {
-				if (ch) {
-					card->ac97.stereo_mixers &= ~(1 << id);
-					card->ac97.stereo_mixers |= card->ac97_stereo_mixers;
-				} else {
+				card->ac97.stereo_mixers &= ~(1 << id);
+				card->ac97.stereo_mixers |= card->ac97_stereo_mixers;
+
+				if (ch == 0) {
 					card->ac97.supported_mixers &= ~(1 << id);
 					card->ac97.supported_mixers |= card->ac97_supported_mixers;
 				}
@@ -499,6 +500,12 @@
 				ret = -EFAULT;
 			break;
 
+		case CMD_AC97_BOOST:
+			if(ctl->val[0])
+				emu10k1_ac97_write(&card->ac97, 0x18, 0x0);	
+			else
+				emu10k1_ac97_write(&card->ac97, 0x18, 0x0808);
+			break;
 		default:
 			ret = -EINVAL;
 			break;
@@ -572,6 +579,8 @@
 	int val;
 	int scale;
 
+	card->ac97.modcnt++;
+
 	if (get_user(val, (int *)arg))
 		return -EFAULT;
 
@@ -626,7 +635,7 @@
 			return 0;
 		}
 
-		if ((_IOC_DIR(cmd) == (_IOC_WRITE|_IOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES)
+		if ((_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) && oss_mixer <= SOUND_MIXER_NRDEVICES)
 			ret = emu10k1_dsp_mixer(card, oss_mixer, arg);
 		else
 			ret = card->ac97.mixer_ioctl(&card->ac97, cmd, arg);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/recmgr.c linux/drivers/sound/emu10k1/recmgr.c
--- linux.orig/drivers/sound/emu10k1/recmgr.c	Sat Aug 11 04:02:19 2001
+++ linux/drivers/sound/emu10k1/recmgr.c	Mon Jan 21 18:48:29 2002
@@ -130,7 +130,7 @@
 		break;
 	}
 
-	DPD(2, "bus addx: %#x\n", buffer->dma_handle);
+	DPD(2, "bus addx: %#lx\n", (unsigned long) buffer->dma_handle);
 
 	sblive_writeptr(card, buffer->addrreg, 0, buffer->dma_handle);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/voicemgr.c linux/drivers/sound/emu10k1/voicemgr.c
--- linux.orig/drivers/sound/emu10k1/voicemgr.c	Sat Aug 11 04:02:19 2001
+++ linux/drivers/sound/emu10k1/voicemgr.c	Mon Jan 21 18:48:29 2002
@@ -32,6 +32,84 @@
 #include "voicemgr.h"
 #include "8010.h"
 
+/**
+ * emu10k1_voice_alloc_buffer -
+ *
+ * allocates the memory buffer for a voice. Two page tables are kept for each buffer.
+ * One (dma_handle) keeps track of the host memory pages used and the other (virtualpagetable)
+ * is passed to the device so that it can do DMA to host memory.
+ *
+ */
+int emu10k1_voice_alloc_buffer(struct emu10k1_card *card, struct voice_mem *mem, u32 pages)
+{
+	u32 pageindex, pagecount;
+	unsigned long busaddx;
+	int i;
+
+	DPD(2, "requested pages is: %d\n", pages);
+
+	if ((mem->emupageindex = emu10k1_addxmgr_alloc(pages * PAGE_SIZE, card)) < 0)
+	{
+		DPF(1, "couldn't allocate emu10k1 address space\n");
+		return -1;
+	}
+
+	/* Fill in virtual memory table */
+	for (pagecount = 0; pagecount < pages; pagecount++) {
+		if ((mem->addr[pagecount] = pci_alloc_consistent(card->pci_dev, PAGE_SIZE, &mem->dma_handle[pagecount]))
+			== NULL) {
+			mem->pages = pagecount;
+			DPF(1, "couldn't allocate dma memory\n");
+			return -1;
+		}
+
+		DPD(2, "Virtual Addx: %p\n", mem->addr[pagecount]);
+
+		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+			busaddx = mem->dma_handle[pagecount] + i * EMUPAGESIZE;
+
+			DPD(3, "Bus Addx: %#lx\n", busaddx);
+
+			pageindex = mem->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+
+			((u32 *) card->virtualpagetable.addr)[pageindex] = cpu_to_le32((busaddx * 2) | pageindex);
+		}
+	}
+
+	mem->pages = pagecount;
+
+	return 0;
+}
+
+/**
+ * emu10k1_voice_free_buffer -
+ *
+ * frees the memory buffer for a voice.
+ */
+void emu10k1_voice_free_buffer(struct emu10k1_card *card, struct voice_mem *mem)
+{
+	u32 pagecount, pageindex;
+	int i;
+
+	if (mem->emupageindex < 0)
+		return;
+
+	for (pagecount = 0; pagecount < mem->pages; pagecount++) {
+		pci_free_consistent(card->pci_dev, PAGE_SIZE,
+					mem->addr[pagecount],
+					mem->dma_handle[pagecount]);
+
+		for (i = 0; i < PAGE_SIZE / EMUPAGESIZE; i++) {
+			pageindex = mem->emupageindex + pagecount * PAGE_SIZE / EMUPAGESIZE + i;
+			((u32 *) card->virtualpagetable.addr)[pageindex] =
+				cpu_to_le32((card->silentpage.dma_handle * 2) | pageindex);
+		}
+	}
+
+	emu10k1_addxmgr_free(card, mem->emupageindex);
+	mem->emupageindex = -1;
+}
+
 int emu10k1_voice_alloc(struct emu10k1_card *card, struct emu_voice *voice)
 {
 	u8 *voicetable = card->voicetable;
@@ -96,8 +174,10 @@
 							VTFT, 0x0000ffff,
 							PTRX_PITCHTARGET, 0,
 							CVCF, 0x0000ffff,
-							CPF, 0,
+							//CPF, 0,
 							TAGLIST_END);
+		
+		sblive_writeptr(card, CPF, voice->num + i, 0);
 	}
 
 	voice->usage = VOICE_USAGE_FREE;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/emu10k1/voicemgr.h linux/drivers/sound/emu10k1/voicemgr.h
--- linux.orig/drivers/sound/emu10k1/voicemgr.h	Sat Aug 11 04:02:19 2001
+++ linux/drivers/sound/emu10k1/voicemgr.h	Mon Jan 21 18:48:29 2002
@@ -64,6 +64,12 @@
 	u32 byampl_env_decay;
 };
 
+struct voice_mem {
+	int emupageindex;
+	void *addr[BUFMAXPAGES];
+	dma_addr_t dma_handle[BUFMAXPAGES];
+	u32 pages;
+};
 
 struct emu_voice
 {
@@ -72,16 +78,20 @@
 	u8 num;			/* Voice ID */
 	u8 flags;		/* Stereo/mono, 8/16 bit */
 
-        u32 startloop;
-        u32 endloop;
+	u32 startloop;
+	u32 endloop;
 	u32 start;
 
 	u32 initial_pitch;
 	u32 pitch_target;
 
 	struct voice_param params[2];
+
+	struct voice_mem mem;
 };
 
+int emu10k1_voice_alloc_buffer(struct emu10k1_card *, struct voice_mem *, u32);
+void emu10k1_voice_free_buffer(struct emu10k1_card *, struct voice_mem *);
 int emu10k1_voice_alloc(struct emu10k1_card *, struct emu_voice *);
 void emu10k1_voice_free(struct emu_voice *);
 void emu10k1_voice_playback_setup(struct emu_voice *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/es1370.c linux/drivers/sound/es1370.c
--- linux.orig/drivers/sound/es1370.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/es1370.c	Mon Jan 14 18:53:53 2002
@@ -2484,12 +2484,8 @@
 				break;
 			if (signal_pending(current))
 				break;
-			if (file->f_flags & O_NONBLOCK) {
-				remove_wait_queue(&s->midi.owait, &wait);
-				set_current_state(TASK_RUNNING);
-				unlock_kernel();
-				return -EBUSY;
-			}
+			if (file->f_flags & O_NONBLOCK) 
+				break;
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
 				DBG(printk(KERN_DEBUG "es1370: midi timed out??\n");)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/es1371.c linux/drivers/sound/es1371.c
--- linux.orig/drivers/sound/es1371.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/es1371.c	Mon Jan 14 18:53:53 2002
@@ -2660,10 +2660,7 @@
 			if (signal_pending(current))
 				break;
 			if (file->f_flags & O_NONBLOCK) {
-				remove_wait_queue(&s->midi.owait, &wait);
-				set_current_state(TASK_RUNNING);
-				unlock_kernel();
-				return -EBUSY;
+				break;
 			}
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/esssolo1.c linux/drivers/sound/esssolo1.c
--- linux.orig/drivers/sound/esssolo1.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/esssolo1.c	Mon Jan 14 18:53:53 2002
@@ -1967,12 +1967,8 @@
 				break;
 			if (signal_pending(current))
 				break;
-			if (file->f_flags & O_NONBLOCK) {
-				remove_wait_queue(&s->midi.owait, &wait);
-				set_current_state(TASK_RUNNING);
-				unlock_kernel();
-				return -EBUSY;
-			}
+			if (file->f_flags & O_NONBLOCK)
+				break;
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
 				printk(KERN_DEBUG "solo1: midi timed out??\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/gus.h linux/drivers/sound/gus.h
--- linux.orig/drivers/sound/gus.h	Mon Dec 11 21:15:23 2000
+++ linux/drivers/sound/gus.h	Mon Feb  4 17:38:23 2002
@@ -1,9 +1,3 @@
-/*
- * gus.h
- *
- * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
- *
- */
 
 #include "ad1848.h"
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/i810_audio.c linux/drivers/sound/i810_audio.c
--- linux.orig/drivers/sound/i810_audio.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/sound/i810_audio.c	Wed Feb 13 17:37:40 2002
@@ -14,7 +14,7 @@
  *	Analog Devices (A major AC97 codec maker)
  *	Intel Corp  (you've probably heard of them already)
  *
- * AC97 clues and assistance provided by
+ *  AC97 clues and assistance provided by
  *	Analog Devices
  *	Zach 'Fufu' Brown
  *	Jeff Garzik
@@ -63,6 +63,8 @@
  *	This is available via the 'ftsodell=1' option. 
  *
  *	If you need to force a specific rate set the clocking= option
+ *
+ *	This driver is cursed. (Ben LaHaise)
  */
  
 #include <linux/module.h>
@@ -102,15 +104,22 @@
 #ifndef PCI_DEVICE_ID_INTEL_440MX
 #define PCI_DEVICE_ID_INTEL_440MX	0x7195
 #endif
+#ifndef PCI_DEVICE_ID_SI_7012
+#define PCI_DEVICE_ID_SI_7012		0x7012
+#endif
+#ifndef PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO
+#define PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO	0x01b1
+#endif
 
 static int ftsodell=0;
 static int strict_clocking=0;
-static unsigned int clocking=48000;
+static unsigned int clocking=0;
 static int spdif_locked=0;
 
 //#define DEBUG
 //#define DEBUG2
 //#define DEBUG_INTERRUPTS
+//#define DEBUG_MMAP
 
 #define ADC_RUNNING	1
 #define DAC_RUNNING	2
@@ -197,7 +206,7 @@
 #define INT_MASK (INT_SEC|INT_PRI|INT_MC|INT_PO|INT_PI|INT_MO|INT_NI|INT_GPI)
 
 
-#define DRIVER_VERSION "0.04"
+#define DRIVER_VERSION "0.21"
 
 /* magic numbers to protect our data structures */
 #define I810_CARD_MAGIC		0x5072696E /* "Prin" */
@@ -220,7 +229,9 @@
 	ICH82901AB,
 	INTEL440MX,
 	INTELICH2,
-	INTELICH3
+	INTELICH3,
+	SI7012,
+	NVIDIA_NFORCE
 };
 
 static char * card_names[] = {
@@ -228,7 +239,9 @@
 	"Intel ICH 82901AB",
 	"Intel 440MX",
 	"Intel ICH2",
-	"Intel ICH3"
+	"Intel ICH3",
+	"SiS 7012",
+	"NVIDIA nForce Audio"
 };
 
 static struct pci_device_id i810_pci_tbl [] __initdata = {
@@ -242,6 +255,10 @@
 	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH2},
 	{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH3,
 	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, INTELICH3},
+	{PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_7012,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, SI7012},
+	{PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_MCP1_AUDIO,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, NVIDIA_NFORCE},
 	{0,}
 };
 
@@ -357,39 +374,17 @@
 	struct i810_channel *(*alloc_rec_pcm_channel)(struct i810_card *);
 	struct i810_channel *(*alloc_rec_mic_channel)(struct i810_card *);
 	void (*free_pcm_channel)(struct i810_card *, int chan);
+
+	/* We have a *very* long init time possibly, so use this to block */
+	/* attempts to open our devices before we are ready (stops oops'es) */
+	int initializing;
 };
 
 static struct i810_card *devs = NULL;
 
 static int i810_open_mixdev(struct inode *inode, struct file *file);
-static int i810_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd,
-				unsigned long arg);
-
-static inline unsigned ld2(unsigned int x)
-{
-	unsigned r = 0;
-	
-	if (x >= 0x10000) {
-		x >>= 16;
-		r += 16;
-	}
-	if (x >= 0x100) {
-		x >>= 8;
-		r += 8;
-	}
-	if (x >= 0x10) {
-		x >>= 4;
-		r += 4;
-	}
-	if (x >= 4) {
-		x >>= 2;
-		r += 2;
-	}
-	if (x >= 2)
-		r++;
-	return r;
-}
-
+static int i810_ioctl_mixdev(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg);
 static u16 i810_ac97_get(struct ac97_codec *dev, u8 reg);
 static void i810_ac97_set(struct ac97_codec *dev, u8 reg, u16 data);
 
@@ -606,9 +601,8 @@
 		rate = 8000;
 		dmabuf->rate = (rate * 48000)/clocking;
 	}
-	
-	new_rate = ac97_set_dac_rate(codec, rate);
 
+        new_rate=ac97_set_dac_rate(codec, rate);
 	if(new_rate != rate) {
 		dmabuf->rate = (new_rate * 48000)/clocking;
 	}
@@ -666,47 +660,49 @@
 static inline unsigned i810_get_dma_addr(struct i810_state *state, int rec)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	unsigned int civ, offset;
-	struct i810_channel *c;
+	unsigned int civ, offset, port, port_picb, bytes = 2;
 	
 	if (!dmabuf->enable)
 		return 0;
+
 	if (rec)
-		c = dmabuf->read_channel;
+		port = state->card->iobase + dmabuf->read_channel->port;
 	else
-		c = dmabuf->write_channel;
+		port = state->card->iobase + dmabuf->write_channel->port;
+
+	if(state->card->pci_id == PCI_DEVICE_ID_SI_7012) {
+		port_picb = port + OFF_SR;
+		bytes = 1;
+	} else
+		port_picb = port + OFF_PICB;
+
 	do {
-		civ = inb(state->card->iobase+c->port+OFF_CIV);
-		offset = (civ + 1) * dmabuf->fragsize -
-			      2 * inw(state->card->iobase+c->port+OFF_PICB);
-		/* CIV changed before we read PICB (very seldom) ?
-		 * then PICB was rubbish, so try again */
-	} while (civ != inb(state->card->iobase+c->port+OFF_CIV));
+		civ = inb(port+OFF_CIV) & 31;
+		offset = inw(port_picb);
+		/* Must have a delay here! */ 
+		if(offset == 0)
+			udelay(1);
+		/* Reread both registers and make sure that that total
+		 * offset from the first reading to the second is 0.
+		 * There is an issue with SiS hardware where it will count
+		 * picb down to 0, then update civ to the next value,
+		 * then set the new picb to fragsize bytes.  We can catch
+		 * it between the civ update and the picb update, making
+		 * it look as though we are 1 fragsize ahead of where we
+		 * are.  The next to we get the address though, it will
+		 * be back in the right place, and we will suddenly think
+		 * we just went forward dmasize - fragsize bytes, causing
+		 * totally stupid *huge* dma overrun messages.  We are
+		 * assuming that the 1us delay is more than long enough
+		 * that we won't have to worry about the chip still being
+		 * out of sync with reality ;-)
+		 */
+	} while (civ != (inb(port+OFF_CIV) & 31) || offset != inw(port_picb));
 		 
-	return offset;
+	return (((civ + 1) * dmabuf->fragsize - (bytes * offset))
+		% dmabuf->dmasize);
 }
 
-//static void resync_dma_ptrs(struct i810_state *state, int rec)
-//{
-//	struct dmabuf *dmabuf = &state->dmabuf;
-//	struct i810_channel *c;
-//	int offset;
-//
-//	if(rec) {
-//		c = dmabuf->read_channel;
-//	} else {
-//		c = dmabuf->write_channel;
-//	}
-//	if(c==NULL)
-//		return;
-//	offset = inb(state->card->iobase+c->port+OFF_CIV);
-//	if(offset == inb(state->card->iobase+c->port+OFF_LVI))
-//		offset++;
-//	offset *= dmabuf->fragsize;
-//	
-//	dmabuf->hwptr=dmabuf->swptr = offset;
-//}
-	
 /* Stop recording (lock held) */
 static inline void __stop_adc(struct i810_state *state)
 {
@@ -718,7 +714,10 @@
 	// wait for the card to acknowledge shutdown
 	while( inb(card->iobase + PI_CR) != 0 ) ;
 	// now clear any latent interrupt bits (like the halt bit)
-	outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
+	if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PI_PICB), card->iobase + PI_PICB );
+	else
+		outb( inb(card->iobase + PI_SR), card->iobase + PI_SR );
 	outl( inl(card->iobase + GLOB_STA) & INT_PI, card->iobase + GLOB_STA);
 }
 
@@ -732,21 +731,27 @@
 	spin_unlock_irqrestore(&card->lock, flags);
 }
 
-static void start_adc(struct i810_state *state)
+static inline void __start_adc(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct i810_card *card = state->card;
-	unsigned long flags;
 
 	if (dmabuf->count < dmabuf->dmasize && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_INPUT)) {
-		spin_lock_irqsave(&card->lock, flags);
 		dmabuf->enable |= ADC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, card->iobase + PI_CR);
-		spin_unlock_irqrestore(&card->lock, flags);
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PI_CR);
 	}
 }
 
+static void start_adc(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_adc(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
+
 /* stop playback (lock held) */
 static inline void __stop_dac(struct i810_state *state)
 {
@@ -758,7 +763,10 @@
 	// wait for the card to acknowledge shutdown
 	while( inb(card->iobase + PO_CR) != 0 ) ;
 	// now clear any latent interrupt bits (like the halt bit)
-	outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
+	if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+		outb( inb(card->iobase + PO_PICB), card->iobase + PO_PICB );
+	else
+		outb( inb(card->iobase + PO_SR), card->iobase + PO_SR );
 	outl( inl(card->iobase + GLOB_STA) & INT_PO, card->iobase + GLOB_STA);
 }
 
@@ -772,20 +780,25 @@
 	spin_unlock_irqrestore(&card->lock, flags);
 }	
 
-static void start_dac(struct i810_state *state)
+static inline void __start_dac(struct i810_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	struct i810_card *card = state->card;
-	unsigned long flags;
 
 	if (dmabuf->count > 0 && dmabuf->ready && !dmabuf->enable &&
 	    (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
-		spin_lock_irqsave(&card->lock, flags);
 		dmabuf->enable |= DAC_RUNNING;
-		outb((1<<4) | (1<<2) | 1, card->iobase + PO_CR);
-		spin_unlock_irqrestore(&card->lock, flags);
+		outb((1<<4) | (1<<2) | 1, state->card->iobase + PO_CR);
 	}
 }
+static void start_dac(struct i810_state *state)
+{
+	struct i810_card *card = state->card;
+	unsigned long flags;
+
+	spin_lock_irqsave(&card->lock, flags);
+	__start_dac(state);
+	spin_unlock_irqrestore(&card->lock, flags);
+}
 
 #define DMABUF_DEFAULTORDER (16-PAGE_SHIFT)
 #define DMABUF_MINORDER 1
@@ -805,6 +818,8 @@
 		dmabuf->ossfragsize = (PAGE_SIZE<<DMABUF_DEFAULTORDER)/dmabuf->ossmaxfrags;
 	size = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
 
+	if(dmabuf->rawbuf && (PAGE_SIZE << dmabuf->buforder) == size)
+		return 0;
 	/* alloc enough to satisfy the oss params */
 	for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) {
 		if ( (PAGE_SIZE<<order) > size )
@@ -873,14 +888,19 @@
 	dmabuf->swptr = dmabuf->hwptr = 0;
 	spin_unlock_irqrestore(&state->card->lock, flags);
 
-	/* allocate DMA buffer if not allocated yet */
-	if (dmabuf->rawbuf)
-		dealloc_dmabuf(state);
+	/* allocate DMA buffer, let alloc_dmabuf determine if we are already
+	 * allocated well enough or if we should replace the current buffer
+	 * (assuming one is already allocated, if it isn't, then allocate it).
+	 */
 	if ((ret = alloc_dmabuf(state)))
 		return ret;
 
 	/* FIXME: figure out all this OSS fragment stuff */
 	/* I did, it now does what it should according to the OSS API.  DL */
+	/* We may not have realloced our dmabuf, but the fragment size to
+	 * fragment number ratio may have changed, so go ahead and reprogram
+	 * things
+	 */
 	dmabuf->dmasize = PAGE_SIZE << dmabuf->buforder;
 	dmabuf->numfrag = SG_LEN;
 	dmabuf->fragsize = dmabuf->dmasize/dmabuf->numfrag;
@@ -922,6 +942,8 @@
 			sg->busaddr=virt_to_bus(dmabuf->rawbuf+dmabuf->fragsize*i);
 			// the card will always be doing 16bit stereo
 			sg->control=dmabuf->fragsamples;
+			if(state->card->pci_id == PCI_DEVICE_ID_SI_7012)
+				sg->control <<= 1;
 			sg->control|=CON_BUFPAD;
 			// set us up to get IOC interrupts as often as needed to
 			// satisfy numfrag requirements, no more
@@ -935,7 +957,6 @@
 		outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
 		outb(0, state->card->iobase+c->port+OFF_CIV);
 		outb(0, state->card->iobase+c->port+OFF_LVI);
-		dmabuf->count = 0;
 
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
@@ -969,31 +990,34 @@
 	else
 		port += dmabuf->write_channel->port;
 
-	if(dmabuf->mapped) {
-		if(rec)
-			dmabuf->swptr = (dmabuf->hwptr + dmabuf->dmasize
-				       	- dmabuf->count) % dmabuf->dmasize;
-		else
-			dmabuf->swptr = (dmabuf->hwptr + dmabuf->count)
-			       		% dmabuf->dmasize;
-	}
-	/*
-	 * two special cases, count == 0 on write
-	 * means no data, and count == dmasize
-	 * means no data on read, handle appropriately
+	/* if we are currently stopped, then our CIV is actually set to our
+	 * *last* sg segment and we are ready to wrap to the next.  However,
+	 * if we set our LVI to the last sg segment, then it won't wrap to
+	 * the next sg segment, it won't even get a start.  So, instead, when
+	 * we are stopped, we set both the LVI value and also we increment
+	 * the CIV value to the next sg segment to be played so that when
+	 * we call start_{dac,adc}, things will operate properly
 	 */
-	if(!rec && dmabuf->count == 0) {
-		outb(inb(port+OFF_CIV),port+OFF_LVI);
-		return;
-	}
-	if(rec && dmabuf->count == dmabuf->dmasize) {
-		outb(inb(port+OFF_CIV),port+OFF_LVI);
-		return;
+	if (!dmabuf->enable && dmabuf->ready) {
+		if(rec && dmabuf->count < dmabuf->dmasize &&
+		   (dmabuf->trigger & PCM_ENABLE_INPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_adc(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		} else if (!rec && dmabuf->count &&
+			   (dmabuf->trigger & PCM_ENABLE_OUTPUT))
+		{
+			outb((inb(port+OFF_CIV)+1)&31, port+OFF_LVI);
+			__start_dac(state);
+			while( !(inb(port + OFF_CR) & ((1<<4) | (1<<2))) ) ;
+		}
 	}
+
 	/* swptr - 1 is the tail of our transfer */
 	x = (dmabuf->dmasize + dmabuf->swptr - 1) % dmabuf->dmasize;
 	x /= dmabuf->fragsize;
-	outb(x&31, port+OFF_LVI);
+	outb(x, port+OFF_LVI);
 }
 
 static void i810_update_lvi(struct i810_state *state, int rec)
@@ -1020,7 +1044,9 @@
 		/* update hardware pointer */
 		hwptr = i810_get_dma_addr(state, 1);
 		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-//		printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("ADC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
 		dmabuf->hwptr = hwptr;
 		dmabuf->total_bytes += diff;
 		dmabuf->count += diff;
@@ -1029,8 +1055,8 @@
 			/* this is normal for the end of a read */
 			/* only give an error if we went past the */
 			/* last valid sg entry */
-			if(inb(state->card->iobase + PI_CIV) !=
-			   inb(state->card->iobase + PI_LVI)) {
+			if((inb(state->card->iobase + PI_CIV) & 31) !=
+			   (inb(state->card->iobase + PI_LVI) & 31)) {
 				printk(KERN_WARNING "i810_audio: DMA overrun on read\n");
 				dmabuf->error++;
 			}
@@ -1043,7 +1069,9 @@
 		/* update hardware pointer */
 		hwptr = i810_get_dma_addr(state, 0);
 		diff = (dmabuf->dmasize + hwptr - dmabuf->hwptr) % dmabuf->dmasize;
-//		printk("HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#if defined(DEBUG_INTERRUPTS) || defined(DEBUG_MMAP)
+		printk("DAC HWP %d,%d,%d\n", hwptr, dmabuf->hwptr, diff);
+#endif
 		dmabuf->hwptr = hwptr;
 		dmabuf->total_bytes += diff;
 		dmabuf->count -= diff;
@@ -1052,13 +1080,13 @@
 			/* this is normal for the end of a write */
 			/* only give an error if we went past the */
 			/* last valid sg entry */
-			if(inb(state->card->iobase + PO_CIV) !=
-			   inb(state->card->iobase + PO_LVI)) {
+			if((inb(state->card->iobase + PO_CIV) & 31) !=
+			   (inb(state->card->iobase + PO_LVI) & 31)) {
 				printk(KERN_WARNING "i810_audio: DMA overrun on write\n");
 				printk("i810_audio: CIV %d, LVI %d, hwptr %x, "
 					"count %d\n",
-					inb(state->card->iobase + PO_CIV),
-					inb(state->card->iobase + PO_LVI),
+					inb(state->card->iobase + PO_CIV) & 31,
+					inb(state->card->iobase + PO_LVI) & 31,
 					dmabuf->hwptr, dmabuf->count);
 				dmabuf->error++;
 			}
@@ -1068,7 +1096,43 @@
 	}
 }
 
-static int drain_dac(struct i810_state *state, int nonblock)
+static inline int i810_get_free_write_space(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int free;
+
+	i810_update_ptr(state);
+	// catch underruns during playback
+	if (dmabuf->count < 0) {
+		dmabuf->count = 0;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	free = dmabuf->dmasize - dmabuf->count;
+	free -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(free < 0)
+		return(0);
+	return(free);
+}
+
+static inline int i810_get_available_read_data(struct i810_state *state)
+{
+	struct dmabuf *dmabuf = &state->dmabuf;
+	int avail;
+
+	i810_update_ptr(state);
+	// catch overruns during record
+	if (dmabuf->count > dmabuf->dmasize) {
+		dmabuf->count = dmabuf->dmasize;
+		dmabuf->swptr = dmabuf->hwptr;
+	}
+	avail = dmabuf->count;
+	avail -= (dmabuf->hwptr % dmabuf->fragsize);
+	if(avail < 0)
+		return(0);
+	return(avail);
+}
+
+static int drain_dac(struct i810_state *state, int signals_allowed)
 {
 	DECLARE_WAITQUEUE(wait, current);
 	struct dmabuf *dmabuf = &state->dmabuf;
@@ -1078,12 +1142,12 @@
 
 	if (!dmabuf->ready)
 		return 0;
-
+	if(dmabuf->mapped) {
+		stop_dac(state);
+		return 0;
+	}
 	add_wait_queue(&dmabuf->wait, &wait);
 	for (;;) {
-		/* It seems that we have to set the current state to TASK_INTERRUPTIBLE
-		   every time to make the process really go to sleep */
-		set_current_state(TASK_INTERRUPTIBLE);
 
 		spin_lock_irqsave(&state->card->lock, flags);
 		i810_update_ptr(state);
@@ -1093,33 +1157,46 @@
 		if (count <= 0)
 			break;
 
-		if (signal_pending(current))
-			break;
-
-		i810_update_lvi(state,0);
-		if (dmabuf->enable != DAC_RUNNING)
-			start_dac(state);
-
-		if (nonblock) {
-			remove_wait_queue(&dmabuf->wait, &wait);
-			set_current_state(TASK_RUNNING);
-			return -EBUSY;
+		/* 
+		 * This will make sure that our LVI is correct, that our
+		 * pointer is updated, and that the DAC is running.  We
+		 * have to force the setting of dmabuf->trigger to avoid
+		 * any possible deadlocks.
+		 */
+		if(!dmabuf->enable) {
+			dmabuf->trigger = PCM_ENABLE_OUTPUT;
+			i810_update_lvi(state,0);
 		}
+                if (signal_pending(current) && signals_allowed) {
+                        break;
+                }
 
-		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
-		tmo >>= 1;
-		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
+		/* It seems that we have to set the current state to
+		 * TASK_INTERRUPTIBLE every time to make the process
+		 * really go to sleep.  This also has to be *after* the
+		 * update_ptr() call because update_ptr is likely to
+		 * do a wake_up() which will unset this before we ever
+		 * try to sleep, resuling in a tight loop in this code
+		 * instead of actually sleeping and waiting for an
+		 * interrupt to wake us up!
+		 */
+		set_current_state(TASK_INTERRUPTIBLE);
+		/*
+		 * set the timeout to significantly longer than it *should*
+		 * take for the DAC to drain the DMA buffer
+		 */
+		tmo = (count * HZ) / (dmabuf->rate);
+		if (!schedule_timeout(tmo >= 2 ? tmo : 2)){
 			printk(KERN_ERR "i810_audio: drain_dac, dma timeout?\n");
+			count = 0;
 			break;
 		}
 	}
-	stop_dac(state);
-	synchronize_irq();
-	remove_wait_queue(&dmabuf->wait, &wait);
 	set_current_state(TASK_RUNNING);
-	if (signal_pending(current))
+	remove_wait_queue(&dmabuf->wait, &wait);
+	if(count > 0 && signal_pending(current) && signals_allowed)
 		return -ERESTARTSYS;
-
+	stop_dac(state);
 	return 0;
 }
 
@@ -1143,52 +1220,72 @@
 		if(!state->dmabuf.ready)
 			continue;
 		dmabuf = &state->dmabuf;
-		if(dmabuf->enable & DAC_RUNNING)
+		if(dmabuf->enable & DAC_RUNNING) {
 			c=dmabuf->write_channel;
-		else if(dmabuf->enable & ADC_RUNNING)
+		} else if(dmabuf->enable & ADC_RUNNING) {
 			c=dmabuf->read_channel;
-		else	/* This can occur going from R/W to close */
+		} else	/* This can occur going from R/W to close */
 			continue;
 		
 		port+=c->port;
-		
-		status = inw(port + OFF_SR);
+
+		if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+			status = inw(port + OFF_PICB);
+		else
+			status = inw(port + OFF_SR);
+
 #ifdef DEBUG_INTERRUPTS
 		printk("NUM %d PORT %X IRQ ( ST%d ", c->num, c->port, status);
 #endif
 		if(status & DMA_INT_COMPLETE)
 		{
+			/* only wake_up() waiters if this interrupt signals
+			 * us being beyond a userfragsize of data open or
+			 * available, and i810_update_ptr() does that for
+			 * us
+			 */
 			i810_update_ptr(state);
 #ifdef DEBUG_INTERRUPTS
 			printk("COMP %d ", dmabuf->hwptr /
 					dmabuf->fragsize);
 #endif
 		}
-		if(status & DMA_INT_LVI)
+		if(status & (DMA_INT_LVI | DMA_INT_DCH))
 		{
+			/* wake_up() unconditionally on LVI and DCH */
 			i810_update_ptr(state);
 			wake_up(&dmabuf->wait);
 #ifdef DEBUG_INTERRUPTS
-			printk("LVI ");
+			if(status & DMA_INT_LVI)
+				printk("LVI ");
+			if(status & DMA_INT_DCH)
+				printk("DCH -");
 #endif
-		}
-		if(status & DMA_INT_DCH)
-		{
-			i810_update_ptr(state);
 			if(dmabuf->enable & DAC_RUNNING)
 				count = dmabuf->count;
 			else
 				count = dmabuf->dmasize - dmabuf->count;
 			if(count > 0) {
 				outb(inb(port+OFF_CR) | 1, port+OFF_CR);
+#ifdef DEBUG_INTERRUPTS
+				printk(" CONTINUE ");
+#endif
 			} else {
+				if (dmabuf->enable & DAC_RUNNING)
+					__stop_dac(state);
+				if (dmabuf->enable & ADC_RUNNING)
+					__stop_adc(state);
+				dmabuf->enable = 0;
 				wake_up(&dmabuf->wait);
 #ifdef DEBUG_INTERRUPTS
-				printk("DCH - STOP ");
+				printk(" STOP ");
 #endif
 			}
 		}
-		outw(status & DMA_INT_MASK, port + OFF_SR);
+		if(card->pci_id == PCI_DEVICE_ID_SI_7012)
+			outw(status & DMA_INT_MASK, port + OFF_PICB);
+		else
+			outw(status & DMA_INT_MASK, port + OFF_SR);
 	}
 #ifdef DEBUG_INTERRUPTS
 	printk(")\n");
@@ -1254,15 +1351,14 @@
 		return ret;
 	if (!access_ok(VERIFY_WRITE, buffer, count))
 		return -EFAULT;
-	dmabuf->trigger &= ~PCM_ENABLE_OUTPUT;
 	ret = 0;
 
         add_wait_queue(&dmabuf->wait, &waita);
 	while (count > 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock_irqsave(&card->lock, flags);
                 if (PM_SUSPENDED(card)) {
                         spin_unlock_irqrestore(&card->lock, flags);
-                        set_current_state(TASK_INTERRUPTIBLE);
                         schedule();
                         if (signal_pending(current)) {
                                 if (!ret) ret = -EAGAIN;
@@ -1271,10 +1367,7 @@
                         continue;
                 }
 		swptr = dmabuf->swptr;
-		if (dmabuf->count > dmabuf->dmasize) {
-			dmabuf->count = dmabuf->dmasize;
-		}
-		cnt = dmabuf->count - dmabuf->fragsize;
+		cnt = i810_get_available_read_data(state);
 		// this is to make the copy_to_user simpler below
 		if(cnt > (dmabuf->dmasize - swptr))
 			cnt = dmabuf->dmasize - swptr;
@@ -1282,20 +1375,39 @@
 
 		if (cnt > count)
 			cnt = count;
+		/* Lop off the last two bits to force the code to always
+		 * write in full samples.  This keeps software that sets
+		 * O_NONBLOCK but doesn't check the return value of the
+		 * write call from getting things out of state where they
+		 * think a full 4 byte sample was written when really only
+		 * a portion was, resulting in odd sound and stereo
+		 * hysteresis.
+		 */
+		cnt &= ~0x3;
 		if (cnt <= 0) {
 			unsigned long tmo;
-			if(!dmabuf->enable) {
-				dmabuf->trigger |= PCM_ENABLE_INPUT;
-				start_adc(state);
-			}
+			/*
+			 * Don't let us deadlock.  The ADC won't start if
+			 * dmabuf->trigger isn't set.  A call to SETTRIGGER
+			 * could have turned it off after we set it to on
+			 * previously.
+			 */
+			dmabuf->trigger = PCM_ENABLE_INPUT;
+			/*
+			 * This does three things.  Updates LVI to be correct,
+			 * makes sure the ADC is running, and updates the
+			 * hwptr.
+			 */
 			i810_update_lvi(state,1);
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
-				return ret;
+				goto done;
 			}
-			/* This isnt strictly right for the 810  but it'll do */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
-			tmo >>= 1;
+			/* Set the timeout to how long it would take to fill
+			 * two of our buffers.  If we haven't been woke up
+			 * by then, then we know something is wrong.
+			 */
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
 			/* There are two situations when sleep_on_timeout returns, one is when
 			   the interrupt is serviced correctly and the process is waked up by
 			   ISR ON TIME. Another is when timeout is expired, which means that
@@ -1303,7 +1415,7 @@
 			   is TOO LATE for the process to be scheduled to run (scheduler latency)
 			   which results in a (potential) buffer overrun. And worse, there is
 			   NOTHING we can do to prevent it. */
-			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+			if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
 #ifdef DEBUG
 				printk(KERN_ERR "i810_audio: recording schedule timeout, "
 				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
@@ -1315,7 +1427,7 @@
 			}
 			if (signal_pending(current)) {
 				ret = ret ? ret : -ERESTARTSYS;
-				return ret;
+				goto done;
 			}
 			continue;
 		}
@@ -1341,10 +1453,8 @@
 		buffer += cnt;
 		ret += cnt;
 	}
-	i810_update_lvi(state,1);
-	if(!(dmabuf->enable & ADC_RUNNING))
-		start_adc(state);
  done:
+	i810_update_lvi(state,1);
         set_current_state(TASK_RUNNING);
         remove_wait_queue(&dmabuf->wait, &waita);
 
@@ -1384,15 +1494,14 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
-	dmabuf->trigger &= ~PCM_ENABLE_INPUT;
 	ret = 0;
 
         add_wait_queue(&dmabuf->wait, &waita);
 	while (count > 0) {
+		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock_irqsave(&state->card->lock, flags);
                 if (PM_SUSPENDED(card)) {
                         spin_unlock_irqrestore(&card->lock, flags);
-                        set_current_state(TASK_INTERRUPTIBLE);
                         schedule();
                         if (signal_pending(current)) {
                                 if (!ret) ret = -EAGAIN;
@@ -1402,12 +1511,14 @@
                 }
 
 		swptr = dmabuf->swptr;
-		if (dmabuf->count < 0) {
-			dmabuf->count = 0;
-		}
-		cnt = dmabuf->dmasize - swptr;
-		if(cnt > (dmabuf->dmasize - dmabuf->count))
-			cnt = dmabuf->dmasize - dmabuf->count;
+		cnt = i810_get_free_write_space(state);
+		/* Bound the maximum size to how much we can copy to the
+		 * dma buffer before we hit the end.  If we have more to
+		 * copy then it will get done in a second pass of this
+		 * loop starting from the beginning of the buffer.
+		 */
+		if(cnt > (dmabuf->dmasize - swptr))
+			cnt = dmabuf->dmasize - swptr;
 		spin_unlock_irqrestore(&state->card->lock, flags);
 
 #ifdef DEBUG2
@@ -1415,22 +1526,30 @@
 #endif
 		if (cnt > count)
 			cnt = count;
+		/* Lop off the last two bits to force the code to always
+		 * write in full samples.  This keeps software that sets
+		 * O_NONBLOCK but doesn't check the return value of the
+		 * write call from getting things out of state where they
+		 * think a full 4 byte sample was written when really only
+		 * a portion was, resulting in odd sound and stereo
+		 * hysteresis.
+		 */
+		cnt &= ~0x3;
 		if (cnt <= 0) {
 			unsigned long tmo;
 			// There is data waiting to be played
+			/*
+			 * Force the trigger setting since we would
+			 * deadlock with it set any other way
+			 */
+			dmabuf->trigger = PCM_ENABLE_OUTPUT;
 			i810_update_lvi(state,0);
-			if(!dmabuf->enable && dmabuf->count) {
-				/* force the starting incase SETTRIGGER has been used */
-				/* to stop it, otherwise this is a deadlock situation */
-				dmabuf->trigger |= PCM_ENABLE_OUTPUT;
-				start_dac(state);
-			}
 			if (file->f_flags & O_NONBLOCK) {
 				if (!ret) ret = -EAGAIN;
 				goto ret;
 			}
 			/* Not strictly correct but works */
-			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 4);
+			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);
 			/* There are two situations when sleep_on_timeout returns, one is when
 			   the interrupt is serviced correctly and the process is waked up by
 			   ISR ON TIME. Another is when timeout is expired, which means that
@@ -1438,7 +1557,7 @@
 			   is TOO LATE for the process to be scheduled to run (scheduler latency)
 			   which results in a (potential) buffer underrun. And worse, there is
 			   NOTHING we can do to prevent it. */
-			if (!interruptible_sleep_on_timeout(&dmabuf->wait, tmo)) {
+			if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {
 #ifdef DEBUG
 				printk(KERN_ERR "i810_audio: playback schedule timeout, "
 				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",
@@ -1480,10 +1599,8 @@
 		x = dmabuf->fragsize - (swptr % dmabuf->fragsize);
 		memset(dmabuf->rawbuf + swptr, '\0', x);
 	}
+ret:
 	i810_update_lvi(state,0);
-	if (!dmabuf->enable && dmabuf->count >= dmabuf->userfragsize)
-		start_dac(state);
- ret:
         set_current_state(TASK_RUNNING);
         remove_wait_queue(&dmabuf->wait, &waita);
 
@@ -1502,22 +1619,19 @@
 		return 0;
 	poll_wait(file, &dmabuf->wait, wait);
 	spin_lock_irqsave(&state->card->lock, flags);
-	i810_update_ptr(state);
-	if (file->f_mode & FMODE_READ && dmabuf->enable & ADC_RUNNING) {
-		if (dmabuf->count >= (signed)dmabuf->fragsize)
+	if (dmabuf->enable & ADC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_INPUT) {
+		if (i810_get_available_read_data(state) >= 
+		    (signed)dmabuf->userfragsize)
 			mask |= POLLIN | POLLRDNORM;
 	}
-	if (file->f_mode & FMODE_WRITE && dmabuf->enable & DAC_RUNNING) {
-		if (dmabuf->mapped) {
-			if (dmabuf->count >= (signed)dmabuf->fragsize)
-				mask |= POLLOUT | POLLWRNORM;
-		} else {
-			if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)
-				mask |= POLLOUT | POLLWRNORM;
-		}
+	if (dmabuf->enable & DAC_RUNNING ||
+	    dmabuf->trigger & PCM_ENABLE_OUTPUT) {
+		if (i810_get_free_write_space(state) >=
+		    (signed)dmabuf->userfragsize)
+			mask |= POLLOUT | POLLWRNORM;
 	}
 	spin_unlock_irqrestore(&state->card->lock, flags);
-
 	return mask;
 }
 
@@ -1559,12 +1673,9 @@
 			     size, vma->vm_page_prot))
 		goto out;
 	dmabuf->mapped = 1;
-	if(vma->vm_flags & VM_WRITE)
-		dmabuf->count = dmabuf->dmasize;
-	else
-		dmabuf->count = 0;
+	dmabuf->trigger = 0;
 	ret = 0;
-#ifdef DEBUG
+#ifdef DEBUG_MMAP
 	printk("i810_audio: mmap'ed %ld bytes of data space\n", size);
 #endif
 out:
@@ -1575,16 +1686,15 @@
 static int i810_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
 	struct i810_state *state = (struct i810_state *)file->private_data;
+	struct i810_channel *c = NULL;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	unsigned long flags;
 	audio_buf_info abinfo;
 	count_info cinfo;
 	unsigned int i_glob_cnt;
-	int val = 0, mapped, ret;
+	int val = 0, ret;
 	struct ac97_codec *codec = state->card->ac97_codec[0];
 
-	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
-		((file->f_mode & FMODE_READ) && dmabuf->mapped);
 #ifdef DEBUG
 	printk("i810_audio: i810_ioctl, arg=0x%x, cmd=", arg ? *(int *)arg : 0);
 #endif
@@ -1601,13 +1711,23 @@
 #ifdef DEBUG
 		printk("SNDCTL_DSP_RESET\n");
 #endif
-		/* FIXME: spin_lock ? */
+		spin_lock_irqsave(&state->card->lock, flags);
 		if (dmabuf->enable == DAC_RUNNING) {
-			stop_dac(state);
+			c = dmabuf->write_channel;
+			__stop_dac(state);
 		}
 		if (dmabuf->enable == ADC_RUNNING) {
-			stop_adc(state);
+			c = dmabuf->read_channel;
+			__stop_adc(state);
 		}
+		if (c != NULL) {
+			outb(2, state->card->iobase+c->port+OFF_CR);   /* reset DMA machine */
+			outl(virt_to_bus(&c->sg[0]), state->card->iobase+c->port+OFF_BDBAR);
+			outb(0, state->card->iobase+c->port+OFF_CIV);
+			outb(0, state->card->iobase+c->port+OFF_LVI);
+		}
+
+		spin_unlock_irqrestore(&state->card->lock, flags);
 		synchronize_irq();
 		dmabuf->ready = 0;
 		dmabuf->swptr = dmabuf->hwptr = 0;
@@ -1620,10 +1740,9 @@
 #endif
 		if (dmabuf->enable != DAC_RUNNING || file->f_flags & O_NONBLOCK)
 			return 0;
-		drain_dac(state, 0);
-		dmabuf->ready = 0;
-		dmabuf->swptr = dmabuf->hwptr = 0;
-		dmabuf->count = dmabuf->total_bytes = 0;
+		if((val = drain_dac(state, 1)))
+			return val;
+		dmabuf->total_bytes = 0;
 		return 0;
 
 	case SNDCTL_DSP_SPEED: /* set smaple rate */
@@ -1674,9 +1793,6 @@
 #ifdef DEBUG
 		printk("SNDCTL_DSP_STEREO\n");
 #endif
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-
 		if (dmabuf->enable & DAC_RUNNING) {
 			stop_dac(state);
 		}
@@ -1709,18 +1825,7 @@
 #ifdef DEBUG
 		printk("SNDCTL_DSP_SETFMT\n");
 #endif
-		if (get_user(val, (int *)arg))
-			return -EFAULT;
-
-		switch ( val ) {
-			case AFMT_S16_LE:
-				break;
-			case AFMT_QUERY:
-			default:
-				val = AFMT_S16_LE;
-				break;
-		}
-		return put_user(val, (int *)arg);
+		return put_user(AFMT_S16_LE, (int *)arg);
 
 	case SNDCTL_DSP_CHANNELS:
 #ifdef DEBUG
@@ -1820,22 +1925,47 @@
 
 		dmabuf->ossfragsize = 1<<(val & 0xffff);
 		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
-		if (dmabuf->ossmaxfrags <= 4)
-			dmabuf->ossmaxfrags = 4;
-		else if (dmabuf->ossmaxfrags <= 8)
-			dmabuf->ossmaxfrags = 8;
-		else if (dmabuf->ossmaxfrags <= 16)
-			dmabuf->ossmaxfrags = 16;
-		else
-			dmabuf->ossmaxfrags = 32;
+		if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
+			return -EINVAL;
+		/*
+		 * Bound the frag size into our allowed range of 256 - 4096
+		 */
+		if (dmabuf->ossfragsize < 256)
+			dmabuf->ossfragsize = 256;
+		else if (dmabuf->ossfragsize > 4096)
+			dmabuf->ossfragsize = 4096;
+		/*
+		 * The numfrags could be something reasonable, or it could
+		 * be 0xffff meaning "Give me as much as possible".  So,
+		 * we check the numfrags * fragsize doesn't exceed our
+		 * 64k buffer limit, nor is it less than our 8k minimum.
+		 * If it fails either one of these checks, then adjust the
+		 * number of fragments, not the size of them.  It's OK if
+		 * our number of fragments doesn't equal 32 or anything
+		 * like our hardware based number now since we are using
+		 * a different frag count for the hardware.  Before we get
+		 * into this though, bound the maxfrags to avoid overflow
+		 * issues.  A reasonable bound would be 64k / 256 since our
+		 * maximum buffer size is 64k and our minimum frag size is
+		 * 256.  On the other end, our minimum buffer size is 8k and
+		 * our maximum frag size is 4k, so the lower bound should
+		 * be 2.
+		 */
+
+		if(dmabuf->ossmaxfrags > 256)
+			dmabuf->ossmaxfrags = 256;
+		else if (dmabuf->ossmaxfrags < 2)
+			dmabuf->ossmaxfrags = 2;
+
 		val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
-		if (val < 16384)
-			val = 16384;
-		if (val > 65536)
-			val = 65536;
-		dmabuf->ossmaxfrags = val/dmabuf->ossfragsize;
-		if(dmabuf->ossmaxfrags<4)
-			dmabuf->ossfragsize = val/4;
+		while (val < 8192) {
+		    val <<= 1;
+		    dmabuf->ossmaxfrags <<= 1;
+		}
+		while (val > 65536) {
+		    val >>= 1;
+		    dmabuf->ossmaxfrags >>= 1;
+		}
 		dmabuf->ready = 0;
 #ifdef DEBUG
 		printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
@@ -1853,13 +1983,13 @@
 		i810_update_ptr(state);
 		abinfo.fragsize = dmabuf->userfragsize;
 		abinfo.fragstotal = dmabuf->userfrags;
-		if(dmabuf->mapped)
-			abinfo.bytes = dmabuf->count;
-		else
-			abinfo.bytes = dmabuf->dmasize - dmabuf->fragsize - dmabuf->count;
+		if (dmabuf->mapped)
+ 			abinfo.bytes = dmabuf->dmasize;
+  		else
+ 			abinfo.bytes = i810_get_free_write_space(state);
 		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n", abinfo.bytes,
 			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
 #endif
@@ -1871,17 +2001,17 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		val = i810_get_free_write_space(state);
 		cinfo.bytes = dmabuf->total_bytes;
 		cinfo.ptr = dmabuf->hwptr;
-		cinfo.blocks = (dmabuf->dmasize - dmabuf->count)/dmabuf->userfragsize;
-		if (dmabuf->mapped) {
-			dmabuf->count = (dmabuf->dmasize - 
-					 (dmabuf->count & (dmabuf->userfragsize-1)));
+		cinfo.blocks = val/dmabuf->userfragsize;
+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
+			dmabuf->count += val;
+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
 			__i810_update_lvi(state, 0);
 		}
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
 			cinfo.blocks, cinfo.ptr, dmabuf->count);
 #endif
@@ -1893,13 +2023,12 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		abinfo.bytes = i810_get_available_read_data(state);
 		abinfo.fragsize = dmabuf->userfragsize;
 		abinfo.fragstotal = dmabuf->userfrags;
-		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
 		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n", abinfo.bytes,
 			abinfo.fragsize, abinfo.fragments, abinfo.fragstotal);
 #endif
@@ -1911,16 +2040,17 @@
 		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
 			return val;
 		spin_lock_irqsave(&state->card->lock, flags);
-		i810_update_ptr(state);
+		val = i810_get_available_read_data(state);
 		cinfo.bytes = dmabuf->total_bytes;
-		cinfo.blocks = dmabuf->count/dmabuf->userfragsize;
+		cinfo.blocks = val/dmabuf->userfragsize;
 		cinfo.ptr = dmabuf->hwptr;
-		if (dmabuf->mapped) {
-			dmabuf->count &= (dmabuf->userfragsize-1);
+		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
+			dmabuf->count -= val;
+			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
 			__i810_update_lvi(state, 1);
 		}
 		spin_unlock_irqrestore(&state->card->lock, flags);
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
 			cinfo.blocks, cinfo.ptr, dmabuf->count);
 #endif
@@ -1950,7 +2080,7 @@
 	case SNDCTL_DSP_SETTRIGGER:
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
-#ifdef DEBUG
+#if defined(DEBUG) || defined(DEBUG_MMAP)
 		printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
 #endif
 		if( !(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
@@ -1960,7 +2090,7 @@
 			stop_dac(state);
 		}
 		dmabuf->trigger = val;
-		if(val & PCM_ENABLE_OUTPUT) {
+		if(val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
 			if (!dmabuf->write_channel) {
 				dmabuf->ready = 0;
 				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
@@ -1970,13 +2100,18 @@
 			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
 				return ret;
 			if (dmabuf->mapped) {
-				dmabuf->count = dmabuf->dmasize;
-				i810_update_lvi(state,0);
-			}
-			if (!dmabuf->enable && dmabuf->count > dmabuf->userfragsize)
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->count = 0;
+				dmabuf->swptr = dmabuf->hwptr;
+				dmabuf->count = i810_get_free_write_space(state);
+				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
+				__i810_update_lvi(state, 0);
+				spin_unlock_irqrestore(&state->card->lock, flags);
+			} else
 				start_dac(state);
 		}
-		if(val & PCM_ENABLE_INPUT) {
+		if(val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
 			if (!dmabuf->read_channel) {
 				dmabuf->ready = 0;
 				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
@@ -1986,12 +2121,14 @@
 			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
 				return ret;
 			if (dmabuf->mapped) {
+				spin_lock_irqsave(&state->card->lock, flags);
+				i810_update_ptr(state);
+				dmabuf->swptr = dmabuf->hwptr;
 				dmabuf->count = 0;
-				i810_update_lvi(state,1);
+				spin_unlock_irqrestore(&state->card->lock, flags);
 			}
-			if (!dmabuf->enable && dmabuf->count <
-			    (dmabuf->dmasize - dmabuf->userfragsize))
-				start_adc(state);
+			i810_update_lvi(state, 1);
+			start_adc(state);
 		}
 		return 0;
 
@@ -2195,7 +2332,19 @@
 
 	/* find an avaiable virtual channel (instance of /dev/dsp) */
 	while (card != NULL) {
-		for (i = 0; i < NR_HW_CH; i++) {
+		/*
+		 * If we are initializing and then fail, card could go
+		 * away unuexpectedly while we are in the for() loop.
+		 * So, check for card on each iteration before we check
+		 * for card->initializing to avoid a possible oops.
+		 * This usually only matters for times when the driver is
+		 * autoloaded by kmod.
+		 */
+		for (i = 0; i < 50 && card && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_HW_CH && card && !card->initializing; i++) {
 			if (card->states[i] == NULL) {
 				state = card->states[i] = (struct i810_state *)
 					kmalloc(sizeof(struct i810_state), GFP_KERNEL);
@@ -2229,8 +2378,8 @@
 			card->states[i] = NULL;;
 			return -EBUSY;
 		}
-		i810_set_adc_rate(state, 8000);
 		dmabuf->trigger |= PCM_ENABLE_INPUT;
+		i810_set_adc_rate(state, 8000);
 	}
 	if(file->f_mode & FMODE_WRITE) {
 		if((dmabuf->write_channel = card->alloc_pcm_channel(card)) == NULL) {
@@ -2241,13 +2390,13 @@
 		/* Initialize to 8kHz?  What if we don't support 8kHz? */
 		/*  Let's change this to check for S/PDIF stuff */
 	
+		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
 		if ( spdif_locked ) {
 			i810_set_dac_rate(state, spdif_locked);
 			i810_set_spdif_output(state, AC97_EA_SPSA_3_4, spdif_locked);
 		} else {
 			i810_set_dac_rate(state, 8000);
 		}
-		dmabuf->trigger |= PCM_ENABLE_OUTPUT;
 	}
 		
 	/* set default sample format. According to OSS Programmer's Guide  /dev/dsp
@@ -2274,11 +2423,10 @@
 	lock_kernel();
 
 	/* stop DMA state machine and free DMA buffers/channels */
-	if(dmabuf->enable & DAC_RUNNING ||
-	   (dmabuf->count && (dmabuf->trigger & PCM_ENABLE_OUTPUT))) {
-		drain_dac(state,0);
+	if(dmabuf->trigger & PCM_ENABLE_OUTPUT) {
+		drain_dac(state, 0);
 	}
-	if(dmabuf->enable & ADC_RUNNING) {
+	if(dmabuf->trigger & PCM_ENABLE_INPUT) {
 		stop_adc(state);
 	}
 	spin_lock_irqsave(&card->lock, flags);
@@ -2344,13 +2492,26 @@
 	int minor = MINOR(inode->i_rdev);
 	struct i810_card *card = devs;
 
-	for (card = devs; card != NULL; card = card->next)
-		for (i = 0; i < NR_AC97; i++)
+	for (card = devs; card != NULL; card = card->next) {
+		/*
+		 * If we are initializing and then fail, card could go
+		 * away unuexpectedly while we are in the for() loop.
+		 * So, check for card on each iteration before we check
+		 * for card->initializing to avoid a possible oops.
+		 * This usually only matters for times when the driver is
+		 * autoloaded by kmod.
+		 */
+		for (i = 0; i < 50 && card && card->initializing; i++) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/20);
+		}
+		for (i = 0; i < NR_AC97 && card && !card->initializing; i++)
 			if (card->ac97_codec[i] != NULL &&
 			    card->ac97_codec[i]->dev_mixer == minor) {
 				file->private_data = card->ac97_codec[i];
 				return 0;
 			}
+	}
 	return -ENODEV;
 }
 
@@ -2696,6 +2857,7 @@
 	}
 	memset(card, 0, sizeof(*card));
 
+	card->initializing = 1;
 	card->iobase = pci_resource_start (pci_dev, 1);
 	card->ac97base = pci_resource_start (pci_dev, 0);
 	card->pci_dev = pci_dev;
@@ -2752,7 +2914,8 @@
 	}
 	pci_set_drvdata(pci_dev, card);
 
-	if(clocking == 48000) {
+	if(clocking == 0) {
+		clocking = 48000;
 		i810_configure_clocking();
 	}
 
@@ -2771,11 +2934,11 @@
 		kfree(card);
 		return -ENODEV;
 	}
-
+ 	card->initializing = 0;
 	return 0;
 }
 
-static void __exit i810_remove(struct pci_dev *pci_dev)
+static void __devexit i810_remove(struct pci_dev *pci_dev)
 {
 	int i;
 	struct i810_card *card = pci_get_drvdata(pci_dev);
@@ -2789,6 +2952,7 @@
 		if (card->ac97_codec[i] != NULL) {
 			unregister_sound_mixer(card->ac97_codec[i]->dev_mixer);
 			kfree (card->ac97_codec[i]);
+			card->ac97_codec[i] = NULL;
 		}
 	unregister_sound_dsp(card->dev_audio);
 	kfree(card);
@@ -2934,7 +3098,7 @@
 	name:		I810_MODULE_NAME,
 	id_table:	i810_pci_tbl,
 	probe:		i810_probe,
-	remove:		i810_remove,
+	remove:		__devexit_p(i810_remove),
 #ifdef CONFIG_PM
 	suspend:	i810_pm_suspend,
 	resume:		i810_pm_resume,
@@ -2957,14 +3121,11 @@
 	if(ftsodell != 0) {
 		printk("i810_audio: ftsodell is now a deprecated option.\n");
 	}
-	if(clocking == 48000) {
-		i810_configure_clocking();
-	}
 	if(spdif_locked > 0 ) {
 		if(spdif_locked == 32000 || spdif_locked == 44100 || spdif_locked == 48000) {
 			printk("i810_audio: Enabling S/PDIF at sample rate %dHz.\n", spdif_locked);
 		} else {
-			printk("i810_audio: S/PDIF can only be locked to 32000, 441000, or 48000Hz.\n");
+			printk("i810_audio: S/PDIF can only be locked to 32000, 44100, or 48000Hz.\n");
 			spdif_locked = 0;
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- linux.orig/drivers/sound/mpu401.c	Sun Sep 30 19:26:08 2001
+++ linux/drivers/sound/mpu401.c	Wed Feb 13 17:37:40 2002
@@ -1227,7 +1227,7 @@
 	return ok;
 }
 
-void __exit unload_mpu401(struct address_info *hw_config)
+void unload_mpu401(struct address_info *hw_config)
 {
 	void *p;
 	int n=hw_config->slots[1];
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/mpu401.h linux/drivers/sound/mpu401.h
--- linux.orig/drivers/sound/mpu401.h	Wed Sep 27 20:53:56 2000
+++ linux/drivers/sound/mpu401.h	Mon Feb  4 17:38:23 2002
@@ -1,9 +1,3 @@
-/*
- *	uart401.h 
- *
- * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
- *
- */
 
 /*	From uart401.c */
 int probe_uart401 (struct address_info *hw_config, struct module *owner);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/opl3.h linux/drivers/sound/opl3.h
--- linux.orig/drivers/sound/opl3.h	Fri Aug 11 15:26:43 2000
+++ linux/drivers/sound/opl3.h	Mon Feb  4 17:38:23 2002
@@ -1,9 +1,3 @@
-/*
- *	opl3.h
- *
- * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
- *
- */
 
 int opl3_detect (int ioaddr, int *osp);
 int opl3_init(int ioaddr, int *osp, struct module *owner);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/opl3sa2.c linux/drivers/sound/opl3sa2.c
--- linux.orig/drivers/sound/opl3sa2.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/opl3sa2.c	Wed Feb  6 20:47:54 2002
@@ -55,6 +55,7 @@
  *                         sb_card.c and awe_wave.c. (Dec 12, 2000)
  * Scott Murray            Some small cleanups to the init code output.
  *                         (Jan 7, 2001)
+ * Zwane Mwaikambo	   Added PM support. (Dec 4 2001)
  *
  */
 
@@ -62,13 +63,17 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/isapnp.h>
-
+#include <linux/pm.h>
+#include <linux/delay.h>
 #include "sound_config.h"
 
 #include "ad1848.h"
 #include "mpu401.h"
 
+#define OPL3SA2_MODULE_NAME	"opl3sa2"
+
 /* Useful control port indexes: */
+#define OPL3SA2_PM	     0x01
 #define OPL3SA2_SYS_CTRL     0x02
 #define OPL3SA2_IRQ_CONFIG   0x03
 #define OPL3SA2_DMA_CONFIG   0x06
@@ -86,6 +91,11 @@
 #define DEFAULT_MIC    50
 #define DEFAULT_TIMBRE 0
 
+/* Power saving modes */
+#define OPL3SA2_PM_MODE1	0x05
+#define OPL3SA2_PM_MODE2	0x04
+#define OPL3SA2_PM_MODE3	0x03
+
 /* For checking against what the card returns: */
 #define VERSION_UNKNOWN 0
 #define VERSION_YMF711  1
@@ -121,6 +131,10 @@
 typedef struct opl3sa2_mixerdata_tag {
 	unsigned short cfg_port;
 	unsigned short padding;
+	unsigned char  pm_reg;
+	unsigned int   in_suspend;
+	struct pm_dev  *pmdev;
+	unsigned int   card;
 	unsigned int   volume_l;
 	unsigned int   volume_r;
 	unsigned int   mic;
@@ -328,6 +342,20 @@
 }
 
 
+static void opl3sa2_mixer_restore(opl3sa2_mixerdata* devc, int card)
+{
+	if (devc) {
+		opl3sa2_set_volume(devc, devc->volume_l, devc->volume_r);
+		opl3sa2_set_mic(devc, devc->mic);
+
+		if (chipset[card] == CHIPSET_OPL3SA3) {
+			opl3sa3_set_bass(devc, devc->bass_l, devc->bass_r);
+			opl3sa3_set_treble(devc, devc->treble_l, devc->treble_r);
+		}
+	}
+}
+
+
 static inline void arg_to_vol_mono(unsigned int vol, int* value)
 {
 	int left;
@@ -608,9 +636,9 @@
 	char tag;
 
 	/*
-	 * Verify that the I/O port range is free.
+	 * Try and allocate our I/O port range.
 	 */
-	if(check_region(hw_config->io_base, 2)) {
+	if(!request_region(hw_config->io_base, 2, OPL3SA2_MODULE_NAME)) {
 		printk(KERN_ERR "opl3sa2: Control I/O port %#x not free\n",
 		       hw_config->io_base);
 		return 0;
@@ -699,7 +727,6 @@
 
 static void __init attach_opl3sa2(struct address_info* hw_config, int card)
 {
-   	request_region(hw_config->io_base, 2, chipset_name[card]);
 
 	/* Initialize IRQ configuration to IRQ-B: -, IRQ-A: WSS+MPU+OPL3 */
 	opl3sa2_write(hw_config->io_base, OPL3SA2_IRQ_CONFIG, 0x0d);
@@ -892,6 +919,81 @@
 
 /* End of component functions */
 
+/* Power Management support functions */
+static int opl3sa2_suspend(struct pm_dev *pdev, unsigned char pm_mode)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	save_flags(flags);
+	cli();
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+
+	switch (pm_mode) {
+	case 1:
+		pm_mode = OPL3SA2_PM_MODE1;
+		break;
+	case 2:
+		pm_mode = OPL3SA2_PM_MODE2;
+		break;
+	case 3:
+		pm_mode = OPL3SA2_PM_MODE3;
+		break;
+	default:
+		/* we don't know howto handle this... */
+		restore_flags(flags);
+		return -EBUSY;
+	}
+
+	p->in_suspend = 1;
+	/* its supposed to automute before suspending, so we wont bother */
+	opl3sa2_read(p->cfg_port, OPL3SA2_PM, &p->pm_reg);
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->pm_reg | pm_mode);
+	/* wait a while for the clock oscillator to stabilise */
+	mdelay(10);
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_resume(struct pm_dev *pdev)
+{
+	unsigned long flags;
+	opl3sa2_mixerdata *p;
+
+	if (!pdev)
+		return -EINVAL;
+
+	p = (opl3sa2_mixerdata *) pdev->data;
+	save_flags(flags);
+	cli();
+
+	/* I don't think this is necessary */
+	opl3sa2_write(p->cfg_port, OPL3SA2_PM, p->pm_reg);
+	opl3sa2_mixer_restore(p, p->card);
+	p->in_suspend = 0;
+
+	restore_flags(flags);
+	return 0;
+}
+
+static int opl3sa2_pm_callback(struct pm_dev *pdev, pm_request_t rqst, void *data)
+{
+	unsigned char mode = (unsigned  char)data;
+
+	switch (rqst) {
+		case PM_SUSPEND:
+			return opl3sa2_suspend(pdev, mode);
+
+		case PM_RESUME:
+			return opl3sa2_resume(pdev);
+	}
+	return 0;
+}
 
 /*
  * Install OPL3-SA2 based card(s).
@@ -989,6 +1091,12 @@
 		attach_opl3sa2_mss(&cfg_mss[card]);
 		attach_opl3sa2_mixer(&cfg[card], card);
 
+		opl3sa2_data[card].card = card;
+		/* register our power management capabilities */
+		opl3sa2_data[card].pmdev = pm_register(PM_ISA_DEV, card, opl3sa2_pm_callback);
+		if (opl3sa2_data[card].pmdev)
+			opl3sa2_data[card].pmdev->data = &opl3sa2_data[card];
+
 		/*
 		 * Set the Yamaha 3D enhancement mode (aka Ymersion) if asked to and
 		 * it's supported.
@@ -1033,6 +1141,9 @@
 	int card;
 
 	for(card = 0; card < opl3sa2_cards_num; card++) {
+		if (opl3sa2_data[card].pmdev)
+			pm_unregister(opl3sa2_data[card].pmdev);
+
 	        if(cfg_mpu[card].slots[1] != -1) {
 			unload_opl3sa2_mpu(&cfg_mpu[card]);
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/pas2.h linux/drivers/sound/pas2.h
--- linux.orig/drivers/sound/pas2.h	Tue Mar  7 21:40:24 2000
+++ linux/drivers/sound/pas2.h	Mon Feb  4 17:38:23 2002
@@ -1,9 +1,3 @@
-/*
- * pas2.h
- *
- * Copyright:	Christoph Hellwig <chhellwig@gmx.net>
- *
- */
 
 /*	From pas_card.c	*/
 int pas_set_intr(int mask);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/pss.c linux/drivers/sound/pss.c
--- linux.orig/drivers/sound/pss.c	Sun Sep 30 19:26:08 2001
+++ linux/drivers/sound/pss.c	Mon Feb  4 17:38:23 2002
@@ -48,7 +48,7 @@
  *          Added module parameter pss_firmware to allow the user to tell 
  *          the driver where the fireware file is located.  The default 
  *          setting is the previous hardcoded setting "/etc/sound/pss_synth".
- * 00-03-03: Christoph Hellwig <chhellwig@gmx.net>
+ * 00-03-03: Christoph Hellwig <chhellwig@infradead.org>
  *	    Adapted to module_init/module_exit
  * 11-10-2000: Bartlomiej Zolnierkiewicz <bkz@linux-ide.org>
  *	    Added __init to probe_pss(), attach_pss() and probe_pss_mpu()
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- linux.orig/drivers/sound/sb_audio.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/sb_audio.c	Mon Jan 14 18:53:53 2002
@@ -602,8 +602,8 @@
 
 	if (speed > 0)
 	{
-		if (speed < 5000)	/* which of these */
-			speed = 4000;	/* is correct ??? */
+		if (speed < 5000)
+			speed = 5000;
 
 		if (speed > max_speed)
 			speed = max_speed;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- linux.orig/drivers/sound/sb_card.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/sound/sb_card.c	Wed Feb  6 21:34:56 2002
@@ -378,11 +378,6 @@
 		0,0,0,0,
 		0,1,1,-1},
 	{"Sound Blaster AWE 32",
-		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0047),
-		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
-		0,0,0,0,
-		0,1,1,-1},
-	{"Sound Blaster AWE 32",
 		ISAPNP_VENDOR('C','T','L'), ISAPNP_DEVICE(0x0048), 
 		ISAPNP_VENDOR('C','T','L'), ISAPNP_FUNCTION(0x0031),
 		0,0,0,0,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/sonicvibes.c linux/drivers/sound/sonicvibes.c
--- linux.orig/drivers/sound/sonicvibes.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/sonicvibes.c	Mon Jan 14 18:53:53 2002
@@ -2226,12 +2226,8 @@
 				break;
 			if (signal_pending(current))
 				break;
-			if (file->f_flags & O_NONBLOCK) {
-				remove_wait_queue(&s->midi.owait, &wait);
-				set_current_state(TASK_RUNNING);
-				unlock_kernel();
-				return -EBUSY;
-			}
+			if (file->f_flags & O_NONBLOCK)
+				break;
 			tmo = (count * HZ) / 3100;
 			if (!schedule_timeout(tmo ? : 1) && tmo)
 				printk(KERN_DEBUG "sv: midi timed out??\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/trident.c linux/drivers/sound/trident.c
--- linux.orig/drivers/sound/trident.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/sound/trident.c	Wed Feb 13 17:37:40 2002
@@ -187,6 +187,7 @@
 #define TRIDENT_STATE_MAGIC	0x63657373 /* "cess" */
 
 #define TRIDENT_DMA_MASK	0x3fffffff /* DMA buffer mask for pci_alloc_consist */
+#define ALI_DMA_MASK		0xffffffff /* ALI Tridents lack the 30-bit limitation */
 
 #define NR_HW_CH		32
 
@@ -3948,13 +3949,20 @@
 	u16 temp;
 	struct pci_dev *pci_dev_m1533 = NULL;
 	int rc = -ENODEV;
+	u64 dma_mask;
 
 	if (pci_enable_device(pci_dev))
 		goto out;
 
-	if (pci_set_dma_mask(pci_dev, TRIDENT_DMA_MASK)) {
+	if (pci_dev->device == PCI_DEVICE_ID_ALI_5451)
+		dma_mask = ALI_DMA_MASK;
+	else
+		dma_mask = TRIDENT_DMA_MASK;
+	if (pci_set_dma_mask(pci_dev, dma_mask)) {
 		printk(KERN_ERR "trident: architecture does not support"
-		       " 30bit PCI busmaster DMA\n");
+		       " %s PCI busmaster DMA\n",
+		       pci_dev->device == PCI_DEVICE_ID_ALI_5451 ?
+		       "32-bit" : "30-bit");
 		goto out;
 	}
 	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &revision);
@@ -4141,7 +4149,7 @@
 	goto out;
 }
 
-static void __exit trident_remove(struct pci_dev *pci_dev)
+static void __devexit trident_remove(struct pci_dev *pci_dev)
 {
 	int i;
 	struct trident_card *card = pci_get_drvdata(pci_dev);
@@ -4194,7 +4202,7 @@
 	name:		TRIDENT_MODULE_NAME,
 	id_table:	trident_pci_tbl,
 	probe:		trident_probe,
-	remove:		trident_remove,
+	remove:		__devexit_p(trident_remove),
 	suspend:	trident_suspend,
 	resume:		trident_resume
 };
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/via82cxxx_audio.c linux/drivers/sound/via82cxxx_audio.c
--- linux.orig/drivers/sound/via82cxxx_audio.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/sound/via82cxxx_audio.c	Wed Feb 13 16:43:01 2002
@@ -311,7 +311,7 @@
  */
 
 static int via_init_one (struct pci_dev *dev, const struct pci_device_id *id);
-static void via_remove_one (struct pci_dev *pdev);
+static void __devexit via_remove_one (struct pci_dev *pdev);
 
 static ssize_t via_dsp_read(struct file *file, char *buffer, size_t count, loff_t *ppos);
 static ssize_t via_dsp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos);
@@ -365,7 +365,7 @@
 	name:		VIA_MODULE_NAME,
 	id_table:	via_pci_tbl,
 	probe:		via_init_one,
-	remove:		via_remove_one,
+	remove:		__devexit_p(via_remove_one),
 };
 
 
@@ -3271,7 +3271,7 @@
 }
 
 
-static void __exit via_remove_one (struct pci_dev *pdev)
+static void __devexit via_remove_one (struct pci_dev *pdev)
 {
 	struct via_info *card;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/sound/ymfpci.c linux/drivers/sound/ymfpci.c
--- linux.orig/drivers/sound/ymfpci.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/sound/ymfpci.c	Wed Feb  6 20:47:54 2002
@@ -46,6 +46,7 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/delay.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
 #include <linux/poll.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/Config.in linux/drivers/usb/Config.in
--- linux.orig/drivers/usb/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/Config.in	Wed Jan  9 15:47:12 2002
@@ -32,6 +32,9 @@
 comment 'USB Device Class drivers'
 dep_tristate '  USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND
 dep_tristate '  USB Bluetooth support (EXPERIMENTAL)' CONFIG_USB_BLUETOOTH $CONFIG_USB $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_SCSI" = "n" ]; then
+   comment '  SCSI support is needed for USB Storage'
+fi
 dep_tristate '  USB Mass Storage support' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI
    dep_mbool '    USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG $CONFIG_USB_STORAGE
    dep_mbool '    Datafab MDCFE-B Compact Flash Reader support' CONFIG_USB_STORAGE_DATAFAB $CONFIG_USB_STORAGE $CONFIG_EXPERIMENTAL
@@ -72,6 +75,8 @@
    dep_tristate '  USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV
    dep_tristate '  USB Philips Cameras' CONFIG_USB_PWC $CONFIG_USB $CONFIG_VIDEO_DEV
    dep_tristate '  USB SE401 Camera support' CONFIG_USB_SE401 $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB STV680 (Pencam) Camera support' CONFIG_USB_STV680 $CONFIG_USB $CONFIG_VIDEO_DEV
+   dep_tristate '  USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
    dep_tristate '  D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
    dep_tristate '  DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
 fi
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/Makefile linux/drivers/usb/Makefile
--- linux.orig/drivers/usb/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/Makefile	Wed Jan  9 15:47:12 2002
@@ -10,19 +10,13 @@
 
 # Objects that export symbols.
 
-export-objs		:= usb.o
+export-objs		:= usb.o ov511.o pwc-uncompress.o
 
 # Multipart objects.
 
-list-multi		:= usbcore.o hid.o
+list-multi		:= usbcore.o hid.o pwc.o
 usbcore-objs		:= usb.o usb-debug.o hub.o
 hid-objs		:= hid-core.o hid-input.o
-
-ifneq ($(CONFIG_USB_PWC),n)
-	export-objs		+= pwc-uncompress.o
-	list-multi		+= pwc.o
-endif
-
 pwc-objs		:= pwc-if.o pwc-misc.o pwc-ctrl.o pwc-uncompress.o
 
 
@@ -65,8 +59,10 @@
 obj-$(CONFIG_USB_MDC800)	+= mdc800.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 obj-$(CONFIG_USB_DABUSB)	+= dabusb.o
+obj-$(CONFIG_USB_VICAM)		+= vicam.o
 obj-$(CONFIG_USB_OV511)		+= ov511.o
 obj-$(CONFIG_USB_SE401)		+= se401.o
+obj-$(CONFIG_USB_STV680)	+= stv680.o
 obj-$(CONFIG_USB_PEGASUS)	+= pegasus.o
 obj-$(CONFIG_USB_CATC)		+= catc.o
 obj-$(CONFIG_USB_KAWETH)        += kaweth.o
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/audio.c linux/drivers/usb/audio.c
--- linux.orig/drivers/usb/audio.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/audio.c	Wed Dec 26 14:28:35 2001
@@ -3378,10 +3378,10 @@
 	if (state->nrchannels > 2)
 		printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]);
 
-		nr_logical_channels=(ftr[0]-7)/ftr[5]-1;
+	nr_logical_channels=(ftr[0]-7)/ftr[5]-1;
 
-		if (nr_logical_channels != state->nrchannels) {
-			printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels);
+	if (nr_logical_channels != state->nrchannels) {
+		printk(KERN_WARNING "usbaudio: warning: found %d of %d logical channels.\n", state->nrchannels,nr_logical_channels);
 
 		if (state->nrchannels == 1 && nr_logical_channels==0) {
 			printk(KERN_INFO "usbaudio: assuming the channel found is the master channel (got a Philips camera?). Should be fine.\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/mdc800.c linux/drivers/usb/mdc800.c
--- linux.orig/drivers/usb/mdc800.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/mdc800.c	Wed Dec 26 14:28:36 2001
@@ -30,8 +30,14 @@
  *
  * The driver supports only one camera.
  * 
- * (08/04/2001) gb
+ * Fix: mdc800 used sleep_on and slept with io_lock held.
+ * Converted sleep_on to waitqueues with schedule_timeout and made io_lock
+ * a semaphore from a spinlock.
+ * by Oliver Neukum <520047054719-0001@t-online.de>
+ * (02/12/2001)
+ * 
  * Identify version on module load.
+ * (08/04/2001) gb
  *
  * version 0.7.5
  * Fixed potential SMP races with Spinlocks.
@@ -136,6 +142,7 @@
 
 	purb_t			irq_urb;
 	wait_queue_head_t	irq_wait;
+	int                     irq_woken;
 	char*			irq_urb_buffer;
 
 	int			camera_busy;          // is camera busy ?
@@ -145,11 +152,13 @@
 	purb_t   		write_urb;
 	char*			write_urb_buffer;
 	wait_queue_head_t	write_wait;
+	int                     written;
 
 
 	purb_t	   		download_urb;
 	char*			download_urb_buffer;
 	wait_queue_head_t	download_wait;
+	int                     downloaded;
 	int			download_left;		// Bytes left to download ?
 
 
@@ -159,7 +168,7 @@
 	int			out_count;	// Bytes in the buffer
 
 	int			open;		// Camera device open ?
-	spinlock_t		io_lock;	// IO -lock	
+	struct semaphore	io_lock;	// IO -lock
 
 	char 			in [8];		// Command Input Buffer
 	int  			in_count;
@@ -284,6 +293,7 @@
 	if (wake_up)
 	{
 		mdc800->camera_request_ready=0;
+		mdc800->irq_woken=1;
 		wake_up_interruptible (&mdc800->irq_wait);
 	}
 }
@@ -300,9 +310,19 @@
  */
 static int mdc800_usb_waitForIRQ (int mode, int msec)
 {
+        DECLARE_WAITQUEUE(wait, current);
+
 	mdc800->camera_request_ready=1+mode;
 
-	interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000);
+	add_wait_queue(&mdc800->irq_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (!mdc800->irq_woken)
+	{
+	        schedule_timeout (msec*HZ/1000);
+	}
+        remove_wait_queue(&mdc800->irq_wait, &wait);
+	set_current_state(TASK_RUNNING);
+	mdc800->irq_woken = 0;
 
 	if (mdc800->camera_request_ready>0)
 	{
@@ -337,6 +357,7 @@
 	{	
 		mdc800->state=READY;
 	}
+	mdc800->written = 1;
 	wake_up_interruptible (&mdc800->write_wait);
 }
 
@@ -364,6 +385,7 @@
 	{
 		err ("request bytes fails (status:%i)", urb->status);
 	}
+	mdc800->downloaded = 1;
 	wake_up_interruptible (&mdc800->download_wait);
 }
 
@@ -445,7 +467,7 @@
 
 	info ("Found Mustek MDC800 on USB.");
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 
 	mdc800->dev=dev;
 	mdc800->open=0;
@@ -484,7 +506,7 @@
 
 	mdc800->state=READY;
 
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	
 	return mdc800;
 }
@@ -558,7 +580,7 @@
 	int retval=0;
 	int errn=0;
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	
 	if (mdc800->state == NOT_CONNECTED)
 	{
@@ -594,7 +616,7 @@
 	dbg ("Mustek MDC800 device opened.");
 
 error_out:
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return errn;
 }
 
@@ -607,10 +629,9 @@
 	int retval=0;
 	dbg ("Mustek MDC800 device closed.");
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->open && (mdc800->state != NOT_CONNECTED))
 	{
-		spin_unlock(&mdc800->io_lock);
 		usb_unlink_urb (mdc800->irq_urb);
 		usb_unlink_urb (mdc800->write_urb);
 		usb_unlink_urb (mdc800->download_urb);
@@ -618,11 +639,10 @@
 	}
 	else
 	{
-		spin_unlock (&mdc800->io_lock);
 		retval=-EIO;
 	}
 
-
+        up(&mdc800->io_lock);
 	return retval;
 }
 
@@ -634,22 +654,23 @@
 {
 	int   left=len, sts=len; /* single transfer size */
 	char* ptr=buf;
+	DECLARE_WAITQUEUE(wait, current);
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->state == NOT_CONNECTED)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (mdc800->state == WORKING)
 	{
 		warn ("Illegal State \"working\" reached during read ?!");
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (!mdc800->open)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 
@@ -657,7 +678,7 @@
 	{
 		if (signal_pending (current)) 
 		{
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -676,21 +697,29 @@
 				if (usb_submit_urb (mdc800->download_urb))
 				{
 					err ("Can't submit download urb (status=%i)",mdc800->download_urb->status);
-					spin_unlock (&mdc800->io_lock);
+					up (&mdc800->io_lock);
 					return len-left;
 				}
-				interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000);
+				add_wait_queue(&mdc800->download_wait, &wait);
+				set_current_state(TASK_INTERRUPTIBLE);
+				if (!mdc800->downloaded)
+				{
+				        schedule_timeout (TO_DOWNLOAD_GET_READY*HZ/1000);
+				}
+                                set_current_state(TASK_RUNNING);
+				remove_wait_queue(&mdc800->download_wait, &wait);
+				mdc800->downloaded = 0;
 				if (mdc800->download_urb->status != 0)
 				{
 					err ("request download-bytes fails (status=%i)",mdc800->download_urb->status);
-					spin_unlock (&mdc800->io_lock);
+					up (&mdc800->io_lock);
 					return len-left;
 				}
 			}
 			else
 			{
 				/* No more bytes -> that's an error*/
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 		}
@@ -704,7 +733,7 @@
 		}
 	}
 
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return len-left;
 }
 
@@ -718,16 +747,17 @@
 static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos)
 {
 	int i=0;
+	DECLARE_WAITQUEUE(wait, current);
 
-	spin_lock (&mdc800->io_lock);
+	down (&mdc800->io_lock);
 	if (mdc800->state != READY)
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 	if (!mdc800->open )
 	{
-		spin_unlock (&mdc800->io_lock);
+		up (&mdc800->io_lock);
 		return -EBUSY;
 	}
 
@@ -735,7 +765,7 @@
 	{
 		if (signal_pending (current)) 
 		{
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EINTR;
 		}
 
@@ -757,7 +787,7 @@
 		else
 		{
 			err ("Command is to long !\n");
-			spin_unlock (&mdc800->io_lock);
+			up (&mdc800->io_lock);
 			return -EIO;
 		}
 
@@ -769,7 +799,7 @@
 			if (mdc800_usb_waitForIRQ (0,TO_GET_READY))
 			{
 				err ("Camera didn't get ready.\n");
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -781,14 +811,22 @@
 			if (usb_submit_urb (mdc800->write_urb))
 			{
 				err ("submitting write urb fails (status=%i)", mdc800->write_urb->status);
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
-			interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000);
+			add_wait_queue(&mdc800->write_wait, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+			if (!mdc800->written)
+			{
+			        schedule_timeout (TO_WRITE_GET_READY*HZ/1000);
+			}
+                        set_current_state(TASK_RUNNING);
+			remove_wait_queue(&mdc800->write_wait, &wait);
+			mdc800->written = 0;
 			if (mdc800->state == WORKING)
 			{
 				usb_unlink_urb (mdc800->write_urb);
-				spin_unlock (&mdc800->io_lock);
+				up (&mdc800->io_lock);
 				return -EIO;
 			}
 
@@ -800,7 +838,7 @@
 					{
 						err ("call 0x07 before 0x05,0x3e");
 						mdc800->state=READY;
-						spin_unlock (&mdc800->io_lock);
+						up (&mdc800->io_lock);
 						return -EIO;
 					}
 					mdc800->pic_len=-1;
@@ -819,7 +857,7 @@
 						if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ))
 						{
 							err ("requesting answer from irq fails");
-							spin_unlock (&mdc800->io_lock);
+							up (&mdc800->io_lock);
 							return -EIO;
 						}
 
@@ -847,7 +885,7 @@
 						if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND))
 						{
 							err ("Command Timeout.");
-							spin_unlock (&mdc800->io_lock);
+							up (&mdc800->io_lock);
 							return -EIO;
 						}
 					}
@@ -857,7 +895,7 @@
 		}
 		i++;
 	}
-	spin_unlock (&mdc800->io_lock);
+	up (&mdc800->io_lock);
 	return i;
 }
 
@@ -916,11 +954,15 @@
 	mdc800->dev=0;
 	mdc800->open=0;
 	mdc800->state=NOT_CONNECTED;
-	spin_lock_init (&mdc800->io_lock);
+	init_MUTEX (&mdc800->io_lock);
 
 	init_waitqueue_head (&mdc800->irq_wait);
 	init_waitqueue_head (&mdc800->write_wait);
 	init_waitqueue_head (&mdc800->download_wait);
+
+	mdc800->irq_woken = 0;
+	mdc800->downloaded = 0;
+	mdc800->written = 0;
 
 	try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL));
 	try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/ov511.c linux/drivers/usb/ov511.c
--- linux.orig/drivers/usb/ov511.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/ov511.c	Mon Feb  4 19:00:37 2002
@@ -1,13 +1,16 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
  *
- * Copyright (c) 1999-2000 Mark W. McClelland
+ * Copyright (c) 1999-2001 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)
  * Snapshot code by Kevin Moore
  * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
  * Changes by Claudio Matsuoka <claudio@conectiva.com>
- * 
+ * Original SAA7111A code by Dave Perks <dperks@ibm.net>
+ * Kernel I2C interface adapted from nt1003 driver
+ *
  * Based on the Linux CPiA driver written by Peter Pregler,
  * Scott J. Bertin and Johannes Erdfelt.
  * 
@@ -30,11 +33,9 @@
  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#define __NO_VERSION__
-
 #include <linux/config.h>
-#include <linux/module.h>
 #include <linux/version.h>
+#include <linux/module.h>
 #include <linux/init.h>
 #include <linux/fs.h>
 #include <linux/vmalloc.h>
@@ -42,32 +43,51 @@
 #include <linux/proc_fs.h>
 #include <linux/ctype.h>
 #include <linux/pagemap.h>
-#include <linux/usb.h>
 #include <asm/io.h>
 #include <asm/semaphore.h>
+#include <asm/processor.h>
 #include <linux/wrapper.h>
 
+#if defined (__i386__)
+	#include <asm/cpufeature.h>
+#endif
+
 #include "ov511.h"
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.28"
-#define DRIVER_AUTHOR "Mark McClelland <mwm@i.am> & Bret Wallach & Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha <cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
+#define DRIVER_VERSION "v1.48a for Linux 2.4"
+#define EMAIL "mmcclell@bigfoot.com"
+#define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & 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"
 
 #define OV511_I2C_RETRIES 3
+#define ENABLE_Y_QUANTABLE 1
+#define ENABLE_UV_QUANTABLE 1
+
+/* Pixel count * 3 bytes for RGB */
+#define MAX_FRAME_SIZE(w, h) ((w) * (h) * 3)
+#define MAX_DATA_SIZE(w, h) (MAX_FRAME_SIZE(w, h) + sizeof(struct timeval))
 
-/* Video Size 640 x 480 x 3 bytes for RGB */
-#define MAX_FRAME_SIZE (640 * 480 * 3)
-#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval))
+/* Max size * bytes per YUV420 pixel (1.5) + one extra isoc frame for safety */
+#define MAX_RAW_DATA_SIZE(w, h) ((w) * (h) * 3 / 2 + 1024)
 
-#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384)
+#define FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
 
 /* PARAMETER VARIABLES: */
-static int autoadjust = 1;    /* CCD dynamically changes exposure, etc... */
+/* (See ov511.txt for detailed descriptions of these.) */
 
-static int video_nr = -1;
+/* Sensor automatically changes brightness */
+static int autobright = 1;
+
+/* Sensor automatically changes gain */
+static int autogain = 1;
+
+/* Sensor automatically changes exposure */
+static int autoexp = 1;
 
 /* 0=no debug messages
  * 1=init/detection/unload and other significant messages,
@@ -77,27 +97,17 @@
  * 5=highly repetitive mesgs
  * NOTE: This should be changed to 0, 1, or 2 for production kernels
  */
-static int debug = 0;
+static int debug; /* = 0 */
 
 /* Fix vertical misalignment of red and blue at 640x480 */
-static int fix_rgb_offset = 0;
+static int fix_rgb_offset; /* = 0 */
 
 /* Snapshot mode enabled flag */
-static int snapshot = 0;
-
-/* Sensor detection override (global for all attached cameras) */
-static int sensor = 0;
-
-/* Increase this if you are getting "Failed to read sensor ID..." */
-static int i2c_detect_tries = 5;
-
-/* For legal values, see the OV7610/7620 specs under register Common F,
- * upper nybble  (set to 0-F) */
-static int aperture = -1;
+static int snapshot; /* = 0 */
 
 /* Force image to be read in RGB instead of BGR. This option allow
  * programs that expect RGB data (e.g. gqcam) to work with this driver. */
-static int force_rgb = 0;
+static int force_rgb; /* = 0 */
 
 /* Number of seconds before inactive buffers are deallocated */
 static int buf_timeout = 5;
@@ -105,76 +115,219 @@
 /* Number of cameras to stream from simultaneously */
 static int cams = 1;
 
-/* Prevent apps from timing out if frame is not done in time */
-static int retry_sync = 0;
-
-/* Enable compression. This is for experimentation only; compressed images
- * still cannot be decoded yet. */
-static int compress = 0;
+/* Enable compression. Needs a fast (>300 MHz) CPU. */
+static int compress; /* = 0 */
 
 /* Display test pattern - doesn't work yet either */
-static int testpat = 0;
+static int testpat; /* = 0 */
 
-/* Setting this to 1 will make the sensor output GBR422 instead on YUV420. Only
+/* Setting this to 1 will make the sensor output GBR422 instead of YUV420. Only
  * affects RGB24 mode. */
-static int sensor_gbr = 0;
+static int sensor_gbr; /* = 0 */
 
-/* Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details. */
-static int dumppix = 0;
+/* Dump raw pixel data. */
+static int dumppix; /* = 0 */
 
-MODULE_PARM(autoadjust, "i");
-MODULE_PARM_DESC(autoadjust, "CCD dynamically changes exposure");
+/* LED policy. Only works on some OV511+ cameras. 0=off, 1=on (default), 2=auto
+ * (on when open) */
+static int led = 1;
+
+/* Set this to 1 to dump the bridge register contents after initialization */
+static int dump_bridge; /* = 0 */
+
+/* Set this to 1 to dump the sensor register contents after initialization */
+static int dump_sensor; /* = 0 */
+
+/* Temporary option for debugging "works, but no image" problem. Prints the
+ * first 12 bytes of data (potentially a packet header) in each isochronous
+ * data frame. */
+static int printph; /* = 0 */
+
+/* Compression parameters - I'm not exactly sure what these do yet */
+static int phy = 0x1f;
+static int phuv = 0x05;
+static int pvy = 0x06;
+static int pvuv = 0x06;
+static int qhy = 0x14;
+static int qhuv = 0x03;
+static int qvy = 0x04;
+static int qvuv = 0x04;
+
+/* Light frequency. Set to 50 or 60 (Hz), or zero for default settings */
+static int lightfreq; /* = 0 */
+
+/* Set this to 1 to enable banding filter by default. Compensates for
+ * alternating horizontal light/dark bands caused by (usually fluorescent)
+ * lights */
+static int bandingfilter; /* = 0 */
+
+/* Pixel clock divisor */
+static int clockdiv = -1;
+
+/* Isoc packet size */
+static int packetsize = -1;
+
+/* Frame drop register (16h) */
+static int framedrop = -1;
+
+/* Allows picture settings (brightness, hue, etc...) to take effect immediately,
+ * even in the middle of a frame. This reduces the time to change settings, but
+ * can ruin frames during the change. Only affects OmniVision sensors. */
+static int fastset; /* = 0 */
+
+/* Forces the palette to a specific value. If an application requests a
+ * different palette, it will be rejected. */
+static int force_palette; /* = 0 */
+
+/* Set tuner type, if not autodetected */
+static int tuner = -1;
+
+/* Allows proper exposure of objects that are illuminated from behind. Only
+ * affects OmniVision sensors. */
+static int backlight; /* = 0 */
+
+/* If you change this, you must also change the MODULE_PARM definition */
+#define OV511_MAX_UNIT_VIDEO 16
+
+/* Allows specified minor numbers to be forced. They will be assigned in the
+ * order that devices are detected. Note that you cannot specify 0 as a minor
+ * number. If you do not specify any, the next available one will be used. This
+ * requires kernel 2.4.5 or later. */
+static int unit_video[OV511_MAX_UNIT_VIDEO];
+
+/* Remove zero-padding from uncompressed incoming data. This will compensate for
+ * the blocks of corruption that appear when the camera cannot keep up with the
+ * speed of the USB bus (eg. at low frame resolutions) */
+static int remove_zeros; /* = 0 */
+
+MODULE_PARM(autobright, "i");
+MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
+MODULE_PARM(autogain, "i");
+MODULE_PARM_DESC(autogain, "Sensor automatically changes gain");
+MODULE_PARM(autoexp, "i");
+MODULE_PARM_DESC(autoexp, "Sensor automatically changes exposure");
 MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug level: 0=none, 1=init/detection, 2=warning, 3=config/control, 4=function call, 5=max");
+MODULE_PARM_DESC(debug,
+  "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=max");
 MODULE_PARM(fix_rgb_offset, "i");
-MODULE_PARM_DESC(fix_rgb_offset, "Fix vertical misalignment of red and blue at 640x480");
+MODULE_PARM_DESC(fix_rgb_offset,
+  "Fix vertical misalignment of red and blue at 640x480");
 MODULE_PARM(snapshot, "i");
 MODULE_PARM_DESC(snapshot, "Enable snapshot mode");
-MODULE_PARM(sensor, "i");
-MODULE_PARM_DESC(sensor, "Override sensor detection");
-MODULE_PARM(i2c_detect_tries, "i");
-MODULE_PARM_DESC(i2c_detect_tries, "Number of tries to detect sensor");
-MODULE_PARM(aperture, "i");
-MODULE_PARM_DESC(aperture, "Read the OV7610/7620 specs");
 MODULE_PARM(force_rgb, "i");
 MODULE_PARM_DESC(force_rgb, "Read RGB instead of BGR");
 MODULE_PARM(buf_timeout, "i");
 MODULE_PARM_DESC(buf_timeout, "Number of seconds before buffer deallocation");
 MODULE_PARM(cams, "i");
 MODULE_PARM_DESC(cams, "Number of simultaneous cameras");
-MODULE_PARM(retry_sync, "i");
-MODULE_PARM_DESC(retry_sync, "Prevent apps from timing out");
 MODULE_PARM(compress, "i");
-MODULE_PARM_DESC(compress, "Turn on compression (not functional yet)");
+MODULE_PARM_DESC(compress, "Turn on compression (not reliable yet)");
 MODULE_PARM(testpat, "i");
-MODULE_PARM_DESC(testpat, "Replace image with vertical bar testpattern (only partially working)");
-MODULE_PARM(sensor_gbr, "i");
-MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+MODULE_PARM_DESC(testpat,
+  "Replace image with vertical bar testpattern (only partially working)");
+
+// Temporarily removed (needs to be rewritten for new format conversion code)
+// MODULE_PARM(sensor_gbr, "i");
+// MODULE_PARM_DESC(sensor_gbr, "Make sensor output GBR422 rather than YUV420");
+
 MODULE_PARM(dumppix, "i");
-MODULE_PARM_DESC(dumppix, "Dump raw pixel data, in one of 3 formats. See ov511_dumppix() for details");
-MODULE_PARM(video_nr,"i");
+MODULE_PARM_DESC(dumppix, "Dump raw pixel data");
+MODULE_PARM(led, "i");
+MODULE_PARM_DESC(led,
+  "LED policy (OV511+ or later). 0=off, 1=on (default), 2=auto (on when open)");
+MODULE_PARM(dump_bridge, "i");
+MODULE_PARM_DESC(dump_bridge, "Dump the bridge registers");
+MODULE_PARM(dump_sensor, "i");
+MODULE_PARM_DESC(dump_sensor, "Dump the sensor registers");
+MODULE_PARM(printph, "i");
+MODULE_PARM_DESC(printph, "Print frame start/end headers");
+MODULE_PARM(phy, "i");
+MODULE_PARM_DESC(phy, "Prediction range (horiz. Y)");
+MODULE_PARM(phuv, "i");
+MODULE_PARM_DESC(phuv, "Prediction range (horiz. UV)");
+MODULE_PARM(pvy, "i");
+MODULE_PARM_DESC(pvy, "Prediction range (vert. Y)");
+MODULE_PARM(pvuv, "i");
+MODULE_PARM_DESC(pvuv, "Prediction range (vert. UV)");
+MODULE_PARM(qhy, "i");
+MODULE_PARM_DESC(qhy, "Quantization threshold (horiz. Y)");
+MODULE_PARM(qhuv, "i");
+MODULE_PARM_DESC(qhuv, "Quantization threshold (horiz. UV)");
+MODULE_PARM(qvy, "i");
+MODULE_PARM_DESC(qvy, "Quantization threshold (vert. Y)");
+MODULE_PARM(qvuv, "i");
+MODULE_PARM_DESC(qvuv, "Quantization threshold (vert. UV)");
+MODULE_PARM(lightfreq, "i");
+MODULE_PARM_DESC(lightfreq,
+  "Light frequency. Set to 50 or 60 Hz, or zero for default settings");
+MODULE_PARM(bandingfilter, "i");
+MODULE_PARM_DESC(bandingfilter,
+  "Enable banding filter (to reduce effects of fluorescent lighting)");
+MODULE_PARM(clockdiv, "i");
+MODULE_PARM_DESC(clockdiv, "Force pixel clock divisor to a specific value");
+MODULE_PARM(packetsize, "i");
+MODULE_PARM_DESC(packetsize, "Force a specific isoc packet size");
+MODULE_PARM(framedrop, "i");
+MODULE_PARM_DESC(framedrop, "Force a specific frame drop register setting");
+MODULE_PARM(fastset, "i");
+MODULE_PARM_DESC(fastset, "Allows picture settings to take effect immediately");
+MODULE_PARM(force_palette, "i");
+MODULE_PARM_DESC(force_palette, "Force the palette to a specific value");
+MODULE_PARM(tuner, "i");
+MODULE_PARM_DESC(tuner, "Set tuner type, if not autodetected");
+MODULE_PARM(backlight, "i");
+MODULE_PARM_DESC(backlight, "For objects that are lit from behind");
+MODULE_PARM(unit_video, "0-16i");
+MODULE_PARM_DESC(unit_video,
+  "Force use of specific minor number(s). 0 is not allowed.");
+MODULE_PARM(remove_zeros, "i");
+MODULE_PARM_DESC(remove_zeros,
+  "Remove zero-padding from uncompressed incoming data");
 
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
 
 static struct usb_driver ov511_driver;
 
-/* I know, I know, global variables suck. This is only a temporary hack */
-int output_offset;
+static struct ov51x_decomp_ops *ov511_decomp_ops;
+static struct ov51x_decomp_ops *ov511_mmx_decomp_ops;
+static struct ov51x_decomp_ops *ov518_decomp_ops;
+static struct ov51x_decomp_ops *ov518_mmx_decomp_ops;
+
+/* Number of times to retry a failed I2C transaction. Increase this if you
+ * are getting "Failed to read sensor ID..." */
+static int i2c_detect_tries = 5;
+
+/* MMX support is present in kernel and CPU. Checked upon decomp module load. */
+static int ov51x_mmx_available;
+
+/* Function prototypes */
+static void ov51x_clear_snapshot(struct usb_ov511 *);
+static int ov51x_check_snapshot(struct usb_ov511 *);
+static inline int sensor_get_picture(struct usb_ov511 *, 
+				     struct video_picture *);
+static int sensor_get_exposure(struct usb_ov511 *, unsigned char *);
+static int ov511_control_ioctl(struct inode *, struct file *, unsigned int,
+			       unsigned long);
 
 /**********************************************************************
  * List of known OV511-based cameras
  **********************************************************************/
 
 static struct cam_list clist[] = {
-	{   0, "generic model (no ID)" },
+	{   0, "Generic Camera (no ID)" },
+	{   1, "Mustek WCam 3X" },
 	{   3, "D-Link DSB-C300" },
-	{   4, "generic OV511/OV7610" },
+	{   4, "Generic OV511/OV7610" },
 	{   5, "Puretek PT-6007" },
+	{   6, "Lifeview USB Life TV (NTSC)" },
 	{  21, "Creative Labs WebCam 3" },
 	{  36, "Koala-Cam" },
-	{  38, "Lifeview USB Life TV" },	/* No support yet! */
+	{  38, "Lifeview USB Life TV" },
+	{  41, "Samsung Anycam MPC-M10" },
+	{  43, "Mtekvision Zeca MV402" },
+	{  46, "Suma eON" },
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
@@ -182,9 +335,11 @@
 };
 
 static __devinitdata struct usb_device_id device_table [] = {
-	{ USB_DEVICE(0x05a9, 0x0511) },  /* OV511 */
-	{ USB_DEVICE(0x05a9, 0xA511) },  /* OV511+ */
-	{ USB_DEVICE(0x0813, 0x0002) },  /* Intel Play Me2Cam OV511+ */
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
+	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) },
+	{ USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) },
 	{ }  /* Terminating entry */
 };
 
@@ -212,6 +367,11 @@
 };
 #endif
 
+static unsigned char yQuanTable511[] = OV511_YQUANTABLE;
+static unsigned char uvQuanTable511[] = OV511_UVQUANTABLE;
+static unsigned char yQuanTable518[] = OV518_YQUANTABLE;
+static unsigned char uvQuanTable518[] = OV518_UVQUANTABLE;
+
 /**********************************************************************
  *
  * Memory management
@@ -231,7 +391,8 @@
 /* Given PGD from the address space's page table, return the kernel
  * virtual mapping of the physical memory mapped at ADR.
  */
-static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+static inline unsigned long 
+uvirt_to_kva(pgd_t *pgd, unsigned long adr)
 {
 	unsigned long ret = 0UL;
 	pmd_t *pmd;
@@ -243,7 +404,8 @@
 			ptep = pte_offset(pmd, adr);
 			pte = *ptep;
 			if (pte_present(pte)) {
-				ret = (unsigned long) page_address(pte_page(pte));
+				ret = (unsigned long) 
+				      page_address(pte_page(pte));
 				ret |= (adr & (PAGE_SIZE - 1));
 			}
 		}
@@ -256,7 +418,8 @@
  * This is used when initializing the contents of the
  * area and marking the pages as reserved.
  */
-static inline unsigned long kvirt_to_pa(unsigned long adr)
+static inline unsigned long 
+kvirt_to_pa(unsigned long adr)
 {
 	unsigned long va, kva, ret;
 
@@ -266,7 +429,8 @@
 	return ret;
 }
 
-static void *rvmalloc(unsigned long size)
+static void *
+rvmalloc(unsigned long size)
 {
 	void *mem;
 	unsigned long adr, page;
@@ -294,7 +458,8 @@
 	return mem;
 }
 
-static void rvfree(void *mem, unsigned long size)
+static void 
+rvfree(void *mem, unsigned long size)
 {
 	unsigned long adr, page;
 
@@ -327,72 +492,96 @@
 static struct proc_dir_entry *ov511_proc_entry = NULL;
 extern struct proc_dir_entry *video_proc_entry;
 
+static struct file_operations ov511_control_fops = {
+	ioctl:		ov511_control_ioctl,
+};
+
 #define YES_NO(x) ((x) ? "yes" : "no")
 
-static int ov511_read_proc(char *page, char **start, off_t off,
-                          int count, int *eof, void *data)
+/* /proc/video/ov511/<minor#>/info */
+static int 
+ov511_read_proc_info(char *page, char **start, off_t off, int count, int *eof,
+		     void *data)
 {
 	char *out = page;
 	int i, j, len;
 	struct usb_ov511 *ov511 = data;
+	struct video_picture p;
+	unsigned char exp;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	sensor_get_picture(ov511, &p);
+	sensor_get_exposure(ov511, &exp);
 
 	/* IMPORTANT: This output MUST be kept under PAGE_SIZE
 	 *            or we need to get more sophisticated. */
 
-	out += sprintf (out, "driver_version  : %s\n", DRIVER_VERSION);
-	out += sprintf (out, "custom_id       : %d\n", ov511->customid);
-	out += sprintf (out, "model           : %s\n", ov511->desc ?
-		clist[ov511->desc].description : "unknown");
-	out += sprintf (out, "streaming       : %s\n", YES_NO (ov511->streaming));
-	out += sprintf (out, "grabbing        : %s\n", YES_NO (ov511->grabbing));
-	out += sprintf (out, "compress        : %s\n", YES_NO (ov511->compress));
-	out += sprintf (out, "subcapture      : %s\n", YES_NO (ov511->sub_flag));
-	out += sprintf (out, "sub_size        : %d %d %d %d\n",
-		ov511->subx, ov511->suby, ov511->subw, ov511->subh);
-	out += sprintf (out, "data_format     : %s\n", force_rgb ? "RGB" : "BGR");
-	out += sprintf (out, "brightness      : %d\n", ov511->brightness >> 8);
-	out += sprintf (out, "colour          : %d\n", ov511->colour >> 8);
-	out += sprintf (out, "contrast        : %d\n", ov511->contrast >> 8);
-	out += sprintf (out, "num_frames      : %d\n", OV511_NUMFRAMES);
+	out += sprintf(out, "driver_version  : %s\n", DRIVER_VERSION);
+	out += sprintf(out, "custom_id       : %d\n", ov511->customid);
+	out += sprintf(out, "model           : %s\n", ov511->desc ?
+		       clist[ov511->desc].description : "unknown");
+	out += sprintf(out, "streaming       : %s\n", YES_NO(ov511->streaming));
+	out += sprintf(out, "grabbing        : %s\n", YES_NO(ov511->grabbing));
+	out += sprintf(out, "compress        : %s\n", YES_NO(ov511->compress));
+	out += sprintf(out, "subcapture      : %s\n", YES_NO(ov511->sub_flag));
+	out += sprintf(out, "sub_size        : %d %d %d %d\n",
+		       ov511->subx, ov511->suby, ov511->subw, ov511->subh);
+	out += sprintf(out, "data_format     : %s\n",
+		       force_rgb ? "RGB" : "BGR");
+	out += sprintf(out, "brightness      : %d\n", p.brightness >> 8);
+	out += sprintf(out, "colour          : %d\n", p.colour >> 8);
+	out += sprintf(out, "contrast        : %d\n", p.contrast >> 8);
+	out += sprintf(out, "hue             : %d\n", p.hue >> 8);
+	out += sprintf(out, "exposure        : %d\n", exp);
+	out += sprintf(out, "num_frames      : %d\n", OV511_NUMFRAMES);
 	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		out += sprintf (out, "frame           : %d\n", i);
-		out += sprintf (out, "  depth         : %d\n",
-			ov511->frame[i].depth);
-		out += sprintf (out, "  size          : %d %d\n",
-			ov511->frame[i].width, ov511->frame[i].height);
-		out += sprintf (out, "  format        : ");
+		out += sprintf(out, "frame           : %d\n", i);
+		out += sprintf(out, "  depth         : %d\n",
+			       ov511->frame[i].depth);
+		out += sprintf(out, "  size          : %d %d\n",
+			       ov511->frame[i].width, ov511->frame[i].height);
+		out += sprintf(out, "  format        : ");
 		for (j = 0; plist[j].num >= 0; j++) {
 			if (plist[j].num == ov511->frame[i].format) {
-				out += sprintf (out, "%s\n", plist[j].name);
+				out += sprintf(out, "%s\n", plist[j].name);
 				break;
 			}
 		}
 		if (plist[j].num < 0)
-			out += sprintf (out, "unknown\n");
-		out += sprintf (out, "  segsize       : %d\n",
-			ov511->frame[i].segsize);
-		out += sprintf (out, "  data_buffer   : 0x%p\n",
-			ov511->frame[i].data);
-	}
-	out += sprintf (out, "snap_enabled    : %s\n", YES_NO (ov511->snap_enabled));
-	out += sprintf (out, "bridge          : %s\n",
-		ov511->bridge == BRG_OV511 ? "OV511" :
-		ov511->bridge == BRG_OV511PLUS ? "OV511+" :
-		"unknown");
-	out += sprintf (out, "sensor          : %s\n",
-		ov511->sensor == SEN_OV6620 ? "OV6620" :
-		ov511->sensor == SEN_OV7610 ? "OV7610" :
-		ov511->sensor == SEN_OV7620 ? "OV7620" :
-		ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
-		"unknown");
-	out += sprintf (out, "packet_size     : %d\n", ov511->packet_size);
-	out += sprintf (out, "framebuffer     : 0x%p\n", ov511->fbuf);
-	
+			out += sprintf(out, "unknown\n");
+		out += sprintf(out, "  data_buffer   : 0x%p\n",
+			       ov511->frame[i].data);
+	}
+	out += sprintf(out, "snap_enabled    : %s\n",
+		       YES_NO(ov511->snap_enabled));
+	out += sprintf(out, "bridge          : %s\n",
+		       ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
+	out += sprintf(out, "sensor          : %s\n",
+		       ov511->sensor == SEN_OV6620 ? "OV6620" :
+			ov511->sensor == SEN_OV6630 ? "OV6630" :
+			ov511->sensor == SEN_OV7610 ? "OV7610" :
+			ov511->sensor == SEN_OV7620 ? "OV7620" :
+			ov511->sensor == SEN_OV7620AE ? "OV7620AE" :
+			ov511->sensor == SEN_OV8600 ? "OV8600" :
+			ov511->sensor == SEN_KS0127 ? "KS0127" :
+			ov511->sensor == SEN_KS0127B ? "KS0127B" :
+			ov511->sensor == SEN_SAA7111A ? "SAA7111A" :
+			"unknown");
+	out += sprintf(out, "packet_size     : %d\n", ov511->packet_size);
+	out += sprintf(out, "framebuffer     : 0x%p\n", ov511->fbuf);
+
 	len = out - page;
 	len -= off;
 	if (len < count) {
 		*eof = 1;
-		if (len <= 0) return 0;
+		if (len <= 0)
+			return 0;
 	} else
 		len = count;
 
@@ -401,69 +590,160 @@
 	return len;
 }
 
-static int ov511_write_proc(struct file *file, const char *buffer,
-                           unsigned long count, void *data)
+/* /proc/video/ov511/<minor#>/button
+ *
+ * When the camera's button is pressed, the output of this will change from a
+ * 0 to a 1 (ASCII). It will retain this value until it is read, after which
+ * it will reset to zero.
+ * 
+ * SECURITY NOTE: Since reading this file can change the state of the snapshot
+ * status, it is important for applications that open it to keep it locked
+ * against access by other processes, using flock() or a similar mechanism. No
+ * locking is provided by this driver.
+ */
+static int 
+ov511_read_proc_button(char *page, char **start, off_t off, int count, int *eof,
+		       void *data)
 {
-	return -EINVAL;
+	char *out = page;
+	int len, status;
+	struct usb_ov511 *ov511 = data;
+
+	if (!ov511 || !ov511->dev)
+		return -ENODEV;
+
+	status = ov51x_check_snapshot(ov511);
+	out += sprintf(out, "%d", status);
+
+	if (status)
+		ov51x_clear_snapshot(ov511);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else {
+		len = count;
+	}
+
+	*start = page + off;
+
+	return len;
 }
 
-static void create_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+create_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
-	struct proc_dir_entry *ent;
-	
+	char dirname[4];
+
 	if (!ov511_proc_entry || !ov511)
 		return;
 
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "creating /proc/video/ov511/%s", name);
-	
-	ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry);
+	/* Create per-device directory */
+	sprintf(dirname, "%d", ov511->vdev.minor);
+	PDEBUG(4, "creating /proc/video/ov511/%s/", dirname);
+	ov511->proc_devdir = create_proc_entry(dirname, S_IFDIR,
+		ov511_proc_entry);
+	if (!ov511->proc_devdir)
+		return;
 
-	if (!ent)
+	/* Create "info" entry (human readable device information) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/info", dirname);
+	ov511->proc_info = create_proc_read_entry("info",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+		ov511_read_proc_info, ov511);
+	if (!ov511->proc_info)
 		return;
 
-	ent->data = ov511;
-	ent->read_proc = ov511_read_proc;
-	ent->write_proc = ov511_write_proc;
-	ov511->proc_entry = ent;
+	/* Don't create it if old snapshot mode on (would cause race cond.) */
+	if (!snapshot) {
+		/* Create "button" entry (snapshot button status) */
+		PDEBUG(4, "creating /proc/video/ov511/%s/button", dirname);
+		ov511->proc_button = create_proc_read_entry("button",
+			S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir,
+			ov511_read_proc_button, ov511);
+		if (!ov511->proc_button)
+			return;
+	}
+
+	/* Create "control" entry (ioctl() interface) */
+	PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname);
+	lock_kernel();
+	ov511->proc_control = create_proc_entry("control",
+		S_IFREG|S_IRUGO|S_IWUSR, ov511->proc_devdir);
+	if (!ov511->proc_control) {
+		unlock_kernel();
+		return;
+	}
+	ov511->proc_control->data = ov511;
+	ov511->proc_control->proc_fops = &ov511_control_fops;
+	unlock_kernel();
 }
 
-static void destroy_proc_ov511_cam (struct usb_ov511 *ov511)
+static void 
+destroy_proc_ov511_cam(struct usb_ov511 *ov511)
 {
-	char name[7];
+	char dirname[4];
 	
-	if (!ov511 || !ov511->proc_entry)
+	if (!ov511 || !ov511->proc_devdir)
 		return;
-	
-	sprintf(name, "video%d", ov511->vdev.minor);
-	PDEBUG (4, "destroying %s", name);
-	remove_proc_entry(name, ov511_proc_entry);
-	ov511->proc_entry = NULL;
+
+	sprintf(dirname, "%d", ov511->vdev.minor);
+
+	/* Destroy "control" entry */
+	if (ov511->proc_control) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/control", dirname);
+		remove_proc_entry("control", ov511->proc_devdir);
+		ov511->proc_control = NULL;
+	}
+
+	/* Destroy "button" entry */
+	if (ov511->proc_button) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/button", dirname);
+		remove_proc_entry("button", ov511->proc_devdir);
+		ov511->proc_button = NULL;
+	}
+
+	/* Destroy "info" entry */
+	if (ov511->proc_info) {
+		PDEBUG(4, "destroying /proc/video/ov511/%s/info", dirname);
+		remove_proc_entry("info", ov511->proc_devdir);
+		ov511->proc_info = NULL;
+	}
+
+	/* Destroy per-device directory */
+	PDEBUG(4, "destroying /proc/video/ov511/%s/", dirname);
+	remove_proc_entry(dirname, ov511_proc_entry);
+	ov511->proc_devdir = NULL;
 }
 
-static void proc_ov511_create(void)
+static void 
+proc_ov511_create(void)
 {
 	/* No current standard here. Alan prefers /proc/video/ as it keeps
 	 * /proc "less cluttered than /proc/randomcardifoundintheshed/"
 	 * -claudio
 	 */
 	if (video_proc_entry == NULL) {
-		err("Unable to initialise /proc/video/ov511");
+		err("Error: /proc/video/ does not exist");
 		return;
 	}
 
-	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry);
+	ov511_proc_entry = create_proc_entry("ov511", S_IFDIR,
+					     video_proc_entry);
 
 	if (ov511_proc_entry)
 		ov511_proc_entry->owner = THIS_MODULE;
 	else
-		err("Unable to initialise /proc/ov511");
+		err("Unable to create /proc/video/ov511");
 }
 
-static void proc_ov511_destroy(void)
+static void 
+proc_ov511_destroy(void)
 {
-	PDEBUG (3, "removing /proc/video/ov511");
+	PDEBUG(3, "removing /proc/video/ov511");
 
 	if (ov511_proc_entry == NULL)
 		return;
@@ -474,23 +754,22 @@
 
 /**********************************************************************
  *
- * Camera interface
+ * Register I/O
  *
  **********************************************************************/
 
-static int ov511_reg_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+static int 
+ov511_reg_write(struct usb_device *dev, unsigned char reg, unsigned char value)
 {
 	int rc;
 
-	rc = usb_control_msg(dev,
-		usb_sndctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, &value, 1, HZ);	
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
-	PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc);
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, &value, 1, HZ);	
 
 	if (rc < 0)
 		err("reg write: error %d", rc);
@@ -499,18 +778,19 @@
 }
 
 /* returns: negative is error, pos or zero is data */
-static int ov511_reg_read(struct usb_device *dev, unsigned char reg)
+static int 
+ov511_reg_read(struct usb_device *dev, unsigned char reg)
 {
 	int rc;
 	unsigned char buffer[1];
 
 	rc = usb_control_msg(dev,
-		usb_rcvctrlpipe(dev, 0),
-		2 /* REG_IO */,
-		USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
-		0, (__u16)reg, buffer, 1, HZ);
+			     usb_rcvctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, buffer, 1, HZ);
                                
-	PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]);
+	PDEBUG(5, "0x%02X:0x%02X", reg, buffer[0]);
 	
 	if (rc < 0) {
 		err("reg read: error %d", rc);
@@ -520,18 +800,197 @@
 	}
 }
 
-static int ov511_i2c_write(struct usb_device *dev,
-			   unsigned char reg,
-			   unsigned char value)
+/*
+ * Writes bits at positions specified by mask to a reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov511_reg_write_mask(struct usb_device *dev,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int ret;
+	unsigned char oldval, newval;
+
+	ret = ov511_reg_read(dev, reg);
+	if (ret < 0)
+		return ret;
+
+	oldval = (unsigned char) ret;
+	oldval &= (~mask);		/* Clear the masked bits */
+	value &= mask;			/* Enforce mask on value */
+	newval = oldval | value;	/* Set the desired bits */
+
+	return (ov511_reg_write(dev, reg, newval));
+}
+
+/* Writes multiple (n) values to a single register. Only valid with certain
+ * registers (0x30 and 0xc4 - 0xce). Used for writing 16 and 24-bit values. */
+static int 
+ov518_reg_write_multi(struct usb_device *dev,
+		      unsigned char reg,
+		      unsigned char *values,
+		      int n)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:[multiple], n=%d", reg, n);  // FIXME
+
+	if (values == NULL) {
+		err("reg write multiple: NULL buffer");
+		return -EINVAL;
+	}
+
+	rc = usb_control_msg(dev,
+			     usb_sndctrlpipe(dev, 0),
+			     2 /* REG_IO */,
+			     USB_TYPE_CLASS | USB_RECIP_DEVICE,
+			     0, (__u16)reg, values, n, HZ);	
+
+	if (rc < 0)
+		err("reg write multiple: error %d", rc);
+
+	return rc;
+}
+
+static int 
+ov511_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable511;
+	unsigned char *pUVTable = uvQuanTable511;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV511_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV511_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* OV518 quantization tables are 8x4 (instead of 8x8) */
+static int 
+ov518_upload_quan_tables(struct usb_device *dev)
+{
+	unsigned char *pYTable = yQuanTable518;
+	unsigned char *pUVTable = uvQuanTable518;
+	unsigned char val0, val1;
+	int i, rc, reg = OV511_OMNICE_Y_LUT_BEGIN;
+
+	PDEBUG(4, "Uploading quantization tables");
+
+	for (i = 0; i < OV518_QUANTABLESIZE / 2; i++)
+	{
+		if (ENABLE_Y_QUANTABLE)
+		{
+			val0 = *pYTable++;
+			val1 = *pYTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg, val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		if (ENABLE_UV_QUANTABLE)
+		{
+			val0 = *pUVTable++;
+			val1 = *pUVTable++;
+			val0 &= 0x0f;
+			val1 &= 0x0f;
+			val0 |= val1 << 4;
+			rc = ov511_reg_write(dev, reg + OV518_QUANTABLESIZE / 2,
+				val0);
+			if (rc < 0)
+				return rc;
+		}
+
+		reg++;
+	}
+
+	return 0;
+}
+
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_write(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
+{
+	int rc;
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Write "value" to I2C data port of OV511 */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value);
+	if (rc < 0) goto error;
+
+	/* Initiate 3-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x01);
+	if (rc < 0) goto error;
+
+	return 0;
+
+error:
+	err("ov518 i2c write: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly! */
+static int 
+ov511_i2c_write_internal(struct usb_device *dev,
+			 unsigned char reg,
+			 unsigned char value)
 {
 	int rc, retries;
 
-	PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 
 	/* Three byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Write "value" to I2C data port of OV511 */
@@ -566,15 +1025,51 @@
 	return rc;
 }
 
-/* returns: negative is error, pos or zero is data */
-static int ov511_i2c_read(struct usb_device *dev, unsigned char reg)
+/* NOTE: Do not call this function directly!
+ * The OV518 I2C I/O procedure is different, hence, this function.
+ * This is normally only called from ov51x_i2c_read(). Note that this function
+ * always succeeds regardless of whether the sensor is present and working.
+ */
+static int 
+ov518_i2c_read_internal(struct usb_device *dev, unsigned char reg)
+{
+	int rc, value;
+
+	/* Select camera register */
+	rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte write cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x03);
+	if (rc < 0) goto error;
+
+	/* Initiate 2-byte read cycle */
+	rc = ov511_reg_write(dev, OV518_REG_I2C_CONTROL, 0x05);
+	if (rc < 0) goto error;
+
+	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
+
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
+
+	return value;
+
+error:
+	err("ov518 i2c read: error %d", rc);
+	return rc;
+}
+
+/* NOTE: Do not call this function directly!
+ * returns: negative is error, pos or zero is data */
+static int 
+ov511_i2c_read_internal(struct usb_device *dev, unsigned char reg)
 {
 	int rc, value, retries;
 
 	/* Two byte write cycle */
 	for (retries = OV511_I2C_RETRIES; ; ) {
 		/* Select camera register */
-		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg);
+		rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE,
+				     reg);
 		if (rc < 0) goto error;
 
 		/* Initiate 2-byte write cycle */
@@ -624,9 +1119,9 @@
 
 	value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT);
 
-	PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value);
+	PDEBUG(5, "0x%02X:0x%02X", reg, value);
 		
-	/* This is needed to make ov511_i2c_write() work */
+	/* This is needed to make ov51x_i2c_write() work */
 	rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05);
 	if (rc < 0)
 		goto error;
@@ -638,19 +1133,207 @@
 	return rc;
 }
 
-static int ov511_write_regvals(struct usb_device *dev,
-			       struct ov511_regvals * pRegvals)
+/* returns: negative is error, pos or zero is data */
+static int 
+ov51x_i2c_read(struct usb_ov511 *ov511, unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+static int 
+ov51x_i2c_write(struct usb_ov511 *ov511,
+		unsigned char reg,
+		unsigned char value)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_write_internal(dev, reg, value);
+	else
+		rc = ov511_i2c_write_internal(dev, reg, value);
+
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Do not call this function directly! */
+static int 
+ov51x_i2c_write_mask_internal(struct usb_device *dev,
+			      unsigned char reg,
+			      unsigned char value,
+			      unsigned char mask)
+{
+	int rc;
+	unsigned char oldval, newval;
+
+	if (mask == 0xff) {
+		newval = value;
+	} else {
+		if (dev->descriptor.idProduct == PROD_OV518 ||
+		    dev->descriptor.idProduct == PROD_OV518PLUS)
+			rc = ov518_i2c_read_internal(dev, reg);
+		else
+			rc = ov511_i2c_read_internal(dev, reg);
+		if (rc < 0)
+			return rc;
+
+		oldval = (unsigned char) rc;
+		oldval &= (~mask);		/* Clear the masked bits */
+		value &= mask;			/* Enforce mask on value */
+		newval = oldval | value;	/* Set the desired bits */
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		return (ov518_i2c_write_internal(dev, reg, newval));
+	else
+		return (ov511_i2c_write_internal(dev, reg, newval));
+}
+
+/* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+ * the same position as 1's in "mask" are cleared and set to "value". Bits
+ * that are in the same position as 0's in "mask" are preserved, regardless 
+ * of their respective state in "value".
+ */
+static int 
+ov51x_i2c_write_mask(struct usb_ov511 *ov511,
+		     unsigned char reg,
+		     unsigned char value,
+		     unsigned char mask)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	up(&ov511->i2c_lock);
+
+	return rc;
+}
+
+/* Write to a specific I2C slave ID and register, using the specified mask */
+static int 
+ov51x_i2c_write_slave(struct usb_ov511 *ov511,
+		      unsigned char slave,
+		      unsigned char reg,
+		      unsigned char value,
+		      unsigned char mask)
+{
+	int rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	rc = ov51x_i2c_write_mask_internal(dev, reg, value, mask);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+/* Read from a specific I2C slave ID and register */
+static int 
+ov51x_i2c_read_slave(struct usb_ov511 *ov511,
+		     unsigned char slave,
+		     unsigned char reg)
+{
+	int rc;
+	struct usb_device *dev = ov511->dev;
+
+	down(&ov511->i2c_lock);
+
+	/* Set new slave IDs */
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (dev->descriptor.idProduct == PROD_OV518 ||
+	    dev->descriptor.idProduct == PROD_OV518PLUS)
+		rc = ov518_i2c_read_internal(dev, reg);
+	else
+		rc = ov511_i2c_read_internal(dev, reg);
+	/* Don't bail out yet if error; IDs must be restored */
+
+	/* Restore primary IDs */
+	slave = ov511->primary_i2c_slave;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, slave) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, slave + 1) < 0) {
+		rc = -EIO;
+		goto out;
+	}
+
+out:
+	up(&ov511->i2c_lock);
+	return rc;
+}
+
+static int 
+ov511_write_regvals(struct usb_ov511 *ov511,
+		    struct ov511_regvals * pRegvals)
 {
 	int rc;
+	struct usb_device *dev = ov511->dev;
 
 	while (pRegvals->bus != OV511_DONE_BUS) {
 		if (pRegvals->bus == OV511_REG_BUS) {
 			if ((rc = ov511_reg_write(dev, pRegvals->reg,
-			                           pRegvals->val)) < 0)
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else if (pRegvals->bus == OV511_I2C_BUS) {
-			if ((rc = ov511_i2c_write(dev, pRegvals->reg, 
-			                           pRegvals->val)) < 0)
+			if ((rc = ov51x_i2c_write(ov511, pRegvals->reg, 
+			                          pRegvals->val)) < 0)
 				goto error;
 		} else {
 			err("Bad regval array");
@@ -667,65 +1350,92 @@
 }
 
 #ifdef OV511_DEBUG 
-static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_i2c_range(struct usb_ov511 *ov511, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
-		rc = ov511_i2c_read(dev, i);
-		PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc);
+	for (i = reg1; i <= regn; i++) {
+		rc = ov51x_i2c_read(ov511, i);
+		info("OV7610[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_i2c_regs(struct usb_device *dev)
+static void 
+ov51x_dump_i2c_regs(struct usb_ov511 *ov511)
 {
-	PDEBUG(3, "I2C REGS");
-	ov511_dump_i2c_range(dev, 0x00, 0x7C);
+	info("I2C REGS");
+	ov511_dump_i2c_range(ov511, 0x00, 0x7C);
 }
 
-#if 0
-static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
+static void 
+ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn)
 {
 	int i;
 	int rc;
-	for(i = reg1; i <= regn; i++) {
+	for (i = reg1; i <= regn; i++) {
 	  rc = ov511_reg_read(dev, i);
-	  PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc);
+	  info("OV511[0x%X] = 0x%X", i, rc);
 	}
 }
 
-static void ov511_dump_regs(struct usb_device *dev)
+static void 
+ov511_dump_regs(struct usb_device *dev)
 {
-	PDEBUG(1, "CAMERA INTERFACE REGS");
+	info("CAMERA INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x10, 0x1f);
-	PDEBUG(1, "DRAM INTERFACE REGS");
+	info("DRAM INTERFACE REGS");
 	ov511_dump_reg_range(dev, 0x20, 0x23);
-	PDEBUG(1, "ISO FIFO REGS");
+	info("ISO FIFO REGS");
 	ov511_dump_reg_range(dev, 0x30, 0x31);
-	PDEBUG(1, "PIO REGS");
+	info("PIO REGS");
 	ov511_dump_reg_range(dev, 0x38, 0x39);
 	ov511_dump_reg_range(dev, 0x3e, 0x3e);
-	PDEBUG(1, "I2C REGS");
+	info("I2C REGS");
 	ov511_dump_reg_range(dev, 0x40, 0x49);
-	PDEBUG(1, "SYSTEM CONTROL REGS");
+	info("SYSTEM CONTROL REGS");
 	ov511_dump_reg_range(dev, 0x50, 0x55);
 	ov511_dump_reg_range(dev, 0x5e, 0x5f);
-	PDEBUG(1, "OmniCE REGS");
+	info("OmniCE REGS");
 	ov511_dump_reg_range(dev, 0x70, 0x79);
+	/* NOTE: Quantization tables are not readable. You will get the value
+	 * in reg. 0x79 for every table register */
 	ov511_dump_reg_range(dev, 0x80, 0x9f);
 	ov511_dump_reg_range(dev, 0xa0, 0xbf);
 
 }
 #endif
-#endif
 
-static int ov511_reset(struct usb_device *dev, unsigned char reset_type)
+/**********************************************************************
+ *
+ * Kernel I2C Interface
+ *
+ **********************************************************************/
+
+/* For as-yet unimplemented I2C interface */
+static void 
+call_i2c_clients(struct usb_ov511 *ov511, unsigned int cmd,
+		 void *arg)
+{
+	/* Do nothing */
+}
+
+/*****************************************************************************/
+
+static int 
+ov511_reset(struct usb_ov511 *ov511, unsigned char reset_type)
 {
 	int rc;
-	
+		
+	/* Setting bit 0 not allowed on 518/518Plus */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		reset_type &= 0xfe;
+
 	PDEBUG(4, "Reset: type=0x%X", reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type);
-	rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0);
+
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, reset_type);
+	rc = ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET, 0);
 
 	if (rc < 0)
 		err("reset: command failed");
@@ -735,27 +1445,148 @@
 
 /* Temporarily stops OV511 from functioning. Must do this before changing
  * registers while the camera is streaming */
-static inline int ov511_stop(struct usb_device *dev)
+static inline int 
+ov511_stop(struct usb_ov511 *ov511)
 {
 	PDEBUG(4, "stopping");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d));
+	ov511->stopped = 1;	
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3a));
+	else
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x3d));
+}
+
+/* Restarts OV511 after ov511_stop() is called. Has no effect if it is not
+ * actually stopped (for performance). */
+static inline int 
+ov511_restart(struct usb_ov511 *ov511)
+{
+	if (ov511->stopped) {
+		PDEBUG(4, "restarting");
+		ov511->stopped = 0;	
+
+		/* Reinitialize the stream */
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_reg_write(ov511->dev, 0x2f, 0x80);
+
+		return (ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_RESET,
+					0x00));
+	}
+
+	return 0;
 }
 
-/* Restarts OV511 after ov511_stop() is called */
-static inline int ov511_restart(struct usb_device *dev)
+/* Resets the hardware snapshot button */
+static void 
+ov51x_clear_snapshot(struct usb_ov511 *ov511)
 {
-	PDEBUG(4, "restarting");
-	return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00));
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot reset not supported yet on OV518(+)");
+	} else {
+		err("clear snap: invalid bridge type");
+	}
+	
 }
 
-static int ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+/* Checks the status of the snapshot button. Returns 1 if it was pressed since
+ * it was last cleared, and zero in all other cases (including errors) */
+static int 
+ov51x_check_snapshot(struct usb_ov511 *ov511)
+{
+	int ret, status = 0;
+
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		ret = ov511_reg_read(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT);
+		if (ret < 0) {
+			err("Error checking snspshot status (%d)", ret);
+		} else if (ret & 0x08) {
+			status = 1;
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		warn("snapshot check not supported yet on OV518(+)");
+	} else {
+		err("check snap: invalid bridge type");
+	}
+
+	return status;
+}
+
+/* Sets I2C read and write slave IDs. Returns <0 for error */
+static int 
+ov51x_set_slave_ids(struct usb_ov511 *ov511,
+		    unsigned char write_id,
+		    unsigned char read_id)
 {
-	int alt, mult;
+	struct usb_device *dev = ov511->dev;
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, write_id) < 0)
 		return -EIO;
 
-	mult = size >> 5;
+	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, read_id) < 0)
+		return -EIO;
+
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
+
+	return 0;
+}
+
+/* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 for failure.
+ */
+static int 
+ov51x_init_ov_sensor(struct usb_ov511 *ov511)
+{
+	int i, success;
+
+	/* Reset the sensor */ 
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+
+	/* Wait for it to initialize */ 
+	schedule_timeout (1 + 150 * HZ / 1000);
+
+	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
+		if ((ov51x_i2c_read(ov511, OV7610_REG_ID_HIGH) == 0x7F) &&
+		    (ov51x_i2c_read(ov511, OV7610_REG_ID_LOW) == 0xA2)) {
+			success = 1;
+			continue;
+		}
+
+		/* Reset the sensor */ 
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -EIO;
+		/* Wait for it to initialize */ 
+		schedule_timeout(1 + 150 * HZ / 1000);
+		/* Dummy read to sync I2C */
+		if (ov51x_i2c_read(ov511, 0x00) < 0) return -EIO;
+	}
+
+	if (!success)
+		return -EIO;
+	
+	PDEBUG(1, "I2C synced in %d attempt(s)", i);
+
+	return 0;
+}
+
+static int 
+ov511_set_packet_size(struct usb_ov511 *ov511, int size)
+{
+	int alt, mult;
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	mult = size >> 5;
 
 	if (ov511->bridge == BRG_OV511) {
 		if (size == 0) alt = OV511_ALT_SIZE_0;
@@ -780,6 +1611,20 @@
 			err("Set packet size: invalid size (%d)", size);
 			return -EINVAL;
 		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (size == 0) alt = OV518_ALT_SIZE_0;
+		else if (size == 128) alt = OV518_ALT_SIZE_128;
+		else if (size == 256) alt = OV518_ALT_SIZE_256;
+		else if (size == 384) alt = OV518_ALT_SIZE_384;
+		else if (size == 512) alt = OV518_ALT_SIZE_512;
+		else if (size == 640) alt = OV518_ALT_SIZE_640;
+		else if (size == 768) alt = OV518_ALT_SIZE_768;
+		else if (size == 896) alt = OV518_ALT_SIZE_896;
+		else {
+			err("Set packet size: invalid size (%d)", size);
+			return -EINVAL;
+		}
 	} else {
 		err("Set packet size: Invalid bridge type");
 		return -EINVAL;
@@ -787,351 +1632,1578 @@
 
 	PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt);
 
-	if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0)
-		return -ENOMEM;
+	// FIXME: Don't know how to do this on OV518 yet
+	if (ov511->bridge != BRG_OV518 &&
+	    ov511->bridge != BRG_OV518PLUS) {
+		if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE,
+				    mult) < 0) {
+			return -EIO;
+		}
+	}
 	
 	if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) {
 		err("Set packet size: set interface error");
 		return -EBUSY;
 	}
 
+	/* Initialize the stream */
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS)
+		if (ov511_reg_write(ov511->dev, 0x2f, 0x80) < 0)
+			return -EIO;
+
 	// FIXME - Should we only reset the FIFO?
-	if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0)
-		return -ENOMEM;
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
+		return -EIO;
 
 	ov511->packet_size = size;
 
-	if (ov511_restart(ov511->dev) < 0)
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
 	return 0;
 }
 
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov511_init_compression(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc = 0;
+
+	if (!ov511->compress_inited) {
 
-static inline int
-ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
+		ov511_reg_write(dev, 0x70, phy);
+		ov511_reg_write(dev, 0x71, phuv);
+		ov511_reg_write(dev, 0x72, pvy);
+		ov511_reg_write(dev, 0x73, pvuv);
+		ov511_reg_write(dev, 0x74, qhy);
+		ov511_reg_write(dev, 0x75, qhuv);
+		ov511_reg_write(dev, 0x76, qvy);
+		ov511_reg_write(dev, 0x77, qvuv);
+
+		if (ov511_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
+
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
+
+/* Upload compression params and quantization tables. Returns 0 for success. */
+static int
+ov518_init_compression(struct usb_ov511 *ov511)
 {
-	int ret;
 	struct usb_device *dev = ov511->dev;
+	int rc = 0;
 
-	PDEBUG(4, "ov511_set_picture");
+	if (!ov511->compress_inited) {
 
-	if (ov511_stop(dev) < 0)
-		return -EIO;
+		if (ov518_upload_quan_tables(dev) < 0) {
+			err("Error uploading quantization tables");
+			rc = -EIO;
+			goto out;
+		}
+	}
 
-	ov511->contrast = p->contrast;
-	ov511->brightness = p->brightness;
-	ov511->colour = p->colour;
-	ov511->hue = p->hue;
-	ov511->whiteness = p->whiteness;
+	ov511->compress_inited = 1;
+out:	
+	return rc;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0)
-		return -EIO;
-#if 0
-	/* disable auto adjust mode */
-	if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0)
-		return -EIO;
-#endif
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE
-		|| ov511->sensor == SEN_OV6620)
-		if (ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0)
-			return -EIO;
+/* -------------------------------------------------------------------------- */
 
-	if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV6620) {
-		if (ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0)
-			return -EIO;
+/* Sets sensor's contrast setting to "val" */
+static int
+sensor_set_contrast(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_RED, 0xFF - (p->hue >> 8)) < 0)
-			return -EIO;
+	PDEBUG(3, "%d", val);
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, p->hue >> 8) < 0)
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
 			return -EIO;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0)
-			return -EIO;
-	} else if ((ov511->sensor == SEN_OV7620) 
-	         || (ov511->sensor == SEN_OV7620AE)) {
-#if 0
-		int cur_sat, new_sat, tmp;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	{
+		rc = ov51x_i2c_write(ov511, OV7610_REG_CNT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_OV7620:
+	{
+		unsigned char ctab[] = {
+			0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+			0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+		};
+
+		/* Use Y gamma control instead. Bit 0 enables it. */
+		rc = ov51x_i2c_write(ov511, 0x64, ctab[val>>12]);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	case SEN_SAA7111A:
+	{
+		rc = ov51x_i2c_write(ov511, 0x0b, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	}
+	default:
+	{
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
+	}
 
-		cur_sat = ov511_i2c_read(dev, OV7610_REG_BLUE);
+	rc = 0;		/* Success */
+	ov511->contrast = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
 
-		tmp = (p->hue >> 8) - cur_sat;
-		new_sat = (tmp < 0) ? (-tmp) | 0x80 : tmp;
+	return rc;
+}
 
-	        PDEBUG(1, "cur=%d target=%d diff=%d", cur_sat, p->hue >> 8, tmp); 
+/* Gets sensor's contrast setting */
+static int
+sensor_get_contrast(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
 
-		if (ov511_i2c_write(dev, OV7610_REG_BLUE, new_sat) < 0)
-			return -EIO;
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_CNT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		/* Use Y gamma reg instead. Bit 0 is the enable bit. */
+		rc = ov51x_i2c_read(ov511, 0x64);
+		if (rc < 0)
+			return rc;
+		else
+			*val = (rc & 0xfe) << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->contrast;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
 
-	        // DEBUG_CODE
-	        PDEBUG(1, "hue=%d", ov511_i2c_read(dev, OV7610_REG_BLUE)); 
+	PDEBUG(3, "%d", *val);
+	ov511->contrast = *val;
 
-#endif
+	return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's brightness setting to "val" */
+static int
+sensor_set_brightness(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
+
+	PDEBUG(4, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+		/* 7620 doesn't like manual changes when in auto mode */
+		if (!ov511->auto_brt) {
+			rc = ov51x_i2c_write(ov511, OV7610_REG_BRT, val >> 8);
+			if (rc < 0)
+				goto out;
+		}
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0a, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
 	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->brightness = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's brightness setting */
+static int
+sensor_get_brightness(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV7620:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BRT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->brightness;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->brightness = *val;
+
 	return 0;
 }
 
-static inline int
-ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+/* -------------------------------------------------------------------------- */
+
+/* Sets sensor's saturation (color intensity) setting to "val" */
+static int
+sensor_set_saturation(struct usb_ov511 *ov511, unsigned short val)
 {
-	int ret;
-	struct usb_device *dev = ov511->dev;
+	int rc;
 
-	PDEBUG(4, "ov511_get_picture");
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+//		rc = ov511_i2c_write(ov511->dev, 0x62, (val >> 9) & 0x7e);
+//		if (rc < 0)
+//			goto out;
+		rc = ov51x_i2c_write(ov511, OV7610_REG_SAT, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0c, val >> 9);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_stop(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->colour = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO;
-	p->colour = ret << 8;
+	return rc;
+}
+
+/* Gets sensor's saturation (color intensity) setting */
+static int
+sensor_get_saturation(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+//		/* Use UV gamma reg instead. Bits 0 & 7 are reserved. */
+//		rc = ov51x_i2c_read(ov511, 0x62);
+//		if (rc < 0)
+//			return rc;
+//		else
+//			*val = (rc & 0x7e) << 9;
+		rc = ov51x_i2c_read(ov511, OV7610_REG_SAT);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->colour;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->colour = *val;
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO;
-	p->contrast = ret << 8;
+	return 0;
+}
 
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO;
-	p->brightness = ret << 8;
+/* -------------------------------------------------------------------------- */
 
-	/* This may not be the best way to do it */
-	if ((ret = ov511_i2c_read(dev, OV7610_REG_BLUE)) < 0) return -EIO;
-	p->hue = ret << 8;
+/* Sets sensor's hue (red/blue balance) setting to "val" */
+static int
+sensor_set_hue(struct usb_ov511 *ov511, unsigned short val)
+{
+	int rc;
 
-	p->whiteness = 105 << 8;
+	PDEBUG(3, "%d", val);
 
-	/* Can we get these from frame[0]? -claudio? */
-	p->depth = ov511->frame[0].depth;
-	p->palette = ov511->frame[0].format;
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_write(ov511, OV7610_REG_RED, 0xFF - (val >> 8));
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, OV7610_REG_BLUE, val >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	case SEN_OV7620:
+// Hue control is causing problems. I will enable it once it's fixed.
+#if 0
+		rc = ov51x_i2c_write(ov511, 0x7a,
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+
+		rc = ov51x_i2c_write(ov511, 0x79, 
+				     (unsigned char)(val >> 8) + 0xb);
+		if (rc < 0)
+			goto out;
+#endif
+		break;
+	case SEN_SAA7111A:
+		rc = ov51x_i2c_write(ov511, 0x0d, (val + 32768) >> 8);
+		if (rc < 0)
+			goto out;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		rc = -EPERM;
+		goto out;
+	}
 
-	if (ov511_restart(dev) < 0)
+	rc = 0;		/* Success */
+	ov511->hue = val;
+out:
+	if (ov511_restart(ov511) < 0)
 		return -EIO;
 
+	return rc;
+}
+
+/* Gets sensor's hue (red/blue balance) setting */
+static int
+sensor_get_hue(struct usb_ov511 *ov511, unsigned short *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+		rc = ov51x_i2c_read(ov511, OV7610_REG_BLUE);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_OV7620:
+		rc = ov51x_i2c_read(ov511, 0x7a);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc << 8;
+		break;
+	case SEN_SAA7111A:
+		*val = ov511->hue;
+		break;
+	default:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->hue = *val;
+
 	return 0;
 }
 
-/* Returns number of bits per pixel (regardless of where they are located; planar or
- * not), or zero for unsupported format.
- */
-static int ov511_get_depth(int palette)
+/* -------------------------------------------------------------------------- */
+
+static inline int
+sensor_set_picture(struct usb_ov511 *ov511, struct video_picture *p)
 {
-	switch (palette) {
-	case VIDEO_PALETTE_GREY:    return 8;
-	case VIDEO_PALETTE_RGB565:  return 16;
-	case VIDEO_PALETTE_RGB24:   return 24;  
-	case VIDEO_PALETTE_YUV422:  return 16;
-	case VIDEO_PALETTE_YUYV:    return 16;
-	case VIDEO_PALETTE_YUV420:  return 24;
-	case VIDEO_PALETTE_YUV422P: return 24; /* Planar */
-	default:		    return 0;  /* Invalid format */
+	int rc;
+
+	PDEBUG(4, "sensor_set_picture");
+
+	ov511->whiteness = p->whiteness;
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_set_contrast(ov511, p->contrast);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_brightness(ov511, p->brightness);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_saturation(ov511, p->colour);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_set_hue(ov511, p->hue);
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	return 0;
+}
+
+static inline int
+sensor_get_picture(struct usb_ov511 *ov511, struct video_picture *p)
+{
+	int rc;
+
+	PDEBUG(4, "sensor_get_picture");
+
+	/* Don't return error if a setting is unsupported, or rest of settings
+         * will not be performed */
+
+	rc = sensor_get_contrast(ov511, &(p->contrast));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_brightness(ov511, &(p->brightness));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_saturation(ov511, &(p->colour));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	rc = sensor_get_hue(ov511, &(p->hue));
+	if (FATAL_ERROR(rc))
+		return rc;
+
+	p->whiteness = 105 << 8;
+
+	/* Can we get these from frame[0]? -claudio? */
+	p->depth = ov511->frame[0].depth;
+	p->palette = ov511->frame[0].format;
+
+	return 0;
+}
+
+// FIXME: Exposure range is only 0x00-0x7f in interlace mode
+/* Sets current exposure for sensor. This only has an effect if auto-exposure
+ * is off */
+static inline int
+sensor_set_exposure(struct usb_ov511 *ov511, unsigned char val)
+{
+	int rc;
+
+	PDEBUG(3, "%d", val);
+
+	if (ov511->stop_during_set)
+		if (ov511_stop(ov511) < 0)
+			return -EIO;
+
+	switch (ov511->sensor) {
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7610:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_write(ov511, 0x10, val);
+		if (rc < 0)
+			goto out;
+
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_exposure");
+		return -EINVAL;
+	}
+
+	rc = 0;		/* Success */
+	ov511->exposure = val;
+out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
+}
+
+/* Gets current exposure level from sensor, regardless of whether it is under
+ * manual control. */
+static int
+sensor_get_exposure(struct usb_ov511 *ov511, unsigned char *val)
+{
+	int rc;
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV6620:
+	case SEN_OV6630:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		rc = ov51x_i2c_read(ov511, 0x10);
+		if (rc < 0)
+			return rc;
+		else
+			*val = rc;
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		val = 0;
+		PDEBUG(3, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for get_exposure");
+		return -EINVAL;
+	}
+
+	PDEBUG(3, "%d", *val);
+	ov511->exposure = *val;
+
+	return 0;
+}
+
+/* Turns on or off the LED. Only has an effect with OV511+/OV518(+) */
+static inline void 
+ov51x_led_control(struct usb_ov511 *ov511, int enable)
+{
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_LED_CTL, 
+			        enable ? 1 : 0);
+	else if (ov511->bridge == BRG_OV518 ||
+		 ov511->bridge == BRG_OV518PLUS)
+		ov511_reg_write_mask(ov511->dev, OV518_REG_GPIO_OUT, 
+				     enable ? 0x02 : 0x00, 0x02);
+	return;
+}
+
+/* Matches the sensor's internal frame rate to the lighting frequency.
+ * Valid frequencies are:
+ *	50 - 50Hz, for European and Asian lighting
+ *	60 - 60Hz, for American lighting
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_light_freq(struct usb_ov511 *ov511, int freq)
+{
+	int sixty;
+
+	PDEBUG(4, "%d Hz", freq);
+
+	if (freq == 60)
+		sixty = 1;
+	else if (freq == 50)
+		sixty = 0;
+	else {
+		err("Invalid light freq (%d Hz)", freq);
+		return -EINVAL;
+	}
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x10, 0x10);
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x10);
+		break;
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x2a, sixty?0x00:0x80, 0x80);
+		ov51x_i2c_write(ov511, 0x2b, sixty?0x00:0xac);
+		ov51x_i2c_write_mask(ov511, 0x76, 0x01, 0x01);
+		break;		
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x2b, sixty?0xa8:0x28);
+		ov51x_i2c_write(ov511, 0x2a, sixty?0x84:0xa4);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_light_freq");
+		return -EINVAL;
+	}
+
+	ov511->lightfreq = freq;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's banding filter, otherwise turn it
+ * off. This filter tries to reduce the pattern of horizontal light/dark bands
+ * caused by some (usually fluorescent) lighting. The light frequency must be
+ * set either before or after enabling it with ov51x_set_light_freq().
+ *
+ * Tested with: OV7610, OV7620, OV7620AE, OV6620.
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_banding_filter(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x04:0x00, 0x04);
+	if (rc < 0)
+		return rc;
+
+	ov511->bandfilt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto brightness control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_brightness(struct usb_ov511 *ov511, int enable)
+{
+	int rc;
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	if (ov511->sensor == SEN_KS0127 || ov511->sensor == SEN_KS0127B
+		|| ov511->sensor == SEN_SAA7111A) {
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	}
+
+	rc = ov51x_i2c_write_mask(ov511, 0x2d, enable?0x10:0x00, 0x10);
+	if (rc < 0)
+		return rc;
+
+	ov511->auto_brt = enable;
+
+	return 0;
+}
+
+/* If enable is true, turn on the sensor's auto exposure control, otherwise
+ * turn it off.
+ *
+ * Unsupported: KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static inline int
+sensor_set_auto_exposure(struct usb_ov511 *ov511, int enable)
+{	
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x00:0x80, 0x80);
+		break;
+	case SEN_OV6620:
+	case SEN_OV7620:
+	case SEN_OV7620AE:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x13, enable?0x01:0x00, 0x01);
+		break;		
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x00:0x10, 0x10);
+		break;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_auto_exposure");
+		return -EINVAL;
+	}
+
+	ov511->auto_exp = enable;
+
+	return 0;
+}
+
+/* Modifies the sensor's exposure algorithm to allow proper exposure of objects
+ * that are illuminated from behind.
+ *
+ * Tested with: OV6620, OV7620
+ * Unsupported: OV7610, OV7620AE, KS0127, KS0127B, SAA7111A
+ * Returns: 0 for success
+ */
+static int
+sensor_set_backlight(struct usb_ov511 *ov511, int enable)
+{
+
+	PDEBUG(4, " (%s)", enable ? "turn on" : "turn off");
+
+	switch (ov511->sensor) {
+	case SEN_OV7620:
+	case SEN_OV8600:
+		ov51x_i2c_write_mask(ov511, 0x68, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;		
+	case SEN_OV6620:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0xe0:0xc0, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x0e, enable?0x80:0x00, 0x80);
+		break;
+	case SEN_OV6630:
+		ov51x_i2c_write_mask(ov511, 0x4e, enable?0x80:0x60, 0xe0);
+		ov51x_i2c_write_mask(ov511, 0x29, enable?0x08:0x00, 0x08);
+		ov51x_i2c_write_mask(ov511, 0x28, enable?0x02:0x00, 0x02);
+		break;
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+	case SEN_KS0127:
+	case SEN_KS0127B:
+	case SEN_SAA7111A:
+		PDEBUG(5, "Unsupported with this sensor");
+		return -EPERM;
+	default:
+		err("Sensor not supported for set_backlight");
+		return -EINVAL;
+	}
+
+	ov511->backlight = enable;
+
+	return 0;
+}
+
+/* Returns number of bits per pixel (regardless of where they are located;
+ * planar or not), or zero for unsupported format.
+ */
+static inline int 
+ov511_get_depth(int palette)
+{
+	switch (palette) {
+	case VIDEO_PALETTE_GREY:    return 8;
+	case VIDEO_PALETTE_RGB565:  return 16;
+	case VIDEO_PALETTE_RGB24:   return 24;  
+	case VIDEO_PALETTE_YUV422:  return 16;
+	case VIDEO_PALETTE_YUYV:    return 16;
+	case VIDEO_PALETTE_YUV420:  return 12;
+	case VIDEO_PALETTE_YUV422P: return 16; /* Planar */
+	case VIDEO_PALETTE_YUV420P: return 12; /* Planar */
+	default:		    return 0;  /* Invalid format */
+	}
+}
+
+/* Bytes per frame. Used by read(). Return of 0 indicates error */
+static inline long int 
+get_frame_length(struct ov511_frame *frame)
+{
+	if (!frame)
+		return 0;
+	else
+		return ((frame->width * frame->height
+			 * ov511_get_depth(frame->format)) >> 3);
+}
+
+static int
+mode_init_ov_sensor_regs(struct usb_ov511 *ov511, int width, int height,
+			 int mode, int sub_flag, int qvga)
+{
+	int clock;
+
+	/******** Mode (VGA/QVGA) and sensor specific regs ********/
+
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+// FIXME: Does this improve the image quality or frame rate?
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, 0x10);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x40:0x8a);
+		ov51x_i2c_write(ov511, 0x2f, qvga?0x30:0xb0);
+		ov51x_i2c_write(ov511, 0x35, qvga?0x1c:0x9c);
+#endif
+		break;
+	case SEN_OV7620:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xf0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+		break;
+	case SEN_OV7620AE:
+//		ov51x_i2c_write(ov511, 0x2b, 0x00);
+		ov51x_i2c_write(ov511, 0x14, qvga?0xa4:0x84);
+// FIXME: Enable this once 7620AE uses 7620 initial settings
+#if 0
+		ov51x_i2c_write_mask(ov511, 0x28, qvga?0x00:0x20, 0x20);
+		ov51x_i2c_write(ov511, 0x24, qvga?0x20:0x3a);
+		ov51x_i2c_write(ov511, 0x25, qvga?0x30:0x60);
+		ov51x_i2c_write_mask(ov511, 0x2d, qvga?0x40:0x00, 0x40);
+		ov51x_i2c_write_mask(ov511, 0x67, qvga?0xb0:0x90, 0xf0);
+		ov51x_i2c_write_mask(ov511, 0x74, qvga?0x20:0x00, 0x20);
+#endif
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:
+		ov51x_i2c_write(ov511, 0x14, qvga?0x24:0x04);
+		/* No special settings yet */
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	/******** Palette-specific regs ********/
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* these aren't valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x40, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x20, 0x20);
+	} else {
+		if (ov511->sensor == SEN_OV7610
+		    || ov511->sensor == SEN_OV7620AE) {
+			/* not valid on the OV6620/OV7620/6630? */
+			ov51x_i2c_write_mask(ov511, 0x0e, 0x00, 0x40);
+		}
+		ov51x_i2c_write_mask(ov511, 0x13, 0x00, 0x20);
+	}
+
+	/******** Clock programming ********/
+
+	// FIXME: Test this with OV6630
+
+	/* The OV6620 needs special handling. This prevents the 
+	 * severe banding that normally occurs */
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630)
+	{
+		/* Clock down */
+
+		ov51x_i2c_write(ov511, 0x2a, 0x04);
+
+		if (ov511->compress) {
+//			clock = 0;    /* This ensures the highest frame rate */
+			clock = 3;
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			clock = 3;    /* Gives better exposure time */
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+
+		ov51x_i2c_write(ov511, 0x2a, 0x84);
+		/* This next setting is critical. It seems to improve
+		 * the gain or the contrast. The "reserved" bits seem
+		 * to have some effect in this case. */
+		ov51x_i2c_write(ov511, 0x2d, 0x85);
+	}
+	else
+	{
+		if (ov511->compress) {
+			clock = 1;    /* This ensures the highest frame rate */
+		} else if (clockdiv == -1) {   /* If user didn't override it */
+			/* Calculate and set the clock divisor */
+			clock = ((sub_flag ? ov511->subw * ov511->subh
+				  : width * height)
+				 * (mode == VIDEO_PALETTE_GREY ? 2 : 3) / 2)
+				 / 66000;
+		} else {
+			clock = clockdiv;
+		}
+
+		PDEBUG(4, "Setting clock divisor to %d", clock);
+
+		ov51x_i2c_write(ov511, 0x11, clock);
+	}
+
+	/******** Special Features ********/
+
+	if (framedrop >= 0)
+		ov51x_i2c_write(ov511, 0x16, framedrop);
+
+	/* We only have code to convert GBR -> RGB24 */
+	if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x08, 0x08);
+	else
+		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x08);
+
+	/* Test Pattern */
+	ov51x_i2c_write_mask(ov511, 0x12, (testpat?0x02:0x00), 0x02);
+
+	/* Auto white balance */
+//	if (awb)
+		ov51x_i2c_write_mask(ov511, 0x12, 0x04, 0x04);
+//	else
+//		ov51x_i2c_write_mask(ov511, 0x12, 0x00, 0x04);
+
+	// This will go away as soon as ov511_mode_init_sensor_regs()
+	// is fully tested.
+	/* 7620/6620/6630? don't have register 0x35, so play it safe */
+	if (ov511->sensor == SEN_OV7610 ||
+	    ov511->sensor == SEN_OV7620AE) {
+		if (width == 640 && height == 480)
+			ov51x_i2c_write(ov511, 0x35, 0x9e);
+		else
+			ov51x_i2c_write(ov511, 0x35, 0x1e);
+	}
+
+	return 0;
+}
+
+static int
+set_ov_sensor_window(struct usb_ov511 *ov511, int width, int height, int mode,
+		     int sub_flag)
+{
+	int ret;
+	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
+	int hoffset, voffset, hwscale = 0, vwscale = 0;
+
+	/* The different sensor ICs handle setting up of window differently.
+	 * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!!! */
+	switch (ov511->sensor) {
+	case SEN_OV7610:
+	case SEN_OV7620AE:
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = vwebase = 0x05;
+		break;
+	case SEN_OV6620:
+	case SEN_OV6630:	// FIXME: Is this right?
+		hwsbase = 0x38;
+		hwebase = 0x3a;
+		vwsbase = 0x05;
+		vwebase = 0x06;
+		break;
+	case SEN_OV7620:
+		hwsbase = 0x2f;		/* From 7620.SET (spec is wrong) */
+		hwebase = 0x2f;
+		vwsbase = vwebase = 0x05;
+		break;
+	default:
+		err("Invalid sensor");
+		return -EINVAL;
+	}
+
+	if (ov511->sensor == SEN_OV6620 || ov511->sensor == SEN_OV6630) {
+		if (width > 176 && height > 144) {  /* CIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			vwscale = 1;  /* The datasheet says 0; it's wrong */
+			hwsize = 352;
+			vwsize = 288;
+		} else if (width > 176 || height > 144) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QCIF */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwsize = 176;
+			vwsize = 144;
+		}
+	} else {
+		if (width > 320 && height > 240) {  /* VGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 0);
+			if (ret < 0)
+				return ret;
+			hwscale = 2;
+			vwscale = 1;
+			hwsize = 640;
+			vwsize = 480;
+		} else if (width > 320 || height > 240) {
+			err("Illegal dimensions");
+			return -EINVAL;
+		} else {			    /* QVGA */
+			ret = mode_init_ov_sensor_regs(ov511, width, height,
+				mode, sub_flag, 1);
+			if (ret < 0)
+				return ret;
+			hwscale = 1;
+			hwsize = 320;
+			vwsize = 240;
+		}
+	}
+
+	/* Center the window */
+	hoffset = ((hwsize - width) / 2) >> hwscale;
+	voffset = ((vwsize - height) / 2) >> vwscale;
+
+	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
+	if (sub_flag) {
+		ov51x_i2c_write(ov511, 0x17, hwsbase+(ov511->subx>>hwscale));
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase+((ov511->subx+ov511->subw)>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase+(ov511->suby>>vwscale));
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase+((ov511->suby+ov511->subh)>>vwscale));
+	} else {
+		ov51x_i2c_write(ov511, 0x17, hwsbase + hoffset);
+		ov51x_i2c_write(ov511, 0x18,
+				hwebase + hoffset + (hwsize>>hwscale));
+		ov51x_i2c_write(ov511, 0x19, vwsbase + voffset);
+		ov51x_i2c_write(ov511, 0x1a,
+				vwebase + voffset + (vwsize>>vwscale));
+	}
+
+#ifdef OV511_DEBUG
+	if (dump_sensor)
+		ov51x_dump_i2c_regs(ov511);
+#endif
+
+	return 0;
+}
+
+/* Set up the OV511/OV511+ with the given image parameters.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
+static int
+ov511_mode_init_regs(struct usb_ov511 *ov511,
+		     int width, int height, int mode, int sub_flag)
+{
+	int lncnt, pxcnt, rc = 0;
+	struct usb_device *dev = ov511->dev;
+
+	if (!ov511 || !dev)
+		return -EFAULT;
+
+	if (sub_flag) {
+		width = ov511->subw;
+		height = ov511->subh;
+	}
+
+	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
+	       width, height, mode, sub_flag);
+
+	// FIXME: This should be moved to a 7111a-specific function once
+	// subcapture is dealt with properly
+	if (ov511->sensor == SEN_SAA7111A) {
+		if (width == 320 && height == 240) {
+			/* No need to do anything special */
+		} else if (width == 640 && height == 480) {
+			/* Set the OV511 up as 320x480, but keep the V4L
+			 * resolution as 640x480 */
+			width = 320;
+		} else {
+			err("SAA7111A only supports 320x240 or 640x480");
+			return -EINVAL;
+		}
+	}
+
+	/* Make sure width and height are a multiple of 8 */
+	if (width % 8 || height % 8) {
+		err("Invalid size (%d, %d) (mode = %d)", width, height, mode);
+		return -EINVAL;
+	}
+
+	if (width < ov511->minwidth || height < ov511->minheight) {
+		err("Requested dimensions are too small");
+		return -EINVAL;
+	}
+
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	if (mode == VIDEO_PALETTE_GREY) {
+		ov511_reg_write(dev, 0x16, 0x00);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x00);
+		ov511_reg_write(dev, 0x1f, 0x01);
+	} else {
+		ov511_reg_write(dev, 0x16, 0x01);
+
+		/* For snapshot */
+		ov511_reg_write(dev, 0x1e, 0x01);
+		ov511_reg_write(dev, 0x1f, 0x03);
+	}
+
+	/* Here I'm assuming that snapshot size == image size.
+	 * I hope that's always true. --claudio
+	 */
+	pxcnt = (width >> 3) - 1;
+	lncnt = (height >> 3) - 1;
+
+	ov511_reg_write(dev, 0x12, pxcnt);
+	ov511_reg_write(dev, 0x13, lncnt);
+	ov511_reg_write(dev, 0x14, 0x00);
+	ov511_reg_write(dev, 0x15, 0x00);
+	ov511_reg_write(dev, 0x18, 0x03);	/* YUV420, low pass filer on */
+
+	/* Snapshot additions */
+	ov511_reg_write(dev, 0x1a, pxcnt);
+	ov511_reg_write(dev, 0x1b, lncnt);
+        ov511_reg_write(dev, 0x1c, 0x00);
+        ov511_reg_write(dev, 0x1d, 0x00);
+
+	if (ov511->compress) {
+		ov511_reg_write(dev, 0x78, 0x07); // Turn on Y & UV compression
+		ov511_reg_write(dev, 0x79, 0x03); // Enable LUTs
+		ov511_reset(ov511, OV511_RESET_OMNICE);
 	}
+//out:
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	return rc;
 }
 
-/* LNCNT values fixed by Lawrence Glaister <lg@jfm.bc.ca> */
-static struct mode_list mlist[] = {
-	/* W    H   C  PXCNT LNCNT PXDIV LNDIV M420  COMA  COML */
-	{ 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x9e },
-	{ 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 448, 336, 1, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 0, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 176, 144, 1, 0x15, 0x12, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 0, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
-	{ 160, 120, 1, 0x13, 0x0e, 0x00, 0x00, 0x03, 0x04, 0x1e },
+static struct mode_list_518 mlist518[] = {
+	/* W    H   reg28 reg29 reg2a reg2c reg2e reg24 reg25 */
+	{ 352, 288, 0x00, 0x16, 0x48, 0x00, 0x00, 0x9f, 0x90 },
+	{ 320, 240, 0x00, 0x14, 0x3c, 0x10, 0x18, 0x9f, 0x90 },
+	{ 176, 144, 0x05, 0x0b, 0x24, 0x00, 0x00, 0xff, 0xf0 },
+	{ 160, 120, 0x05, 0x0a, 0x1e, 0x08, 0x0c, 0xff, 0xf0 },
 	{ 0, 0 }
 };
 
+/* Sets up the OV518/OV518+ with the given image parameters
+ *
+ * OV518 needs a completely different approach, until we can figure out what
+ * the individual registers do. Many register ops are commented out until we
+ * can find out if they are still valid. Also, only 15 FPS is supported now.
+ *
+ * Do not put any sensor-specific code in here (including I2C I/O functions)
+ */
 static int
-ov511_mode_init_regs(struct usb_ov511 *ov511,
+ov518_mode_init_regs(struct usb_ov511 *ov511,
 		     int width, int height, int mode, int sub_flag)
 {
 	int i;
 	struct usb_device *dev = ov511->dev;
-	int hwsbase, hwebase, vwsbase, vwebase, hwsize, vwsize; 
-	int hwscale = 0, vwscale = 0;
+	unsigned char b[3]; /* Multiple-value reg buffer */
 
 	PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d",
 	       width, height, mode, sub_flag);
 
-	if (ov511_stop(ov511->dev) < 0)
+	if (ov511_stop(ov511) < 0)
+		return -EIO;
+
+	for (i = 0; mlist518[i].width; i++) {
+//		int lncnt, pxcnt;
+
+		if (width != mlist518[i].width || height != mlist518[i].height)
+			continue;
+
+// FIXME: Subcapture won't be possible until we know what the registers do
+// FIXME: We can't handle anything but YUV420 so far
+
+//		/* Here I'm assuming that snapshot size == image size.
+//		 * I hope that's always true. --claudio
+//		 */
+//		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
+//		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+//
+//		ov511_reg_write(dev, 0x12, pxcnt);
+//		ov511_reg_write(dev, 0x13, lncnt);
+
+		/******** Set the mode ********/		
+
+		/* Mode independent regs */
+		ov511_reg_write(dev, 0x2b, 0x00);
+		ov511_reg_write(dev, 0x2d, 0x00);
+		ov511_reg_write(dev, 0x3b, 0x00);
+		ov511_reg_write(dev, 0x3d, 0x00);
+
+		/* Mode dependent regs. Regs 38 - 3e are always the same as
+		 * regs 28 - 2e */
+		ov511_reg_write_mask(dev, 0x28, mlist518[i].reg28
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x29, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x2a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x2c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x2e, mlist518[i].reg2e);
+		ov511_reg_write_mask(dev, 0x38, mlist518[i].reg28 
+			| (mode == VIDEO_PALETTE_GREY) ? 0x80:0x00, 0x8f);
+		ov511_reg_write(dev, 0x39, mlist518[i].reg29);
+		ov511_reg_write(dev, 0x3a, mlist518[i].reg2a);
+		ov511_reg_write(dev, 0x3c, mlist518[i].reg2c);
+		ov511_reg_write(dev, 0x3e, mlist518[i].reg2e);
+		ov511_reg_write(dev, 0x24, mlist518[i].reg24);
+		ov511_reg_write(dev, 0x25, mlist518[i].reg25);
+
+		/* Windows driver does this here; who knows why */
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/******** Set the framerate (to 15 FPS) ********/		
+
+		/* Mode independent, but framerate dependent, regs */
+		/* These are for 15 FPS only */
+		ov511_reg_write(dev, 0x51, 0x08);
+		ov511_reg_write(dev, 0x22, 0x18);
+		ov511_reg_write(dev, 0x23, 0xff);
+		ov511_reg_write(dev, 0x71, 0x19);  /* Compression-related? */
+
+		// FIXME: Sensor-specific
+		/* Bit 5 is what matters here. Of course, it is "reserved" */
+		ov51x_i2c_write(ov511, 0x54, 0x23);
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		/* Mode dependent regs */
+		if ((width == 352 && height == 288) ||
+		    (width == 320 && height == 240)) {
+			b[0]=0x80; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0x90; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0xf4; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x8e; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x1a; b[1]=0x00; b[2]=0x02;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x14; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xd0; b[1]=0x07;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x20; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x60; b[1]=0x02;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+
+		} else if ((width == 176 && height == 144) ||
+			   (width == 160 && height == 120)) {
+			b[0]=0x80; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0x30, b, 2);
+			b[0]=0xc8; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc4, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc6, b, 2);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xc7, b, 2);
+			b[0]=0x60; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xc8, b, 2);
+			b[0]=0x0f; b[1]=0x33; b[2]=0x01;
+			ov518_reg_write_multi(dev, 0xca, b, 3);
+			b[0]=0x40; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xcb, b, 2);
+			b[0]=0xec; b[1]=0x04;
+			ov518_reg_write_multi(dev, 0xcc, b, 2);
+			b[0]=0x13; b[1]=0x00;
+			ov518_reg_write_multi(dev, 0xcd, b, 2);
+			b[0]=0x6d; b[1]=0x01;
+			ov518_reg_write_multi(dev, 0xce, b, 2);
+		} else {
+			/* Can't happen, since we already handled this case */
+			err("ov518_mode_init_regs(): **** logic error ****");
+		}
+
+		ov511_reg_write(dev, 0x2f, 0x80);
+
+		break;
+	}
+
+	if (ov511_restart(ov511) < 0)
+		return -EIO;
+
+	/* Reset it just for good measure */
+	if (ov511_reset(ov511, OV511_RESET_NOREGS) < 0)
 		return -EIO;
 
-	/* Dumppix only works with RGB24 */
-	if (dumppix && (mode != VIDEO_PALETTE_RGB24)) {
-		err("dumppix only supported with RGB 24");
+	if (mlist518[i].width == 0) {
+		err("Unknown mode (%d, %d): %d", width, height, mode);
 		return -EINVAL;
 	}
 
-	if (mode == VIDEO_PALETTE_GREY) {
-		ov511_reg_write(dev, 0x16, 0x00);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* these aren't valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x44);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20);
+	return 0;
+}
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x00);
-		ov511_reg_write(dev, 0x1f, 0x01);
-	} else {
-		ov511_reg_write(dev, 0x16, 0x01);
-		if (ov511->sensor == SEN_OV7610
-		    || ov511->sensor == SEN_OV7620AE) {
-			/* not valid on the OV6620/OV7620 */
-			ov511_i2c_write(dev, 0x0e, 0x04);
-		}
-		ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00);
+/* This is a wrapper around the OV511, OV518, and sensor specific functions */
+static int
+mode_init_regs(struct usb_ov511 *ov511,
+	       int width, int height, int mode, int sub_flag)
+{
+	int rc = 0;
 
-		/* For snapshot */
-		ov511_reg_write(dev, 0x1e, 0x01);
-		ov511_reg_write(dev, 0x1f, 0x03);
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		rc = ov518_mode_init_regs(ov511, width, height, mode, sub_flag);
+	} else {
+		rc = ov511_mode_init_regs(ov511, width, height, mode, sub_flag);
 	}
 
-	/* The different sensor ICs handle setting up of window differently */
+	if (FATAL_ERROR(rc))
+		return rc;
+
 	switch (ov511->sensor) {
 	case SEN_OV7610:
+	case SEN_OV7620:
 	case SEN_OV7620AE:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = vwebase = 0x05;
-		break;
+	case SEN_OV8600:
 	case SEN_OV6620:
-		hwsbase = 0x38;
-		hwebase = 0x3a;
-		vwsbase = 0x05;
-		vwebase = 0x06;
+	case SEN_OV6630:
+		rc = set_ov_sensor_window(ov511, width, height, mode, sub_flag);
 		break;
-	case SEN_OV7620:
-		hwsbase = 0x2c;
-		hwebase = 0x2d;
-		vwsbase = vwebase = 0x05;
+	case SEN_KS0127:
+	case SEN_KS0127B:
+		err("KS0127-series decoders not supported yet");
+		rc = -EINVAL;
+		break;
+	case SEN_SAA7111A:
+//		rc = mode_init_saa_sensor_regs(ov511, width, height, mode, 
+//					       sub_flag);
+
+		PDEBUG(1, "SAA status = 0X%x", ov51x_i2c_read(ov511, 0x1f));
 		break;
 	default:
-		err("Invalid sensor");
-		return -EINVAL;
+		err("Unknown sensor");
+		rc = -EINVAL;
 	}
 
-	/* Bit 5 of COM C register varies with sensor */ 
-	if (ov511->sensor == SEN_OV6620) {
-		if (width > 176 && height > 144) {  /* CIF */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 1;
-			vwscale = 1;  /* The datasheet says 0; it's wrong */
-			hwsize = 352;
-			vwsize = 288;
-		} else {			    /* QCIF */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwsize = 176;
-			vwsize = 144;
-		}
-	}
-	else {
-		if (width > 320 && height > 240) {  /* VGA */
-			ov511_i2c_write(dev, 0x14, 0x04);
-			hwscale = 2;
-			vwscale = 1;
-			hwsize = 640;
-			vwsize = 480;
-		} else {			    /* QVGA */
-			ov511_i2c_write(dev, 0x14, 0x24);
-			hwscale = 1;
-			hwsize = 320;
-			vwsize = 240;
-		}	
-	}
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	/* FIXME! - This needs to be changed to support 160x120 and 6620!!! */
-	if (sub_flag) {
-		ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>hwscale));
-		ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase+(ov511->suby>>vwscale));
-		ov511_i2c_write(dev, 0x1a, vwebase+((ov511->suby+ov511->subh)>>vwscale));
-	} else {
-		ov511_i2c_write(dev, 0x17, hwsbase);
-		ov511_i2c_write(dev, 0x18, hwebase + (hwsize>>hwscale));
-		ov511_i2c_write(dev, 0x19, vwsbase);
-		ov511_i2c_write(dev, 0x1a, vwebase + (vwsize>>vwscale));
-	}
+	/* Sensor-independent settings */
+	rc = sensor_set_auto_brightness(ov511, ov511->auto_brt);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-	for (i = 0; mlist[i].width; i++) {
-		int lncnt, pxcnt, clock;
+	rc = sensor_set_auto_exposure(ov511, ov511->auto_exp);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (width != mlist[i].width || height != mlist[i].height)
-			continue;
+	rc = sensor_set_banding_filter(ov511, bandingfilter);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		if (!mlist[i].color && mode != VIDEO_PALETTE_GREY)
-			continue;
+	if (ov511->lightfreq) {
+		rc = sensor_set_light_freq(ov511, lightfreq);
+		if (FATAL_ERROR(rc))
+			return rc;
+	}
 
-		/* Here I'm assuming that snapshot size == image size.
-		 * I hope that's always true. --claudio
-		 */
-		pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt;
-		lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt;
+	rc = sensor_set_backlight(ov511, ov511->backlight);
+	if (FATAL_ERROR(rc))
+		return rc;
 
-		ov511_reg_write(dev, 0x12, pxcnt);
-		ov511_reg_write(dev, 0x13, lncnt);
-		ov511_reg_write(dev, 0x14, mlist[i].pxdv);
-		ov511_reg_write(dev, 0x15, mlist[i].lndv);
-		ov511_reg_write(dev, 0x18, mlist[i].m420);
-
-		/* Snapshot additions */
-		ov511_reg_write(dev, 0x1a, pxcnt);
-		ov511_reg_write(dev, 0x1b, lncnt);
-                ov511_reg_write(dev, 0x1c, mlist[i].pxdv);
-                ov511_reg_write(dev, 0x1d, mlist[i].lndv);
-
-		/* Calculate and set the clock divisor */
-		clock = ((sub_flag ? ov511->subw * ov511->subh : width * height)
-			* (mlist[i].color ? 3 : 2) / 2) / 66000;
-#if 0
-		clock *= cams;
-#endif
-		ov511_i2c_write(dev, 0x11, clock);
+	return 0;
+}
+
+/* This sets the default image parameters (Size = max, RGB24). This is
+ * useful for apps that use read() and do not set these.
+ */
+static int 
+ov51x_set_default_params(struct usb_ov511 *ov511)
+{
+	int i;
+
+	PDEBUG(3, "%dx%d, RGB24", ov511->maxwidth, ov511->maxheight);
 
-		/* We only have code to convert GBR -> RGB24 */
-		if ((mode == VIDEO_PALETTE_RGB24) && sensor_gbr)
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x0a:0x08));
+	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
+	 * (using read() instead). */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].width = ov511->maxwidth;
+		ov511->frame[i].height = ov511->maxheight;
+		ov511->frame[i].bytes_read = 0;
+		if (force_palette)
+			ov511->frame[i].format = force_palette;
 		else
-			ov511_i2c_write(dev, 0x12, mlist[i].common_A | (testpat?0x02:0x00));
+			ov511->frame[i].format = VIDEO_PALETTE_RGB24;
+		ov511->frame[i].depth = ov511_get_depth(ov511->frame[i].format);
+	}
+
+	/* Initialize to max width/height, RGB24 */
+	if (mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
+			   ov511->frame[0].format, 0) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**********************************************************************
+ *
+ * Video decoder stuff
+ *
+ **********************************************************************/
 
-		/* 7620/6620 don't have register 0x35, so play it safe */
-		if (ov511->sensor == SEN_OV7610 ||
-		    ov511->sensor == SEN_OV7620AE)
-			ov511_i2c_write(dev, 0x35, mlist[i].common_L);
+/* Set analog input port of decoder */
+static int 
+decoder_set_input(struct usb_ov511 *ov511, int input)
+{
+	PDEBUG(4, "port %d", input);
 
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		/* Select mode */
+		ov51x_i2c_write_mask(ov511, 0x02, input, 0x07);
+		/* Bypass chrominance trap for modes 4..7 */
+		ov51x_i2c_write_mask(ov511, 0x09,
+				     (input > 3) ? 0x80:0x00, 0x80);
 		break;
 	}
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Get ASCII name of video input */
+static int 
+decoder_get_input_name(struct usb_ov511 *ov511, int input, char *name)
+{
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		if (input < 0 || input > 7)
+			return -EINVAL;
+		else if (input < 4)
+			sprintf(name, "CVBS-%d", input);
+		else // if (input < 8)
+			sprintf(name, "S-Video-%d", input - 4);
 
-	if (compress) {
-		ov511_reg_write(dev, 0x78, 0x03); // Turn on Y compression
-		ov511_reg_write(dev, 0x79, 0x00); // Disable LUTs
+		break;
+	}
+	default:
+		sprintf(name, "%s", "Camera");
 	}
 
-	if (ov511_restart(ov511->dev) < 0)
-		return -EIO;
+	return 0;
+}
 
-	if (mlist[i].width == 0) {
-		err("Unknown mode (%d, %d): %d", width, height, mode);
+/* Set norm (NTSC, PAL, SECAM, AUTO) */
+static int 
+decoder_set_norm(struct usb_ov511 *ov511, int norm)
+{
+	PDEBUG(4, "%d", norm);
+
+	switch (ov511->sensor) {
+	case SEN_SAA7111A:
+	{
+		int reg_8, reg_e;
+
+		if (norm == VIDEO_MODE_NTSC) {
+			reg_8 = 0x40;	/* 60 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_PAL) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */	
+		} else if (norm == VIDEO_MODE_AUTO) {
+			reg_8 = 0x80;	/* Auto field detect */
+			reg_e = 0x00;	/* NTSC M / PAL BGHI */
+		} else if (norm == VIDEO_MODE_SECAM) {
+			reg_8 = 0x00;	/* 50 Hz */
+			reg_e = 0x50;	/* SECAM / PAL 4.43 */
+		} else {
+			return -EINVAL;
+		}
+
+		ov51x_i2c_write_mask(ov511, 0x08, reg_8, 0xc0);
+		ov51x_i2c_write_mask(ov511, 0x0e, reg_e, 0x70);
+		break;
+	}
+	default:
 		return -EINVAL;
 	}
 
-#ifdef OV511_DEBUG
-	if (debug >= 5)
-		ov511_dump_i2c_regs(dev);
-#endif
-
 	return 0;
 }
 
+
 /**********************************************************************
  *
  * Color correction functions
@@ -1169,7 +3241,7 @@
 
 static inline void
 ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, 
-	int rowPixels, unsigned char * rgb, int bits)
+		     int rowPixels, unsigned char * rgb, int bits)
 {
 	const int rvScale = 91881;
 	const int guScale = -22553;
@@ -1192,34 +3264,104 @@
 
 	if (bits == 24) {
 		/* Write out top two pixels */
-		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL);
-		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR);
+		rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
+		rgb[2] = LIMIT(r+yTL);
+
+		rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
+		rgb[5] = LIMIT(r+yTR);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 3 * rowPixels;
-		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL);
-		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR);
+		rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
+		rgb[2] = LIMIT(r+yBL);
+
+		rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
+		rgb[5] = LIMIT(r+yBR);
 	} else if (bits == 16) {
 		/* Write out top two pixels */
-		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) | ((LIMIT(g+yTL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07) | (LIMIT(r+yTL) & 0xF8);
-
-		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) | ((LIMIT(g+yTR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) | (LIMIT(r+yTR) & 0xF8);
+		rgb[0] = ((LIMIT(b+yTL) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yTL) >> 5) & 0x07)
+			| (LIMIT(r+yTL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yTR) >> 3) & 0x1F) 
+			| ((LIMIT(g+yTR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yTR) >> 5) & 0x07) 
+			| (LIMIT(r+yTR) & 0xF8);
 
 		/* Skip down to next line to write out bottom two pixels */
 		rgb += 2 * rowPixels;
 
-		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F) | ((LIMIT(g+yBL) << 3) & 0xE0);
-		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07) | (LIMIT(r+yBL) & 0xF8);
+		rgb[0] = ((LIMIT(b+yBL) >> 3) & 0x1F)
+			| ((LIMIT(g+yBL) << 3) & 0xE0);
+		rgb[1] = ((LIMIT(g+yBL) >> 5) & 0x07)
+			| (LIMIT(r+yBL) & 0xF8);
+
+		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F)
+			| ((LIMIT(g+yBR) << 3) & 0xE0);
+		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07)
+			| (LIMIT(r+yBR) & 0xF8);
+	}
+}
+
+/**********************************************************************
+ *
+ * Raw data parsing
+ *
+ **********************************************************************/
+
+/* Copies a 64-byte segment at pIn to an 8x8 block at pOut. The width of the
+ * array at pOut is specified by w.
+ */
+static inline void 
+ov511_make_8x8(unsigned char *pIn, unsigned char *pOut, int w)
+{
+	unsigned char *pOut1 = pOut;
+	int x, y;
+
+	for (y = 0; y < 8; y++) {
+		pOut1 = pOut;
+		for (x = 0; x < 8; x++) {
+			*pOut1++ = *pIn++;
+		}
+		pOut += w;
+	}
+		
+}
+
+/*
+ * For RAW BW (YUV400) images, data shows up in 256 byte segments.
+ * The segments represent 4 squares of 8x8 pixels as follows:
+ *
+ *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
+ *      8  9 ... 15    72  73 ...  79        200 201 ... 207
+ *           ...              ...                    ...
+ *     56 57 ... 63   120 121 ... 127        248 249 ... 255
+ *
+ */ 
+static void
+yuv400raw_to_yuv400p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
+{
+	int x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
 
-		rgb[2] = ((LIMIT(b+yBR) >> 3) & 0x1F) | ((LIMIT(g+yBR) << 3) & 0xE0);
-		rgb[3] = ((LIMIT(g+yBR) >> 5) & 0x07) | (LIMIT(r+yBR) & 0xF8);
+	/* Copy Y */
+	pIn = pIn0;
+	pOutLine = pOut0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+		}
+		pOutLine += 8 * frame->rawwidth;
 	}
 }
 
 /*
- * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments.
+ * For YUV4:2:0 images, the data shows 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:
  *
@@ -1239,7 +3381,9 @@
  *     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.
+ * area, but the Y data represents 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.
  *
  * If dumppix module param is set, _parse_data just dumps the incoming segments,
  * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
@@ -1248,391 +3392,504 @@
  * this data is scrambled.
  */
 
-#define HDIV 8
-#define WDIV (256/HDIV)
-
+/* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
+ *
+ * FIXME: Currently only handles width and height that are multiples of 16
+ */
 static void
-ov511_parse_gbr422_to_rgb24(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth)
+yuv420raw_to_yuv420p(struct ov511_frame *frame,
+		     unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
+	int k, x, y;
+	unsigned char *pIn, *pOut, *pOutLine;
+	const unsigned int a = frame->rawwidth * frame->rawheight;
+	const unsigned int w = frame->rawwidth / 2;
 
+	/* Copy U and V */
 	pIn = pIn0;
-	pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0);
-		
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	pOutLine = pOut0 + a;
+	for (y = 0; y < frame->rawheight - 1; y += 16) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 16) {
+			ov511_make_8x8(pIn, pOut, w);
+			ov511_make_8x8(pIn + 64, pOut + a/4, w);
+			pIn += 384;
+			pOut += 8;
 		}
-		pOut += iWidth*3*2;
+		pOutLine += 8 * w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2);
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) =
-				*(pOut1 + iWidth*3 + 3) = *pIn++;
-			pOut1 += 6;
+	/* Copy Y */
+	pIn = pIn0 + 128;
+	pOutLine = pOut0;
+	k = 0;
+	for (y = 0; y < frame->rawheight - 1; y += 8) {
+		pOut = pOutLine;
+		for (x = 0; x < frame->rawwidth - 1; x += 8) {
+			ov511_make_8x8(pIn, pOut, frame->rawwidth);
+			pIn += 64;
+			pOut += 8;
+			if ((++k) > 3) {
+				k = 0;
+				pIn += 128;
+			}
+		}
+		pOutLine += 8 * frame->rawwidth;
+	}
+}
+
+/*
+ * fixFrameRGBoffset--
+ * My camera seems to return the red channel about 1 pixel
+ * low, and the blue channel about 1 pixel high. After YUV->RGB
+ * conversion, we can correct this easily. OSL 2/24/2000.
+ */
+static void 
+fixFrameRGBoffset(struct ov511_frame *frame)
+{
+	int x, y;
+	int rowBytes = frame->width*3, w = frame->width;
+	unsigned char *rgb = frame->data;
+	const int shift = 1;  /* Distance to shift pixels by, vertically */
+
+	/* Don't bother with little images */
+	if (frame->width < 400) 
+		return;
+
+	/* This only works with RGB24 */
+	if (frame->format != VIDEO_PALETTE_RGB24)
+		return;
+
+	/* Shift red channel up */
+	for (y = shift; y < frame->height; y++)	{
+		int lp = (y-shift)*rowBytes;     /* Previous line offset */
+		int lc = y*rowBytes;             /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	}
+
+	/* Shift blue channel down */
+	for (y = frame->height-shift-1; y >= 0; y--) {
+		int ln = (y + shift) * rowBytes;  /* Next line offset */
+		int lc = y * rowBytes;            /* Current line offset */
+		for (x = 0; x < w; x++)
+			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	}
+}
+
+/**********************************************************************
+ *
+ * Decompression
+ *
+ **********************************************************************/
+
+/* Chooses a decompression module, locks it, and sets ov511->decomp_ops
+ * accordingly. Returns -ENXIO if decompressor is not available, otherwise
+ * returns 0 if no other error.
+ */
+static int 
+ov51x_request_decompressor(struct usb_ov511 *ov511)
+{
+	if (!ov511)
+		return -ENODEV;
+
+	if (ov511->decomp_ops) {
+		err("ERROR: Decompressor already requested!");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	/* Try to get MMX, and fall back on no-MMX if necessary */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV511 MMX decompressor");
+			ov511->decomp_ops = ov511_mmx_decomp_ops;
+		} else if (ov511_decomp_ops) {
+			PDEBUG(3, "Using OV511 decompressor");
+			ov511->decomp_ops = ov511_decomp_ops;
+		} else {
+			err("No decompressor available");
+		}
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_mmx_decomp_ops) {
+			PDEBUG(3, "Using OV518 MMX decompressor");
+			ov511->decomp_ops = ov518_mmx_decomp_ops;
+		} else if (ov518_decomp_ops) {
+			PDEBUG(3, "Using OV518 decompressor");
+			ov511->decomp_ops = ov518_decomp_ops;
+		} else {
+			err("No decompressor available");
 		}
-		pOut += iWidth*3*2;
+	} else {
+		err("Unknown bridge");
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY + 1;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = *pIn++;
-				pOut1 += 3;
-			}
-			pOut1 += (iWidth - 8) * 3;
+	if (ov511->decomp_ops) {
+		if (!ov511->decomp_ops->decomp_lock) {
+			ov511->decomp_ops = NULL;
+			unlock_kernel();
+			return -ENOSYS;
 		}
-		pOut += 8 * 3;
+		ov511->decomp_ops->decomp_lock();
+		unlock_kernel();
+		return 0;
+	} else {
+		unlock_kernel();
+		return -ENXIO;
 	}
 }
 
-static void
-ov511_parse_yuv420_to_rgb(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iHalf, int iWidth, int bits)
+/* Unlocks decompression module and nulls ov511->decomp_ops. Safe to call even
+ * if ov511->decomp_ops is NULL.
+ */
+static void 
+ov51x_release_decompressor(struct usb_ov511 *ov511)
 {
-	int k, l, m;
-	int bytes = bits >> 3;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-
-	/* Just copy the Y's if in the first stripe */
-	if (!iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l = 0; l < 8; l++) {
-				for (m = 0; m < 8; m++) {
-					*pOut1 = *pIn++;
-					pOut1 += bytes;
-				}
-				pOut1 += (iWidth - 8) * bytes;
-			}
-			pOut += 8 * bytes;
-		}
-	}
+	int released = 0;	/* Did we actually do anything? */
 
-	/* Use the first half of VUs to calculate value */
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV;
-	for (l = 0; l < 4; l++) {
-		for (m=0; m<8; m++) {
-			int y00 = *(pOut);
-			int y01 = *(pOut+bytes);
-			int y10 = *(pOut+iWidth*bytes);
-			int y11 = *(pOut+iWidth*bytes+bytes);
-			int v   = *(pIn+64) - 128;
-			int u   = *pIn++ - 128;
-			ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth,
-				pOut, bits);
-			pOut += 2 * bytes;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
+	if (!ov511)
+		return;
+
+	lock_kernel();
+
+	if (ov511->decomp_ops && ov511->decomp_ops->decomp_unlock) {
+		ov511->decomp_ops->decomp_unlock();
+		released = 1;
 	}
 
-	/* Just copy the other UV rows */
-	for (l = 0; l < 4; l++) {
-		for (m = 0; m < 8; m++) {
-			*pOut++ = *(pIn + 64);
-			*pOut = *pIn++;
-			pOut += 2 * bytes - 1;
-		}
-		pOut += (iWidth*2 - 16) * bytes;
-	}
-
-	/* Calculate values if it's the second half */
-	if (iHalf) {
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (k = 0; k < 4; k++) {
-			pOut1 = pOut;
-			for (l=0; l<4; l++) {
-				for (m=0; m<4; m++) {
-					int y10 = *(pIn+8);
-					int y00 = *pIn++;
-					int y11 = *(pIn+8);
-					int y01 = *pIn++;
-					int v   = *pOut1 - 128;
-					int u   = *(pOut1+1) - 128;
-					ov511_move_420_block(y00, y01, y10,
-						y11, u, v, iWidth, pOut1, bits);
-					pOut1 += 2 * bytes;
-				}
-				pOut1 += (iWidth*2 - 8) * bytes;
-				pIn += 8;
-			}
-			pOut += 8 * bytes;
-		}
+	ov511->decomp_ops = NULL;
+	
+	unlock_kernel();
+
+	if (released)
+		PDEBUG(3, "Decompressor released");
+}
+
+static void 
+ov51x_decompress(struct usb_ov511 *ov511, struct ov511_frame *frame,
+		 unsigned char *pIn0, unsigned char *pOut0)
+{
+	if (!ov511->decomp_ops)
+		if (ov51x_request_decompressor(ov511))
+			return;
+
+	PDEBUG(4, "Decompressing %d bytes", frame->bytes_recvd);
+
+	if (frame->format == VIDEO_PALETTE_GREY 
+	    && ov511->decomp_ops->decomp_400) {
+		int ret = ov511->decomp_ops->decomp_400(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_400 returned %d", ret);
+	} else if (ov511->decomp_ops->decomp_420) {
+		int ret = ov511->decomp_ops->decomp_420(
+			pIn0,
+			pOut0,
+			frame->rawwidth,
+			frame->rawheight,
+			frame->bytes_recvd);
+		PDEBUG(4, "DEBUG: decomp_420 returned %d", ret);
+	} else {
+		err("Decompressor does not support this format");
 	}
 }
 
-static void
-ov511_dumppix(unsigned char *pIn0, unsigned char *pOut0,
-	      int iOutY, int iOutUV, int iHalf, int iWidth)
-{
-	int i, j, k;
-	unsigned char *pIn, *pOut, *pOut1;
+/**********************************************************************
+ *
+ * Format conversion
+ *
+ **********************************************************************/
 
-	switch (dumppix) {
-	case 1: /* Just dump YUV data straight out for debug */
-		pOut0 += iOutY;
-		for (i = 0; i < HDIV; i++) {
-			for (j = 0; j < WDIV; j++) {
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-				*pOut0++ = *pIn0++;
-			}
-			pOut0 += (iWidth - WDIV) * 3;
-		}
-		break;
-	case 2: /* This converts the Y data to "black-and-white" RGB data */
-		/* Useful for experimenting with compression */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + iOutY;
-		for (i = 0; i < 4; i++) {
-			pOut1 = pOut;
-			for (j = 0; j < 8; j++) {
-				for (k = 0; k < 8; k++) {
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn;
-					*pOut1++ = *pIn++;
-				}
-				pOut1 += (iWidth - 8) * 3;
-			}
-			pOut += 8 * 3;
-		}
-		break;
-	case 3: /* This will dump only the Y channel data stream as-is */
-		pIn = pIn0 + 128;
-		pOut = pOut0 + output_offset;
-		for (i = 0; i < 256; i++) {
-			*pOut++ = *pIn;
-			*pOut++ = *pIn;
-			*pOut++ = *pIn++;
-			output_offset += 3;
-		}
-		break;
-	} /* End switch (dumppix) */
-}
+/* Converts from planar YUV420 to RGB24. */
+static void 
+yuv420p_to_rgb(struct ov511_frame *frame,
+	       unsigned char *pIn0, unsigned char *pOut0, int bits)
+{
+	const int numpix = frame->width * frame->height;
+	const int bytes = bits >> 3;
+	int i, j, y00, y01, y10, y11, u, v;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			y00 = *pY;
+			y01 = *(pY + 1);
+			y10 = *(pY + frame->width);
+			y11 = *(pY + frame->width + 1);
+			u = (*pU++) - 128;
+			v = (*pV++) - 128;
 
-/* This converts YUV420 segments to YUYV */
-static void
-ov511_parse_data_yuv422(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth)
-{
-	int k, l, m;
-	unsigned char *pIn, *pOut, *pOut1;
+			ov511_move_420_block(y00, y01, y10, y11, u, v,
+					     frame->width, pOut, bits);
+	
+			pY += 2;
+			pOut += 2 * bytes;
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1 = (*pIn++);
-				pOut1 += 2;
-			}
-			pOut1 += (iWidth - 8) * 2;
 		}
-		pOut += 8 * 2;
+		pY += frame->width;
+		pOut += frame->width * bytes;
 	}
+}
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + 1;
-	for (l = 0; l < 8; l++) {
-		for (m=0; m<8; m++) {
-			int v   = *(pIn+64);
-			int u   = *pIn++;
+/* Converts from planar YUV420 to YUV422 (YUYV). */
+static void
+yuv420p_to_yuv422(struct ov511_frame *frame,
+		  unsigned char *pIn0, unsigned char *pOut0)
+{
+	const int numpix = frame->width * frame->height;
+	int i, j;
+	unsigned char *pY = pIn0;
+	unsigned char *pU = pY + numpix;
+	unsigned char *pV = pU + numpix / 4;
+	unsigned char *pOut = pOut0;
+
+	for (i = 0; i < numpix; i++) {
+		*pOut = *(pY + i);
+		pOut += 2;
+	}
+
+	pOut = pOut0 + 1;
+	for (j = 0; j <= frame->height - 2 ; j += 2) {
+		for (i = 0; i <= frame->width - 2; i += 2) {
+			int u = *pU++;
+			int v = *pV++;
 			
 			*pOut = u;
 			*(pOut+2) = v;
-			*(pOut+iWidth) = u;
-			*(pOut+iWidth+2) = v;
+			*(pOut+frame->width*2) = u;
+			*(pOut+frame->width*2+2) = v;
 			pOut += 4;
 		}
-		pOut += (iWidth*4 - 32);
+		pOut += (frame->width * 2);
 	}
 }
 
+/* Converts pData from planar YUV420 to planar YUV422 **in place**. */
 static void
-ov511_parse_data_yuv420(unsigned char *pIn0, unsigned char *pOut0,
-		        int iOutY, int iOutUV, int iWidth, int iHeight)
+yuv420p_to_yuv422p(struct ov511_frame *frame, unsigned char *pData)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
-
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
+	const int numpix = frame->width * frame->height;
+	const int w = frame->width;
+	int j;
+	unsigned char *pIn, *pOut;
+
+	/* Clear U and V */
+	memset(pData + numpix + numpix / 2, 127, numpix / 2);
+
+	/* Convert V starting from beginning and working forward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix +numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
+		pIn += w/2;
 		pOut += w;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/4;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) *pOut1++ = *pIn++;
-		pOut += w;
-	}
-
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
-		}
-		pOut += 8;
+	/* Convert U, starting from end and working backward */
+	pIn = pData + numpix + numpix / 4;
+	pOut = pData + numpix + numpix / 2;
+	for (j = 0; j <= frame->height - 2; j += 2) {
+		pIn -= w/2;
+		pOut -= w;
+		memmove(pOut, pIn, w/2);
+		memmove(pOut + w/2, pIn, w/2);
 	}
 }
 
+/* Fuses even and odd fields together, and doubles width.
+ * INPUT: an odd field followed by an even field at pIn0, in YUV planar format
+ * OUTPUT: a normal YUV planar image, with correct aspect ratio
+ */
 static void
-ov511_parse_data_yuv422p(unsigned char *pIn0, unsigned char *pOut0,
-		       int iOutY, int iOutUV, int iWidth, int iHeight)
+deinterlace(struct ov511_frame *frame, int rawformat,
+            unsigned char *pIn0, unsigned char *pOut0)
 {
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
-	unsigned a = iWidth * iHeight;
-	unsigned w = iWidth / 2;
+	const int fieldheight = frame->rawheight / 2;
+	const int fieldpix = fieldheight * frame->rawwidth;
+	const int w = frame->width;
+	int x, y;
+	unsigned char *pInEven, *pInOdd, *pOut;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutUV + a;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	PDEBUG(5, "fieldheight=%d", fieldheight);
+
+	if (frame->rawheight != frame->height) {
+		err("invalid height");
+		return;
 	}
 
-	pIn = pIn0 + 64;
-	pOut = pOut0 + iOutUV + a + a/2;
-	for (k = 0; k < 8; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			*pOut1 = *(pOut1 + w) = *pIn++;
-			pOut1++;
-		}
-		pOut += iWidth;
+	if ((frame->rawwidth * 2) != frame->width) {
+		err("invalid width");
+		return;
 	}
 
-	pIn = pIn0 + 128;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++)
-				*pOut1++ =*pIn++;
-			pOut1 += iWidth - 8;
+	/* Y */
+	pInOdd = pIn0;
+	pInEven = pInOdd + fieldpix;
+	pOut = pOut0;
+	for (y = 0; y < fieldheight; y++) {
+		for (x = 0; x < frame->rawwidth; x++) {
+			*pOut = *pInEven;
+			*(pOut+1) = *pInEven++;
+			*(pOut+w) = *pInOdd;
+			*(pOut+w+1) = *pInOdd++;
+			pOut += 2;
 		}
-		pOut += 8;
+		pOut += w;
 	}
-}
-
-/*
- * For 640x480 RAW BW images, data shows up in 1200 256 byte segments.
- * The segments represent 4 squares of 8x8 pixels as follows:
- *
- *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
- *      8  9 ... 15    72  73 ...  79        200 201 ... 207
- *           ...              ...                    ...
- *     56 57 ... 63   120 121 ... 127        248 249 ... 255
- *
- */ 
-static void
-ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0,
-		      int iOutY, int iWidth)		    
-{
-	int k, l, m;
-	unsigned char *pIn;
-	unsigned char *pOut, *pOut1;
 
-	pIn = pIn0;
-	pOut = pOut0 + iOutY;
-	for (k = 0; k < 4; k++) {
-		pOut1 = pOut;
-		for (l = 0; l < 8; l++) {
-			for (m = 0; m < 8; m++) {
-				*pOut1++ = *pIn++;
+	if (rawformat == RAWFMT_YUV420) {
+	/* U */
+		pInOdd = pIn0 + fieldpix * 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
+			}
+			pOut += w/2;
+		}
+	/* V */
+		pInOdd = pIn0 + fieldpix * 2 + fieldpix / 2;
+		pInEven = pInOdd + fieldpix / 4;
+		for (y = 0; y < fieldheight / 2; y++) {
+			for (x = 0; x < frame->rawwidth / 2; x++) {
+				*pOut = *pInEven;
+				*(pOut+1) = *pInEven++;
+				*(pOut+w/2) = *pInOdd;
+				*(pOut+w/2+1) = *pInOdd++;
+				pOut += 2;
 			}
-			pOut1 += iWidth - 8;
+			pOut += w/2;
 		}
-		pOut += 8;
 	}
 }
 
-/*
- * fixFrameRGBoffset--
- * My camera seems to return the red channel about 1 pixel
- * low, and the blue channel about 1 pixel high. After YUV->RGB
- * conversion, we can correct this easily. OSL 2/24/2000.
+/* Post-processes the specified frame. This consists of:
+ * 	1. Decompress frame, if necessary
+ *	2. Deinterlace frame and scale to proper size, if necessary
+ * 	3. Convert from YUV planar to destination format, if necessary
+ * 	4. Fix the RGB offset, if necessary
  */
-static void fixFrameRGBoffset(struct ov511_frame *frame)
+static void 
+ov511_postprocess(struct usb_ov511 *ov511, struct ov511_frame *frame)
 {
-	int x, y;
-	int rowBytes = frame->width*3, w = frame->width;
-	unsigned char *rgb = frame->data;
-	const int shift = 1;  /* Distance to shift pixels by, vertically */
+	if (dumppix) {
+		memset(frame->data, 0, 
+			MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
+		PDEBUG(4, "Dumping %d bytes", frame->bytes_recvd);
+		memmove(frame->data, frame->rawdata, frame->bytes_recvd);
+		return;
+	}
+
+	/* YUV400 must be handled separately */
+	if (frame->format == VIDEO_PALETTE_GREY) {
+		/* Deinterlace frame, if necessary */
+		if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->tempdata);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->tempdata);
+
+			deinterlace(frame, RAWFMT_YUV400, frame->tempdata,
+			            frame->data);
+		} else {
+			if (frame->compressed)
+				ov51x_decompress(ov511, frame, frame->rawdata,
+						 frame->data);
+			else
+				yuv400raw_to_yuv400p(frame, frame->rawdata,
+						     frame->data);
+		}
 
-	/* Don't bother with little images */
-	if (frame->width < 400) 
 		return;
+	}
 
-	/* Shift red channel up */
-	for (y = shift; y < frame->height; y++)	{
-		int lp = (y-shift)*rowBytes;     /* Previous line offset */
-		int lc = y*rowBytes;             /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */
+	/* Process frame->data to frame->rawdata */
+	if (frame->compressed)
+		ov51x_decompress(ov511, frame, frame->rawdata, frame->tempdata);
+	else
+		yuv420raw_to_yuv420p(frame, frame->rawdata, frame->tempdata);
+
+	/* Deinterlace frame, if necessary */
+	if (ov511->sensor == SEN_SAA7111A && frame->rawheight == 480) {
+		memmove(frame->rawdata, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		deinterlace(frame, RAWFMT_YUV420, frame->rawdata,
+		            frame->tempdata);
 	}
 
-	/* Shift blue channel down */
-	for (y = frame->height-shift-1; y >= 0; y--) {
-		int ln = (y + shift) * rowBytes;  /* Next line offset */
-		int lc = y * rowBytes;            /* Current line offset */
-		for (x = 0; x < w; x++)
-			rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */
+	/* Frame should be (width x height) and not (rawwidth x rawheight) at
+         * this point. */
+
+#if 0
+	/* Clear output buffer for testing purposes */
+	memset(frame->data, 0, MAX_DATA_SIZE(frame->width, frame->height));
+#endif
+
+	/* Process frame->tempdata to frame->data */
+	switch (frame->format) {
+	case VIDEO_PALETTE_RGB565:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 16);
+		break;
+	case VIDEO_PALETTE_RGB24:
+		yuv420p_to_rgb(frame, frame->tempdata, frame->data, 24);
+		break;
+	case VIDEO_PALETTE_YUV422:
+	case VIDEO_PALETTE_YUYV:
+		yuv420p_to_yuv422(frame, frame->tempdata, frame->data);
+		break;
+	case VIDEO_PALETTE_YUV420:
+	case VIDEO_PALETTE_YUV420P:
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+		break;
+	case VIDEO_PALETTE_YUV422P:
+		/* Data is converted in place, so copy it in advance */
+		memmove(frame->data, frame->tempdata,
+			MAX_RAW_DATA_SIZE(frame->width, frame->height));
+
+		yuv420p_to_yuv422p(frame, frame->data);
+		break;
+	default:
+		err("Cannot convert data to this format");
 	}
+
+	if (fix_rgb_offset)
+		fixFrameRGBoffset(frame);
 }
 
 /**********************************************************************
  *
- * OV511 data transfer, IRQ handler
+ * OV51x data transfer, IRQ handler
  *
  **********************************************************************/
 
-static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
+static int 
+ov511_move_data(struct usb_ov511 *ov511, urb_t *urb)
 {
 	unsigned char *cdata;
-	int i, totlen = 0;
-	int aPackNum[10];
+	int data_size, num, offset, i, totlen = 0;
+	int aPackNum[FRAMES_PER_DESC];
 	struct ov511_frame *frame;
-	unsigned char *pData;
-	int iPix;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
 
-	PDEBUG (4, "Moving %d packets", urb->number_of_packets);
+	data_size = ov511->packet_size - 1;
 
 	for (i = 0; i < urb->number_of_packets; i++) {
 		int n = urb->iso_frame_desc[i].actual_length;
@@ -1652,44 +3909,65 @@
 			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
 
 		frame = &ov511->frame[ov511->curframe];
-		
+
 		/* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th
 		 * byte non-zero. The EOF packet has image width/height in the
-		 * 10th and 11th packets. The 9th bit is given as follows:
+		 * 10th and 11th bytes. The 9th byte is given as follows:
 		 *
 		 * bit 7: EOF
 		 *     6: compression enabled
 		 *     5: 422/420/400 modes
 		 *     4: 422/420/400 modes
 		 *     3: 1
-		 *     2: snapshot bottom on
+		 *     2: snapshot button on
 		 *     1: snapshot frame
 		 *     0: even/odd field
 		 */
 
+		if (printph) {
+			info("packet header (%3d): %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[ov511->packet_size - 1],
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
+
 		/* Check for SOF/EOF packet */
-		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | 
+		if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] |
 		     cdata[4] | cdata[5] | cdata[6] | cdata[7]) ||
 		     (~cdata[8] & 0x08))
 			goto check_middle;
 
 		/* Frame end */
 		if (cdata[8] & 0x80) {
-			struct timeval *ts;
-
-			ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE);
-			do_gettimeofday (ts);
-
-		 	PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d",
-				ov511->curframe, (int)(cdata[ov511->packet_size - 1]),
-				(int)(cdata[9]), (int)(cdata[10]));
+			ts = (struct timeval *)(frame->data 
+			      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+			do_gettimeofday(ts);
+
+			/* Get the actual frame size from the EOF header */
+			frame->rawwidth = ((int)(cdata[9]) + 1) * 8;
+			frame->rawheight = ((int)(cdata[10]) + 1) * 8;
+
+	 		PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d, recvd=%d",
+				ov511->curframe,
+				(int)(cdata[ov511->packet_size - 1]),
+				frame->rawwidth,
+				frame->rawheight,
+				frame->bytes_recvd);
+
+			/* Validate the header data */
+			RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+			RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+			/* Don't allow byte count to exceed buffer size */
+			RESTRICT_TO_RANGE(frame->bytes_recvd,
+					  8, 
+					  MAX_RAW_DATA_SIZE(ov511->maxwidth,
+					                    ov511->maxheight));
 
 			if (frame->scanstate == STATE_LINES) {
 		    		int iFrameNext;
 
-				if (fix_rgb_offset)
-					fixFrameRGBoffset(frame);
-				frame->grabstate = FRAME_DONE;
+				frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
 
 				if (waitqueue_active(&frame->wq)) {
 					frame->grabstate = FRAME_DONE;
@@ -1713,6 +3991,8 @@
 
 					ov511->curframe = -1;
 				}
+			} else {
+				PDEBUG(5, "Frame done, but not scanning");
 			}
 			/* Image corruption caused by misplaced frame->segment = 0
 			 * fixed by carlosf@conectiva.com.br
@@ -1721,13 +4001,6 @@
 			/* Frame start */
 			PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
 
-#if 0
-			/* Make sure no previous data carries over; necessary
-			 * for compression experimentation */
-			memset(frame->data, 0, MAX_DATA_SIZE);
-#endif
-			output_offset = 0;
-
 			/* Check to see if it's a snapshot frame */
 			/* FIXME?? Should the snapshot reset go here? Performance? */
 			if (cdata[8] & 0x02) {
@@ -1736,129 +4009,324 @@
 			}
 
 			frame->scanstate = STATE_LINES;
-			frame->segment = 0;
+			frame->bytes_recvd = 0;
+			frame->compressed = cdata[8] & 0x40;
 		}
 
 check_middle:
 		/* Are we in a frame? */
-		if (frame->scanstate != STATE_LINES)
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(5, "Not in a frame; packet skipped");
+			continue;
+		}
+
+#if 0
+		/* Skip packet if first 9 bytes are zero. These are common, so
+		 * we use a less expensive test here instead of later */
+		if (frame->compressed) {
+			int b, skip = 1;
+
+			for (b = 0; b < 9; b++) { 
+				if (cdata[b])
+					skip=0;
+			}
+
+			if (skip) {
+				PDEBUG(5, "Skipping packet (all zero)");
+				continue;
+			}
+		}
+#endif
+		/* If frame start, skip header */
+		if (frame->bytes_recvd == 0)
+			offset = 9;
+		else
+			offset = 0;
+
+		num = n - offset - 1;
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n - 1;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - (n - 1),
+					&cdata[0], n - 1);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else if (!frame->compressed && !remove_zeros) {
+			frame->bytes_recvd += num;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - num,
+					&cdata[offset], num);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else { /* Remove all-zero FIFO lines (aligned 32-byte blocks) */
+			int b, in = 0, allzero, copied=0;
+			if (offset) {
+				frame->bytes_recvd += 32 - offset;	// Bytes out
+				memmove(frame->rawdata,
+					&cdata[offset], 32 - offset);
+				in += 32;
+			}
+
+			while (in < n - 1) {
+				allzero = 1;
+				for (b = 0; b < 32; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
+
+				if (allzero) {
+					/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 32
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 32);
+						copied += 32;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 32;
+			}
+
+			frame->bytes_recvd += copied;
+		}
+
+	}
+
+	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
+		aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
+		aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
+
+	return totlen;
+}
+
+static int 
+ov518_move_data(struct usb_ov511 *ov511, urb_t *urb)
+{
+	unsigned char *cdata;
+	int i, data_size, totlen = 0;
+	struct ov511_frame *frame;
+	struct timeval *ts;
+
+	PDEBUG(5, "Moving %d packets", urb->number_of_packets);
+
+	/* OV518(+) has no packet numbering */
+	data_size = ov511->packet_size;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		int n = urb->iso_frame_desc[i].actual_length;
+		int st = urb->iso_frame_desc[i].status;
+
+		urb->iso_frame_desc[i].actual_length = 0;
+		urb->iso_frame_desc[i].status = 0;
+
+		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+		if (!n) {
+			PDEBUG(4, "Zero-length packet");
 			continue;
+		}
+
+		if (ov511->curframe == -1) {
+			PDEBUG(4, "No frame currently active");
+			continue;
+		}
+
+		if (st)
+			PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st);
+
+		frame = &ov511->frame[ov511->curframe];
+
+#if 0
+		{
+			int d;
+			/* Print all data */
+			for (d = 0; d <= data_size - 16; d += 16) {
+				info("%4x: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x", d,
+					cdata[d], cdata[d+1], cdata[d+2], cdata[d+3],
+					cdata[d+4], cdata[d+5], cdata[d+6], cdata[d+7],
+					cdata[d+8], cdata[d+9], cdata[d+10], cdata[d+11],
+					cdata[d+12], cdata[d+13], cdata[d+14], cdata[d+15]);
+			}
+		}
+#endif
+
+		if (printph) {
+			info("packet header: %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x",
+				cdata[0], cdata[1], cdata[2], cdata[3], cdata[4], cdata[5],
+				cdata[6], cdata[7], cdata[8], cdata[9], cdata[10], cdata[11]);
+		}
 
-		/* Deal with leftover from last segment, if any */
-		if (frame->segment) {
-			pData = ov511->scratch;
-			iPix = -ov511->scratchlen;
-			memmove(pData + ov511->scratchlen, cdata,
-				iPix+frame->segsize);
+		/* A false positive here is likely, until OVT gives me
+		 * the definitive SOF/EOF format */
+		if ((!(cdata[0] | cdata[1] | cdata[2] | cdata[3] |
+		      cdata[5])) && cdata[6]) {
+			
+			if (frame->scanstate == STATE_LINES) {
+				PDEBUG(4, "Detected frame end/start");
+				goto eof;
+			} else { //scanstate == STATE_SCANNING
+				/* Frame start */
+				PDEBUG(4, "Frame start, framenum = %d", ov511->curframe);
+				goto sof;
+			}
 		} else {
-			pData = &cdata[iPix = 9];
-	 	}
+			goto check_middle;
+		}
+	
+eof:
+		ts = (struct timeval *)(frame->data
+		      + MAX_FRAME_SIZE(ov511->maxwidth, ov511->maxheight));
+		do_gettimeofday(ts);
+
+ 		PDEBUG(4, "Frame end, curframe = %d, hw=%d, vw=%d, recvd=%d",
+			ov511->curframe,
+			(int)(cdata[9]), (int)(cdata[10]), frame->bytes_recvd);
+
+		// FIXME: Since we don't know the header formats yet,
+		// there is no way to know what the actual image size is
+		frame->rawwidth = frame->width;
+		frame->rawheight = frame->height;
+
+		/* Validate the header data */
+		RESTRICT_TO_RANGE(frame->rawwidth, ov511->minwidth, ov511->maxwidth);
+		RESTRICT_TO_RANGE(frame->rawheight, ov511->minheight, ov511->maxheight);
+
+		/* Don't allow byte count to exceed buffer size */
+		RESTRICT_TO_RANGE(frame->bytes_recvd,
+				  8, 
+				  MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 
-		/* Parse the segments */
-		while (iPix <= (ov511->packet_size - 1) - frame->segsize &&
-		    frame->segment < frame->width * frame->height / 256) {
-			int iSegY, iSegUV;
-			int iY, jY, iUV, jUV;
-			int iOutY, iOutYP, iOutUV, iOutUVP;
-			unsigned char *pOut;
-
-			iSegY = iSegUV = frame->segment;
-			pOut = frame->data;
-			frame->segment++;
-			iPix += frame->segsize;
-
-			/* Handle subwindow */
-			if (frame->sub_flag) {
-				int iSeg1;
-
-				iSeg1 = iSegY / (ov511->subw / 32);
-				iSeg1 *= frame->width / 32;
-				iSegY = iSeg1 + (iSegY % (ov511->subw / 32));
-				if (iSegY >= frame->width * ov511->subh / 256)
-					break;
-
-				iSeg1 = iSegUV / (ov511->subw / 16);
-				iSeg1 *= frame->width / 16;
-				iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16));
-
-				pOut += (ov511->subx + ov511->suby * frame->width) *
-					(frame->depth >> 3);
-			}
-
-			/* 
-			 * i counts segment lines
-			 * j counts segment columns
-			 * iOut is the offset (in bytes) of the upper left corner
-			 */
-			iY = iSegY / (frame->width / WDIV);
-			jY = iSegY - iY * (frame->width / WDIV);
-			iOutYP = iY*HDIV*frame->width + jY*WDIV;
-			iOutY = iOutYP * (frame->depth >> 3);
-			iUV = iSegUV / (frame->width / WDIV * 2);
-			jUV = iSegUV - iUV * (frame->width / WDIV * 2);
-			iOutUVP = iUV*HDIV*2*frame->width + jUV*WDIV/2;
-			iOutUV = iOutUVP * (frame->depth >> 3);
-
-			switch (frame->format) {
-			case VIDEO_PALETTE_GREY:
-				ov511_parse_data_grey (pData, pOut, iOutY, frame->width);
-				break;
-			case VIDEO_PALETTE_RGB24:
-				if (dumppix)
-					ov511_dumppix(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else if (sensor_gbr)
-					ov511_parse_gbr422_to_rgb24(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width);
-				else
-					ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-						iY & 1, frame->width, 24);
-				break;
-			case VIDEO_PALETTE_RGB565:
-				ov511_parse_yuv420_to_rgb(pData, pOut, iOutY, iOutUV,
-					iY & 1, frame->width, 16);
-				break;
-			case VIDEO_PALETTE_YUV422:
-			case VIDEO_PALETTE_YUYV:
-				ov511_parse_data_yuv422(pData, pOut, iOutY, iOutUV, frame->width);
-				break;
-			case VIDEO_PALETTE_YUV420:
-				ov511_parse_data_yuv420 (pData, pOut, iOutYP, iUV*HDIV*frame->width/2 + jUV*WDIV/4,
-					frame->width, frame->height);
-				break;
-			case VIDEO_PALETTE_YUV422P:
-				ov511_parse_data_yuv422p (pData, pOut, iOutYP, iOutUVP/2,
-					frame->width, frame->height);
-				break;
-			default:
-				err("Unsupported format: %d", frame->format);
+		if (frame->scanstate == STATE_LINES) {
+	    		int iFrameNext;
+
+			frame->grabstate = FRAME_DONE;	// FIXME: Is this right?
+
+			if (waitqueue_active(&frame->wq)) {
+				frame->grabstate = FRAME_DONE;
+				wake_up_interruptible(&frame->wq);
 			}
 
-			pData = &cdata[iPix];
-		}
+			/* If next frame is ready or grabbing,
+			 * point to it */
+			iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES;
+			if (ov511->frame[iFrameNext].grabstate == FRAME_READY
+			    || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) {
+				ov511->curframe = iFrameNext;
+				ov511->frame[iFrameNext].scanstate = STATE_SCANNING;
+				frame = &ov511->frame[iFrameNext];
+			} else {
+				if (frame->grabstate == FRAME_DONE) {
+					PDEBUG(4, "Frame done! congratulations");
+				} else {
+					PDEBUG(4, "Frame not ready? state = %d",
+						ov511->frame[iFrameNext].grabstate);
+				}
+
+				ov511->curframe = -1;
+				PDEBUG(4, "SOF dropped (no active frame)");
+				continue;  /* Nowhere to store this frame */
+			}
+		}
+		/* Image corruption caused by misplaced frame->segment = 0
+		 * fixed by carlosf@conectiva.com.br
+		 */
+sof:
+		PDEBUG(4, "Starting capture on frame %d", frame->framenum);
+// Snapshot not reverse-engineered yet.
+#if 0
+		/* Check to see if it's a snapshot frame */
+		/* FIXME?? Should the snapshot reset go here? Performance? */
+		if (cdata[8] & 0x02) {
+			frame->snapshot = 1;
+			PDEBUG(3, "snapshot detected");
+		}
+#endif
+		frame->scanstate = STATE_LINES;
+		frame->bytes_recvd = 0;
+//		frame->compressed = 1;
+
+check_middle:
+		/* Are we in a frame? */
+		if (frame->scanstate != STATE_LINES) {
+			PDEBUG(4, "scanstate: no SOF yet");
+			continue;
+		}
+
+		/* Dump all data exactly as received */
+		if (dumppix == 2) {
+			frame->bytes_recvd += n;
+			if (frame->bytes_recvd <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight))
+				memmove(frame->rawdata + frame->bytes_recvd - n,
+					&cdata[0], n);
+			else
+				PDEBUG(3, "Raw data buffer overrun!! (%d)",
+					frame->bytes_recvd
+					- MAX_RAW_DATA_SIZE(ov511->maxwidth,
+							    ov511->maxheight));
+		} else {
+			/* All incoming data are divided into 8-byte segments. If the
+			 * segment contains all zero bytes, it must be skipped. These
+			 * zero-segments allow the OV518 to mainain a constant data rate
+			 * regardless of the effectiveness of the compression. Segments
+			 * are aligned relative to the beginning of each isochronous
+			 * packet. The first segment is a header.
+			 */
+
+			int b, in = 0, allzero, copied=0;
+
+// Decompressor expects the header
+#if 0
+			if (frame->bytes_recvd == 0)
+				in += 8;  /* Skip header */
+#endif
+
+			while (in < n) {
+				allzero = 1;
+				for (b = 0; b < 8; b++) {
+					if (cdata[in + b]) {
+						allzero = 0;
+						break;
+					}
+				}
 
-		/* Save extra data for next time */
-		if (frame->segment < frame->width * frame->height / 256) {
-			ov511->scratchlen = (ov511->packet_size - 1) - iPix;
-			if (ov511->scratchlen < frame->segsize)
-				memmove(ov511->scratch, pData, ov511->scratchlen);
-			else
-				ov511->scratchlen = 0;
+				if (allzero) {
+				/* Don't copy it */
+				} else {
+					if (frame->bytes_recvd + copied + 8
+					    <= MAX_RAW_DATA_SIZE(ov511->maxwidth, ov511->maxheight)) {
+						memmove(frame->rawdata + frame->bytes_recvd + copied,
+							&cdata[in], 8);
+						copied += 8;
+					} else {
+						PDEBUG(3, "Raw data buffer overrun!!");
+					}
+				}
+				in += 8;
+			}
+			frame->bytes_recvd += copied;
 		}
 	}
 
-	PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d",
-	       aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4],
-	       aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]);
-
 	return totlen;
 }
 
-static void ov511_isoc_irq(struct urb *urb)
+static void 
+ov511_isoc_irq(struct urb *urb)
 {
 	int len;
 	struct usb_ov511 *ov511;
-	struct ov511_sbuf *sbuf;
 
 	if (!urb->context) {
 		PDEBUG(4, "no context");
@@ -1876,60 +4344,86 @@
 		PDEBUG(4, "hmmm... not streaming, but got interrupt");
 		return;
 	}
-	
-	sbuf = &ov511->sbuf[ov511->cursbuf];
 
-	/* Copy the data received into our scratch buffer */
+	/* Copy the data received into our frame buffer */
 	if (ov511->curframe >= 0) {
-		len = ov511_move_data(ov511, urb);
+		if (ov511->bridge == BRG_OV511 || 
+		    ov511->bridge == BRG_OV511PLUS)
+			len = ov511_move_data(ov511, urb);
+		else if (ov511->bridge == BRG_OV518 ||
+			 ov511->bridge == BRG_OV518PLUS)
+			len = ov518_move_data(ov511, urb);
+		else
+			err("Unknown bridge device (%d)", ov511->bridge);
 	} else if (waitqueue_active(&ov511->wq)) {
 		wake_up_interruptible(&ov511->wq);
 	}
 
-	/* Move to the next sbuf */
-	ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF;
-
 	urb->dev = ov511->dev;
 
 	return;
 }
 
-static int ov511_init_isoc(struct usb_ov511 *ov511)
+/****************************************************************************
+ *
+ * Stream initialization and termination
+ *
+ ***************************************************************************/
+
+static int 
+ov511_init_isoc(struct usb_ov511 *ov511)
 {
 	urb_t *urb;
 	int fx, err, n, size;
 
 	PDEBUG(3, "*** Initializing capture ***");
 
-	ov511->compress = 0;
 	ov511->curframe = -1;
-	ov511->cursbuf = 0;
-	ov511->scratchlen = 0;
 
-	if (ov511->bridge == BRG_OV511)
-		if (cams == 1)			 size = 993;
-		else if (cams == 2)		 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
+	if (ov511->bridge == BRG_OV511) {
+		if (cams == 1)				size = 993;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else if (ov511->bridge == BRG_OV511PLUS)
-		if (cams == 1)                      size = 961;
-		else if (cams == 2)                 size = 513;
-		else if (cams == 3 || cams == 4) size = 257;
-		else if (cams >= 5 && cams <= 8)    size = 129;
-		else if (cams >= 9 && cams <= 31)   size = 33;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (cams == 1)				size = 961;
+		else if (cams == 2)			size = 513;
+		else if (cams == 3 || cams == 4)	size = 257;
+		else if (cams >= 5 && cams <= 8)	size = 129;
+		else if (cams >= 9 && cams <= 31)	size = 33;
 		else {
 			err("\"cams\" parameter too high!");
 			return -1;
 		}
-	else {
+	} else if (ov511->bridge == BRG_OV518 ||
+		   ov511->bridge == BRG_OV518PLUS) {
+		if (cams == 1)				size = 896;
+		else if (cams == 2)			size = 512;
+		else if (cams == 3 || cams == 4)	size = 256;
+		else if (cams >= 5 && cams <= 8)	size = 128;
+		else {
+			err("\"cams\" parameter too high!");
+			return -1;
+		}
+	} else {
 		err("invalid bridge type");
 		return -1;
 	}
 
-	ov511_set_packet_size(ov511, size);
+	if (packetsize == -1) {
+		// FIXME: OV518 is hardcoded to 15 FPS (alternate 5) for now
+		if (ov511->bridge == BRG_OV518 ||
+		    ov511->bridge == BRG_OV518PLUS)
+			ov511_set_packet_size(ov511, 640);
+		else
+			ov511_set_packet_size(ov511, size);
+	} else {
+			info("Forcing packet size to %d", packetsize);
+			ov511_set_packet_size(ov511, packetsize);
+	}
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		urb = usb_alloc_urb(FRAMES_PER_DESC);
@@ -1946,13 +4440,17 @@
 		urb->transfer_buffer = ov511->sbuf[n].data;
 		urb->complete = ov511_isoc_irq;
 		urb->number_of_packets = FRAMES_PER_DESC;
-		urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC;
+		urb->transfer_buffer_length =
+		 ov511->packet_size * FRAMES_PER_DESC;
 		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
-			urb->iso_frame_desc[fx].offset = ov511->packet_size * fx;
+			urb->iso_frame_desc[fx].offset = 
+			 ov511->packet_size * fx;
 			urb->iso_frame_desc[fx].length = ov511->packet_size;
 		}
 	}
 
+	ov511->streaming = 1;
+
 	ov511->sbuf[OV511_NUMSBUF - 1].urb->next = ov511->sbuf[0].urb;
 	for (n = 0; n < OV511_NUMSBUF - 1; n++)
 		ov511->sbuf[n].urb->next = ov511->sbuf[n+1].urb;
@@ -1964,19 +4462,18 @@
 			err("init isoc: usb_submit_urb(%d) ret %d", n, err);
 	}
 
-	ov511->streaming = 1;
-
 	return 0;
 }
 
-static void ov511_stop_isoc(struct usb_ov511 *ov511)
+static void 
+ov511_stop_isoc(struct usb_ov511 *ov511)
 {
 	int n;
 
 	if (!ov511->streaming || !ov511->dev)
 		return;
 
-	PDEBUG (3, "*** Stopping capture ***");
+	PDEBUG(3, "*** Stopping capture ***");
 
 	ov511_set_packet_size(ov511, 0);
 
@@ -1993,9 +4490,11 @@
 	}
 }
 
-static int ov511_new_frame(struct usb_ov511 *ov511, int framenum)
+static int 
+ov511_new_frame(struct usb_ov511 *ov511, int framenum)
 {
 	struct ov511_frame *frame;
+	int newnum;
 
 	PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe,
 		framenum);
@@ -2005,19 +4504,19 @@
 	/* If we're not grabbing a frame right now and the other frame is */
 	/* ready to be grabbed into, then use it instead */
 	if (ov511->curframe == -1) {
-		if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY)
-			framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		newnum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES;
+		if (ov511->frame[newnum].grabstate == FRAME_READY)
+			framenum = newnum;
 	} else
 		return 0;
 
 	frame = &ov511->frame[framenum];
 
-	PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, 
-		frame->width, frame->height);
+	PDEBUG(4, "framenum = %d, width = %d, height = %d", framenum, 
+	       frame->width, frame->height);
 
 	frame->grabstate = FRAME_GRABBING;
 	frame->scanstate = STATE_SCANNING;
-	frame->scanlength = 0;		/* accumulated in ov511_parse_data() */
 	frame->snapshot = 0;
 
 	ov511->curframe = framenum;
@@ -2041,9 +4540,12 @@
  * Buffer management
  *
  ***************************************************************************/
-static int ov511_alloc(struct usb_ov511 *ov511)
+static int 
+ov511_alloc(struct usb_ov511 *ov511)
 {
 	int i;
+	int w = ov511->maxwidth;
+	int h = ov511->maxheight;
 
 	PDEBUG(4, "entered");
 	down(&ov511->buf_lock);
@@ -2056,15 +4558,29 @@
 	if (ov511->buf_state == BUF_ALLOCATED)
 		goto out;
 
-	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE);
+	ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 	if (!ov511->fbuf)
 		goto error;
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].grabstate = FRAME_UNUSED;
-		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE;
-		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	ov511->rawfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->rawfbuf) {
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->rawfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
 
+	ov511->tempfbuf = vmalloc(OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+	if (!ov511->tempfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
+		ov511->fbuf = NULL;
+		goto error;
+	}
+	memset(ov511->tempfbuf, 0, OV511_NUMFRAMES * MAX_RAW_DATA_SIZE(w, h));
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC *
 			MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL);
 		if (!ov511->sbuf[i].data) {
@@ -2072,12 +4588,28 @@
 				kfree(ov511->sbuf[i].data);
 				ov511->sbuf[i].data = NULL;
 			}
-			rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+			vfree(ov511->tempfbuf);
+			ov511->tempfbuf = NULL;
+			vfree(ov511->rawfbuf);
+			ov511->rawfbuf = NULL;
+			rvfree(ov511->fbuf,
+			       OV511_NUMFRAMES * MAX_DATA_SIZE(w, h));
 			ov511->fbuf = NULL;
+
 			goto error;
 		}
 		PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data);
 	}
+
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE(w, h);
+		ov511->frame[i].rawdata = ov511->rawfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		ov511->frame[i].tempdata = ov511->tempfbuf 
+		 + i * MAX_RAW_DATA_SIZE(w, h);
+		PDEBUG(4, "frame[%d] @ %p", i, ov511->frame[i].data);
+	}
+
 	ov511->buf_state = BUF_ALLOCATED;
 out:
 	up(&ov511->buf_lock);
@@ -2095,29 +4627,48 @@
  * - Because this code will free any non-null pointer, you must be sure to null
  *   them if you explicitly free them somewhere else!
  */
-static void ov511_do_dealloc(struct usb_ov511 *ov511)
+static void 
+ov511_do_dealloc(struct usb_ov511 *ov511)
 {
 	int i;
 	PDEBUG(4, "entered");
 
 	if (ov511->fbuf) {
-		rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE);
+		rvfree(ov511->fbuf, OV511_NUMFRAMES
+		       * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight));
 		ov511->fbuf = NULL;
 	}
 
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
+	if (ov511->rawfbuf) {
+		vfree(ov511->rawfbuf);
+		ov511->rawfbuf = NULL;
+	}
+
+	if (ov511->tempfbuf) {
+		vfree(ov511->tempfbuf);
+		ov511->tempfbuf = NULL;
+	}
+
+	for (i = 0; i < OV511_NUMSBUF; i++) {
 		if (ov511->sbuf[i].data) {
 			kfree(ov511->sbuf[i].data);
 			ov511->sbuf[i].data = NULL;
 		}
 	}
 
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].data = NULL;
+		ov511->frame[i].rawdata = NULL;
+		ov511->frame[i].tempdata = NULL;
+	}
+
 	PDEBUG(4, "buffer memory deallocated");
 	ov511->buf_state = BUF_NOT_ALLOCATED;
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_buf_callback(unsigned long data)
+static void 
+ov511_buf_callback(unsigned long data)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)data;
 	PDEBUG(4, "entered");
@@ -2130,7 +4681,8 @@
 	PDEBUG(4, "leaving");
 }
 
-static void ov511_dealloc(struct usb_ov511 *ov511, int now)
+static void 
+ov511_dealloc(struct usb_ov511 *ov511, int now)
 {
 	struct timer_list *bt = &(ov511->buf_timer);
 	PDEBUG(4, "entered");
@@ -2163,13 +4715,14 @@
  *
  ***************************************************************************/
 
-static int ov511_open(struct video_device *dev, int flags)
+static int 
+ov511_open(struct video_device *vdev, int flags)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int err;
+	struct usb_ov511 *ov511 = vdev->priv;
+	int err, i;
 
-	MOD_INC_USE_COUNT;
 	PDEBUG(4, "opening");
+
 	down(&ov511->lock);
 
 	err = -EBUSY;
@@ -2182,6 +4735,24 @@
 
 	ov511->sub_flag = 0;
 
+	/* In case app doesn't set them... */
+	if (ov51x_set_default_params(ov511) < 0)
+		goto out;
+
+	/* Make sure frames are reset */
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].grabstate = FRAME_UNUSED;
+		ov511->frame[i].bytes_read = 0;
+	}
+
+	/* If compression is on, make sure now that a 
+	 * decompressor can be loaded */
+	if (ov511->compress && !ov511->decomp_ops) {
+		err = ov51x_request_decompressor(ov511);
+		if (err)
+			goto out;
+	}
+
 	err = ov511_init_isoc(ov511);
 	if (err) {
 		ov511_dealloc(ov511, 0);
@@ -2189,17 +4760,18 @@
 	}
 
 	ov511->user++;
+	
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 1);
 
 out:
 	up(&ov511->lock);
 
-	if (err)
-		MOD_DEC_USE_COUNT;
-
 	return err;
 }
 
-static void ov511_close(struct video_device *dev)
+static void 
+ov511_close(struct video_device *dev)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
 
@@ -2210,40 +4782,50 @@
 	ov511->user--;
 	ov511_stop_isoc(ov511);
 
+	ov51x_release_decompressor(ov511);
+
+	if (ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+
 	if (ov511->dev)
 		ov511_dealloc(ov511, 0);
 
 	up(&ov511->lock);
 
+	/* Device unplugged while open. Only a minimum of unregistration is done
+	 * here; the disconnect callback already did the rest. */
 	if (!ov511->dev) {
 		ov511_dealloc(ov511, 1);
 		video_unregister_device(&ov511->vdev);
 		kfree(ov511);
 		ov511 = NULL;
 	}
-
-	MOD_DEC_USE_COUNT;
 }
 
-static int ov511_init_done(struct video_device *dev)
+static int 
+ov511_init_done(struct video_device *vdev)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
-	create_proc_ov511_cam((struct usb_ov511 *)dev);
+	create_proc_ov511_cam((struct usb_ov511 *)vdev);
 #endif
 
 	return 0;
 }
 
-static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+static long 
+ov511_write(struct video_device *vdev, const char *buf,
+	    unsigned long count, int noblock)
 {
 	return -EINVAL;
 }
 
-static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+/* Do not call this function directly! */
+static int 
+ov511_ioctl_internal(struct video_device *vdev, unsigned int cmd, void *arg)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev;
 
-	PDEBUG(4, "IOCtl: 0x%X", cmd);
+	PDEBUG(5, "IOCtl: 0x%X", cmd);
 
 	if (!ov511->dev)
 		return -EIO;	
@@ -2253,17 +4835,24 @@
 	{
 		struct video_capability b;
 
-		PDEBUG (4, "VIDIOCGCAP");
+		PDEBUG(4, "VIDIOCGCAP");
 
 		memset(&b, 0, sizeof(b));
-		strcpy(b.name, "OV511 USB Camera");
+		sprintf(b.name, "%s USB Camera",
+			ov511->bridge == BRG_OV511 ? "OV511" :
+			ov511->bridge == BRG_OV511PLUS ? "OV511+" :
+			ov511->bridge == BRG_OV518 ? "OV518" :
+			ov511->bridge == BRG_OV518PLUS ? "OV518+" :
+			"unknown");
 		b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE;
-		b.channels = 1;
-		b.audios = 0;
+		if (ov511->has_tuner)
+			b.type |= VID_TYPE_TUNER;
+		b.channels = ov511->num_inputs;
+		b.audios = ov511->has_audio_proc ? 1:0;
 		b.maxwidth = ov511->maxwidth;
 		b.maxheight = ov511->maxheight;
-		b.minwidth = 160;
-		b.minheight = 120;
+		b.minwidth = ov511->minwidth;
+		b.minheight = ov511->minheight;
 
 		if (copy_to_user(arg, &b, sizeof(b)))
 			return -EFAULT;
@@ -2274,15 +4863,23 @@
 	{
 		struct video_channel v;
 
+		PDEBUG(4, "VIDIOCGCHAN");
+
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
-		if (v.channel != 0)
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
 
-		v.flags = 0;
-		v.tuners = 0;
-		v.type = VIDEO_TYPE_CAMERA;
-		strcpy(v.name, "Camera");
+		v.norm = ov511->norm;
+		v.type = (ov511->has_tuner) ? VIDEO_TYPE_TV : VIDEO_TYPE_CAMERA;
+		v.flags = (ov511->has_tuner) ? VIDEO_VC_TUNER : 0;
+		v.flags |= (ov511->has_audio_proc) ? VIDEO_VC_AUDIO : 0;
+//		v.flags |= (ov511->has_decoder) ? VIDEO_VC_NORM : 0;
+		v.tuners = (ov511->has_tuner) ? 1:0;
+		decoder_get_input_name(ov511, v.channel, v.name);
 
 		if (copy_to_user(arg, &v, sizeof(v)))
 			return -EFAULT;
@@ -2291,13 +4888,42 @@
 	}
 	case VIDIOCSCHAN:
 	{
-		int v;
+		struct video_channel v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSCHAN");
 
 		if (copy_from_user(&v, arg, sizeof(v)))
 			return -EFAULT;
 
-		if (v != 0)
+		/* Make sure it's not a camera */
+		if (!ov511->has_decoder) {
+			if (v.channel == 0)
+				return 0;
+			else
+				return -EINVAL;
+		}
+
+		if (v.norm != VIDEO_MODE_PAL &&
+		    v.norm != VIDEO_MODE_NTSC &&
+		    v.norm != VIDEO_MODE_SECAM &&
+		    v.norm != VIDEO_MODE_AUTO) {
+			err("Invalid norm (%d)", v.norm);
+			return -EINVAL;
+		}
+
+		if ((unsigned)(v.channel) >= ov511->num_inputs) {
+			err("Invalid channel (%d)", v.channel);
 			return -EINVAL;
+		}
+
+		err = decoder_set_input(ov511, v.channel);
+		if (err)
+			return err;
+
+		err = decoder_set_norm(ov511, v.norm);
+		if (err)
+			return err;
 
 		return 0;
 	}
@@ -2305,11 +4931,11 @@
 	{
 		struct video_picture p;
 
-		PDEBUG (4, "VIDIOCGPICT");
+		PDEBUG(4, "VIDIOCGPICT");
 
 		memset(&p, 0, sizeof(p));
 
-		if (ov7610_get_picture(ov511, &p))
+		if (sensor_get_picture(ov511, &p))
 			return -EIO;
 
 		if (copy_to_user(arg, &p, sizeof(p)))
@@ -2322,22 +4948,40 @@
 		struct video_picture p;
 		int i;
 
-		PDEBUG (4, "VIDIOCSPICT");
+		PDEBUG(4, "VIDIOCSPICT");
 
 		if (copy_from_user(&p, arg, sizeof(p)))
 			return -EFAULT;
 
 		if (!ov511_get_depth(p.palette))
 			return -EINVAL;
-			
-		if (ov7610_set_picture(ov511, &p))
+
+		if (sensor_set_picture(ov511, &p))
 			return -EIO;
 
+		if (force_palette && p.palette != force_palette) {
+			info("Palette rejected (%d)", p.palette);
+			return -EINVAL;
+		}
+
+		// FIXME: Format should be independent of frames
+		if (p.palette != ov511->frame[0].format) {
+			PDEBUG(4, "Detected format change");
+
+			/* If we're collecting previous frame wait
+			   before changing modes */
+			interruptible_sleep_on(&ov511->wq);
+			if (signal_pending(current)) return -EINTR;
+
+			mode_init_regs(ov511, ov511->frame[0].width,
+				ov511->frame[0].height,	p.palette,
+				ov511->sub_flag);
+		}
+
 		PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette);
 		for (i = 0; i < OV511_NUMFRAMES; i++) {
 			ov511->frame[i].depth = p.depth;
 			ov511->frame[i].format = p.palette;
-			ov511->frame[i].segsize = GET_SEGSIZE(p.palette);
 		}
 
 		return 0;
@@ -2346,7 +4990,7 @@
 	{
 		int vf;
 
-		PDEBUG (4, "VIDIOCGCAPTURE");
+		PDEBUG(4, "VIDIOCGCAPTURE");
 
 		if (copy_from_user(&vf, arg, sizeof(vf)))
 			return -EFAULT;
@@ -2357,6 +5001,8 @@
 	{
 		struct video_capture vc;
 
+		PDEBUG(4, "VIDIOCSCAPTURE");
+
 		if (copy_from_user(&vc, arg, sizeof(vc)))
 			return -EFAULT;
 		if (vc.flags)
@@ -2391,7 +5037,7 @@
 		if (copy_from_user(&vw, arg, sizeof(vw)))
 			return -EFAULT;
 
-		PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d",
+		PDEBUG(4, "VIDIOCSWIN: width=%d, height=%d",
 			vw.width, vw.height);
 
 #if 0
@@ -2410,7 +5056,7 @@
 		interruptible_sleep_on(&ov511->wq);
 		if (signal_pending(current)) return -EINTR;
 
-		result = ov511_mode_init_regs(ov511, vw.width, vw.height,
+		result = mode_init_regs(ov511, vw.width, vw.height,
 			ov511->frame[0].format, ov511->sub_flag);
 		if (result < 0)
 			return result;
@@ -2433,7 +5079,7 @@
 		vw.height = ov511->frame[0].height;
 		vw.flags = 30;
 
-		PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
+		PDEBUG(4, "VIDIOCGWIN: %dx%d", vw.width, vw.height);
 
 		if (copy_to_user(arg, &vw, sizeof(vw)))
 			return -EFAULT;
@@ -2444,14 +5090,18 @@
 	{
 		struct video_mbuf vm;
 		int i;
-		
+
+		PDEBUG(4, "VIDIOCGMBUF");
+
 		memset(&vm, 0, sizeof(vm));
-		vm.size = OV511_NUMFRAMES * MAX_DATA_SIZE;
+		vm.size = OV511_NUMFRAMES
+			* MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		vm.frames = OV511_NUMFRAMES;
+
 		vm.offsets[0] = 0;
 		for (i = 1; i < OV511_NUMFRAMES; i++) {
-			vm.offsets[i] = vm.offsets[i-1] + MAX_FRAME_SIZE
-				+ sizeof (struct timeval);
+			vm.offsets[i] = vm.offsets[i-1]
+			   + MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight);
 		}
 
 		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
@@ -2482,63 +5132,74 @@
 			return -EINVAL;
 		}
 
-		if (vm.width > ov511->maxwidth || vm.height > ov511->maxheight) {
+		if (vm.width > ov511->maxwidth 
+		    || vm.height > ov511->maxheight) {
 			err("VIDIOCMCAPTURE: requested dimensions too big");
 			return -EINVAL;
 		}
 
-		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING)
+		if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) {
+			PDEBUG(4, "VIDIOCMCAPTURE: already grabbing");
 			return -EBUSY;
+		}
+
+		if (force_palette && vm.format != force_palette) {
+			info("palette rejected (%d)", vm.format);
+			return -EINVAL;
+		}
 
-		/* Don't compress if the size changed */
 		if ((ov511->frame[vm.frame].width != vm.width) ||
 		    (ov511->frame[vm.frame].height != vm.height) ||
 		    (ov511->frame[vm.frame].format != vm.format) ||
-		    (ov511->frame[vm.frame].sub_flag !=
-		     ov511->sub_flag)) {
+		    (ov511->frame[vm.frame].sub_flag != ov511->sub_flag) ||
+		    (ov511->frame[vm.frame].depth != depth)) {
+			PDEBUG(4, "VIDIOCMCAPTURE: change in image parameters");
+
 			/* If we're collecting previous frame wait
 			   before changing modes */
 			interruptible_sleep_on(&ov511->wq);
 			if (signal_pending(current)) return -EINTR;
-			ret = ov511_mode_init_regs(ov511, vm.width, vm.height,
+			ret = mode_init_regs(ov511, vm.width, vm.height,
 				vm.format, ov511->sub_flag);
 #if 0
-			if (ret < 0)
+			if (ret < 0) {
+				PDEBUG(1, "Got error while initializing regs ");
 				return ret;
+			}
 #endif
+			ov511->frame[vm.frame].width = vm.width;
+			ov511->frame[vm.frame].height = vm.height;
+			ov511->frame[vm.frame].format = vm.format;
+			ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
+			ov511->frame[vm.frame].depth = depth;
 		}
 
-		ov511->frame[vm.frame].width = vm.width;
-		ov511->frame[vm.frame].height = vm.height;
-		ov511->frame[vm.frame].format = vm.format;
-		ov511->frame[vm.frame].sub_flag = ov511->sub_flag;
-		ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format);
-		ov511->frame[vm.frame].depth = depth;
-
 		/* Mark it as ready */
 		ov511->frame[vm.frame].grabstate = FRAME_READY;
 
+		PDEBUG(4, "VIDIOCMCAPTURE: renewing frame %d", vm.frame);
+
 		return ov511_new_frame(ov511, vm.frame);
 	}
 	case VIDIOCSYNC:
 	{
-		int frame;
+		int fnum, rc;
+		struct ov511_frame *frame;
 
-		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+		if (copy_from_user((void *)&fnum, arg, sizeof(int)))
 			return -EFAULT;
 
-		if ((unsigned)frame >= OV511_NUMFRAMES) {
-			err("VIDIOCSYNC: invalid frame (%d)", frame);
+		if ((unsigned)fnum >= OV511_NUMFRAMES) {
+			err("VIDIOCSYNC: invalid frame (%d)", fnum);
 			return -EINVAL;
 		}
 
-		PDEBUG(4, "syncing to frame %d, grabstate = %d", frame,
-		       ov511->frame[frame].grabstate);
+		frame = &ov511->frame[fnum];
 
-		if(frame < 0 || frame >= OV511_NUMFRAMES)
-			return -EINVAL;
-			
-		switch (ov511->frame[frame].grabstate) {
+		PDEBUG(4, "syncing to frame %d, grabstate = %d", fnum,
+		       frame->grabstate);
+
+		switch (frame->grabstate) {
 		case FRAME_UNUSED:
 			return -EINVAL;
 		case FRAME_READY:
@@ -2548,56 +5209,41 @@
 			if (!ov511->dev)
 				return -EIO;
 
-			do {
-#if 0
-				init_waitqueue_head(&ov511->frame[frame].wq);
-#endif
-				interruptible_sleep_on(&ov511->frame[frame].wq);
-				if (signal_pending(current)) {
-					if (retry_sync) {
-						PDEBUG(3, "***retry sync***");
-
-						/* Polling apps will destroy frames with that! */
-						ov511_new_frame(ov511, frame);
-						ov511->curframe = -1;
-
-						/* This will request another frame. */
-						if (waitqueue_active(&ov511->frame[frame].wq))
-							wake_up_interruptible(&ov511->frame[frame].wq);
-
-						return 0;
- 					} else {
-						return -EINTR;
-					}
-				}
-			} while (ov511->frame[frame].grabstate == FRAME_GRABBING);
+			rc = wait_event_interruptible(frame->wq,
+			    (frame->grabstate == FRAME_DONE)
+			    || (frame->grabstate == FRAME_ERROR));
+
+			if (rc)
+				return rc;
 
-			if (ov511->frame[frame].grabstate == FRAME_ERROR) {
+			if (frame->grabstate == FRAME_ERROR) {
 				int ret;
 
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
-			}			
+			}
+			/* Fall through */			
 		case FRAME_DONE:
-			if (ov511->snap_enabled && !ov511->frame[frame].snapshot) {
+			if (ov511->snap_enabled && !frame->snapshot) {
 				int ret;
-				if ((ret = ov511_new_frame(ov511, frame)) < 0)
+				if ((ret = ov511_new_frame(ov511, fnum)) < 0)
 					return ret;
 				goto redo;
 			}
 
-			ov511->frame[frame].grabstate = FRAME_UNUSED;
+			frame->grabstate = FRAME_UNUSED;
 
 			/* Reset the hardware snapshot button */
 			/* FIXME - Is this the best place for this? */
-			if ((ov511->snap_enabled) &&
-			    (ov511->frame[frame].snapshot)) {
-				ov511->frame[frame].snapshot = 0;
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-				ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+			if ((ov511->snap_enabled) && (frame->snapshot)) {
+				frame->snapshot = 0;
+				ov51x_clear_snapshot(ov511);
 			}
+
+			/* Decompression, format conversion, etc... */
+			ov511_postprocess(ov511, frame);
+
 			break;
 		} /* end switch */
 
@@ -2607,6 +5253,8 @@
 	{
 		struct video_buffer vb;
 
+		PDEBUG(4, "VIDIOCSCHAN");
+
 		memset(&vb, 0, sizeof(vb));
 		vb.base = NULL;	/* frame buffer not supported, not used */
 
@@ -2615,43 +5263,170 @@
 
 		return 0;
 	}
-	case VIDIOCKEY:
+	case VIDIOCGUNIT:
+	{
+		struct video_unit vu;
+
+		PDEBUG(4, "VIDIOCGUNIT");
+
+		memset(&vu, 0, sizeof(vu));
+
+		vu.video = ov511->vdev.minor;	/* Video minor */
+		vu.vbi = VIDEO_NO_UNIT;		/* VBI minor */
+		vu.radio = VIDEO_NO_UNIT;	/* Radio minor */
+		vu.audio = VIDEO_NO_UNIT;	/* Audio minor */
+		vu.teletext = VIDEO_NO_UNIT;	/* Teletext minor */
+
+		if (copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
+			return -EFAULT;
+
 		return 0;
-	case VIDIOCCAPTURE:
-		return -EINVAL;
-	case VIDIOCSFBUF:
-		return -EINVAL;
+	}
 	case VIDIOCGTUNER:
+	{
+		struct video_tuner v;
+
+		PDEBUG(4, "VIDIOCGTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		if (!ov511->has_tuner || v.tuner)	// Only tuner 0
+			return -EINVAL;
+
+		strcpy(v.name, "Television");
+
+		// FIXME: Need a way to get the real values
+		v.rangelow = 0;
+		v.rangehigh = ~0;
+
+		v.flags = VIDEO_TUNER_PAL | VIDEO_TUNER_NTSC |
+		    VIDEO_TUNER_SECAM;
+		v.mode = 0; 		/* FIXME:  Not sure what this is yet */
+		v.signal = 0xFFFF;	/* unknown */
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSTUNER:
-		return -EINVAL;
+	{
+		struct video_tuner v;
+		int err;
+
+		PDEBUG(4, "VIDIOCSTUNER");
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		/* Only no or one tuner for now */
+		if (!ov511->has_tuner || v.tuner)
+			return -EINVAL;
+
+		/* and it only has certain valid modes */
+		if (v.mode != VIDEO_MODE_PAL &&
+		    v.mode != VIDEO_MODE_NTSC &&
+		    v.mode != VIDEO_MODE_SECAM) return -EOPNOTSUPP;
+
+		/* Is this right/necessary? */
+		err = decoder_set_norm(ov511, v.mode);
+		if (err)
+			return err;
+
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGFREQ:
+	{
+		unsigned long v = ov511->freq;
+
+		PDEBUG(4, "VIDIOCGFREQ");
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+#if 0
+		/* FIXME: this is necessary for testing */
+		v = 46*16;
+#endif
+		if (copy_to_user(arg, &v, sizeof(v)))
+			return -EFAULT;
+
+		return 0;
+	}
 	case VIDIOCSFREQ:
-		return -EINVAL;
+	{
+		unsigned long v;
+
+		if (!ov511->has_tuner)
+			return -EINVAL;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			return -EFAULT;
+
+		PDEBUG(4, "VIDIOCSFREQ: %lx", v);
+
+		ov511->freq = v;
+		call_i2c_clients(ov511, cmd, &v);
+
+		return 0;
+	}
 	case VIDIOCGAUDIO:
 	case VIDIOCSAUDIO:
-		return -EINVAL;
+	{
+		/* FIXME: Implement this... */
+		return 0;
+	}
 	default:
+		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
 		return -ENOIOCTLCMD;
 	} /* end switch */
 
 	return 0;
 }
 
-static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
+static int 
+ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
-	int i;
-	int frmx = -1;
-	volatile struct ov511_frame *frame;
+	int rc;
+	struct usb_ov511 *ov511 = vdev->priv;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
+	rc = ov511_ioctl_internal(vdev, cmd, arg);
+
+	up(&ov511->lock);
+	return rc;
+}
+
+static inline long 
+ov511_read(struct video_device *vdev, char *buf, unsigned long count,
+	   int noblock)
+{
+	struct usb_ov511 *ov511 = vdev->priv;
+	int i, rc = 0, frmx = -1;
+	struct ov511_frame *frame;
+
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
 
 	PDEBUG(4, "%ld bytes, noblock=%d", count, noblock);
 
-	if (!dev || !buf)
-		return -EFAULT;
+	if (!vdev || !buf) {
+		rc = -EFAULT;
+		goto error;
+	}
 
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
+// FIXME: Only supports two frames
 	/* See if a frame is completed, then use it. */
 	if (ov511->frame[0].grabstate >= FRAME_DONE)	/* _DONE or _ERROR */
 		frmx = 0;
@@ -2659,8 +5434,10 @@
 		frmx = 1;
 
 	/* If nonblocking we return immediately */
-	if (noblock && (frmx == -1))
-		return -EAGAIN;
+	if (noblock && (frmx == -1)) {
+		rc = -EAGAIN;
+		goto error;
+	}
 
 	/* If no FRAME_DONE, look for a FRAME_GRABBING state. */
 	/* See if a frame is in process (grabbing), then use it. */
@@ -2672,89 +5449,116 @@
 	}
 
 	/* If no frame is active, start one. */
-	if (frmx == -1)
-		ov511_new_frame(ov511, frmx = 0);
+	if (frmx == -1) {
+		if ((rc = ov511_new_frame(ov511, frmx = 0))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
+	}
 
 	frame = &ov511->frame[frmx];
 
 restart:
-	if (!ov511->dev)
-		return -EIO;
+	if (!ov511->dev) {
+		rc = -EIO;
+		goto error;
+	}
 
 	/* Wait while we're grabbing the image */
 	PDEBUG(4, "Waiting image grabbing");
-	while (frame->grabstate == FRAME_GRABBING) {
-		interruptible_sleep_on(&ov511->frame[frmx].wq);
-		if (signal_pending(current))
-			return -EINTR;
-	}
+	rc = wait_event_interruptible(frame->wq, 
+		(frame->grabstate == FRAME_DONE)
+		|| (frame->grabstate == FRAME_ERROR));
+
+	if (rc)
+		goto error;
+
 	PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate);
+	PDEBUG(4, "bytes_recvd = %d", frame->bytes_recvd);
 
 	if (frame->grabstate == FRAME_ERROR) {
 		frame->bytes_read = 0;
 		err("** ick! ** Errored frame %d", ov511->curframe);
-		if (ov511_new_frame(ov511, frmx))
+		if (ov511_new_frame(ov511, frmx)) {
 			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 
 	/* Repeat until we get a snapshot frame */
 	if (ov511->snap_enabled)
-		PDEBUG (4, "Waiting snapshot frame");
+		PDEBUG(4, "Waiting snapshot frame");
 	if (ov511->snap_enabled && !frame->snapshot) {
 		frame->bytes_read = 0;
-		if (ov511_new_frame(ov511, frmx))
-			err("ov511_new_frame error");
+		if ((rc = ov511_new_frame(ov511, frmx))) {
+			err("read: ov511_new_frame error");
+			goto error;
+		}
 		goto restart;
 	}
 
 	/* Clear the snapshot */
 	if (ov511->snap_enabled && frame->snapshot) {
 		frame->snapshot = 0;
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03);
-		ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01);
+		ov51x_clear_snapshot(ov511);
 	}
 
-	PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx,
-		frame->bytes_read, frame->scanlength);
+	/* Decompression, format conversion, etc... */
+	ov511_postprocess(ov511, frame);
+
+	PDEBUG(4, "frmx=%d, bytes_read=%ld, length=%ld", frmx,
+		frame->bytes_read,
+		get_frame_length(frame));
 
 	/* copy bytes to user space; we allow for partials reads */
-//	if ((count + frame->bytes_read) > frame->scanlength)
+//	if ((count + frame->bytes_read) 
+//	    > get_frame_length((struct ov511_frame *)frame))
 //		count = frame->scanlength - frame->bytes_read;
 
 	/* FIXME - count hardwired to be one frame... */
-	count = frame->width * frame->height * (frame->depth >> 3);
+	count = get_frame_length(frame);
 
 	PDEBUG(4, "Copy to user space: %ld bytes", count);
 	if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) {
 		PDEBUG(4, "Copy failed! %d bytes not copied", i);
-		return -EFAULT;
+		rc = -EFAULT;
+		goto error;
 	}
 
 	frame->bytes_read += count;
 	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
 		count, frame->bytes_read);
 
-	if (frame->bytes_read >= frame->scanlength) { /* All data has been read */
+	/* If all data has been read... */
+	if (frame->bytes_read
+	    >= get_frame_length(frame)) {
 		frame->bytes_read = 0;
 
+// FIXME: Only supports two frames
 		/* Mark it as available to be used again. */
 		ov511->frame[frmx].grabstate = FRAME_UNUSED;
-		if (ov511_new_frame(ov511, !frmx))
+		if ((rc = ov511_new_frame(ov511, !frmx))) {
 			err("ov511_new_frame returned error");
+			goto error;
+		}
 	}
 
 	PDEBUG(4, "read finished, returning %ld (sweet)", count);
 
+	up(&ov511->lock);
 	return count;
+
+error:
+	up(&ov511->lock);
+	return rc;
 }
 
-static int ov511_mmap(struct video_device *dev, const char *adr,
-	unsigned long size)
+static int 
+ov511_mmap(struct video_device *vdev, const char *adr, unsigned long size)
 {
-	struct usb_ov511 *ov511 = (struct usb_ov511 *)dev;
+	struct usb_ov511 *ov511 = vdev->priv;
 	unsigned long start = (unsigned long)adr;
 	unsigned long page, pos;
 
@@ -2763,14 +5567,21 @@
 
 	PDEBUG(4, "mmap: %ld (%lX) bytes", size, size);
 
-	if (size > (((OV511_NUMFRAMES * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((OV511_NUMFRAMES
+	              * MAX_DATA_SIZE(ov511->maxwidth, ov511->maxheight)
+	              + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))))
 		return -EINVAL;
 
+	if (down_interruptible(&ov511->lock))
+		return -EINTR;
+
 	pos = (unsigned long)ov511->fbuf;
 	while (size > 0) {
 		page = kvirt_to_pa(pos);
-		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&ov511->lock);
 			return -EAGAIN;
+		}
 		start += PAGE_SIZE;
 		pos += PAGE_SIZE;
 		if (size > PAGE_SIZE)
@@ -2779,10 +5590,12 @@
 			size = 0;
 	}
 
+	up(&ov511->lock);
 	return 0;
 }
 
 static struct video_device ov511_template = {
+	owner:		THIS_MODULE,
 	name:		"OV511 USB Camera",
 	type:		VID_TYPE_CAPTURE,
 	hardware:	VID_HARDWARE_OV511,
@@ -2795,15 +5608,237 @@
 	initialize:	ov511_init_done,
 };
 
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+static int 
+ov511_control_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+		    unsigned long ularg)
+{
+	struct proc_dir_entry *pde;
+	struct usb_ov511 *ov511;
+	void *arg = (void *) ularg;
+	int rc;
+
+	pde = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!pde)
+		return -ENOENT;
+
+	ov511 = (struct usb_ov511 *) pde->data;
+	if (!ov511)
+		return -ENODEV;
+
+	if (!ov511->dev)
+		return -EIO;
+
+	/* Should we pass through standard V4L IOCTLs? */
+
+	switch (cmd) {
+	case OV511IOC_GINTVER:
+	{
+		int ver = OV511_INTERFACE_VER;
+
+		PDEBUG(4, "Get interface version: %d", ver);
+		if (copy_to_user(arg, &ver, sizeof(ver)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_GUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_get_brightness(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_get_saturation(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_get_hue(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_get_contrast(ov511, &(opt.val));
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid get short option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUSHORT:
+	{
+		struct ov511_ushort_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_USOPT_BRIGHT:
+			rc = sensor_set_brightness(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_SAT:
+			rc = sensor_set_saturation(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_HUE:
+			rc = sensor_set_hue(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_USOPT_CONTRAST:
+			rc = sensor_set_contrast(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		default:
+			err("Invalid set short option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_GUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			opt.val = ov511->lightfreq;
+			break;
+		case OV511_UIOPT_BFILTER:
+			opt.val = ov511->bandfilt;
+			break;
+		case OV511_UIOPT_LED:
+			opt.val = ov511->led_policy;
+			break;
+		case OV511_UIOPT_DEBUG:
+			opt.val = debug;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			opt.val = ov511->compress;
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		if (copy_to_user(arg, &opt, sizeof(opt)))
+			return -EFAULT;
+
+		return 0;
+	}
+	case OV511IOC_SUINT:
+	{
+		struct ov511_uint_opt opt;
+
+		if (copy_from_user(&opt, arg, sizeof(opt)))
+			return -EFAULT;
+
+		switch (opt.optnum) {
+		case OV511_UIOPT_POWER_FREQ:
+			rc = sensor_set_light_freq(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_BFILTER:
+			rc = sensor_set_banding_filter(ov511, opt.val);
+			if (rc)	return rc;
+			break;
+		case OV511_UIOPT_LED:
+			if (opt.val <= 2) {
+				ov511->led_policy = opt.val;
+				if (ov511->led_policy == LED_OFF)
+					ov51x_led_control(ov511, 0);
+				else if (ov511->led_policy == LED_ON)
+					ov51x_led_control(ov511, 1);
+			} else {
+				return -EINVAL;
+			}
+			break;
+		case OV511_UIOPT_DEBUG:
+			if (opt.val <= 5)
+				debug = opt.val;
+			else
+				return -EINVAL;
+			break;
+		case OV511_UIOPT_COMPRESS:
+			ov511->compress = opt.val;
+			if (ov511->compress) {
+				if (ov511->bridge == BRG_OV511 ||
+				    ov511->bridge == BRG_OV511PLUS)
+					ov511_init_compression(ov511);
+				else if (ov511->bridge == BRG_OV518 ||
+					 ov511->bridge == BRG_OV518PLUS)
+					ov518_init_compression(ov511);
+			}
+			break;
+		default:
+			err("Invalid get int option number");
+			return -EINVAL;
+		}
+
+		return 0;
+	}
+	case OV511IOC_WI2C:
+	{
+		struct ov511_i2c_struct w;
+
+		if (copy_from_user(&w, arg, sizeof(w)))
+			return -EFAULT;
+
+		return ov51x_i2c_write_slave(ov511, w.slave, w.reg, w.value,
+			w.mask);
+	}
+	case OV511IOC_RI2C:
+	{
+		struct ov511_i2c_struct r;
+
+		if (copy_from_user(&r, arg, sizeof(r)))
+			return -EFAULT;
+
+		rc = ov51x_i2c_read_slave(ov511, r.slave, r.reg);
+		if (rc < 0)
+			return rc;
+
+		r.value = rc;
+
+		if (copy_to_user(arg, &r, sizeof(r)))
+			return -EFAULT;
+
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	} /* end switch */
+
+	return 0;
+}
+#endif
+
 /****************************************************************************
  *
- * OV511/OV7610 configuration
+ * OV511 and sensor configuration
  *
  ***************************************************************************/
 
-static int ov76xx_configure(struct usb_ov511 *ov511)
+/* This initializes the OV7610, OV7620, or OV7620AE sensor. The OV7620AE uses
+ * the same register settings as the OV7610, since they are very similar.
+ */
+static int 
+ov7xx0_configure(struct usb_ov511 *ov511)
 {
-	struct usb_device *dev = ov511->dev;
 	int i, success;
 	int rc;
 
@@ -2832,9 +5867,11 @@
 		{ OV511_I2C_BUS, 0x23, 0x2a },
 		{ OV511_I2C_BUS, 0x24, 0x10 },
 		{ OV511_I2C_BUS, 0x25, 0x8a },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
 		{ OV511_I2C_BUS, 0x27, 0xc2 },
 		{ OV511_I2C_BUS, 0x2a, 0x04 },
 		{ OV511_I2C_BUS, 0x2c, 0xfe },
+		{ OV511_I2C_BUS, 0x2d, 0x93 },
 		{ OV511_I2C_BUS, 0x30, 0x71 },
 		{ OV511_I2C_BUS, 0x31, 0x60 },
 		{ OV511_I2C_BUS, 0x32, 0x26 },
@@ -2848,81 +5885,96 @@
 	};
 
 	static struct ov511_regvals aRegvalsNorm7620[] = {
-		{ OV511_I2C_BUS, 0x10, 0xff },
-		{ OV511_I2C_BUS, 0x16, 0x06 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2b, 0xac },
-		{ OV511_I2C_BUS, 0x12, 0x00 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x0f, 0x85 },	/* lg's setting */
-		{ OV511_I2C_BUS, 0x15, 0x01 },
-		{ OV511_I2C_BUS, 0x23, 0x00 },
-		{ OV511_I2C_BUS, 0x24, 0x10 },
-		{ OV511_I2C_BUS, 0x25, 0x8a },
-		{ OV511_I2C_BUS, 0x27, 0xe2 },
-		{ OV511_I2C_BUS, 0x2a, 0x00 },
-		{ OV511_I2C_BUS, 0x2c, 0xfe },
-		{ OV511_I2C_BUS, 0x30, 0x71 },
-		{ OV511_I2C_BUS, 0x31, 0x60 },
-		{ OV511_I2C_BUS, 0x32, 0x26 },
-		{ OV511_I2C_BUS, 0x33, 0x20 },
-		{ OV511_I2C_BUS, 0x34, 0x48 },
-		{ OV511_I2C_BUS, 0x12, 0x24 },
-		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x80 },
+		{ OV511_I2C_BUS, 0x02, 0x80 },
+		{ OV511_I2C_BUS, 0x03, 0xc0 },
+		{ OV511_I2C_BUS, 0x06, 0x60 },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0c, 0x24 },
 		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ OV511_I2C_BUS, 0x12, 0x24 },
+		{ OV511_I2C_BUS, 0x13, 0x01 },
+		{ OV511_I2C_BUS, 0x14, 0x84 },
+		{ OV511_I2C_BUS, 0x15, 0x01 },
+		{ OV511_I2C_BUS, 0x16, 0x03 },
+		{ OV511_I2C_BUS, 0x17, 0x2f },
+		{ OV511_I2C_BUS, 0x18, 0xcf },
+		{ OV511_I2C_BUS, 0x19, 0x06 },
+		{ OV511_I2C_BUS, 0x1a, 0xf5 },
+		{ OV511_I2C_BUS, 0x1b, 0x00 },
+		{ OV511_I2C_BUS, 0x20, 0x18 },
+		{ OV511_I2C_BUS, 0x21, 0x80 },
+		{ OV511_I2C_BUS, 0x22, 0x80 },
+		{ OV511_I2C_BUS, 0x23, 0x00 },
+		{ OV511_I2C_BUS, 0x26, 0xa2 },
+		{ OV511_I2C_BUS, 0x27, 0xea },
+		{ OV511_I2C_BUS, 0x28, 0x20 },
+		{ OV511_I2C_BUS, 0x29, 0x00 },
+		{ OV511_I2C_BUS, 0x2a, 0x10 },
+		{ OV511_I2C_BUS, 0x2b, 0x00 },
+		{ OV511_I2C_BUS, 0x2c, 0x88 },
+		{ OV511_I2C_BUS, 0x2d, 0x91 },
+		{ OV511_I2C_BUS, 0x2e, 0x80 },
+		{ OV511_I2C_BUS, 0x2f, 0x44 },
+		{ OV511_I2C_BUS, 0x60, 0x27 },
+		{ OV511_I2C_BUS, 0x61, 0x02 },
+		{ OV511_I2C_BUS, 0x62, 0x5f },
+		{ OV511_I2C_BUS, 0x63, 0xd5 },
+		{ OV511_I2C_BUS, 0x64, 0x57 },
+		{ OV511_I2C_BUS, 0x65, 0x83 },
+		{ OV511_I2C_BUS, 0x66, 0x55 },
+		{ OV511_I2C_BUS, 0x67, 0x92 },
+		{ OV511_I2C_BUS, 0x68, 0xcf },
+		{ OV511_I2C_BUS, 0x69, 0x76 },
+		{ OV511_I2C_BUS, 0x6a, 0x22 },
+		{ OV511_I2C_BUS, 0x6b, 0x00 },
+		{ OV511_I2C_BUS, 0x6c, 0x02 },
+		{ OV511_I2C_BUS, 0x6d, 0x44 },
+		{ OV511_I2C_BUS, 0x6e, 0x80 },
+		{ OV511_I2C_BUS, 0x6f, 0x1d },
+		{ OV511_I2C_BUS, 0x70, 0x8b },
+		{ OV511_I2C_BUS, 0x71, 0x00 },
+		{ OV511_I2C_BUS, 0x72, 0x14 },
+		{ OV511_I2C_BUS, 0x73, 0x54 },
+		{ OV511_I2C_BUS, 0x74, 0x00 },
+		{ OV511_I2C_BUS, 0x75, 0x8e },
+		{ OV511_I2C_BUS, 0x76, 0x00 },
+		{ OV511_I2C_BUS, 0x77, 0xff },
+		{ OV511_I2C_BUS, 0x78, 0x80 },
+		{ OV511_I2C_BUS, 0x79, 0x80 },
+		{ OV511_I2C_BUS, 0x7a, 0x80 },
+		{ OV511_I2C_BUS, 0x7b, 0xe2 },
+		{ OV511_I2C_BUS, 0x7c, 0x00 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	PDEBUG (4, "starting configuration");
+	PDEBUG(4, "starting configuration");
 
 	/* This looks redundant, but is necessary for WebCam 3 */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                    OV7610_I2C_WRITE_ID) < 0)
-		return -1;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                    OV7610_I2C_READ_ID) < 0)
-		return -1;
-
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		return -1;
 
-	/* Reset the 76xx */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
-
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
-
-		/* Reset the 76xx */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
-	}
-
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s) (method 1)", i);
+	if (ov51x_init_ov_sensor(ov511) >= 0) {
+		PDEBUG(1, "OV7xx0 sensor initalized (method 1)");
 	} else {
 		/* Reset the 76xx */
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) return -1;
 
 		/* Wait for it to initialize */
-		schedule_timeout (1 + 150 * HZ / 1000);
+		schedule_timeout(1 + 150 * HZ / 1000);
 
 		i = 0;
 		success = 0;
 		while (i <= i2c_detect_tries) {
-			if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-			    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
+			if ((ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_HIGH) == 0x7F) &&
+			    (ov51x_i2c_read(ov511,
+					    OV7610_REG_ID_LOW) == 0xA2)) {
 				success = 1;
 				break;
 			} else {
@@ -2930,311 +5982,705 @@
 			}
 		}
 
-		if ((i == i2c_detect_tries) && (success == 0)) {
-			err("Failed to read sensor ID. You might not have an OV7610/20,");
-			err("or it may be not responding. Report this to");
-			err("mwm@i.am");
-			return -1;
+// Was (i == i2c_detect_tries) previously. This obviously used to always report
+// success. Whether anyone actually depended on that bug is unknown
+		if ((i >= i2c_detect_tries) && (success == 0)) {
+			err("Failed to read sensor ID. You might not have an");
+			err("OV7610/20, or it may be not responding. Report");
+			err("this to " EMAIL);
+			err("This is only a warning. You can attempt to use");
+			err("your camera anyway");
+// Only issue a warning for now  
+//			return -1;
 		} else {
-			PDEBUG(1, "I2C synced in %d attempt(s) (method 2)", i+1);
+			PDEBUG(1, "OV7xx0 initialized (method 2, %dx)", i+1);
 		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else if((rc & 3) == 3) {
-			info("Sensor is an OV7610");
-			ov511->sensor = SEN_OV7610;
-		} else if((rc & 3) == 1) {
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV7610");
+		ov511->sensor = SEN_OV7610;
+	} else if ((rc & 3) == 1) {
+		/* I don't know what's different about the 76BE yet */
+		if (ov51x_i2c_read(ov511, 0x15) & 1)
 			info("Sensor is an OV7620AE");
-			ov511->sensor = SEN_OV7620AE;
-		} else if((rc & 3) == 0) {
-			info("Sensor is an OV7620");
+		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 (ov511->bridge == BRG_OV511PLUS) {
+			info("Enabling 511+/7620AE workaround");
 			ov511->sensor = SEN_OV7620;
 		} else {
-			err("Unknown image sensor version: %d", rc & 3);
-			return -1;
+			ov511->sensor = SEN_OV7620AE;
 		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV7620");
+		ov511->sensor = SEN_OV7620;
+	} else {
+		err("Unknown image sensor version: %d", rc & 3);
+		return -1;
 	}
 
 	if (ov511->sensor == SEN_OV7620) {
 		PDEBUG(4, "Writing 7620 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7620))
+		if (ov511_write_regvals(ov511, aRegvalsNorm7620))
 			return -1;
 	} else {
 		PDEBUG(4, "Writing 7610 registers");
-		if (ov511_write_regvals(dev, aRegvalsNorm7610))
+		if (ov511_write_regvals(ov511, aRegvalsNorm7610))
+			return -1;
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	return 0;
+}
+
+/* This initializes the OV6620, OV6630, OV6630AE, or OV6630AF sensor. */
+static int 
+ov6xx0_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+	static struct ov511_regvals aRegvalsNorm6x20[] = {
+		{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x01 },
+		{ 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 */
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
+		{ OV511_I2C_BUS, 0x14, 0x04 },
+		/* 0x16: 0x06 helps frame stability with moving objects */
+		{ OV511_I2C_BUS, 0x16, 0x06 },
+//		{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+		{ OV511_I2C_BUS, 0x28, 0x05 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//		{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
+		{ OV511_I2C_BUS, 0x2d, 0x99 },
+		{ OV511_I2C_BUS, 0x34, 0xd2 }, /* Max A/D range */
+		{ OV511_I2C_BUS, 0x38, 0x8b },
+		{ OV511_I2C_BUS, 0x39, 0x40 },
+		
+		{ 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 */
+
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+		/* These next two registers (0x4a, 0x4b) are undocumented. They
+		 * control the color balance */
+		{ OV511_I2C_BUS, 0x4a, 0x80 },
+		{ OV511_I2C_BUS, 0x4b, 0x80 },
+		{ OV511_I2C_BUS, 0x4d, 0xd2 }, /* This reduces noise a bit */
+		{ OV511_I2C_BUS, 0x4e, 0xc1 },
+		{ OV511_I2C_BUS, 0x4f, 0x04 },
+// Do 50-53 have any effect?
+// Toggle 0x12[2] off and on here?
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+	/* 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, 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 */
+		// 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 */
+
+		/* 0x28: 0x05 Selects RGB format if RGB on */
+//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x05 },
+//	/*04?*/	{ 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, 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, 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 },
+
+		/* 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 },
+	};
+
+	PDEBUG(4, "starting sensor configuration");
+	
+	if (ov51x_init_ov_sensor(ov511) < 0) {
+		err("Failed to read sensor ID. You might not have an OV6xx0,");
+		err("or it may be not responding. Report this to " EMAIL);
+		return -1;
+	} else {
+		PDEBUG(1, "OV6xx0 sensor detected");
+	}
+
+	/* Detect sensor (sub)type */
+	rc = ov51x_i2c_read(ov511, OV7610_REG_COM_I);
+
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if ((rc & 3) == 0) {
+		info("Sensor is an OV6630");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 1) {
+		info("Sensor is an OV6620");
+		ov511->sensor = SEN_OV6620;
+	} else if ((rc & 3) == 2) {
+		info("Sensor is an OV6630AE");
+		ov511->sensor = SEN_OV6630;
+	} else if ((rc & 3) == 3) {
+		info("Sensor is an OV6630AF");
+		ov511->sensor = SEN_OV6630;
+	} 
+
+	/* Set sensor-specific vars */
+	if (ov511->sensor == SEN_OV6620) {
+		ov511->maxwidth = 352;
+		ov511->maxheight = 288;
+	} else {
+		/* 352x288 not working with OV518 yet */
+		ov511->maxwidth = 320;
+		ov511->maxheight = 240;
+	}
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	if (ov511->sensor == SEN_OV6620) {
+		PDEBUG(4, "Writing 6x20 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x20))
+			return -1;
+	} else {
+		PDEBUG(4, "Writing 6x30 registers");
+		if (ov511_write_regvals(ov511, aRegvalsNorm6x30))
+			return -1;
+	}
+	
+	return 0;
+}
+
+/* This initializes the KS0127 and KS0127B video decoders. */
+static int 
+ks0127_configure(struct usb_ov511 *ov511)
+{
+	int rc;
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_ks_sensor(ov511) < 0) {
+		err("Failed to initialize the KS0127");
+		return -1;
+	} else {
+		PDEBUG(1, "KS012x(B) sensor detected");
+	}
+#endif
+
+	/* Detect decoder subtype */
+	rc = ov51x_i2c_read(ov511, 0x00);
+	if (rc < 0) {
+		err("Error detecting sensor type");
+		return -1;
+	} else if (rc & 0x08) {
+		rc = ov51x_i2c_read(ov511, 0x3d);
+		if (rc < 0) {
+			err("Error detecting sensor type");
 			return -1;
+		} else if ((rc & 0x0f) == 0) {
+			info("Sensor is a KS0127");
+			ov511->sensor = SEN_KS0127;
+		} else if ((rc & 0x0f) == 9) {
+			info("Sensor is a KS0127B Rev. A");
+			ov511->sensor = SEN_KS0127B;
+		}
+	} else {
+		err("Error: Sensor is an unsupported KS0122");
+		return -1;
+	}
+
+	/* Set sensor-specific vars */
+	ov511->maxwidth = 640;
+	ov511->maxheight = 480;
+	ov511->minwidth = 64;
+	ov511->minheight = 48;
+
+	// FIXME: These do not match the actual settings yet
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x80 << 8;
+	ov511->colour = 0x80 << 8;
+	ov511->hue = 0x80 << 8;
+
+	/* This device is not supported yet. Bail out now... */
+	err("This sensor is not supported yet.");
+	return -1;
+
+	return 0;
+}
+
+/* This initializes the SAA7111A video decoder. */
+static int 
+saa7111a_configure(struct usb_ov511 *ov511)
+{
+	struct usb_device *dev = ov511->dev;
+	int rc;
+
+	/* Since there is no register reset command, all registers must be
+	 * written, otherwise gives erratic results */
+	static struct ov511_regvals aRegvalsNormSAA7111A[] = {
+		{ OV511_I2C_BUS, 0x06, 0xce },
+		{ OV511_I2C_BUS, 0x07, 0x00 },
+		{ OV511_I2C_BUS, 0x10, 0x44 }, /* YUV422, 240/286 lines */
+		{ OV511_I2C_BUS, 0x0e, 0x01 }, /* NTSC M or PAL BGHI */
+		{ OV511_I2C_BUS, 0x00, 0x00 },
+		{ OV511_I2C_BUS, 0x01, 0x00 },
+		{ OV511_I2C_BUS, 0x03, 0x23 },
+		{ OV511_I2C_BUS, 0x04, 0x00 },
+		{ OV511_I2C_BUS, 0x05, 0x00 },
+		{ OV511_I2C_BUS, 0x08, 0xc8 }, /* Auto field freq */
+		{ OV511_I2C_BUS, 0x09, 0x01 }, /* Chrom. trap off, APER=0.25 */
+		{ OV511_I2C_BUS, 0x0a, 0x80 }, /* BRIG=128 */
+		{ OV511_I2C_BUS, 0x0b, 0x40 }, /* CONT=1.0 */
+		{ OV511_I2C_BUS, 0x0c, 0x40 }, /* SATN=1.0 */
+		{ OV511_I2C_BUS, 0x0d, 0x00 }, /* HUE=0 */
+		{ OV511_I2C_BUS, 0x0f, 0x00 },
+		{ OV511_I2C_BUS, 0x11, 0x0c },
+		{ OV511_I2C_BUS, 0x12, 0x00 },
+		{ OV511_I2C_BUS, 0x13, 0x00 },
+		{ OV511_I2C_BUS, 0x14, 0x00 },
+		{ OV511_I2C_BUS, 0x15, 0x00 },
+		{ OV511_I2C_BUS, 0x16, 0x00 },
+		{ OV511_I2C_BUS, 0x17, 0x00 },
+		{ OV511_I2C_BUS, 0x02, 0xc0 },	/* Composite input 0 */
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
+
+// FIXME: I don't know how to sync or reset it yet
+#if 0
+	if (ov51x_init_saa_sensor(ov511) < 0) {
+		err("Failed to initialize the SAA7111A");
+		return -1;
+	} else {
+		PDEBUG(1, "SAA7111A sensor detected");
 	}
+#endif
 
 	/* Set sensor-specific vars */
 	ov511->maxwidth = 640;
-	ov511->maxheight = 480;
+	ov511->maxheight = 480;		/* Even/Odd fields */
+	ov511->minwidth = 320;
+	ov511->minheight = 240;		/* Even field only */
+
+	ov511->has_decoder = 1;
+	ov511->num_inputs = 8;
+	ov511->norm = VIDEO_MODE_AUTO;
+	ov511->stop_during_set = 0;	/* Decoder guarantees stable image */
+
+	/* Decoder doesn't change these values, so we use these instead of
+	 * acutally reading the registers (which doesn't work) */
+	ov511->brightness = 0x80 << 8;
+	ov511->contrast = 0x40 << 9;
+	ov511->colour = 0x40 << 9;
+	ov511->hue = 32768;
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
-	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
+	PDEBUG(4, "Writing SAA7111A registers");
+	if (ov511_write_regvals(ov511, aRegvalsNormSAA7111A))
 		return -1;
-	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	/* Detect version of decoder. This must be done after writing the
+         * initial regs or the decoder will lock up. */
+	rc = ov51x_i2c_read(ov511, 0x00);
+
+	if (rc < 0) {
+		err("Error detecting sensor version");
+		return -1;
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		info("Sensor is an SAA7111A (version 0x%x)", rc);
+		ov511->sensor = SEN_SAA7111A;
 	}
 
+	// FIXME: Fix this for OV518(+)
+	/* Latch to negative edge of clock. Otherwise, we get incorrect
+	 * colors and jitter in the digital signal. */
+	if (ov511->bridge == BRG_OV511 || ov511->bridge == BRG_OV511PLUS)
+		ov511_reg_write(dev, 0x11, 0x00);
+	else
+		warn("SAA7111A not yet supported with OV518/OV518+");
+
 	return 0;
 }
 
-static int ov6xx0_configure(struct usb_ov511 *ov511)
+/* This initializes the OV511/OV511+ and the sensor */
+static int 
+ov511_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i, success, rc;
+	int i;
 
-	static struct ov511_regvals aRegvalsNorm6x20[] = {
-		{ OV511_I2C_BUS, 0x12, 0x80 },  /* reset */
-		{ OV511_I2C_BUS, 0x11, 0x01 },
-		{ OV511_I2C_BUS, 0x03, 0xd0 },
-		{ OV511_I2C_BUS, 0x05, 0x7f },
-		{ OV511_I2C_BUS, 0x07, 0xa8 },
-		{ OV511_I2C_BUS, 0x0c, 0x24 },
-		{ OV511_I2C_BUS, 0x0d, 0x24 },
-		{ OV511_I2C_BUS, 0x10, 0xff },  /* ? */
-		{ OV511_I2C_BUS, 0x14, 0x04 },
-		{ OV511_I2C_BUS, 0x16, 0x06 },  /* ? */
-		{ OV511_I2C_BUS, 0x19, 0x04 },
-		{ OV511_I2C_BUS, 0x1a, 0x93 },
-		{ OV511_I2C_BUS, 0x20, 0x28 },
-		{ OV511_I2C_BUS, 0x27, 0xa2 },
-		{ OV511_I2C_BUS, 0x28, 0x24 },
-		{ OV511_I2C_BUS, 0x2a, 0x04 },  /* 84? */
-		{ OV511_I2C_BUS, 0x2b, 0xac },  /* a8? */
-		{ OV511_I2C_BUS, 0x2d, 0x95 },
-		{ OV511_I2C_BUS, 0x33, 0x28 },
-		{ OV511_I2C_BUS, 0x34, 0xc7 },
-		{ OV511_I2C_BUS, 0x38, 0x8b },
-		{ OV511_I2C_BUS, 0x3c, 0x5c },
-		{ OV511_I2C_BUS, 0x3d, 0x80 },
-		{ OV511_I2C_BUS, 0x3f, 0x00 },
-		{ OV511_I2C_BUS, 0x4a, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4b, 0x80 }, /* undocumented */
-		{ OV511_I2C_BUS, 0x4d, 0xd2 },
-		{ OV511_I2C_BUS, 0x4e, 0xc1 },
-		{ OV511_I2C_BUS, 0x4f, 0x04 },
-		{ OV511_DONE_BUS, 0x0, 0x00 },
+	static struct ov511_regvals aRegvalsInit511[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	PDEBUG (4, "starting sensor configuration");
-	
-	/* Reset the 6xx0 */ 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
+	static struct ov511_regvals aRegvalsNorm511[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	/* Wait for it to initialize */ 
-	schedule_timeout (1 + 150 * HZ / 1000);
+	static struct ov511_regvals aRegvalsNorm511Plus[] = {
+		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0xff },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x03 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x01 },
+		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0xff },
+		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x00 },
+		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },
+	};
 
-	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
-		if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) &&
-		    (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) {
-			success = 1;
-			continue;
-		}
+	PDEBUG(4, "");
 
-		/* Reset the 6xx0 */ 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1;
-		/* Wait for it to initialize */ 
-		schedule_timeout (1 + 150 * HZ / 1000);
-		/* Dummy read to sync I2C */
-		if (ov511_i2c_read(dev, 0x00) < 0) return -1;
+	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
+	if (ov511->customid < 0) {
+		err("Unable to read camera bridge registers");
+		goto error;
 	}
 
-	if (success) {
-		PDEBUG(1, "I2C synced in %d attempt(s)", i);
-	} else {
-		err("Failed to read sensor ID. You might not have an OV6xx0,");
-		err("or it may be not responding. Report this to");
-		err("mwm@i.am");
-		return -1;
+	ov511->desc = -1;
+	PDEBUG (1, "CustomID = %d", ov511->customid);
+	for (i = 0; clist[i].id >= 0; i++) {
+		if (ov511->customid == clist[i].id) {
+			info("model: %s", clist[i].description);
+			ov511->desc = i;
+			break;
+		}
 	}
 
-	/* Detect sensor if user didn't use override param */
-	if (sensor == 0) {
-		rc = ov511_i2c_read(dev, OV7610_REG_COM_I);
+	if (clist[i].id == -1) {
+		err("Camera type (%d) not recognized", ov511->customid);
+		err("Please notify " EMAIL " of the name,");
+		err("manufacturer, model, and this number of your camera.");
+		err("Also include the output of the detection process.");
+	} 
 
-		if (rc < 0) {
-			err("Error detecting sensor type");
-			return -1;
-		} else {
-			info("Sensor is an OV6xx0 (version %d)", rc & 3);
-			ov511->sensor = SEN_OV6620;
-		}
-	} else {	/* sensor != 0; user overrode detection */
-		ov511->sensor = sensor;
-		info("Sensor set to type %d", ov511->sensor);
+	if (clist[i].id == 6) {	/* USB Life TV (NTSC) */
+		ov511->tuner_type = 8;		/* Temic 4036FY5 3X 1981 */
 	}
 
-	/* Set sensor-specific vars */
-	ov511->maxwidth = 352;
-	ov511->maxheight = 288;
+	if (ov511_write_regvals(ov511, aRegvalsInit511)) goto error;
 
-	PDEBUG(4, "Writing 6x20 registers");
-	if (ov511_write_regvals(dev, aRegvalsNorm6x20))
-		return -1;
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
 
-	if (aperture < 0) {          /* go with the default */
-		if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1;
-	} else if (aperture <= 0xf) {  /* user overrode default */
-		if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0)
-			return -1;
+	/* The OV511+ has undocumented bits in the flow control register.
+	 * Setting it to 0xff fixes the corruption with moving objects. */
+	if (ov511->bridge == BRG_OV511) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511)) goto error;
+	} else if (ov511->bridge == BRG_OV511PLUS) {
+		if (ov511_write_regvals(ov511, aRegvalsNorm511Plus)) goto error;
 	} else {
-		err("Invalid setting for aperture; legal value: 0 - 15");
-		return -1;
+		err("Invalid bridge");
 	}
 
-	if (autoadjust) {
-		if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1;
+	if (ov511_init_compression(ov511)) goto error;
+
+	ov511_set_packet_size(ov511, 0);
+
+	ov511->snap_enabled = snapshot;	
+
+	/* Test for 7xx0 */
+	PDEBUG(3, "Testing for 0V7xx0");
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
+		goto error;
+
+	if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+		/* Test for 6xx0 */
+		PDEBUG(3, "Testing for 0V6xx0");
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
+			goto error;
+
+		if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+			/* Test for 8xx0 */
+			PDEBUG(3, "Testing for 0V8xx0");
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID))
+				goto error;
+
+			if (ov51x_i2c_write(ov511, 0x12, 0x80) < 0) {
+				/* Test for SAA7111A */
+				PDEBUG(3, "Testing for SAA7111A");
+				ov511->primary_i2c_slave = SAA7111A_I2C_WRITE_ID;
+				if (ov51x_set_slave_ids(ov511, SAA7111A_I2C_WRITE_ID,
+							SAA7111A_I2C_READ_ID))
+					goto error;
+
+				if (ov51x_i2c_write(ov511, 0x0d, 0x00) < 0) {
+					/* Test for KS0127 */
+					PDEBUG(3, "Testing for KS0127");
+					ov511->primary_i2c_slave = KS0127_I2C_WRITE_ID;
+					if (ov51x_set_slave_ids(ov511, KS0127_I2C_WRITE_ID,
+								KS0127_I2C_READ_ID))
+						goto error;
+
+					if (ov51x_i2c_write(ov511, 0x10, 0x00) < 0) {
+						err("Can't determine sensor slave IDs");
+		 				goto error;
+					} else {
+						if(ks0127_configure(ov511) < 0) {
+							err("Failed to configure KS0127");
+	 						goto error;
+						}
+					}
+				} else {
+					if(saa7111a_configure(ov511) < 0) {
+						err("Failed to configure SAA7111A");
+	 					goto error;
+					}
+				}
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if(ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
+		}
 	} else {
-		if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1;
-		if (ov511_i2c_write(dev, 0x2d, 
-		     ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1;
-		ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8);
+		if(ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
+	 		goto error;
+		}
 	}
 
 	return 0;
-}
 
+error:
+	err("OV511 Config failed");
+
+	return -EBUSY;
+}
 
-static int ov511_configure(struct usb_ov511 *ov511)
+/* This initializes the OV518/OV518+ and the sensor */
+static int 
+ov518_configure(struct usb_ov511 *ov511)
 {
 	struct usb_device *dev = ov511->dev;
-	int i;
 
-	static struct ov511_regvals aRegvalsInit[] = {
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d },
+	static struct ov511_regvals aRegvalsInit518[] = {
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x40 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+	 	{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3e },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x00 },
+		{ OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0xe1 },
+		{ OV511_REG_BUS, 0x46, 0x00 }, 
+		{ OV511_REG_BUS, 0x5d, 0x03 },
 		{ OV511_DONE_BUS, 0x0, 0x00},
 	};
 
-	static struct ov511_regvals aRegvalsNorm511[] = {
-		{ OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 },
-		{ OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 },
-		{ OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x08 },
-		{ OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 },
-		{ OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 },
-		{ OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 },
+	/* New values, based on Windows driver. Since what they do is not
+	 * known yet, this may be incorrect. */
+	static struct ov511_regvals aRegvalsNorm518[] = {
+		{ OV511_REG_BUS, 0x52, 0x02 }, /* Reset snapshot */
+		{ OV511_REG_BUS, 0x52, 0x01 }, /* Enable snapshot */
+		{ OV511_REG_BUS, 0x31, 0x0f },
+		{ 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, 0x51, 0x04 },
+		{ OV511_REG_BUS, 0x71, 0x19 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
 	};
 
-	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	PDEBUG(4, "");
 
-	for (i = 0; i < OV511_NUMFRAMES; i++)
-		init_waitqueue_head(&ov511->frame[i].wq);
+	/* First 5 bits of custom ID reg are a revision ID on OV518 */
+	info("Device revision %d",
+	     0x1F & ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID));
 
-	init_waitqueue_head(&ov511->wq);
+	if (ov511_write_regvals(ov511, aRegvalsInit518)) goto error;
+
+	/* Set LED GPIO pin to output mode */
+	if (ov511_reg_write_mask(dev, 0x57,0x00, 0x02) < 0) goto error;
+
+	/* LED is off by default with OV518; have to explicitly turn it on */
+	if (ov511->led_policy == LED_OFF || ov511->led_policy == LED_AUTO)
+		ov51x_led_control(ov511, 0);
+	else
+		ov51x_led_control(ov511, 1);
+
+	/* Don't require compression if dumppix is enabled; otherwise it's
+	 * required. OV518 has no uncompressed mode, to save RAM. */
+	if (!dumppix && !ov511->compress) {
+		ov511->compress = 1;
+		warn("Compression required with OV518...enabling");
+	}
+
+	if (ov511_write_regvals(ov511, aRegvalsNorm518)) goto error;
 
-	if (ov511_write_regvals(dev, aRegvalsInit)) goto error;
-	if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error;
+	if (ov511_reg_write(dev, 0x2f,0x80) < 0) goto error;
+
+	if (ov518_init_compression(ov511)) goto error;
 
 	ov511_set_packet_size(ov511, 0);
 
-	ov511->snap_enabled = snapshot;	
+	ov511->snap_enabled = snapshot;
 
 	/* Test for 76xx */
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-	                   OV7610_I2C_WRITE_ID) < 0)
-		goto error;
-
-	if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-	                   OV7610_I2C_READ_ID) < 0)
+	ov511->primary_i2c_slave = OV7xx0_I2C_WRITE_ID;
+	if (ov51x_set_slave_ids(ov511, OV7xx0_I2C_WRITE_ID,
+				OV7xx0_I2C_READ_ID) < 0)
 		goto error;
 
-	if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-		goto error;
+	/* The OV518 must be more aggressive about sensor detection since
+	 * I2C write will never fail if the sensor is not present. We have
+	 * to try to initialize the sensor to detect its presence */
 
-	if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
+	if (ov51x_init_ov_sensor(ov511) < 0) {
 		/* Test for 6xx0 */
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE,
-		                    OV6xx0_I2C_WRITE_ID) < 0)
-			goto error;
-
-		if (ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ,
-		                    OV6xx0_I2C_READ_ID) < 0)
+		ov511->primary_i2c_slave = OV6xx0_I2C_WRITE_ID;
+		if (ov51x_set_slave_ids(ov511, OV6xx0_I2C_WRITE_ID,
+					OV6xx0_I2C_READ_ID) < 0)
 			goto error;
 
-		if (ov511_reset(dev, OV511_RESET_NOREGS) < 0)
-			goto error;
+		if (ov51x_init_ov_sensor(ov511) < 0) {
+			/* Test for 8xx0 */
+			ov511->primary_i2c_slave = OV8xx0_I2C_WRITE_ID;
+			if (ov51x_set_slave_ids(ov511, OV8xx0_I2C_WRITE_ID,
+						OV8xx0_I2C_READ_ID) < 0)
+				goto error;
 
-		if (ov511_i2c_write(dev, 0x12, 0x80) < 0) {
-			err("Can't determine sensor slave IDs");
-			goto error;
-		}
-		
-		if(ov6xx0_configure(ov511) < 0) {
-			err("failed to configure OV6xx0");
- 			goto error;
+			if (ov51x_init_ov_sensor(ov511) < 0) {
+				err("Can't determine sensor slave IDs");
+ 				goto error;
+			} else {
+				err("Detected unsupported OV8xx0 sensor");
+				goto error;
+			}
+		} else {
+			if (ov6xx0_configure(ov511) < 0) {
+				err("Failed to configure OV6xx0");
+ 				goto error;
+			}
 		}
 	} else {
-		if(ov76xx_configure(ov511) < 0) {
-			err("failed to configure OV76xx");
+		if (ov7xx0_configure(ov511) < 0) {
+			err("Failed to configure OV7xx0");
 	 		goto error;
 		}
 	}
-	
-	/* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used
-	 * (using read() instead). */
-	for (i = 0; i < OV511_NUMFRAMES; i++) {
-		ov511->frame[i].width = ov511->maxwidth;
-		ov511->frame[i].height = ov511->maxheight;
-		ov511->frame[i].depth = 24;
-		ov511->frame[i].bytes_read = 0;
-		ov511->frame[i].segment = 0;
-		ov511->frame[i].format = VIDEO_PALETTE_RGB24;
-		ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format);
-	}
 
-	/* Initialize to max width/height, RGB24 */
-	if (ov511_mode_init_regs(ov511, ov511->maxwidth, ov511->maxheight,
-				 VIDEO_PALETTE_RGB24, 0) < 0)
-		goto error;
+	// The OV518 cannot go as low as the sensor can
+	ov511->minwidth = 160;
+	ov511->minheight = 120;
 
 	return 0;
-	
+
 error:
-	usb_driver_release_interface(&ov511_driver,
-		&dev->actconfig->interface[ov511->iface]);
+	err("OV518 Config failed");
 
-	return -EBUSY;	
+	return -EBUSY;
 }
 
 
@@ -3245,12 +6691,13 @@
  ***************************************************************************/
 
 static void *
-ov511_probe(struct usb_device *dev, unsigned int ifnum,
-	const struct usb_device_id *id)
+ov51x_probe(struct usb_device *dev, unsigned int ifnum,
+	    const struct usb_device_id *id)
 {
 	struct usb_interface_descriptor *interface;
 	struct usb_ov511 *ov511;
 	int i;
+	int registered = 0;
 
 	PDEBUG(1, "probing for device...");
 
@@ -3271,98 +6718,147 @@
 
 	if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) {
 		err("couldn't kmalloc ov511 struct");
-		goto error;
+		goto error_unlock;
 	}
 
 	memset(ov511, 0, sizeof(*ov511));
 
 	ov511->dev = dev;
 	ov511->iface = interface->bInterfaceNumber;
+	ov511->led_policy = led;
+	ov511->compress = compress;
+	ov511->lightfreq = lightfreq;
+	ov511->num_inputs = 1;	   /* Video decoder init functs. change this */
+	ov511->stop_during_set = !fastset;
+	ov511->tuner_type = tuner;
+	ov511->backlight = backlight;
+
+	ov511->auto_brt = autobright;
+	ov511->auto_gain = autogain;
+	ov511->auto_exp = autoexp;
 
 	switch (dev->descriptor.idProduct) {
-	case 0x0511:
+	case PROD_OV511:
 		info("USB OV511 camera found");
 		ov511->bridge = BRG_OV511;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0xA511:
+	case PROD_OV511PLUS:
 		info("USB OV511+ camera found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
-	case 0x0002:
-		if (dev->descriptor.idVendor != 0x0813)
+	case PROD_OV518:
+		info("USB OV518 camera found");
+		ov511->bridge = BRG_OV518;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_OV518PLUS:
+		info("USB OV518+ camera found");
+		ov511->bridge = BRG_OV518PLUS;
+		ov511->bclass = BCL_OV518;
+		break;
+	case PROD_ME2CAM:
+		if (dev->descriptor.idVendor != VEND_MATTEL)
 			goto error;
 		info("Intel Play Me2Cam (OV511+) found");
 		ov511->bridge = BRG_OV511PLUS;
+		ov511->bclass = BCL_OV511;
 		break;
 	default:
-		err("Unknown product ID");
-		goto error;
+		err("Unknown product ID 0x%x", dev->descriptor.idProduct);
+		goto error_dealloc;
 	}
 
-	ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID);
-	if (ov511->customid < 0) {
-		err("Unable to read camera bridge registers");
-		goto error;
+	/* Workaround for some applications that want data in RGB
+	 * instead of BGR. */
+	if (force_rgb)
+		info("data format set to RGB");
+
+	init_waitqueue_head(&ov511->wq);
+
+	init_MUTEX(&ov511->lock);	/* to 1 == available */
+	init_MUTEX(&ov511->buf_lock);
+	init_MUTEX(&ov511->param_lock);
+	init_MUTEX(&ov511->i2c_lock);
+	ov511->buf_state = BUF_NOT_ALLOCATED;
+
+	if (ov511->bridge == BRG_OV518 ||
+	    ov511->bridge == BRG_OV518PLUS) {
+		if (ov518_configure(ov511) < 0)
+			goto error;
+	} else {
+		if (ov511_configure(ov511) < 0)
+			goto error;
 	}
 
-	ov511->desc = -1;
-	PDEBUG (4, "CustomID = %d", ov511->customid);
-	for (i = 0; clist[i].id >= 0; i++) {
-		if (ov511->customid == clist[i].id) {
-			info("camera: %s", clist[i].description);
-			ov511->desc = i;
-			break;
-		}
+	for (i = 0; i < OV511_NUMFRAMES; i++) {
+		ov511->frame[i].framenum = i;
+		init_waitqueue_head(&ov511->frame[i].wq);
 	}
 
-	/* Lifeview USB Life TV not supported */
-	if (clist[i].id == 38) {
-		err("This device is not supported yet.");
+	/* Unnecessary? (This is done on open(). Need to make sure variables
+	 * are properly initialized without this before removing it, though). */
+	if (ov51x_set_default_params(ov511) < 0)
 		goto error;
-	}
 
-	if (clist[i].id == -1) {
-		err("Camera type (%d) not recognized", ov511->customid);
-		err("Please contact mwm@i.am to request");
-		err("support for your camera.");
-	}
+#ifdef OV511_DEBUG
+	if (dump_bridge)
+		ov511_dump_regs(dev);
+#endif
 
-	/* Workaround for some applications that want data in RGB
-	 * instead of BGR */
-	if (force_rgb)
-		info("data format set to RGB");
+	memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template));
+	ov511->vdev.priv = ov511;
 
-	if (!ov511_configure(ov511)) {
-		ov511->user = 0;
-		init_MUTEX(&ov511->lock);	/* to 1 == available */
-		init_MUTEX(&ov511->buf_lock);
-		ov511->buf_state = BUF_NOT_ALLOCATED;
-	} else {
-		err("Failed to configure camera");
-		goto error;
+	for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) {
+		/* Minor 0 cannot be specified; assume user wants autodetect */
+		if (unit_video[i] == 0)
+			break;
+
+		if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER,
+			unit_video[i]) >= 0) {
+			registered = 1;
+			break;
+		}
 	}
 
-	if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, video_nr) < 0) {
+	/* Use the next available one */
+	if (!registered &&
+	    video_register_device(&ov511->vdev, VFL_TYPE_GRABBER, -1) < 0) {
 		err("video_register_device failed");
 		goto error;
 	}
 
+	info("Device registered on minor %d", ov511->vdev.minor);
+
 	MOD_DEC_USE_COUNT;
      	return ov511;
 
 error:
+	err("Camera initialization failed");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	/* Safe to call even if entry doesn't exist */
+	destroy_proc_ov511_cam(ov511);
+#endif
+
+	usb_driver_release_interface(&ov511_driver,
+		&dev->actconfig->interface[ov511->iface]);
+
+error_dealloc:
 	if (ov511) {
 		kfree(ov511);
 		ov511 = NULL;
 	}
 
+error_unlock:
 	MOD_DEC_USE_COUNT;
 	return NULL;
 }
 
 
 static void
-ov511_disconnect(struct usb_device *dev, void *ptr)
+ov51x_disconnect(struct usb_device *dev, void *ptr)
 {
 	struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr;
 	int n;
@@ -3401,14 +6897,14 @@
 		}
 	}
 
-	usb_driver_release_interface(&ov511_driver,
-		&ov511->dev->actconfig->interface[ov511->iface]);
-	ov511->dev = NULL;
-
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         destroy_proc_ov511_cam(ov511);
 #endif
 
+	usb_driver_release_interface(&ov511_driver,
+		&ov511->dev->actconfig->interface[ov511->iface]);
+	ov511->dev = NULL;
+
 	/* Free the memory */
 	if (ov511 && !ov511->user) {
 		ov511_dealloc(ov511, 1);
@@ -3422,8 +6918,8 @@
 static struct usb_driver ov511_driver = {
 	name:		"ov511",
 	id_table:       device_table,
-	probe:		ov511_probe,
-	disconnect:	ov511_disconnect
+	probe:		ov51x_probe,
+	disconnect:	ov51x_disconnect
 };
 
 
@@ -3433,7 +6929,88 @@
  *
  ***************************************************************************/
 
-static int __init usb_ov511_init(void)
+/* Returns 0 for success */
+int 
+ov511_register_decomp_module(int ver, struct ov51x_decomp_ops *ops, int ov518,
+			     int mmx)
+{
+	if (ver != DECOMP_INTERFACE_VER) {
+		err("Decompression module has incompatible");
+		err("interface version %d", ver);
+		err("Interface version %d is required", DECOMP_INTERFACE_VER);
+		return -EINVAL;
+	}
+
+	if (!ops)
+		return -EFAULT;
+
+	if (mmx && !ov51x_mmx_available) {
+		err("MMX not available on this system or kernel");
+		return -EINVAL;
+	}
+
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx) {
+			if (ov518_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_mmx_decomp_ops = ops;
+		} else {
+			if (ov518_decomp_ops)
+				goto err_in_use;
+			else
+				ov518_decomp_ops = ops;
+		}
+	} else {
+		if (mmx) {
+			if (ov511_mmx_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_mmx_decomp_ops = ops;
+		} else {
+			if (ov511_decomp_ops)
+				goto err_in_use;
+			else
+				ov511_decomp_ops = ops;
+		}
+	}
+
+	MOD_INC_USE_COUNT;
+
+	unlock_kernel();
+	return 0;
+
+err_in_use:
+	unlock_kernel();
+	return -EBUSY;
+}
+
+void 
+ov511_deregister_decomp_module(int ov518, int mmx)
+{
+	lock_kernel();
+
+	if (ov518) {
+		if (mmx)
+			ov518_mmx_decomp_ops = NULL;
+		else
+			ov518_decomp_ops = NULL;
+	} else {
+		if (mmx)
+			ov511_mmx_decomp_ops = NULL;
+		else
+			ov511_decomp_ops = NULL;
+	}
+	
+	MOD_DEC_USE_COUNT;
+
+	unlock_kernel();
+}
+
+static int __init 
+usb_ov511_init(void)
 {
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_create();
@@ -3442,20 +7019,33 @@
 	if (usb_register(&ov511_driver) < 0)
 		return -1;
 
-	info(DRIVER_VERSION ":" DRIVER_DESC);
+	// FIXME: Don't know how to determine this yet
+	ov51x_mmx_available = 0;
+
+#if defined (__i386__)
+	if (test_bit(X86_FEATURE_MMX, &boot_cpu_data.x86_capability))
+		ov51x_mmx_available = 1;
+#endif
+
+	info(DRIVER_VERSION " : " DRIVER_DESC);
 
 	return 0;
 }
 
-static void __exit usb_ov511_exit(void)
+static void __exit 
+usb_ov511_exit(void)
 {
 	usb_deregister(&ov511_driver);
 	info("driver deregistered");
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
         proc_ov511_destroy();
-#endif 
+#endif
 }
 
 module_init(usb_ov511_init);
 module_exit(usb_ov511_exit);
+
+/* No version, for compatibility with binary-only modules */
+EXPORT_SYMBOL_NOVERS(ov511_register_decomp_module);
+EXPORT_SYMBOL_NOVERS(ov511_deregister_decomp_module);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/ov511.h linux/drivers/usb/ov511.h
--- linux.orig/drivers/usb/ov511.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/ov511.h	Wed Jan  9 15:42:02 2002
@@ -1,20 +1,41 @@
-
 #ifndef __LINUX_OV511_H
 #define __LINUX_OV511_H
 
 #include <asm/uaccess.h>
 #include <linux/videodev.h>
 #include <linux/smp_lock.h>
+#include <linux/usb.h>
 
 #define OV511_DEBUG	/* Turn on debug messages */
 
 #ifdef OV511_DEBUG
 #  define PDEBUG(level, fmt, args...) \
-if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args)
+if (debug >= (level)) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , \
+	## args)
 #else
 #  define PDEBUG(level, fmt, args...) do {} while(0)
 #endif
 
+/* This macro restricts an int variable to an inclusive range */
+#define RESTRICT_TO_RANGE(v,mi,ma) { \
+	if ((v) < (mi)) (v) = (mi); \
+	else if ((v) > (ma)) (v) = (ma); \
+}
+
+/* --------------------------------- */
+/* DEFINES FOR OV511 AND OTHER CHIPS */
+/* --------------------------------- */
+
+/* USB IDs */
+#define VEND_OMNIVISION	0x05A9
+#define PROD_OV511	0x0511
+#define PROD_OV511PLUS	0xA511
+#define PROD_OV518	0x0518
+#define PROD_OV518PLUS	0xA518
+
+#define VEND_MATTEL	0x0813
+#define PROD_ME2CAM	0x0002
+
 /* Camera interface register numbers */
 #define OV511_REG_CAMERA_DELAY_MODE		0x10
 #define OV511_REG_CAMERA_EDGE_MODE		0x11
@@ -52,6 +73,7 @@
 
 /* I2C register numbers */
 #define OV511_REG_I2C_CONTROL			0x40
+#define OV518_REG_I2C_CONTROL			0x47	/* OV518(+) only */
 #define OV511_REG_I2C_SLAVE_ID_WRITE		0x41
 #define OV511_REG_I2C_SUB_ADDRESS_3_BYTE	0x42
 #define OV511_REG_I2C_SUB_ADDRESS_2_BYTE	0x43
@@ -78,8 +100,16 @@
 #define OV511_REG_SYSTEM_CLOCK_DIVISOR		0x51
 #define OV511_REG_SYSTEM_SNAPSHOT		0x52
 #define OV511_REG_SYSTEM_INIT         		0x53
-#define OV511_REG_SYSTEM_PWR_CLK		0x54	/* OV511+ only */
+#define OV511_REG_SYSTEM_PWR_CLK		0x54 /* OV511+/OV518(+) only */
 #define OV511_REG_SYSTEM_LED_CTL		0x55	/* OV511+ only */
+#define OV518_REG_GPIO_IN			0x55	/* OV518(+) only */
+#define OV518_REG_GPIO_OUT			0x56	/* OV518(+) only */
+#define OV518_REG_GPIO_CTL			0x57	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_IN			0x58	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_CLEAR		0x59	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_POLARITY		0x5a	/* OV518(+) only */
+#define OV518_REG_GPIO_PULSE_EN			0x5b	/* OV518(+) only */
+#define OV518_REG_GPIO_RESET			0x5c	/* OV518(+) only */
 #define OV511_REG_SYSTEM_USER_DEFINED		0x5E
 #define OV511_REG_SYSTEM_CUSTOM_ID		0x5F
 
@@ -119,6 +149,16 @@
 #define OV511PLUS_ALT_SIZE_769	6
 #define OV511PLUS_ALT_SIZE_961	7
 
+/* Alternate numbers for various max packet sizes (OV518(+) only) */
+#define OV518_ALT_SIZE_0	0
+#define OV518_ALT_SIZE_128	1
+#define OV518_ALT_SIZE_256	2
+#define OV518_ALT_SIZE_384	3
+#define OV518_ALT_SIZE_512	4
+#define OV518_ALT_SIZE_640	5
+#define OV518_ALT_SIZE_768	6
+#define OV518_ALT_SIZE_896	7
+
 /* OV7610 registers */
 #define OV7610_REG_GAIN          0x00	/* gain setting (5:0) */
 #define OV7610_REG_BLUE          0x01	/* blue channel balance */
@@ -170,44 +210,74 @@
 					/* 36-37 reserved */
 #define OV7610_REG_COM_K         0x38	/* misc registers */
 
-
-#define SCRATCH_BUF_SIZE 512
-
 #define FRAMES_PER_DESC		10	/* FIXME - What should this be? */
 #define FRAME_SIZE_PER_DESC	993	/* FIXME - Deprecated */
 #define MAX_FRAME_SIZE_PER_DESC	993	/* For statically allocated stuff */
+#define PIXELS_PER_SEG		256	/* Pixels per segment */
 
 #define OV511_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
 
-// CAMERA SPECIFIC
-// FIXME - these can vary between specific models
-#define OV7610_I2C_WRITE_ID 0x42
-#define OV7610_I2C_READ_ID  0x43
-#define OV6xx0_I2C_WRITE_ID 0xC0
-#define OV6xx0_I2C_READ_ID  0xC1
+/* I2C addresses */
+#define OV7xx0_I2C_WRITE_ID   0x42
+#define OV7xx0_I2C_READ_ID    0x43
+#define OV6xx0_I2C_WRITE_ID   0xC0
+#define OV6xx0_I2C_READ_ID    0xC1
+#define OV8xx0_I2C_WRITE_ID   0xA0
+#define OV8xx0_I2C_READ_ID    0xA1
+#define KS0127_I2C_WRITE_ID   0xD8
+#define KS0127_I2C_READ_ID    0xD9
+#define SAA7111A_I2C_WRITE_ID 0x48
+#define SAA7111A_I2C_READ_ID  0x49
 
 #define OV511_I2C_CLOCK_PRESCALER 0x03
 
-/* Prototypes */
-int usb_ov511_reg_read(struct usb_device *dev, unsigned char reg);
-int usb_ov511_reg_write(struct usb_device *dev,
-                        unsigned char reg,
-                        unsigned char value);
-
 /* Bridge types */
 enum {
+	BRG_UNKNOWN,
 	BRG_OV511,
 	BRG_OV511PLUS,
+	BRG_OV518,
+	BRG_OV518PLUS,
+};
+
+/* Bridge classes */
+enum {
+	BCL_UNKNOWN,
+	BCL_OV511,
+	BCL_OV518,
 };
 
 /* Sensor types */
 enum {
 	SEN_UNKNOWN,
+	SEN_OV76BE,
 	SEN_OV7610,
 	SEN_OV7620,
 	SEN_OV7620AE,
 	SEN_OV6620,
+	SEN_OV6630,
+	SEN_OV6630AE,
+	SEN_OV6630AF,
+	SEN_OV8600,
+	SEN_KS0127,
+	SEN_KS0127B,
+	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 */
@@ -222,7 +292,77 @@
 	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
-struct usb_device;
+/* --------- Definition of ioctl interface --------- */
+
+#define OV511_INTERFACE_VER 101
+
+/* LED options */
+enum {
+	LED_OFF,
+	LED_ON,
+	LED_AUTO,
+};
+
+/* Raw frame formats */
+enum {
+	RAWFMT_INVALID,
+	RAWFMT_YUV400,
+	RAWFMT_YUV420,
+	RAWFMT_YUV422,
+	RAWFMT_GBR422,
+};
+
+/* Unsigned short option numbers */
+enum {
+	OV511_USOPT_INVALID,
+	OV511_USOPT_BRIGHT,
+	OV511_USOPT_SAT,
+	OV511_USOPT_HUE,
+	OV511_USOPT_CONTRAST,
+};
+
+/* Unsigned int option numbers */
+enum {
+	OV511_UIOPT_INVALID,
+	OV511_UIOPT_POWER_FREQ,
+	OV511_UIOPT_BFILTER,
+	OV511_UIOPT_LED,
+	OV511_UIOPT_DEBUG,
+	OV511_UIOPT_COMPRESS,
+};
+
+struct ov511_ushort_opt {
+	int optnum;		/* Specific option number */
+	unsigned short val;
+};
+
+struct ov511_uint_opt {
+	int optnum;		/* Specific option number */
+	unsigned int val;
+};
+
+struct ov511_i2c_struct {
+	unsigned char slave; /* Write slave ID (read ID - 1) */
+	unsigned char reg;   /* Index of register */
+	unsigned char value; /* User sets this w/ write, driver does w/ read */
+	unsigned char mask;  /* Bits to be changed. Not used with read ops */
+};
+
+/* ioctls */
+#define OV511IOC_GINTVER  _IOR('v', BASE_VIDIOCPRIVATE + 0, int)
+#define OV511IOC_GUSHORT _IOWR('v', BASE_VIDIOCPRIVATE + 1, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_SUSHORT  _IOW('v', BASE_VIDIOCPRIVATE + 2, \
+			       struct ov511_ushort_opt)
+#define OV511IOC_GUINT   _IOWR('v', BASE_VIDIOCPRIVATE + 3, \
+			       struct ov511_uint_opt)
+#define OV511IOC_SUINT    _IOW('v', BASE_VIDIOCPRIVATE + 4, \
+			       struct ov511_uint_opt)
+#define OV511IOC_WI2C     _IOW('v', BASE_VIDIOCPRIVATE + 5, \
+			       struct ov511_i2c_struct)
+#define OV511IOC_RI2C    _IOWR('v', BASE_VIDIOCPRIVATE + 6, \
+			       struct ov511_i2c_struct)
+/* ------------- End IOCTL interface -------------- */
 
 struct ov511_sbuf {
 	char *data;
@@ -248,36 +388,51 @@
 };
 
 struct ov511_frame {
+	int framenum;		/* Index of this frame */
 	char *data;		/* Frame buffer */
+	char *tempdata;		/* Temp buffer for multi-stage conversions */
+	char *rawdata;		/* Raw camera data buffer */
 
 	int depth;		/* Bytes per pixel */
 	int width;		/* Width application is expecting */
-	int height;		/* Height */
+	int height;		/* Height application is expecting */
 
-	int hdrwidth;		/* Width the frame actually is */
-	int hdrheight;		/* Height */
+	int rawwidth;		/* Actual width of frame sent from camera */
+	int rawheight;		/* Actual height of frame sent from camera */
 
 	int sub_flag;		/* Sub-capture mode for this frame? */
 	unsigned int format;	/* Format for this frame */
-	int segsize;		/* How big is each segment from the camera? */
+	int compressed;		/* Is frame compressed? */
 
 	volatile int grabstate;	/* State of grabbing */
 	int scanstate;		/* State of scanning */
 
-	int curline;		/* Line of frame we're working on */
-	int curpix;
-	int segment;		/* Segment from the incoming data */
+	int bytes_recvd;	/* Number of image bytes received from camera */
 
-	long scanlength;	/* uncompressed, raw data length of frame */
-	long bytes_read;	/* amount of scanlength that has been read from *data */
+	long bytes_read;	/* Amount that has been read() */
 
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snapshot;		/* True if frame was a snapshot */
 };
 
+#define DECOMP_INTERFACE_VER 2
+
+/* Compression module operations */
+struct ov51x_decomp_ops {
+	int (*decomp_400)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_420)(unsigned char *, unsigned char *, int, int, int);
+	int (*decomp_422)(unsigned char *, unsigned char *, int, int, int);
+	void (*decomp_lock)(void);
+	void (*decomp_unlock)(void);
+};
+
 #define OV511_NUMFRAMES	2
-#define OV511_NUMSBUF	2
+#if OV511_NUMFRAMES > VIDEO_MAX_FRAME
+#error "OV511_NUMFRAMES is too high"
+#endif
+
+#define OV511_NUMSBUF		2
 
 struct usb_ov511 {
 	struct video_device vdev;
@@ -292,22 +447,37 @@
 	/* Determined by sensor type */
 	int maxwidth;
 	int maxheight;
+	int minwidth;
+	int minheight;
 
 	int brightness;
 	int colour;
 	int contrast;
 	int hue;
 	int whiteness;
+	int exposure;
+	int auto_brt;		/* Auto brightness enabled flag */
+	int auto_gain;		/* Auto gain control enabled flag */
+	int auto_exp;		/* Auto exposure enabled flag */
+	int backlight;		/* Backlight exposure algorithm flag */
 
-	struct semaphore lock;
+	int led_policy;		/* LED: off|on|auto; OV511+ only */
+
+	struct semaphore lock;	/* Serializes user-accessible operations */
 	int user;		/* user count for exclusive use */
 
 	int streaming;		/* Are we streaming Isochronous? */
 	int grabbing;		/* Are we grabbing? */
 
 	int compress;		/* Should the next frame be compressed? */
+	int compress_inited;	/* Are compression params uploaded? */
+
+	int lightfreq;		/* Power (lighting) frequency */
+	int bandfilt;		/* Banding filter enabled flag */
 
 	char *fbuf;		/* Videodev buffer area */
+	char *tempfbuf;		/* Temporary (intermediate) buffer area */
+	char *rawfbuf;		/* Raw camera data buffer area */
 
 	int sub_flag;		/* Pix Array subcapture on flag */
 	int subx;		/* Pix Array subcapture x offset */
@@ -318,30 +488,53 @@
 	int curframe;		/* Current receiving sbuf */
 	struct ov511_frame frame[OV511_NUMFRAMES];	
 
-	int cursbuf;		/* Current receiving sbuf */
 	struct ov511_sbuf sbuf[OV511_NUMSBUF];
 
-	/* Scratch space from the Isochronous pipe */
-	unsigned char scratch[SCRATCH_BUF_SIZE];
-	int scratchlen;
-
 	wait_queue_head_t wq;	/* Processes waiting */
 
 	int snap_enabled;	/* Snapshot mode enabled */
 	
-	int bridge;		/* Type of bridge (OV511 or OV511+) */
-	int sensor;		/* Type of image sensor chip */
+	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 tuner;		/* Type of TV tuner */
 
 	int packet_size;	/* Frame size per isoc desc */
 
-				/* proc interface */
 	struct semaphore param_lock;	/* params lock for this camera */
-	struct proc_dir_entry *proc_entry;	/* /proc/ov511/videoX */
-	
+
+	/* /proc entries, relative to /proc/video/ov511/ */
+	struct proc_dir_entry *proc_devdir;   /* Per-device proc directory */
+	struct proc_dir_entry *proc_info;     /* <minor#>/info entry */
+	struct proc_dir_entry *proc_button;   /* <minor#>/button entry */
+	struct proc_dir_entry *proc_control;  /* <minor#>/control entry */
+
 	/* Framebuffer/sbuf management */
 	int buf_state;
 	struct semaphore buf_lock;
 	struct timer_list buf_timer;
+
+	struct ov51x_decomp_ops *decomp_ops;
+
+	/* Stop streaming while changing picture settings */
+	int stop_during_set;
+
+	int stopped;		/* Streaming is temporarily paused */
+
+	/* Video decoder stuff */
+	int input;		/* Composite, S-VIDEO, etc... */
+	int num_inputs;		/* Number of inputs */
+	int norm; 		/* NTSC / PAL / SECAM */
+	int has_decoder;	/* Device has a video decoder */
+	int has_tuner;		/* Device has a TV tuner */
+	int has_audio_proc;	/* Device has an audio processor */
+	int freq;		/* Current tuner frequency */
+	int tuner_type;		/* Specific tuner model */
+
+	/* I2C interface to kernel */
+	struct semaphore i2c_lock;	  /* Protect I2C controller regs */
+	unsigned char primary_i2c_slave;  /* I2C write id of sensor */
 };
 
 struct cam_list {
@@ -354,18 +547,57 @@
 	char *name;
 };
 
-struct mode_list {
+struct mode_list_518 {
 	int width;
 	int height;
-	int color;		/* 0=grayscale, 1=color */
-	u8 pxcnt;		/* pixel counter */
-	u8 lncnt;		/* line counter */
-	u8 pxdv;		/* pixel divisor */
-	u8 lndv;		/* line divisor */
-	u8 m420;
-	u8 common_A;
-	u8 common_L;
-};
+	u8 reg28;
+	u8 reg29;
+	u8 reg2a;
+	u8 reg2c;
+	u8 reg2e;
+	u8 reg24;
+	u8 reg25;
+};
+
+/* Compression stuff */
+
+#define OV511_QUANTABLESIZE	64
+#define OV518_QUANTABLESIZE	32
+
+#define OV511_YQUANTABLE { \
+	0, 1, 1, 2, 2, 3, 3, 4, \
+	1, 1, 1, 2, 2, 3, 4, 4, \
+	1, 1, 2, 2, 3, 4, 4, 4, \
+	2, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 5, 5, 5, \
+	3, 3, 4, 4, 5, 5, 5, 5, \
+	3, 4, 4, 4, 5, 5, 5, 5, \
+	4, 4, 4, 4, 5, 5, 5, 5  \
+}
+
+#define OV511_UVQUANTABLE { \
+	0, 2, 2, 3, 4, 4, 4, 4, \
+	2, 2, 2, 4, 4, 4, 4, 4, \
+	2, 2, 3, 4, 4, 4, 4, 4, \
+	3, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4, \
+	4, 4, 4, 4, 4, 4, 4, 4  \
+}
+
+#define OV518_YQUANTABLE { \
+	5, 4, 5, 6, 6, 7, 7, 7, \
+	5, 5, 5, 5, 6, 7, 7, 7, \
+	6, 6, 6, 6, 7, 7, 7, 8, \
+	7, 7, 6, 7, 7, 7, 8, 8  \
+}
+
+#define OV518_UVQUANTABLE { \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 7, \
+	6, 6, 6, 7, 7, 7, 7, 8, \
+	7, 7, 7, 7, 7, 7, 8, 8  \
+}
 
 #endif
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/pegasus.c linux/drivers/usb/pegasus.c
--- linux.orig/drivers/usb/pegasus.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/usb/pegasus.c	Mon Feb  4 19:00:37 2002
@@ -883,6 +883,7 @@
 	if ( reset_mac(pegasus) ) {
 		err("can't reset MAC");
 		unregister_netdev( pegasus->net );
+		kfree(pegasus->net);
 		kfree(pegasus);
 		pegasus = NULL;
 		return NULL;
@@ -919,6 +920,7 @@
 	pegasus->flags |= PEGASUS_UNPLUG;
 	unregister_netdev( pegasus->net );
 	usb_dec_dev_use( dev );
+	kfree( pegasus->net );
 	kfree( pegasus );
 	pegasus = NULL;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/pwc-ctrl.c linux/drivers/usb/pwc-ctrl.c
--- linux.orig/drivers/usb/pwc-ctrl.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/pwc-ctrl.c	Wed Jan  9 15:42:48 2002
@@ -1008,6 +1008,8 @@
 
 	if (pdev->type < 730)
 		return 0;
+	on_value /= 100;
+	off_value /= 100;
 	if (on_value < 0)
 		on_value = 0;
 	if (on_value > 0xff)
@@ -1048,8 +1050,8 @@
 
 	if (ret < 0)
 		return ret;
-	*on_value = buf[0];
-	*off_value = buf[1];
+	*on_value = buf[0] * 100;
+	*off_value = buf[1] * 100;
 	return 0;
 }
 
@@ -1175,6 +1177,8 @@
 			wb.read_red = pwc_read_red_gain(pdev);
 			wb.read_blue = pwc_read_blue_gain(pdev);
 		}
+		if (copy_to_user(arg, &wb, sizeof(wb)))
+			return -EFAULT;
 		break;
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/pwc-if.c linux/drivers/usb/pwc-if.c
--- linux.orig/drivers/usb/pwc-if.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/pwc-if.c	Wed Jan  9 15:42:48 2002
@@ -39,7 +39,10 @@
 
 /* Contributors:
    - Alvarado: adding whitebalance code
-   - Alistar Moire: QuickCam 3000 Pro testing
+   - Alistar Moire: QuickCam 3000 Pro device/product ID
+   - Tony Hoyle: Creative Labs Webcam 5 device/product ID
+   - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged
+   - Jk Fang: SOTEC device/product ID
 */
 
 #include <linux/errno.h>
@@ -76,6 +79,8 @@
 	{ USB_DEVICE(0x046D, 0x08b0) },
 	{ USB_DEVICE(0x055D, 0x9000) },
 	{ USB_DEVICE(0x055D, 0x9001) },
+	{ USB_DEVICE(0x041E, 0x400C) },
+	{ USB_DEVICE(0x04CC, 0x8116) },
 	{ }
 };
 MODULE_DEVICE_TABLE(usb, pwc_device_table);
@@ -887,6 +892,7 @@
 	/* Stop camera, but only if we are sure the camera is still there */
 	if (!pdev->unplugged)
 		usb_set_interface(pdev->udev, 0, 0);
+	/* Unlinking ISOC buffers one by one */
 	for (i = MAX_ISO_BUFS - 1; i >= 0; i--) {
 		pdev->sbuf[i].urb->next = NULL;
 		usb_unlink_urb(pdev->sbuf[i].urb);
@@ -1493,6 +1499,12 @@
 			 */
 			add_wait_queue(&pdev->frameq, &wait);
 			while (pdev->full_frames == NULL) {
+				if (pdev->unplugged) {
+					remove_wait_queue(&pdev->frameq, &wait);
+					set_current_state(TASK_RUNNING);
+					return -ENODEV;
+				}
+			
 	                	if (signal_pending(current)) {
 	                		remove_wait_queue(&pdev->frameq, &wait);
 		                	set_current_state(TASK_RUNNING);
@@ -1710,7 +1722,29 @@
 			break;
 		}
 	}
-	else return NULL; /* Not Philips, Askey, Logitech or Samsung, for sure. */
+	else if (vendor_id == 0x041e) {
+		switch(product_id) {
+		case 0x400c:
+			Info("Creative Labs Webcam 5 detected.\n");
+			type_id = 730;
+			break;
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else if (vendor_id == 0x04cc) { 
+		switch(product_id) {
+		case 0x8116:
+			Info("SOTEC CMS-001 USB webcam detected.\n");
+			type_id = 730;
+			break;  
+		default:
+			return NULL;
+			break;
+		}
+	}
+	else return NULL; /* Not Philips, Askey, Logitech, Samsung, Creative or SOTEC, for sure. */
 
 	memset(serial_number, 0, 30);
 	usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
@@ -1780,16 +1814,6 @@
 	if (hint < MAX_DEV_HINTS) 
 		device_hint[hint].pdev = pdev;
 
-#if 0
-	/* Shut down camera now (some people like the LED off) */
-	if (power_save) {
-		Trace(TRACE_PROBE, "Powering down camera");
-		i = pwc_camera_power(pdev, 0);
-		if (i < 0)
-			Info("Failed to power-down the camera (%d)\n", i);
-	}
-#endif
-
 	Trace(TRACE_PROBE, "probe() function returning struct at 0x%p.\n", pdev);
 	return pdev;
 }
@@ -1799,6 +1823,7 @@
 {
 	struct pwc_device *pdev;
 	int hint;
+	DECLARE_WAITQUEUE(wait, current);
 
 	lock_kernel();
 	free_mem_leak();
@@ -1833,13 +1858,19 @@
 			 */
 			wake_up(&pdev->frameq);
 			
-			/* Wait until we get a 'go' from _close(). This
-			   had a gigantic race condition, since we kfree()
+			/* Wait until we get a 'go' from _close(). This used
+			   to have a gigantic race condition, since we kfree()
 			   stuff here, but we have to wait until close() 
-			   is finished. */
+			   is finished. 
+			 */
 			   
 			Trace(TRACE_PROBE, "Sleeping on remove_ok.\n");
-			sleep_on(&pdev->remove_ok);
+			add_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			/* ... wait ... */
+			schedule();
+			remove_wait_queue(&pdev->remove_ok, &wait);
+			set_current_state(TASK_RUNNING);
 			Trace(TRACE_PROBE, "Done sleeping.\n");
 			set_mem_leak(pdev->vdev);
 			pdev->vdev = NULL;
@@ -1920,7 +1951,7 @@
 	char *sizenames[PSZ_MAX] = { "sqcif", "qsif", "qcif", "sif", "cif", "vga" };
 
 	Info("Philips PCA645/646 + PCVC675/680/690 + PCVC730/740/750 webcam module version " PWC_VERSION " loaded.\n");
-	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro and the Samsung MPC-C10 and MPC-C30.\n");
+	Info("Also supports the Askey VC010, Logitech Quickcam 3000 Pro, Samsung MPC-C10 and MPC-C30, the Creative WebCam 5 and the SOTEC CMS-001.\n");
 
 	if (fps) {
 		if (fps < 5 || fps > 30) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/pwc-ioctl.h linux/drivers/usb/pwc-ioctl.h
--- linux.orig/drivers/usb/pwc-ioctl.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/pwc-ioctl.h	Wed Jan  9 15:42:48 2002
@@ -79,8 +79,8 @@
 /* Used with VIDIOCPWC[SG]LED */
 struct pwc_leds
 {
-	int led_on;			/* Led on-time; range = 0..255 */
-	int led_off;			/*  */
+	int led_on;			/* Led on-time; range = 0..25000 */
+	int led_off;			/* Led off-time; range = 0..25000  */
 };
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/pwc.h linux/drivers/usb/pwc.h
--- linux.orig/drivers/usb/pwc.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/pwc.h	Wed Jan  9 15:42:48 2002
@@ -60,8 +60,8 @@
 
 /* Version block */
 #define PWC_MAJOR	8
-#define PWC_MINOR	4
-#define PWC_VERSION 	"8.4"
+#define PWC_MINOR	5
+#define PWC_VERSION 	"8.5"
 #define PWC_NAME 	"pwc"
 
 /* Turn certain features on/off */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/Config.in linux/drivers/usb/serial/Config.in
--- linux.orig/drivers/usb/serial/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/serial/Config.in	Wed Dec 26 15:09:47 2001
@@ -15,6 +15,7 @@
 dep_tristate '  USB Empeg empeg-car Mark I/II Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EMPEG $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Handspring Visor / Palm m50x / Sony Clie Driver' CONFIG_USB_SERIAL_VISOR $CONFIG_USB_SERIAL
+dep_tristate '  USB Compaq iPAQ Driver' CONFIG_USB_SERIAL_IPAQ $CONFIG_USB_SERIAL
 dep_tristate '  USB IR Dongle Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_IR $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Inside Out Edgeport Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_EDGEPORT $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
@@ -28,6 +29,7 @@
    dep_mbool '    USB Keyspan USA-19W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA19W $CONFIG_USB_SERIAL_KEYSPAN
    dep_mbool '    USB Keyspan USA-49W Firmware' CONFIG_USB_SERIAL_KEYSPAN_USA49W $CONFIG_USB_SERIAL_KEYSPAN
 dep_tristate '  USB MCT Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_MCT_U232 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
+dep_tristate '  USB KL5KUSB105 (Palmconnect) Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KLSI $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Prolific 2303 Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_PL2303 $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB REINER SCT cyberJack pinpad/e-com chipcard reader (EXPERIMENTAL)' CONFIG_USB_SERIAL_CYBERJACK $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
 dep_tristate '  USB Xircom / Entregra Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_XIRCOM $CONFIG_USB_SERIAL $CONFIG_EXPERIMENTAL
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/Makefile linux/drivers/usb/serial/Makefile
--- linux.orig/drivers/usb/serial/Makefile	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/serial/Makefile	Wed Dec 26 15:09:47 2001
@@ -8,6 +8,7 @@
 
 obj-$(CONFIG_USB_SERIAL)                        += usbserial.o
 obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
+obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
 obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
 obj-$(CONFIG_USB_SERIAL_FTDI_SIO)		+= ftdi_sio.o
 obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA)		+= keyspan_pda.o
@@ -22,7 +23,8 @@
 obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
 obj-$(CONFIG_USB_SERIAL_CYBERJACK)		+= cyberjack.o
 obj-$(CONFIG_USB_SERIAL_IR)			+= ir-usb.o
- 
+obj-$(CONFIG_USB_SERIAL_KLSI)			+= kl5kusb105.o
+
 # Objects that export symbols.
 export-objs	:= usbserial.o
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/ipaq.c linux/drivers/usb/serial/ipaq.c
--- linux.orig/drivers/usb/serial/ipaq.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/ipaq.c	Wed Dec 26 14:28:36 2001
@@ -0,0 +1,532 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	static int debug = 1;
+#else
+	static int debug = 0;
+#endif
+
+#include "usb-serial.h"
+#include "ipaq.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
+#define DRIVER_DESC "USB Compaq iPAQ driver"
+
+/* Function prototypes for an ipaq */
+static int  ipaq_open (struct usb_serial_port *port, struct file *filp);
+static void ipaq_close (struct usb_serial_port *port, struct file *filp);
+static int  ipaq_startup (struct usb_serial *serial);
+static void ipaq_shutdown (struct usb_serial *serial);
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+		       int count);
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+			   int count);
+static int ipaq_write_flush(struct usb_serial_port *port);
+static void ipaq_read_bulk_callback (struct urb *urb);
+static void ipaq_write_bulk_callback(struct urb *urb);
+static int ipaq_write_room(struct usb_serial_port *port);
+static int ipaq_chars_in_buffer(struct usb_serial_port *port);
+static void ipaq_destroy_lists(struct usb_serial_port *port);
+
+
+static __devinitdata struct usb_device_id ipaq_id_table [] = {
+	{ USB_DEVICE(IPAQ_VENDOR_ID, IPAQ_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, ipaq_id_table);
+
+/* All of the device info needed for the Compaq iPAQ */
+struct usb_serial_device_type ipaq_device = {
+	name:			"Compaq iPAQ",
+	id_table:		ipaq_id_table,
+	needs_interrupt_in:	MUST_HAVE_NOT,
+	needs_bulk_in:		MUST_HAVE,
+	needs_bulk_out:		MUST_HAVE,
+	num_interrupt_in:	0,
+	num_bulk_in:		1,
+	num_bulk_out:		1,
+	num_ports:		1,
+	open:			ipaq_open,
+	close:			ipaq_close,
+	startup:		ipaq_startup,
+	shutdown:		ipaq_shutdown,
+	write:			ipaq_write,
+	write_room:		ipaq_write_room,
+	chars_in_buffer:	ipaq_chars_in_buffer,
+	read_bulk_callback:	ipaq_read_bulk_callback,
+	write_bulk_callback:	ipaq_write_bulk_callback,
+};
+
+static spinlock_t	write_list_lock;
+static int		bytes_in;
+static int		bytes_out;
+
+static int ipaq_open(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial = port->serial;
+	struct ipaq_private	*priv;
+	struct ipaq_packet	*pkt;
+	int			i, result = 0;
+
+	if (port_paranoia_check(port, __FUNCTION__)) {
+		return -ENODEV;
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down(&port->sem);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+	
+	if (!port->active) {
+		port->active = 1;
+		bytes_in = 0;
+		bytes_out = 0;
+		priv = (struct ipaq_private *)kmalloc(sizeof(struct ipaq_private), GFP_KERNEL);
+		if (priv == NULL) {
+			err(__FUNCTION__ " - Out of memory");
+			return -ENOMEM;
+		}
+		port->private = (void *)priv;
+		priv->active = 0;
+		priv->queue_len = 0;
+		INIT_LIST_HEAD(&priv->queue);
+		INIT_LIST_HEAD(&priv->freelist);
+
+		for (i = 0; i < URBDATA_QUEUE_MAX / PACKET_SIZE; i++) {
+			pkt = kmalloc(sizeof(struct ipaq_packet), GFP_KERNEL);
+			if (pkt == NULL) {
+				goto enomem;
+			}
+			pkt->data = kmalloc(PACKET_SIZE, GFP_KERNEL);
+			if (pkt->data == NULL) {
+				kfree(pkt);
+				goto enomem;
+			}
+			pkt->len = 0;
+			pkt->written = 0;
+			INIT_LIST_HEAD(&pkt->list);
+			list_add(&pkt->list, &priv->freelist);
+			priv->free_len += PACKET_SIZE;
+		}
+
+		/*
+		 * Force low latency on. This will immediately push data to the line
+		 * discipline instead of queueing.
+		 */
+
+		port->tty->low_latency = 1;
+
+		/*
+		 * Lose the small buffers usbserial provides. Make larger ones.
+		 */
+
+		kfree(port->bulk_in_buffer);
+		kfree(port->bulk_out_buffer);
+		port->bulk_in_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+		if (port->bulk_in_buffer == NULL) {
+			goto enomem;
+		}
+		port->bulk_out_buffer = kmalloc(URBDATA_SIZE, GFP_KERNEL);
+		if (port->bulk_out_buffer == NULL) {
+			kfree(port->bulk_in_buffer);
+			goto enomem;
+		}
+		port->read_urb->transfer_buffer = port->bulk_in_buffer;
+		port->write_urb->transfer_buffer = port->bulk_out_buffer;
+		port->read_urb->transfer_buffer_length = URBDATA_SIZE;
+		port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
+		
+		/* Start reading from the device */
+		FILL_BULK_URB(port->read_urb, serial->dev, 
+			      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+			      ipaq_read_bulk_callback, port);
+		result = usb_submit_urb(port->read_urb);
+		if (result) {
+			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+		}
+
+		/*
+		 * Send out two control messages observed in win98 sniffs. Not sure what
+		 * they do.
+		 */
+
+		result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+				0x1, 0, NULL, 0, 5 * HZ);
+		if (result < 0) {
+			err(__FUNCTION__ " - failed doing control urb, error %d", result);
+		}
+		result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
+				0x1, 0, NULL, 0, 5 * HZ);
+		if (result < 0) {
+			err(__FUNCTION__ " - failed doing control urb, error %d", result);
+		}
+	}
+	
+	up(&port->sem);
+	
+	return result;
+
+enomem:
+	ipaq_destroy_lists(port);
+	kfree(priv);
+	err(__FUNCTION__ " - Out of memory");
+	return -ENOMEM;
+}
+
+
+static void ipaq_close(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial	*serial;
+	struct ipaq_private	*priv = port->private;
+
+	if (port_paranoia_check(port, __FUNCTION__)) {
+		return; 
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+			 
+	serial = get_usb_serial(port, __FUNCTION__);
+	if (!serial)
+		return;
+	
+	down (&port->sem);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+
+		/*
+		 * shut down bulk read and write
+		 */
+
+		usb_unlink_urb(port->write_urb);
+		usb_unlink_urb(port->read_urb);
+		ipaq_destroy_lists(port);
+		kfree(priv);
+		port->private = NULL;
+		port->active = 0;
+		port->open_count = 0;
+
+	}
+	up (&port->sem);
+
+	/* Uncomment the following line if you want to see some statistics in your syslog */
+	/* info ("Bytes In = %d  Bytes Out = %d", bytes_in, bytes_out); */
+
+	MOD_DEC_USE_COUNT;
+}
+
+static void ipaq_read_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
+	struct usb_serial	*serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct	*tty;
+	unsigned char		*data = urb->transfer_buffer;
+	int			i, result;
+
+	if (port_paranoia_check(port, __FUNCTION__))
+		return;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+	if (urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless tty->low_latency is set */
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+		bytes_in += urb->actual_length;
+	}
+
+	/* Continue trying to always read  */
+	FILL_BULK_URB(port->read_urb, serial->dev, 
+		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+		      ipaq_read_bulk_callback, port);
+	result = usb_submit_urb(port->read_urb);
+	if (result)
+		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
+	return;
+}
+
+static int ipaq_write(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+		       int count)
+{
+	const unsigned char	*current_position = buf;
+	int			bytes_sent = 0;
+	int			transfer_size;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	usb_serial_debug_data(__FILE__, __FUNCTION__, count, buf);
+	
+	while (count > 0) {
+		transfer_size = min(count, PACKET_SIZE);
+		if (ipaq_write_bulk(port, from_user, current_position, transfer_size)) {
+			break;
+		}
+		current_position += transfer_size;
+		bytes_sent += transfer_size;
+		count -= transfer_size;
+		bytes_out += transfer_size;
+	}
+
+	return bytes_sent;
+} 
+
+static int ipaq_write_bulk(struct usb_serial_port *port, int from_user, const unsigned char *buf,
+			   int count)
+{
+	struct ipaq_private	*priv = port->private;
+	struct ipaq_packet	*pkt = NULL;
+	int			result = 0;
+	unsigned long		flags;
+
+	if (priv->free_len <= 0) {
+		dbg(__FUNCTION__ " - we're stuffed");
+		return -EAGAIN;
+	}
+
+	spin_lock_irqsave(&write_list_lock, flags);
+	if (!list_empty(&priv->freelist)) {
+		pkt = list_entry(priv->freelist.next, struct ipaq_packet, list);
+		list_del(&pkt->list);
+		priv->free_len -= PACKET_SIZE;
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	if (pkt == NULL) {
+		dbg(__FUNCTION__ " - we're stuffed");
+		return -EAGAIN;
+	}
+
+	if (from_user) {
+		copy_from_user(pkt->data, buf, count);
+	} else {
+		memcpy(pkt->data, buf, count);
+	}
+	usb_serial_debug_data(__FILE__, __FUNCTION__, count, pkt->data);
+
+	pkt->len = count;
+	pkt->written = 0;
+	spin_lock_irqsave(&write_list_lock, flags);
+	list_add_tail(&pkt->list, &priv->queue);
+	priv->queue_len += count;
+	if (priv->active == 0) {
+		priv->active = 1;
+		result = ipaq_write_flush(port);
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	return result;
+}
+
+static int ipaq_write_flush(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	struct usb_serial	*serial = port->serial;
+	int			count, room, result;
+	struct ipaq_packet	*pkt;
+	struct urb		*urb = port->write_urb;
+	struct list_head	*tmp;
+
+	if (urb->status == -EINPROGRESS) {
+		/* Should never happen */
+		err(__FUNCTION__ " - flushing while urb is active !");
+		return -EAGAIN;
+	}
+	room = URBDATA_SIZE;
+	for (tmp = priv->queue.next; tmp != &priv->queue;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		count = min(room, (int)(pkt->len - pkt->written));
+		memcpy(urb->transfer_buffer + (URBDATA_SIZE - room),
+		       pkt->data + pkt->written, count);
+		room -= count;
+		pkt->written += count;
+		priv->queue_len -= count;
+		if (pkt->written == pkt->len) {
+			list_del(&pkt->list);
+			list_add(&pkt->list, &priv->freelist);
+			priv->free_len += PACKET_SIZE;
+		}
+		if (room == 0) {
+			break;
+		}
+	}
+
+	count = URBDATA_SIZE - room;
+	FILL_BULK_URB(port->write_urb, serial->dev, 
+		      usb_sndbulkpipe(serial->dev, port->bulk_out_endpointAddress),
+		      port->write_urb->transfer_buffer, count, ipaq_write_bulk_callback,
+		      port);
+	result = usb_submit_urb(urb);
+	if (result) {
+		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+	}
+	return result;
+}
+
+static void ipaq_write_bulk_callback(struct urb *urb)
+{
+	struct usb_serial_port	*port = (struct usb_serial_port *)urb->context;
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	unsigned long		flags;
+
+	if (port_paranoia_check (port, __FUNCTION__)) {
+		return;
+	}
+	
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+	}
+
+	spin_lock_irqsave(&write_list_lock, flags);
+	if (!list_empty(&priv->queue)) {
+		ipaq_write_flush(port);
+	} else {
+		priv->active = 0;
+	}
+	spin_unlock_irqrestore(&write_list_lock, flags);
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+	
+	return;
+}
+
+static int ipaq_write_room(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+
+	dbg(__FUNCTION__ " - freelen %d", priv->free_len);
+	return priv->free_len;
+}
+
+static int ipaq_chars_in_buffer(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+
+	dbg(__FUNCTION__ " - queuelen %d", priv->queue_len);
+	return priv->queue_len;
+}
+
+static void ipaq_destroy_lists(struct usb_serial_port *port)
+{
+	struct ipaq_private	*priv = (struct ipaq_private *)port->private;
+	struct list_head	*tmp;
+	struct ipaq_packet	*pkt;
+
+	for (tmp = priv->queue.next; tmp != &priv->queue;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		kfree(pkt->data);
+		kfree(pkt);
+	}
+	for (tmp = priv->freelist.next; tmp != &priv->freelist;) {
+		pkt = list_entry(tmp, struct ipaq_packet, list);
+		tmp = tmp->next;
+		kfree(pkt->data);
+		kfree(pkt);
+	}
+	return;
+}
+
+
+static int ipaq_startup(struct usb_serial *serial)
+{
+	dbg(__FUNCTION__);
+	usb_set_configuration(serial->dev, 1);
+	return 0;
+}
+
+static void ipaq_shutdown(struct usb_serial *serial)
+{
+	int i;
+
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		while (serial->port[i].open_count > 0) {
+			ipaq_close(&serial->port[i], NULL);
+		}
+	}
+}
+
+static int __init ipaq_init(void)
+{
+	usb_serial_register(&ipaq_device);
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	return 0;
+}
+
+
+static void __exit ipaq_exit(void)
+{
+	usb_serial_deregister(&ipaq_device);
+}
+
+
+module_init(ipaq_init);
+module_exit(ipaq_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/ipaq.h linux/drivers/usb/serial/ipaq.h
--- linux.orig/drivers/usb/serial/ipaq.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/ipaq.h	Wed Dec 26 14:28:36 2001
@@ -0,0 +1,60 @@
+/*
+ * USB Compaq iPAQ driver
+ *
+ *	Copyright (C) 2001
+ *	    Ganesh Varadarajan <ganesh@veritas.com>
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ * 
+ */
+
+#ifndef __LINUX_USB_SERIAL_IPAQ_H
+#define __LINUX_USB_SERIAL_IPAQ_H
+
+
+#define IPAQ_VENDOR_ID			0x049f
+#define IPAQ_PRODUCT_ID			0x0003
+
+/*
+ * Since we can't queue our bulk write urbs (don't know why - it just
+ * doesn't work), we can send down only one write urb at a time. The simplistic
+ * approach taken by the generic usbserial driver will work, but it's not good
+ * for performance. Therefore, we buffer upto URBDATA_QUEUE_MAX bytes of write
+ * requests coming from the line discipline. This is done by chaining them
+ * in lists of struct ipaq_packet, each packet holding a maximum of
+ * PACKET_SIZE bytes.
+ *
+ * ipaq_write() can be called from bottom half context; hence we can't
+ * allocate memory for packets there. So we initialize a pool of packets at
+ * the first open and maintain a freelist.
+ *
+ * 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.
+ */
+
+struct ipaq_packet {
+	char			*data;
+	size_t			len;
+	size_t			written;
+	struct list_head	list;
+};
+
+struct ipaq_private {
+	int			active;
+	int			queue_len;
+	int			free_len;
+	struct list_head	queue;
+	struct list_head	freelist;
+};
+
+#define URBDATA_SIZE		4096
+#define URBDATA_QUEUE_MAX	(64 * 1024)
+#define PACKET_SIZE		256
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/ir-usb.c linux/drivers/usb/serial/ir-usb.c
--- linux.orig/drivers/usb/serial/ir-usb.c	Mon Nov 12 17:53:56 2001
+++ linux/drivers/usb/serial/ir-usb.c	Wed Jan 16 20:13:57 2002
@@ -2,6 +2,7 @@
  * USB IR Dongle driver
  *
  *	Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *	Copyright (C) 2002 Gary Brubaker (xavyer@ix.netcom.com)
  *
  *	This program is free software; you can redistribute it and/or modify
  *	it under the terms of the GNU General Public License as published by
@@ -20,6 +21,12 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  *
+ * 2002_Jan_14  gb
+ *	Added module parameter to force specific number of XBOFs.
+ *	Added ir_xbof_change().
+ *	Reorganized read_bulk_callback error handling.
+ *	Switched from FILL_BULK_URB() to usb_fill_bulk_urb().
+ *
  * 2001_Nov_08  greg kh
  *	Changed the irda_usb_find_class_desc() function based on comments and
  *	code from Martin Diehl.
@@ -62,13 +69,15 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.3"
+#define DRIVER_VERSION "v0.4"
 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
 #define DRIVER_DESC "USB IR Dongle driver"
 
 /* if overridden by the user, then use their value for the size of the read and
  * write urbs */
 static int buffer_size = 0;
+/* if overridden by the user, then use the specified number of XBOFs */
+static int xbof = -1;
 
 static int  ir_startup (struct usb_serial *serial);
 static int  ir_open (struct usb_serial_port *port, struct file *filep);
@@ -78,6 +87,9 @@
 static void ir_read_bulk_callback (struct urb *urb);
 static void ir_set_termios (struct usb_serial_port *port, struct termios *old_termios);
 
+static u8 ir_baud = 0;
+static u8 ir_xbof = 0;
+static u8 ir_add_bof = 0;
 
 static __devinitdata struct usb_device_id id_table [] = {
 	{ USB_DEVICE(0x050f, 0x0180) },		/* KC Technology, KC-180 */
@@ -150,14 +162,16 @@
 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 			0, ifnum, desc, sizeof(*desc), MSECS_TO_JIFFIES(500));
 	
-	dbg(__FUNCTION__ " -  ret=%d", ret);
+	dbg("%s -  ret=%d", __FUNCTION__, ret);
 	if (ret < sizeof(*desc)) {
-		dbg(__FUNCTION__ " - class descriptor read %s (%d)",
-				(ret<0) ? "failed" : "too short", ret);
+		dbg("%s - class descriptor read %s (%d)",
+				__FUNCTION__, 
+				(ret<0) ? "failed" : "too short",
+				ret);
 		goto error;
 	}
 	if (desc->bDescriptorType != USB_DT_IRDA) {
-		dbg(__FUNCTION__ " - bad class descriptor type");
+		dbg("%s - bad class descriptor type", __FUNCTION__);
 		goto error;
 	}
 	
@@ -168,6 +182,28 @@
 	return NULL;
 }
 
+
+static u8 ir_xbof_change(u8 xbof)
+{
+	u8 result;
+	/* reference irda-usb.c */
+	switch(xbof) {
+		case 48: result = 0x10; break;
+		case 28:
+		case 24: result = 0x20; break;
+		default:
+		case 12: result = 0x30; break;
+		case  5:
+		case  6: result = 0x40; break;
+		case  3: result = 0x50; break;
+		case  2: result = 0x60; break;
+		case  1: result = 0x70; break;
+		case  0: result = 0x80; break;
+	}
+	return(result);
+}
+
+
 static int ir_startup (struct usb_serial *serial)
 {
 	struct irda_class_desc *irda_desc;
@@ -177,16 +213,30 @@
 		err ("IRDA class descriptor not found, device not bound");
 		return -ENODEV;
 	}
-	dbg (__FUNCTION__" - Baud rates supported: %s%s%s%s%s%s%s%s%s",
-	     (irda_desc->wBaudRate & 0x0001) ? "2400 " : "",
-	     irda_desc->wBaudRate & 0x0002 ? "9600 " : "",
-	     irda_desc->wBaudRate & 0x0004 ? "19200 " : "",
-	     irda_desc->wBaudRate & 0x0008 ? "38400 " : "",
-	     irda_desc->wBaudRate & 0x0010 ? "57600 " : "",
-	     irda_desc->wBaudRate & 0x0020 ? "115200 " : "",
-	     irda_desc->wBaudRate & 0x0040 ? "576000 " : "",
-	     irda_desc->wBaudRate & 0x0080 ? "1152000 " : "",
-	     irda_desc->wBaudRate & 0x0100 ? "4000000 " : "");
+
+	dbg ("%s - Baud rates supported:%s%s%s%s%s%s%s%s%s",
+		__FUNCTION__,
+		(irda_desc->wBaudRate & 0x0001) ? " 2400"    : "",
+		(irda_desc->wBaudRate & 0x0002) ? " 9600"    : "",
+		(irda_desc->wBaudRate & 0x0004) ? " 19200"   : "",
+		(irda_desc->wBaudRate & 0x0008) ? " 38400"   : "",
+		(irda_desc->wBaudRate & 0x0010) ? " 57600"   : "",
+		(irda_desc->wBaudRate & 0x0020) ? " 115200"  : "",
+		(irda_desc->wBaudRate & 0x0040) ? " 576000"  : "",
+		(irda_desc->wBaudRate & 0x0080) ? " 1152000" : "",
+		(irda_desc->wBaudRate & 0x0100) ? " 4000000" : "");
+
+	switch( irda_desc->bmAdditionalBOFs ) {
+		case 0x01: ir_add_bof = 48; break;
+		case 0x02: ir_add_bof = 24; break;
+		case 0x04: ir_add_bof = 12; break;
+		case 0x08: ir_add_bof =  6; break;
+		case 0x10: ir_add_bof =  3; break;
+		case 0x20: ir_add_bof =  2; break;
+		case 0x40: ir_add_bof =  1; break;
+		case 0x80: ir_add_bof =  0; break;
+		default:
+	}
 
 	kfree (irda_desc);
 
@@ -202,7 +252,7 @@
 	if (port_paranoia_check (port, __FUNCTION__))
 		return -ENODEV;
 	
-	dbg(__FUNCTION__ " - port %d", port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	down (&port->sem);
 	
@@ -216,7 +266,7 @@
 			/* override the default buffer sizes */
 			buffer = kmalloc (buffer_size, GFP_KERNEL);
 			if (!buffer) {
-				err (__FUNCTION__ " - out of memory.");
+				err ("%s - out of memory.", __FUNCTION__);
 				return -ENOMEM;
 			}
 			kfree (port->read_urb->transfer_buffer);
@@ -225,7 +275,7 @@
 
 			buffer = kmalloc (buffer_size, GFP_KERNEL);
 			if (!buffer) {
-				err (__FUNCTION__ " - out of memory.");
+				err ("%s - out of memory.", __FUNCTION__);
 				return -ENOMEM;
 			}
 			kfree (port->write_urb->transfer_buffer);
@@ -235,13 +285,18 @@
 		}
 
 		/* Start reading from the device */
-		FILL_BULK_URB(port->read_urb, serial->dev, 
-			      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-			      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
-			      ir_read_bulk_callback, port);
+		usb_fill_bulk_urb (
+			port->read_urb,
+			serial->dev, 
+			usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+			port->read_urb->transfer_buffer,
+			port->read_urb->transfer_buffer_length,
+			ir_read_bulk_callback,
+			port);
+		port->read_urb->transfer_flags = USB_QUEUE_BULK;
 		result = usb_submit_urb(port->read_urb);
 		if (result)
-			err(__FUNCTION__ " - failed submitting read urb, error %d", result);
+			err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
 	}
 	
 	up (&port->sem);
@@ -256,7 +311,7 @@
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
 	
-	dbg(__FUNCTION__ " - port %d", port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 			 
 	serial = get_usb_serial (port, __FUNCTION__);
 	if (!serial)
@@ -283,11 +338,12 @@
 {
 	unsigned char *transfer_buffer;
 	int result;
+	int transfer_size;
 
-	dbg(__FUNCTION__ " - port = %d, count = %d", port->number, count);
+	dbg("%s - port = %d, count = %d", __FUNCTION__, port->number, count);
 
 	if (!port->tty) {
-		err (__FUNCTION__ " - no tty???");
+		err ("%s - no tty???", __FUNCTION__);
 		return 0;
 	}
 
@@ -295,37 +351,49 @@
 		return 0;
 
 	if (port->write_urb->status == -EINPROGRESS) {
-		dbg (__FUNCTION__ " - already writing");
+		dbg ("%s - already writing", __FUNCTION__);
 		return 0;
 	}
 
+	transfer_buffer = port->write_urb->transfer_buffer;
+	transfer_size = min(count, port->bulk_out_size - 1);
+
 	/*
-	 * The first byte of the packet we send to the device contains a BOD
-	 * and baud rate change.  So we set it to 0.
+	 * The first byte of the packet we send to the device contains an
+	 * inband header which indicates an additional number of BOFs and
+	 * a baud rate change.
+	 *
 	 * See section 5.4.2.2 of the USB IrDA spec.
 	 */
-	transfer_buffer = port->write_urb->transfer_buffer;
-	count = min (port->bulk_out_size-1, count);
+	*transfer_buffer = ir_xbof | ir_baud;
+	++transfer_buffer;
+
 	if (from_user) {
-		if (copy_from_user (&transfer_buffer[1], buf, count))
+		if (copy_from_user (transfer_buffer, buf, transfer_size))
 			return -EFAULT;
 	} else {
-		memcpy (&transfer_buffer[1], buf, count);
+		memcpy (transfer_buffer, buf, transfer_size);
 	}
 
-	/* use 12 XBOF's as default */
-	transfer_buffer[0] = 0x30;
-
-	usb_serial_debug_data (__FILE__, __FUNCTION__, count+1, transfer_buffer);
+	usb_fill_bulk_urb (
+		port->write_urb,
+		port->serial->dev,
+		usb_sndbulkpipe(port->serial->dev,
+			port->bulk_out_endpointAddress),
+		port->write_urb->transfer_buffer,
+		transfer_size + 1,
+		ir_write_bulk_callback,
+		port);
+
+	port->write_urb->transfer_flags
+		= USB_QUEUE_BULK
+		| USB_ZERO_PACKET;
 
-	port->write_urb->transfer_buffer_length = count + 1;
-	port->write_urb->dev = port->serial->dev;
-	port->write_urb->transfer_flags |= USB_ZERO_PACKET;
 	result = usb_submit_urb (port->write_urb);
 	if (result)
-		err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+		err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
 	else
-		result = count;
+		result = transfer_size;
 
 	return result;
 }
@@ -337,13 +405,19 @@
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
 	
-	dbg(__FUNCTION__ " - port %d", port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 	
 	if (urb->status) {
-		dbg(__FUNCTION__ " - nonzero write bulk status received: %d", urb->status);
+		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
 		return;
 	}
 
+	usb_serial_debug_data (
+		__FILE__,
+		__FUNCTION__,
+		urb->actual_length,
+		urb->transfer_buffer);
+
 	queue_task(&port->tqueue, &tq_immediate);
 	mark_bh(IMMEDIATE_BH);
 	
@@ -361,34 +435,87 @@
 	if (port_paranoia_check (port, __FUNCTION__))
 		return;
 
-	dbg(__FUNCTION__ " - port %d", port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	if (!serial) {
-		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
 		return;
 	}
 
-	if (urb->status) {
-		dbg(__FUNCTION__ " - nonzero read bulk status received: %d", urb->status);
+	if (!port->active) {
+		dbg("%s - port closed.", __FUNCTION__);
 		return;
 	}
 
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	switch (urb->status) {
+
+		case 0: /* Successful */
+
+			/*
+			 * The first byte of the packet we get from the device
+			 * contains a busy indicator and baud rate change.
+			 * See section 5.4.1.2 of the USB IrDA spec.
+			 */
+			if((*data & 0x0f) > 0) ir_baud = *data & 0x0f;
+
+			usb_serial_debug_data (
+				__FILE__,
+				__FUNCTION__,
+				urb->actual_length,
+				data);
+
+			/*
+			 * Bypass flip-buffers, and feed the ldisc directly
+			 * due to our potentally large buffer size.  Since we
+			 * used to set low_latency, this is exactly what the
+			 * tty layer did anyway :)
+			 */
+			tty = port->tty;
+
+			tty->ldisc.receive_buf(
+				tty,
+				data+1,
+				NULL,
+				urb->actual_length-1);
+
+			/*
+			 * No break here.
+			 * We want to resubmit the urb so we can read
+			 * again.
+			 */
+
+		case -EPROTO: /* taking inspiration from pl2303.c */
+
+			/* Continue trying to always read */
+			usb_fill_bulk_urb (
+				port->read_urb,
+				serial->dev, 
+				usb_rcvbulkpipe(serial->dev,
+					port->bulk_in_endpointAddress),
+				port->read_urb->transfer_buffer,
+				port->read_urb->transfer_buffer_length,
+				ir_read_bulk_callback,
+				port);
+
+			port->read_urb->transfer_flags = USB_QUEUE_BULK;
+
+			result = usb_submit_urb(port->read_urb);
+
+			if (result)
+				err("%s - failed resubmitting read urb, error %d",
+					__FUNCTION__, 
+					result);
+
+			break ;
+
+		default:
+			dbg("%s - nonzero read bulk status received: %d",
+				__FUNCTION__, 
+				urb->status);
+			break ;
+
+	}
 
-	/* Bypass flip-buffers, and feed the ldisc directly due to our 
-	 * potentally large buffer size.  Since we used to set low_latency,
-	 * this is exactly what the tty layer did anyway :) */
-	tty = port->tty;
-	tty->ldisc.receive_buf(tty, data+1, NULL, urb->actual_length-1);
-
-	/* Continue trying to always read  */
-	FILL_BULK_URB(port->read_urb, serial->dev, 
-		      usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-		      port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
-		      ir_read_bulk_callback, port);
-	result = usb_submit_urb(port->read_urb);
-	if (result)
-		err(__FUNCTION__ " - failed resubmitting read urb, error %d", result);
 	return;
 }
 
@@ -397,12 +524,11 @@
 	unsigned char *transfer_buffer;
 	unsigned int cflag;
 	int result;
-	u8 baud;
 
-	dbg(__FUNCTION__ " - port %d", port->number);
+	dbg("%s - port %d", __FUNCTION__, port->number);
 
 	if ((!port->tty) || (!port->tty->termios)) {
-		dbg(__FUNCTION__" - no tty structures");
+		dbg("%s - no tty structures", __FUNCTION__);
 		return;
 	}
 
@@ -411,44 +537,70 @@
 	if (old_termios) {
 		if ((cflag == old_termios->c_cflag) &&
 		    (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
-			dbg(__FUNCTION__ " - nothing to change...");
+			dbg("%s - nothing to change...", __FUNCTION__);
 			return;
 		}
 	}
 
 	/* All we can change is the baud rate */
 	if (cflag & CBAUD) {
-		dbg (__FUNCTION__ " - asking for baud %d", tty_get_baud_rate(port->tty));
+
+		dbg ("%s - asking for baud %d",
+			__FUNCTION__,
+			tty_get_baud_rate(port->tty));
+
 		/* 
 		 * FIXME, we should compare the baud request against the
 		 * capability stated in the IR header that we got in the
 		 * startup funtion.
 		 */
 		switch (cflag & CBAUD) {
-			case B2400:	baud = SPEED_2400;	break;
-			case B9600:	baud = SPEED_9600;	break;
-			case B19200:	baud = SPEED_19200;	break;
-			case B38400:	baud = SPEED_38400;	break;
-			case B57600:	baud = SPEED_57600;	break;
-			case B115200:	baud = SPEED_115200;	break;
-			case B576000:	baud = SPEED_576000;	break;
-			case B1152000:	baud = SPEED_1152000;	break;
-			case B4000000:	baud = SPEED_4000000;	break;
+			case B2400:    ir_baud = SPEED_2400;    break;
 			default:
-				err ("ir-usb driver does not support the baudrate (%d) requested", tty_get_baud_rate(port->tty));
-				return;
+			case B9600:    ir_baud = SPEED_9600;    break;
+			case B19200:   ir_baud = SPEED_19200;   break;
+			case B38400:   ir_baud = SPEED_38400;   break;
+			case B57600:   ir_baud = SPEED_57600;   break;
+			case B115200:  ir_baud = SPEED_115200;  break;
+			case B576000:  ir_baud = SPEED_576000;  break;
+			case B1152000: ir_baud = SPEED_1152000; break;
+			case B4000000: ir_baud = SPEED_4000000; break;
 		}
-		
+
+		if (xbof == -1) {
+			ir_xbof = ir_xbof_change(ir_add_bof);
+		} else {
+			ir_xbof = ir_xbof_change(xbof) ;
+		}
+
+		/* Notify the tty driver that the termios have changed. */
+		port->tty->ldisc.set_termios(port->tty, NULL);
+
 		/* FIXME need to check to see if our write urb is busy right
-		 * now, or use a urb pool. */
-		/* send the baud change out on an "empty" data packet */
+		 * now, or use a urb pool.
+		 *
+		 * send the baud change out on an "empty" data packet
+		 */
 		transfer_buffer = port->write_urb->transfer_buffer;
-		transfer_buffer[0] = baud;
-		port->write_urb->transfer_buffer_length = 1;
-		port->write_urb->dev = port->serial->dev;
+		*transfer_buffer = ir_xbof | ir_baud;
+
+		usb_fill_bulk_urb (
+			port->write_urb,
+			port->serial->dev,
+			usb_sndbulkpipe(port->serial->dev,
+				port->bulk_out_endpointAddress),
+			port->write_urb->transfer_buffer,
+			1,
+			ir_write_bulk_callback,
+			port);
+
+		port->write_urb->transfer_flags
+			= USB_QUEUE_BULK
+			| USB_ZERO_PACKET;
+
 		result = usb_submit_urb (port->write_urb);
 		if (result)
-			err(__FUNCTION__ " - failed submitting write urb, error %d", result);
+			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
 	}
 	return;
 }
@@ -477,6 +629,8 @@
 
 MODULE_PARM(debug, "i");
 MODULE_PARM_DESC(debug, "Debug enabled or not");
+MODULE_PARM(xbof, "i");
+MODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
 MODULE_PARM(buffer_size, "i");
 MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers");
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/kl5kusb105.c linux/drivers/usb/serial/kl5kusb105.c
--- linux.orig/drivers/usb/serial/kl5kusb105.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/kl5kusb105.c	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,1132 @@
+/*
+ * KLSI KL5KUSB105 chip RS232 converter driver
+ *
+ *   Copyright (C) 2001 Utz-Uwe Haus <haus@uuhaus.de>
+ *
+ *   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.
+ *
+ * All information about the device was acquired using SniffUSB ans snoopUSB
+ * on Windows98.
+ * It was written out of frustration with the PalmConnect USB Serial adapter
+ * sold by Palm Inc.
+ * Neither Palm, nor their contractor (MCCI) or their supplier (KLSI) provided
+ * information that was not already available.
+ *
+ * It seems that KLSI bought some silicon-design information from ScanLogic, 
+ * whose SL11R processor is at the core of the KL5KUSB chipset from KLSI.
+ * KLSI has firmware available for their devices; it is probable that the
+ * firmware differs from that used by KLSI in their products. If you have an
+ * original KLSI device and can provide some information on it, I would be 
+ * most interested in adding support for it here. If you have any information 
+ * on the protocol used (or find errors in my reverse-engineered stuff), please
+ * let me know.
+ *
+ * The code was only tested with a PalmConnect USB adapter; if you
+ * are adventurous, try it with any KLSI-based device and let me know how it
+ * breaks so that I can fix it!
+ */
+
+/* TODO:
+ *	check modem line signals
+ *	implement handshaking or decide that we do not support it
+ */
+
+/* History:
+ *   0.3a - implemented pools of write URBs
+ *   0.3  - alpha version for public testing
+ *   0.2  - TIOCMGET works, so autopilot(1) can be used!
+ *   0.1  - can be used to to pilot-xfer -p /dev/ttyUSB0 -l
+ *
+ *   The driver skeleton is mainly based on mct_u232.c and various other 
+ *   pieces of code shamelessly copied from the drivers/usb/serial/ directory.
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+/*#include <linux/fcntl.h>*/
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+/*#include <linux/spinlock.h>*/
+#include <linux/usb.h>
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+ 	static int debug = 1;
+#else
+ 	static int debug;
+#endif
+
+#include "usb-serial.h"
+#include "kl5kusb105.h"
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.3a"
+#define DRIVER_AUTHOR "Utz-Uwe Haus <haus@uuhaus.de>"
+#define DRIVER_DESC "KLSI KL5KUSB105 chipset USB->Serial Converter driver"
+
+
+/*
+ * Function prototypes
+ */
+static int  klsi_105_startup	         (struct usb_serial *serial);
+static void klsi_105_shutdown	         (struct usb_serial *serial);
+static int  klsi_105_open	         (struct usb_serial_port *port,
+					  struct file *filp);
+static void klsi_105_close	         (struct usb_serial_port *port,
+					  struct file *filp);
+static int  klsi_105_write	         (struct usb_serial_port *port,
+					  int from_user,
+					  const unsigned char *buf,
+					  int count);
+static void klsi_105_write_bulk_callback (struct urb *urb);
+static int  klsi_105_chars_in_buffer     (struct usb_serial_port *port);
+static int  klsi_105_write_room          (struct usb_serial_port *port);
+
+static void klsi_105_read_bulk_callback  (struct urb *urb);
+static void klsi_105_set_termios         (struct usb_serial_port *port,
+					  struct termios * old);
+static int  klsi_105_ioctl	         (struct usb_serial_port *port,
+					  struct file * file,
+					  unsigned int cmd,
+					  unsigned long arg);
+static void klsi_105_throttle		 (struct usb_serial_port *port);
+static void klsi_105_unthrottle		 (struct usb_serial_port *port);
+/*
+static void klsi_105_break_ctl	         (struct usb_serial_port *port,
+					  int break_state );
+ */
+
+/*
+ * All of the device info needed for the MCT USB-RS232 converter.
+ */
+static __devinitdata struct usb_device_id id_table_combined [] = {
+	{ USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+	{ USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+	{ }		/* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id palmconnect_table [] = {
+        { USB_DEVICE(PALMCONNECT_VID, PALMCONNECT_PID) },
+        { }                        /* Terminating entry */
+};
+
+static __devinitdata struct usb_device_id kl5kusb105d_table [] = {
+        { USB_DEVICE(KLSI_VID, KLSI_KL5KUSB105D_PID) },
+        { }                        /* Terminating entry */
+};
+
+
+MODULE_DEVICE_TABLE (usb, id_table_combined);
+
+
+static struct usb_serial_device_type palmconnect_device = {
+	name:		     "PalmConnect USB Serial",
+	id_table:	     palmconnect_table,
+	needs_interrupt_in:  MUST_HAVE,	  /* 1 interrupt-in endpoints */
+	needs_bulk_in:	     MUST_HAVE,   /* 1 bulk-in endpoint */
+	needs_bulk_out:	     MUST_HAVE,	  /* 1 bulk-out endpoint */
+	num_interrupt_in:    1,
+	num_bulk_in:	     1,
+	num_bulk_out:	     1,
+	num_ports:	     1,
+	open:		     klsi_105_open,
+	close:		     klsi_105_close,
+	write:		     klsi_105_write,
+	write_bulk_callback: klsi_105_write_bulk_callback,
+	chars_in_buffer:     klsi_105_chars_in_buffer,
+	write_room:          klsi_105_write_room,
+	read_bulk_callback:  klsi_105_read_bulk_callback,
+	ioctl:		     klsi_105_ioctl,
+	set_termios:	     klsi_105_set_termios,
+	/*break_ctl:	     klsi_105_break_ctl,*/
+	startup:	     klsi_105_startup,
+	shutdown:	     klsi_105_shutdown,
+	throttle:	     klsi_105_throttle,
+	unthrottle:	     klsi_105_unthrottle,
+};
+
+static struct usb_serial_device_type kl5kusb105d_device = {
+	name:		     "generic KL5KUSB105D USB->Serial",
+	id_table:	     kl5kusb105d_table,
+	needs_interrupt_in:  MUST_HAVE,	 /* 1 interrupt-in endpoints */
+	needs_bulk_in:	     MUST_HAVE,  /* 1 bulk-in endpoint */
+	needs_bulk_out:	     MUST_HAVE,	 /* 1 bulk-out endpoint */
+	num_interrupt_in:    1,
+	num_bulk_in:	     1,
+	num_bulk_out:	     1,
+	num_ports:	     1,
+	open:		     klsi_105_open,
+	close:		     klsi_105_close,
+	write:		     klsi_105_write,
+	write_bulk_callback: klsi_105_write_bulk_callback,
+	chars_in_buffer:     klsi_105_chars_in_buffer,
+	write_room:          klsi_105_write_room,
+	read_bulk_callback:  klsi_105_read_bulk_callback,
+	ioctl:		     klsi_105_ioctl,
+	set_termios:	     klsi_105_set_termios,
+	/*break_ctl:	     klsi_105_break_ctl,*/
+	startup:	     klsi_105_startup,
+	shutdown:	     klsi_105_shutdown,
+	throttle:	     klsi_105_throttle,
+	unthrottle:	     klsi_105_unthrottle,
+};
+
+
+struct klsi_105_port_settings {
+	__u8	pktlen;		/* always 5, it seems */
+	__u8	baudrate;
+	__u8	databits;
+	__u8	unknown1;
+	__u8	unknown2;
+} __attribute__ ((packed));
+
+/* we implement a pool of NUM_URBS urbs per usb_serial */
+#define NUM_URBS			1
+#define URB_TRANSFER_BUFFER_SIZE	64
+struct klsi_105_private {
+	struct klsi_105_port_settings	cfg;
+	struct termios			termios;
+	unsigned long			line_state; /* modem line settings */
+	/* write pool */
+	struct urb *			write_urb_pool[NUM_URBS];
+	spinlock_t			write_urb_pool_lock;
+	unsigned long			bytes_in;
+	unsigned long			bytes_out;
+};
+
+
+/*
+ * Handle vendor specific USB requests
+ */
+
+
+#define KLSI_TIMEOUT	 (HZ * 5 ) /* default urb timeout */
+
+static int klsi_105_chg_port_settings(struct usb_serial *serial,
+				      struct klsi_105_port_settings *settings)
+{
+	int rc;
+
+        rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+			     KL5KUSB105A_SIO_SET_DATA,
+                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_INTERFACE,
+			     0, /* value */
+			     0, /* index */
+			     settings,
+			     sizeof(struct klsi_105_port_settings),
+			     KLSI_TIMEOUT);
+	if (rc < 0)
+		err("Change port settings failed (error = %d)", rc);
+	info(__FUNCTION__ " - %d byte block, baudrate %x, databits %d, u1 %d, u2 %d",
+	    settings->pktlen,
+	    settings->baudrate, settings->databits,
+	    settings->unknown1, settings->unknown2);
+        return rc;
+} /* klsi_105_chg_port_settings */
+
+/* translate a 16-bit status value from the device to linux's TIO bits */
+static unsigned long klsi_105_status2linestate(const __u16 status)
+{
+	unsigned long res = 0;
+
+	res =   ((status & KL5KUSB105A_DSR) ? TIOCM_DSR : 0)
+	      | ((status & KL5KUSB105A_CTS) ? TIOCM_CTS : 0)
+	      ;
+
+	return res;
+}
+/* 
+ * Read line control via vendor command and return result through
+ * *line_state_p 
+ */
+/* It seems that the status buffer has always only 2 bytes length */
+#define KLSI_STATUSBUF_LEN	2
+static int klsi_105_get_line_state(struct usb_serial *serial,
+				   unsigned long *line_state_p)
+{
+	int rc;
+	__u8 status_buf[KLSI_STATUSBUF_LEN] = { -1,-1};
+	__u16 status;
+
+	info(__FUNCTION__ " - sending SIO Poll request");
+        rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+			     KL5KUSB105A_SIO_POLL,
+                             USB_TYPE_VENDOR | USB_DIR_IN,
+			     0, /* value */
+			     0, /* index */
+			     status_buf, KLSI_STATUSBUF_LEN,
+			     10*HZ
+			     );
+	if (rc < 0)
+		err("Reading line status failed (error = %d)", rc);
+	else {
+		status = status_buf[0] + (status_buf[1]<<8);
+
+		info(__FUNCTION__ " - read status %x %x",
+		     status_buf[0], status_buf[1]);
+
+		*line_state_p = klsi_105_status2linestate(status);
+	}
+
+        return rc;
+}
+
+
+/*
+ * Driver's tty interface functions
+ */
+
+static int klsi_105_startup (struct usb_serial *serial)
+{
+	struct klsi_105_private *priv;
+	int i;
+
+	/* check if we support the product id (see keyspan.c)
+	 * FIXME
+	 */
+
+	/* allocate the private data structure */
+	for (i=0; i<serial->num_ports; i++) {
+		serial->port[i].private = kmalloc(sizeof(struct klsi_105_private),
+						   GFP_KERNEL);
+		if (!serial->port[i].private) {
+			dbg(__FUNCTION__ "kmalloc for klsi_105_private failed.");
+			return (-1); /* error */
+		}
+		priv = (struct klsi_105_private *)serial->port[i].private;
+		/* set initial values for control structures */
+		priv->cfg.pktlen    = 5;
+		priv->cfg.baudrate  = kl5kusb105a_sio_b9600;
+		priv->cfg.databits  = kl5kusb105a_dtb_8;
+		priv->cfg.unknown1  = 0;
+		priv->cfg.unknown2  = 1;
+
+		priv->line_state    = 0;
+
+		priv->bytes_in	    = 0;
+		priv->bytes_out	    = 0;
+
+		spin_lock_init (&priv->write_urb_pool_lock);
+		for (i=0; i<NUM_URBS; i++) {
+			struct urb* urb = usb_alloc_urb(0);
+
+			priv->write_urb_pool[i] = urb;
+			if (urb == NULL) {
+				err("No more urbs???");
+				continue;
+			}
+
+			urb->transfer_buffer = NULL;
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE,
+							GFP_KERNEL);
+			if (!urb->transfer_buffer) {
+				err (__FUNCTION__ 
+				     " - out of memory for urb buffers.");
+				continue;
+			}
+		}
+
+		/* priv->termios is left uninitalized until port opening */
+		init_waitqueue_head(&serial->port[i].write_wait);
+	}
+	
+	return (0);
+} /* klsi_105_startup */
+
+
+static void klsi_105_shutdown (struct usb_serial *serial)
+{
+	int i;
+	
+	dbg (__FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		struct klsi_105_private *priv = 
+			(struct klsi_105_private*) serial->port[i].private;
+		unsigned long flags;
+		while (serial->port[i].open_count > 0) {
+			klsi_105_close (&serial->port[i], NULL);
+		}
+
+		if (priv) {
+			/* kill our write urb pool */
+			int j;
+			struct urb **write_urbs = priv->write_urb_pool;
+			spin_lock_irqsave(&priv->write_urb_pool_lock,flags);
+
+			for (j = 0; j < NUM_URBS; j++) {
+				if (write_urbs[j]) {
+					/* FIXME - uncomment the following
+					 * usb_unlink_urb call when the host
+					 * controllers get fixed to set
+					 * urb->dev = NULL after the urb is
+					 * finished.  Otherwise this call
+					 * oopses. */
+					/* usb_unlink_urb(write_urbs[j]); */
+					if (write_urbs[j]->transfer_buffer)
+						    kfree(write_urbs[j]->transfer_buffer);
+					usb_free_urb (write_urbs[j]);
+				}
+			}
+
+			spin_unlock_irqrestore (&priv->write_urb_pool_lock,
+					       	flags);
+
+			kfree(serial->port[i].private);
+		}
+	}
+} /* klsi_105_shutdown */
+
+static int  klsi_105_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	int retval = 0;
+
+	dbg(__FUNCTION__" port %d", port->number);
+
+	down (&port->sem);
+	
+	++port->open_count;
+	MOD_INC_USE_COUNT;
+
+	if (!port->active) {
+		int rc;
+		int i;
+		unsigned long line_state;
+		port->active = 1;
+
+		/* force low_latency on so that our tty_push actually forces
+		 * the data through
+		 * port->tty->low_latency = 1; */
+
+		/* Do a defined restart:
+		 * Set up sane default baud rate and send the 'READ_ON'
+		 * vendor command. 
+		 * FIXME: set modem line control (how?)
+		 * Then read the modem line control and store values in
+		 * priv->line_state.
+		 */
+		priv->cfg.pktlen   = 5;
+		priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+		priv->cfg.databits = kl5kusb105a_dtb_8;
+		priv->cfg.unknown1 = 0;
+		priv->cfg.unknown2 = 1;
+		klsi_105_chg_port_settings(serial, &(priv->cfg));
+		
+		/* set up termios structure */
+		priv->termios.c_iflag = port->tty->termios->c_iflag;
+		priv->termios.c_oflag = port->tty->termios->c_oflag;
+		priv->termios.c_cflag = port->tty->termios->c_cflag;
+		priv->termios.c_lflag = port->tty->termios->c_lflag;
+		for (i=0; i<NCCS; i++)
+			priv->termios.c_cc[i] = port->tty->termios->c_cc[i];
+
+
+		/* READ_ON and urb submission */
+		FILL_BULK_URB(port->read_urb, serial->dev, 
+			      usb_rcvbulkpipe(serial->dev,
+					      port->bulk_in_endpointAddress),
+			      port->read_urb->transfer_buffer,
+			      port->read_urb->transfer_buffer_length,
+			      klsi_105_read_bulk_callback,
+			      port);
+		port->read_urb->transfer_flags |= USB_QUEUE_BULK;
+
+		rc = usb_submit_urb(port->read_urb);
+		if (rc) {
+			err(__FUNCTION__ 
+			    " - failed submitting read urb, error %d", rc);
+			retval = rc;
+			goto exit;
+		}
+
+		rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev,0),
+				     KL5KUSB105A_SIO_CONFIGURE,
+				     USB_TYPE_VENDOR|USB_DIR_OUT|USB_RECIP_INTERFACE,
+				     KL5KUSB105A_SIO_CONFIGURE_READ_ON,
+				     0, /* index */
+				     NULL,
+				     0,
+				     KLSI_TIMEOUT);
+		if (rc < 0) {
+			err("Enabling read failed (error = %d)", rc);
+			retval = rc;
+		} else 
+			dbg(__FUNCTION__ " - enabled reading");
+
+		rc = klsi_105_get_line_state(serial, &line_state);
+		if (rc >= 0) {
+			priv->line_state = line_state;
+			dbg(__FUNCTION__ 
+			    " - read line state 0x%lx", line_state);
+			retval = 0;
+		} else
+			retval = rc;
+	}
+
+exit:
+	up (&port->sem);
+	
+	return retval;
+} /* klsi_105_open */
+
+
+static void klsi_105_close (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial;
+	struct klsi_105_private *priv 
+		= (struct klsi_105_private *)port->private;
+	dbg(__FUNCTION__" port %d", port->number);
+
+	serial = get_usb_serial (port, __FUNCTION__);
+
+	if(!serial)
+		return;
+
+	down (&port->sem);
+
+	--port->open_count;
+
+	if (port->open_count <= 0) {
+		/* send READ_OFF */
+		int rc = usb_control_msg(serial->dev,
+					 usb_sndctrlpipe(serial->dev, 0),
+					 KL5KUSB105A_SIO_CONFIGURE,
+					 USB_TYPE_VENDOR | USB_DIR_OUT,
+					 KL5KUSB105A_SIO_CONFIGURE_READ_OFF,
+					 0, /* index */
+					 NULL, 0,
+					 KLSI_TIMEOUT);
+		if (rc < 0)
+			    err("Disabling read failed (error = %d)", rc);
+
+		/* shutdown our bulk reads and writes */
+		usb_unlink_urb (port->write_urb);
+		usb_unlink_urb (port->read_urb);
+		/* unlink our write pool */
+		/* FIXME */
+		/* wgg - do I need this? I think so. */
+		usb_unlink_urb (port->interrupt_in_urb);
+		port->active = 0;
+		info("kl5kusb105 port stats: %ld bytes in, %ld bytes out", priv->bytes_in, priv->bytes_out);
+	}
+	
+	up (&port->sem);
+	MOD_DEC_USE_COUNT;
+} /* klsi_105_close */
+
+
+/* We need to write a complete 64-byte data block and encode the
+ * number actually sent in the first double-byte, LSB-order. That 
+ * leaves at most 62 bytes of payload.
+ */
+#define KLSI_105_DATA_OFFSET	2   /* in the bulk urb data block */
+
+static int klsi_105_write (struct usb_serial_port *port, int from_user,
+			   const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+	int result, size;
+	int bytes_sent=0;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);	/* to lock against someone else trying to
+				   take an URB we just selected from the pool */
+
+	while (count > 0) {
+		/* try to find a free urb (write 0 bytes if none) */
+		struct urb *urb = NULL;
+		unsigned long flags;
+		int i;
+		/* since the pool is per-port we might not need the spin lock !? */
+		spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+		for (i=0; i<NUM_URBS; i++) {
+			if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+				urb = priv->write_urb_pool[i];
+				dbg(__FUNCTION__ " - using pool URB %d", i);
+				break;
+			}
+		}
+		spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+		if (urb==NULL) {
+			dbg (__FUNCTION__ " - no more free urbs");
+			goto exit;
+		}
+
+		if (urb->transfer_buffer == NULL) {
+			urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+			if (urb->transfer_buffer == NULL) {
+				err(__FUNCTION__ " - no more kernel memory...");
+				goto exit;
+			}
+		}
+
+		size = min (count, port->bulk_out_size - KLSI_105_DATA_OFFSET);
+		size = min (size, URB_TRANSFER_BUFFER_SIZE - KLSI_105_DATA_OFFSET);
+
+		if (from_user) {
+			if (copy_from_user(urb->transfer_buffer
+					   + KLSI_105_DATA_OFFSET, buf, size)) {
+				up (&port->sem);
+				return -EFAULT;
+			}
+		} else {
+			memcpy (urb->transfer_buffer + KLSI_105_DATA_OFFSET,
+			       	buf, size);
+		}
+
+		/* write payload size into transfer buffer */
+		((__u8 *)urb->transfer_buffer)[0] = (__u8) (size & 0xFF);
+		((__u8 *)urb->transfer_buffer)[1] = (__u8) ((size & 0xFF00)>>8);
+
+		/* set up our urb */
+		FILL_BULK_URB(urb, serial->dev,
+			      usb_sndbulkpipe(serial->dev,
+					      port->bulk_out_endpointAddress),
+			      urb->transfer_buffer,
+			      URB_TRANSFER_BUFFER_SIZE,
+			      klsi_105_write_bulk_callback,
+			      port);
+		urb->transfer_flags |= USB_QUEUE_BULK;
+
+
+		/* send the data out the bulk port */
+		result = usb_submit_urb(urb);
+		if (result) {
+			err(__FUNCTION__
+			    " - failed submitting write urb, error %d", result);
+			goto exit;
+		}
+		buf += size;
+		bytes_sent += size;
+		count -= size;
+	}
+exit:
+	up (&port->sem);
+	priv->bytes_out+=bytes_sent;
+
+	return bytes_sent;	/* that's how much we wrote */
+} /* klsi_105_write */
+
+static void klsi_105_write_bulk_callback ( struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+	
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+
+	if (urb->status) {
+		dbg(__FUNCTION__ " - nonzero write bulk status received: %d",
+		    urb->status);
+		return;
+	}
+
+	/* from generic_write_bulk_callback */
+	queue_task(&port->tqueue, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	return;
+} /* klsi_105_write_bulk_completion_callback */
+
+
+/* return number of characters currently in the writing process */
+static int klsi_105_chars_in_buffer (struct usb_serial_port *port)
+{
+	int chars = 0;
+	int i;
+	unsigned long flags;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+
+	spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (priv->write_urb_pool[i]->status == -EINPROGRESS) {
+			chars += URB_TRANSFER_BUFFER_SIZE;
+		}
+	}
+
+	spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+	dbg (__FUNCTION__ " - returns %d", chars);
+	return (chars);
+}
+
+static int klsi_105_write_room (struct usb_serial_port *port)
+{
+	unsigned long flags;
+	int i;
+	int room = 0;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+
+	spin_lock_irqsave (&priv->write_urb_pool_lock, flags);
+	for (i = 0; i < NUM_URBS; ++i) {
+		if (priv->write_urb_pool[i]->status != -EINPROGRESS) {
+			room += URB_TRANSFER_BUFFER_SIZE;
+		}
+	}
+
+	spin_unlock_irqrestore (&priv->write_urb_pool_lock, flags);
+
+	dbg(__FUNCTION__ " - returns %d", room);
+	return (room);
+}
+
+
+
+static void klsi_105_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = 
+		(struct klsi_105_private*) port->private;
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int rc;
+
+        dbg(__FUNCTION__ " - port %d", port->number);
+
+	/* The urb might have been killed. */
+        if (urb->status) {
+                dbg(__FUNCTION__ " - nonzero read bulk status received: %d",
+		    urb->status);
+                return;
+        }
+	if (!serial) {
+		dbg(__FUNCTION__ " - bad serial pointer, exiting");
+		return;
+	}
+	
+	/* The data received is again preceded by a length double-byte in LSB-
+	 * first order (see klsi_105_write() )
+	 */
+	if (urb->actual_length == 0) {
+		/* empty urbs seem to happen, we ignore them */
+		/* dbg(__FUNCTION__ " - emtpy URB"); */
+	       ;
+	} else if (urb->actual_length <= 2) {
+		dbg(__FUNCTION__ " - size %d URB not understood",
+		    urb->actual_length);
+		usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+	} else {
+		int i;
+		int bytes_sent = ((__u8 *) data)[0] +
+				 ((unsigned int) ((__u8 *) data)[1] << 8);
+		tty = port->tty;
+		/* we should immediately resubmit the URB, before attempting
+		 * to pass the data on to the tty layer. But that needs locking
+		 * against re-entry an then mixed-up data because of
+		 * intermixed tty_flip_buffer_push()s
+		 * FIXME
+		 */ 
+		usb_serial_debug_data (__FILE__, __FUNCTION__,
+				       urb->actual_length, data);
+
+		if (bytes_sent + 2 > urb->actual_length) {
+			dbg(__FUNCTION__ 
+			    " - trying to read more data than available"
+			    " (%d vs. %d)",
+			    bytes_sent+2, urb->actual_length);
+			/* cap at implied limit */
+			bytes_sent = urb->actual_length - 2;
+		}
+
+		for (i = 2; i < 2+bytes_sent; i++) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters,
+			 * we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless 
+			 * tty->low_latency is set */
+			tty_insert_flip_char(tty, ((__u8*) data)[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+		priv->bytes_in += bytes_sent;
+	}
+	/* Continue trying to always read  */
+	FILL_BULK_URB(port->read_urb, serial->dev, 
+		      usb_rcvbulkpipe(serial->dev,
+				      port->bulk_in_endpointAddress),
+		      port->read_urb->transfer_buffer,
+		      port->read_urb->transfer_buffer_length,
+		      klsi_105_read_bulk_callback,
+		      port);
+	rc = usb_submit_urb(port->read_urb);
+	if (rc)
+		err(__FUNCTION__ 
+		    " - failed resubmitting read urb, error %d", rc);
+} /* klsi_105_read_bulk_callback */
+
+
+static void klsi_105_set_termios (struct usb_serial_port *port,
+				  struct termios *old_termios)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	unsigned int iflag = port->tty->termios->c_iflag;
+	unsigned int old_iflag = old_termios->c_iflag;
+	unsigned int cflag = port->tty->termios->c_cflag;
+	unsigned int old_cflag = old_termios->c_cflag;
+	
+	/*
+	 * Update baud rate
+	 */
+	if( (cflag & CBAUD) != (old_cflag & CBAUD) ) {
+	        /* reassert DTR and (maybe) RTS on transition from B0 */
+		if( (old_cflag & CBAUD) == B0 ) {
+			dbg(__FUNCTION__ ": baud was B0");
+#if 0
+			priv->control_state |= TIOCM_DTR;
+			/* don't set RTS if using hardware flow control */
+			if (!(old_cflag & CRTSCTS)) {
+				priv->control_state |= TIOCM_RTS;
+			}
+			mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		}
+		
+		switch(cflag & CBAUD) {
+		case B0: /* handled below */
+			break;
+		case B1200: priv->cfg.baudrate = kl5kusb105a_sio_b1200;
+			break;
+		case B2400: priv->cfg.baudrate = kl5kusb105a_sio_b2400;
+			break;
+		case B4800: priv->cfg.baudrate = kl5kusb105a_sio_b4800;
+			break;
+		case B9600: priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+			break;
+		case B19200: priv->cfg.baudrate = kl5kusb105a_sio_b19200;
+			break;
+		case B38400: priv->cfg.baudrate = kl5kusb105a_sio_b38400;
+			break;
+		case B57600: priv->cfg.baudrate = kl5kusb105a_sio_b57600;
+			break;
+		case B115200: priv->cfg.baudrate = kl5kusb105a_sio_b115200;
+			break;
+		default:
+			err("KLSI USB->Serial converter:"
+			    " unsupported baudrate request, using default"
+			    " of 9600");
+			priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+			break;
+		}
+		if ((cflag & CBAUD) == B0 ) {
+			dbg(__FUNCTION__ ": baud is B0");
+			/* Drop RTS and DTR */
+			/* maybe this should be simulated by sending read
+			 * disable and read enable messages?
+			 */
+			;
+#if 0
+			priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+        		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		}
+	}
+
+	if ((cflag & CSIZE) != (old_cflag & CSIZE)) {
+		/* set the number of data bits */
+		switch (cflag & CSIZE) {
+		case CS5:
+			dbg(__FUNCTION__ " - 5 bits/byte not supported");
+			return ;
+		case CS6:
+			dbg(__FUNCTION__ " - 6 bits/byte not supported");
+			return ;
+		case CS7:
+			priv->cfg.databits = kl5kusb105a_dtb_7;
+			break;
+		case CS8:
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		default:
+			err("CSIZE was not CS5-CS8, using default of 8");
+			priv->cfg.databits = kl5kusb105a_dtb_8;
+			break;
+		}
+	}
+
+	/*
+	 * Update line control register (LCR)
+	 */
+	if ((cflag & (PARENB|PARODD)) != (old_cflag & (PARENB|PARODD))
+	    || (cflag & CSTOPB) != (old_cflag & CSTOPB) ) {
+		
+#if 0
+		priv->last_lcr = 0;
+
+		/* set the parity */
+		if (cflag & PARENB)
+			priv->last_lcr |= (cflag & PARODD) ?
+				MCT_U232_PARITY_ODD : MCT_U232_PARITY_EVEN;
+		else
+			priv->last_lcr |= MCT_U232_PARITY_NONE;
+
+		/* set the number of stop bits */
+		priv->last_lcr |= (cflag & CSTOPB) ?
+			MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
+
+		mct_u232_set_line_ctrl(serial, priv->last_lcr);
+#endif
+		;
+	}
+	
+	/*
+	 * Set flow control: well, I do not really now how to handle DTR/RTS.
+	 * Just do what we have seen with SniffUSB on Win98.
+	 */
+	if( (iflag & IXOFF) != (old_iflag & IXOFF)
+	    || (iflag & IXON) != (old_iflag & IXON)
+	    ||  (cflag & CRTSCTS) != (old_cflag & CRTSCTS) ) {
+		
+		/* Drop DTR/RTS if no flow control otherwise assert */
+#if 0
+		if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS) )
+			priv->control_state |= TIOCM_DTR | TIOCM_RTS;
+		else
+			priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+#endif
+		;
+	}
+
+	/* now commit changes to device */
+	klsi_105_chg_port_settings(serial, &(priv->cfg));
+} /* klsi_105_set_termios */
+
+
+#if 0
+static void mct_u232_break_ctl( struct usb_serial_port *port, int break_state )
+{
+	struct usb_serial *serial = port->serial;
+	struct mct_u232_private *priv = (struct mct_u232_private *)port->private;
+	unsigned char lcr = priv->last_lcr;
+
+	dbg (__FUNCTION__ "state=%d", break_state);
+
+	if (break_state)
+		lcr |= MCT_U232_SET_BREAK;
+
+	mct_u232_set_line_ctrl(serial, lcr);
+} /* mct_u232_break_ctl */
+#endif
+
+static int klsi_105_ioctl (struct usb_serial_port *port, struct file * file,
+			   unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial *serial = port->serial;
+	struct klsi_105_private *priv = (struct klsi_105_private *)port->private;
+	int mask;
+	
+	dbg (__FUNCTION__ "cmd=0x%x", cmd);
+
+	/* Based on code from acm.c and others */
+	switch (cmd) {
+	case TIOCMGET: {
+		int rc;
+		unsigned long line_state;
+		dbg (__FUNCTION__ " - TIOCMGET request, just guessing");
+
+		rc = klsi_105_get_line_state(serial, &line_state);
+		if (rc < 0) {
+			err("Reading line control failed (error = %d)", rc);
+			/* better return value? EAGAIN? */
+			return -ENOIOCTLCMD;
+		} else {
+			priv->line_state = line_state;
+			dbg(__FUNCTION__ " - read line state 0x%lx", line_state);
+		}
+		return put_user(priv->line_state, (unsigned long *) arg); 
+	       };
+
+	case TIOCMSET: /* Turns on and off the lines as specified by the mask */
+	case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */
+	case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */
+		if (get_user(mask, (unsigned long *) arg))
+			return -EFAULT;
+
+		if ((cmd == TIOCMSET) || (mask & TIOCM_RTS)) {
+			/* RTS needs set */
+			if( ((cmd == TIOCMSET) && (mask & TIOCM_RTS)) ||
+			    (cmd == TIOCMBIS) )
+				dbg (__FUNCTION__ " - set RTS not handled");
+				/* priv->control_state |=  TIOCM_RTS; */
+			else
+				dbg (__FUNCTION__ " - clear RTS not handled");
+				/* priv->control_state &= ~TIOCM_RTS; */
+		}
+
+		if ((cmd == TIOCMSET) || (mask & TIOCM_DTR)) {
+			/* DTR needs set */
+			if( ((cmd == TIOCMSET) && (mask & TIOCM_DTR)) ||
+			    (cmd == TIOCMBIS) )
+				dbg (__FUNCTION__ " - set DTR not handled");
+			/*	priv->control_state |=  TIOCM_DTR; */
+			else
+				dbg (__FUNCTION__ " - clear DTR not handled");
+				/* priv->control_state &= ~TIOCM_DTR; */
+		}
+		/*
+		mct_u232_set_modem_ctrl(serial, priv->control_state);
+		*/
+		break;
+					
+	case TIOCMIWAIT:
+		/* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/
+		/* TODO */
+		dbg (__FUNCTION__ " - TIOCMIWAIT not handled");
+		return -ENOIOCTLCMD;
+
+	case TIOCGICOUNT:
+		/* return count of modemline transitions */
+		/* TODO */
+		dbg (__FUNCTION__ " - TIOCGICOUNT not handled");
+		return -ENOIOCTLCMD;
+	case TCGETS: {
+	     /* return current info to caller */
+	     int retval;
+
+	     dbg (__FUNCTION__ " - TCGETS data faked/incomplete");
+
+	     retval = verify_area(VERIFY_WRITE, (void *)arg,
+				  sizeof(struct termios));
+
+	     if (retval)
+			 return(retval);
+
+	     kernel_termios_to_user_termios((struct termios *)arg,  
+					    &priv->termios);
+	     return(0);
+	     }
+	case TCSETS: {
+		/* set port termios to the one given by the user */
+		int retval;
+
+		dbg (__FUNCTION__ " - TCSETS not handled");
+
+		retval = verify_area(VERIFY_READ, (void *)arg,
+				     sizeof(struct termios));
+
+		if (retval)
+			    return(retval);
+
+		user_termios_to_kernel_termios(&priv->termios,
+					       (struct termios *)arg);
+		klsi_105_set_termios(port, &priv->termios);
+		return(0);
+	     }
+	case TCSETSW: {
+		/* set port termios and try to wait for completion of last
+		 * write operation */
+		/* We guess here. If there are not too many write urbs
+		 * outstanding, we lie. */
+		/* what is the right way to wait here? schedule() ? */
+	        /*
+		while (klsi_105_chars_in_buffer(port) > (NUM_URBS / 4 ) * URB_TRANSFER_BUFFER_SIZE)
+			    schedule();
+		 */
+		return -ENOIOCTLCMD;
+		      }
+	default:
+		dbg(__FUNCTION__ ": arg not supported - 0x%04x",cmd);
+		return(-ENOIOCTLCMD);
+		break;
+	}
+	return 0;
+} /* klsi_105_ioctl */
+
+static void klsi_105_throttle (struct usb_serial_port *port)
+{
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);
+
+	usb_unlink_urb (port->read_urb);
+
+	up (&port->sem);
+
+	return;
+}
+static void klsi_105_unthrottle (struct usb_serial_port *port)
+{
+	int result;
+
+	dbg(__FUNCTION__ " - port %d", port->number);
+
+	down (&port->sem);
+
+	port->read_urb->dev = port->serial->dev;
+	result = usb_submit_urb(port->read_urb);
+	if (result)
+		err(__FUNCTION__ " - failed submitting read urb, error %d",
+		    result);
+
+	up (&port->sem);
+
+	return;
+}
+
+
+
+static int __init klsi_105_init (void)
+{
+	usb_serial_register (&palmconnect_device);
+	usb_serial_register (&kl5kusb105d_device);
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+
+static void __exit klsi_105_exit (void)
+{
+	usb_serial_deregister (&palmconnect_device);
+	usb_serial_deregister (&kl5kusb105d_device);
+}
+
+
+module_init (klsi_105_init);
+module_exit (klsi_105_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL"); 
+
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "enable extensive debugging messages");
+/* FIXME: implement
+MODULE_PARM(num_urbs, "i");
+MODULE_PARM_DESC(num_urbs, "number of URBs to use in write pool");
+*/
+
+/* vim: set sts=8 ts=8 sw=8: */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/kl5kusb105.h linux/drivers/usb/serial/kl5kusb105.h
--- linux.orig/drivers/usb/serial/kl5kusb105.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/serial/kl5kusb105.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,69 @@
+/*
+ * Definitions for the KLSI KL5KUSB105 serial port adapter
+ */
+
+/* vendor/product pairs that are known to contain this chipset */
+#define PALMCONNECT_VID		0x0830
+#define PALMCONNECT_PID		0x0080
+
+#define KLSI_VID		0x05e9
+#define KLSI_KL5KUSB105D_PID	0x00c0
+
+/* Vendor commands: */
+
+
+/* port table -- the chip supports up to 4 channels */
+
+/* baud rates */
+
+typedef enum {
+  kl5kusb105a_sio_b115200 = 0,
+  kl5kusb105a_sio_b57600  = 1,
+  kl5kusb105a_sio_b38400  = 2,
+  kl5kusb105a_sio_b19200  = 4,
+  kl5kusb105a_sio_b14400  = 5,
+  kl5kusb105a_sio_b9600   = 6,
+  kl5kusb105a_sio_b4800   = 8,   /* unchecked */
+  kl5kusb105a_sio_b2400   = 9,   /* unchecked */
+  kl5kusb105a_sio_b1200   = 0xa,  /* unchecked */
+  kl5kusb105a_sio_b600    = 0xb   /* unchecked */
+} KL5KUSB105A_SIO_baudrate_t;
+
+/* data bits */
+#define kl5kusb105a_dtb_7   7
+#define kl5kusb105a_dtb_8   8
+
+
+
+/* requests: */
+#define KL5KUSB105A_SIO_SET_DATA  1
+#define KL5KUSB105A_SIO_POLL      2
+#define KL5KUSB105A_SIO_CONFIGURE      3
+/* values used for request KL5KUSB105A_SIO_CONFIGURE */
+#define KL5KUSB105A_SIO_CONFIGURE_READ_ON      3
+#define KL5KUSB105A_SIO_CONFIGURE_READ_OFF     2
+
+/* Interpretation of modem status lines */
+/* These need sorting out by individually connecting pins and checking
+ * results. FIXME!
+ * When data is being sent we see 0x30 in the lower byte; this must
+ * contain DSR and CTS ...
+ */
+#define KL5KUSB105A_DSR			((1<<4) | (1<<5))
+#define KL5KUSB105A_CTS			((1<<5) | (1<<4))
+
+#define KL5KUSB105A_WANTS_TO_SEND	0x30
+//#define KL5KUSB105A_DTR			/* Data Terminal Ready */
+//#define KL5KUSB105A_CTS			/* Clear To Send */
+//#define KL5KUSB105A_CD			/* Carrier Detect */
+//#define KL5KUSB105A_DSR			/* Data Set Ready */
+//#define KL5KUSB105A_RxD			/* Receive pin */
+
+//#define KL5KUSB105A_LE
+//#define KL5KUSB105A_RTS		
+//#define KL5KUSB105A_ST		
+//#define KL5KUSB105A_SR		
+//#define KL5KUSB105A_RI			/* Ring Indicator */
+
+/* vim: set ts=8 sts=8: */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/usb-serial.h linux/drivers/usb/serial/usb-serial.h
--- linux.orig/drivers/usb/serial/usb-serial.h	Thu Oct 11 06:42:47 2001
+++ linux/drivers/usb/serial/usb-serial.h	Wed Jan 16 20:13:57 2002
@@ -132,8 +132,12 @@
 
 	struct list_head	driver_list;
 	
-	/* function call to make before accepting driver */
-	/* return 0 to continue initialization, anything else to abort */
+	/* function call to make before accepting driver
+	 * return 0 to continue initialization,
+	 * < 0 aborts startup,
+	 * > 0 does not set up anything else and is useful for devices that have
+	 * downloaded firmware, and will reset themselves shortly.
+	 */
 	int (*startup) (struct usb_serial *serial);
 	
 	void (*shutdown) (struct usb_serial *serial);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/usbserial.c linux/drivers/usb/serial/usbserial.c
--- linux.orig/drivers/usb/serial/usbserial.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/usb/serial/usbserial.c	Wed Jan 16 20:13:57 2002
@@ -1203,9 +1203,11 @@
 
 	/* if this device type has a startup function, call it */
 	if (type->startup) {
-		if (type->startup (serial)) {
+		i = type->startup (serial);
+		if (i < 0)
 			goto probe_error;
-		}
+		if (i > 0)
+			return serial;
 	}
 
 	/* set up the endpoint information */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/serial/visor.c linux/drivers/usb/serial/visor.c
--- linux.orig/drivers/usb/serial/visor.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/serial/visor.c	Wed Dec 26 14:28:36 2001
@@ -12,6 +12,10 @@
  *
  * See Documentation/usb/usb-serial.txt for more information on using this driver
  * 
+ * (12/18/2001) gkh
+ *	Added better Clie support for 3.5 devices.  Thanks to Geoffrey Levand
+ *	for the patch.
+ *
  * (11/11/2001) gkh
  *	Added support for the m125 devices, and added check to prevent oopses
  *	for Clié devices that lie about the number of ports they have.
@@ -145,6 +149,7 @@
 static void visor_set_termios	(struct usb_serial_port *port, struct termios *old_termios);
 static void visor_write_bulk_callback	(struct urb *urb);
 static void visor_read_bulk_callback	(struct urb *urb);
+static int  clie_3_5_startup	(struct usb_serial *serial);
 
 
 static __devinitdata struct usb_device_id visor_id_table [] = {
@@ -251,6 +256,7 @@
 	close:			visor_close,
 	throttle:		visor_throttle,
 	unthrottle:		visor_unthrottle,
+	startup:		clie_3_5_startup,
 	ioctl:			visor_ioctl,
 	set_termios:		visor_set_termios,
 	write:			visor_write,
@@ -705,6 +711,46 @@
 	return 0;
 }
 
+static int clie_3_5_startup (struct usb_serial *serial)
+{
+	int result;
+	u8 data;
+
+	dbg(__FUNCTION__);
+
+	/*
+	 * Note that PEG-300 series devices expect the following two calls.
+	 */
+
+	/* get the config number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_CONFIGURATION, USB_DIR_IN,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get config number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get config number bad return length: %d", result);
+		return -EIO;
+	}
+
+	/* get the interface number */
+	result = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+				  USB_REQ_GET_INTERFACE, 
+				  USB_DIR_IN | USB_DT_DEVICE,
+				  0, 0, &data, 1, HZ * 3);
+	if (result < 0) {
+		err(__FUNCTION__ ": get interface number failed: %d", result);
+		return result;
+	}
+	if (result != 1) {
+		err(__FUNCTION__ ": get interface number bad return length: %d", result);
+		return -EIO;
+	}
+
+	return 0;
+}
 
 static void visor_shutdown (struct usb_serial *serial)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/Makefile linux/drivers/usb/storage/Makefile
--- linux.orig/drivers/usb/storage/Makefile	Mon Jul 30 04:11:50 2001
+++ linux/drivers/usb/storage/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the USB Mass Storage device drivers.
 #
-# 15 Aug 2000, Christoph Hellwig <hch@caldera.de>
+# 15 Aug 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/datafab.c linux/drivers/usb/storage/datafab.c
--- linux.orig/drivers/usb/storage/datafab.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/datafab.c	Wed Jan  9 15:45:08 2002
@@ -208,7 +208,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) dest;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -333,7 +333,7 @@
 
 		if (use_sg) {
 			sg = (struct scatterlist *) src;
-			buffer = kmalloc(len, GFP_KERNEL);
+			buffer = kmalloc(len, GFP_NOIO);
 			if (buffer == NULL)
 				return USB_STOR_TRANSPORT_ERROR;
 			ptr = buffer;
@@ -665,7 +665,7 @@
 	};
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct datafab_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct datafab_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("datafab_transport:  Gah! Can't allocate storage for Datafab info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/freecom.c linux/drivers/usb/storage/freecom.c
--- linux.orig/drivers/usb/storage/freecom.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/freecom.c	Wed Jan  9 15:45:08 2002
@@ -1,6 +1,6 @@
 /* Driver for Freecom USB/IDE adaptor
  *
- * $Id: freecom.c,v 1.19 2001/11/11 05:42:34 mdharm Exp $
+ * $Id: freecom.c,v 1.21 2001/12/29 03:47:33 mdharm Exp $
  *
  * Freecom v0.1:
  *
@@ -206,9 +206,7 @@
 
         return USB_STOR_TRANSPORT_GOOD;
 }
-#endif
 
-#if 0 /* Unused at this time */
 /* Read a value from an ide register. */
 static int
 freecom_ide_read (struct us_data *us, int reg, int *value)
@@ -435,7 +433,7 @@
 		/* Get the status again */
 		fcb->Type = FCM_PACKET_STATUS;
 		fcb->Timeout = 0;
-		memset (fcb->Atapi, 0, sizeof(fcb->Filler));
+		memset (fcb->Atapi, 0, sizeof(fcb->Atapi));
 		memset (fcb->Filler, 0, sizeof (fcb->Filler));
 
         	/* Send it out. */
@@ -487,10 +485,19 @@
          * and such will hang. */
         US_DEBUGP("Device indicates that it has %d bytes available\n",
                         le16_to_cpu (fst->Count));
+        US_DEBUGP("SCSI requested %d\n", usb_stor_transfer_length(srb));
 
         /* Find the length we desire to read. */
-        length = usb_stor_transfer_length (srb);
-        US_DEBUGP("SCSI requested %d\n", length);
+	switch (srb->cmnd[0]) {
+		case INQUIRY:
+		case REQUEST_SENSE:		/* 16 or 18 bytes? spec says 18, lots of devices only have 16 */
+		case MODE_SENSE:
+		case MODE_SENSE_10:
+			length = fst->Count;
+			break;
+		default:
+ 			length = usb_stor_transfer_length (srb);
+	}
 
 	/* verify that this amount is legal */
 	if (length > srb->request_bufflen) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/jumpshot.c linux/drivers/usb/storage/jumpshot.c
--- linux.orig/drivers/usb/storage/jumpshot.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/jumpshot.c	Wed Jan  9 15:45:08 2002
@@ -284,7 +284,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) dest;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -399,7 +399,7 @@
 
                 if (use_sg) {
                         sg = (struct scatterlist *) src;
-                        buffer = kmalloc(len, GFP_KERNEL);
+                        buffer = kmalloc(len, GFP_NOIO);
                         if (buffer == NULL)
                                 return USB_STOR_TRANSPORT_ERROR;
                         ptr = buffer;
@@ -665,7 +665,7 @@
 
 
 	if (!us->extra) {
-		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_KERNEL);
+		us->extra = kmalloc(sizeof(struct jumpshot_info), GFP_NOIO);
 		if (!us->extra) {
 			US_DEBUGP("jumpshot_transport:  Gah! Can't allocate storage for jumpshot info struct!\n");
 			return USB_STOR_TRANSPORT_ERROR;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/protocol.c linux/drivers/usb/storage/protocol.c
--- linux.orig/drivers/usb/storage/protocol.c	Mon Jul 30 04:11:50 2001
+++ linux/drivers/usb/storage/protocol.c	Wed Jan 16 20:13:57 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: protocol.c,v 1.10 2001/07/30 00:27:59 mdharm Exp $
+ * $Id: protocol.c,v 1.11 2002/01/13 06:40:25 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -76,7 +76,7 @@
 		data_ptr = (unsigned char *)srb->request_buffer;
 
 	/* Change the SCSI revision number */
-	data_ptr[2] |= 0x2;
+	data_ptr[2] = (data_ptr[2] & ~7) | 2;
 }
 
 /***********************************************************************
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/sddr09.c linux/drivers/usb/storage/sddr09.c
--- linux.orig/drivers/usb/storage/sddr09.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/sddr09.c	Wed Jan  9 15:45:08 2002
@@ -1,6 +1,6 @@
 /* Driver for SanDisk SDDR-09 SmartMedia reader
  *
- * $Id: sddr09.c,v 1.21 2001/11/06 03:18:36 mdharm Exp $
+ * $Id: sddr09.c,v 1.22 2001/12/08 23:32:48 mdharm Exp $
  *
  * SDDR09 driver v0.1:
  *
@@ -79,7 +79,7 @@
 	// copy the data into the buffer.
 /*
 	if (xfer_len > 0) {
-		buffer = kmalloc(xfer_len, GFP_KERNEL);
+		buffer = kmalloc(xfer_len, GFP_NOIO);
 		if (!(command[0] & USB_DIR_IN))
 			memcpy(buffer, xfer_data, xfer_len);
 	}
@@ -303,7 +303,7 @@
 
 	if (use_sg) {
 		sg = (struct scatterlist *)content;
-		buffer = kmalloc(len, GFP_KERNEL);
+		buffer = kmalloc(len, GFP_NOIO);
 		if (buffer == NULL)
 			return USB_STOR_TRANSPORT_ERROR;
 		ptr = buffer;
@@ -630,17 +630,17 @@
 
 	alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
 	sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
-		GFP_KERNEL);
+		GFP_NOIO);
 	if (sg == NULL)
 		return 0;
 
 	for (i=0; i<alloc_blocks; i++) {
 		if (i<alloc_blocks-1) {
-			sg[i].address = kmalloc( (1<<17), GFP_KERNEL );
+			sg[i].address = kmalloc( (1<<17), GFP_NOIO );
 			sg[i].page = NULL;
 			sg[i].length = (1<<17);
 		} else {
-			sg[i].address = kmalloc(alloc_len, GFP_KERNEL);
+			sg[i].address = kmalloc(alloc_len, GFP_NOIO);
 			sg[i].page = NULL;
 			sg[i].length = alloc_len;
 		}
@@ -672,8 +672,8 @@
 		kfree(info->lba_to_pba);
 	if (info->pba_to_lba)
 		kfree(info->pba_to_lba);
-	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
-	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_KERNEL);
+	info->lba_to_pba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
+	info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
 
 	if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
 		if (info->lba_to_pba != NULL)
@@ -842,7 +842,7 @@
 
 	if (!us->extra) {
 		us->extra = kmalloc(
-			sizeof(struct sddr09_card_info), GFP_KERNEL);
+			sizeof(struct sddr09_card_info), GFP_NOIO);
 		if (!us->extra)
 			return USB_STOR_TRANSPORT_ERROR;
 		memset(us->extra, 0, sizeof(struct sddr09_card_info));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/shuttle_usbat.c linux/drivers/usb/storage/shuttle_usbat.c
--- linux.orig/drivers/usb/storage/shuttle_usbat.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/shuttle_usbat.c	Wed Jan  9 15:45:08 2002
@@ -1,6 +1,6 @@
 /* Driver for SCM Microsystems USB-ATAPI cable
  *
- * $Id: shuttle_usbat.c,v 1.14 2001/03/28 01:02:06 groovyjava Exp $
+ * $Id: shuttle_usbat.c,v 1.15 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000, 2001 Robert Baruch (autophile@starband.net)
@@ -681,7 +681,7 @@
 
 	len = (65535/srb->transfersize) * srb->transfersize;
 	US_DEBUGP("Max read is %d bytes\n", len);
-	buffer = kmalloc(len, GFP_KERNEL);
+	buffer = kmalloc(len, GFP_NOIO);
 	if (buffer == NULL) // bloody hell!
 		return USB_STOR_TRANSPORT_FAILED;
 	sector = short_pack(data[7+3], data[7+2]);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c
--- linux.orig/drivers/usb/storage/transport.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/transport.c	Wed Jan  9 15:45:08 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.41 2001/10/15 07:02:32 mdharm Exp $
+ * $Id: transport.c,v 1.42 2001/12/08 23:32:48 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -388,7 +388,7 @@
 	devrequest *dr;
 
 	/* allocate the device request structure */
-	dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	dr = kmalloc(sizeof(devrequest), GFP_NOIO);
 	if (!dr)
 		return -ENOMEM;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/unusual_devs.h linux/drivers/usb/storage/unusual_devs.h
--- linux.orig/drivers/usb/storage/unusual_devs.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/storage/unusual_devs.h	Wed Jan 16 20:13:57 2002
@@ -1,7 +1,7 @@
 /* Driver for USB Mass Storage compliant devices
  * Ununsual Devices File
  *
- * $Id: unusual_devs.h,v 1.20 2001/09/02 05:12:57 mdharm Exp $
+ * $Id: unusual_devs.h,v 1.25 2002/01/13 06:39:17 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -58,6 +58,11 @@
 		"HP",
 		"CD-Writer+ 8200e",
 		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
+
+UNUSUAL_DEV(  0x03f0, 0x0307, 0x0001, 0x0001, 
+		"HP",
+		"CD-Writer+ CD-4e",
+		US_SC_8070, US_PR_SCM_ATAPI, init_8200e, 0), 
 #endif
 
 #ifdef CONFIG_USB_STORAGE_DPCM
@@ -86,6 +91,25 @@
 		"FinePix 1400Zoom",
 		US_SC_8070, US_PR_CBI, NULL, US_FL_FIX_INQUIRY),
 
+/* Reported by Peter Wächtler <pwaechtler@loewe-komp.de>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x04ce, 0x0002, 0x0074, 0x0074,
+		"ScanLogic",
+		"SL11R-IDE",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY),
+
+/* Reported by Kriston Fincher <kriston@airmail.net>
+ * Patch submitted by Sean Millichamp <sean@bruenor.org>
+ * This is to support the Panasonic PalmCam PV-SD4090
+ * This entry is needed because the device reports Sub=ff 
+ */
+UNUSUAL_DEV(  0x04da, 0x0901, 0x0100, 0x0200,
+               "Panasonic",
+               "LS-120 Camera",
+               US_SC_UFI, US_PR_CBI, NULL, 0),
+
 /* Most of the following entries were developed with the help of
  * Shuttle/SCM directly.
  */
@@ -161,14 +185,24 @@
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ),
 
+/* Iomega Clik! Drive 
+ * Reported by David Chatenay <dchatenay@hotmail.com>
+ * The reason this is needed is not fully known.
+ */
+UNUSUAL_DEV(  0x0525, 0xa140, 0x0100, 0x0100,
+		"Iomega",
+		"USB Clik! 40",
+		US_SC_8070, US_PR_BULK, NULL,
+		US_FL_FIX_INQUIRY | US_FL_START_STOP ),
+
 /* This entry is needed because the device reports Sub=ff */
-UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0322, 
+UNUSUAL_DEV(  0x054c, 0x0010, 0x0106, 0x0422, 
 		"Sony",
 		"DSC-S30/S70/S75/505V/F505", 
 		US_SC_SCSI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP | US_FL_MODE_XLATE ),
 
-/* Reported by win@geeks.nl */
+/* Reported by wim@geeks.nl */
 UNUSUAL_DEV(  0x054c, 0x0025, 0x0100, 0x0100, 
 		"Sony",
 		"Memorystick NW-MS7",
@@ -194,6 +228,13 @@
 		US_SC_UFI, US_PR_CB, NULL,
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 		
+/* Submitted by Nathan Babb <nathan@lexi.com> */
+UNUSUAL_DEV(  0x054c, 0x006d, 0x0000, 0x9999,
+                "Sony",
+		"PEG Mass Storage",
+		US_SC_8070, US_PR_CBI, NULL,
+		US_FL_FIX_INQUIRY ),
+		
 UNUSUAL_DEV(  0x057b, 0x0000, 0x0000, 0x0299, 
 		"Y-E Data",
 		"Flashbuster-U",
@@ -264,6 +305,14 @@
 		US_FL_SINGLE_LUN | US_FL_START_STOP ),
 #endif
 
+/* Submitted by f.brugmans@hccnet.nl
+ * Needed for START_STOP flag */
+UNUSUAL_DEV( 0x0686, 0x4007, 0x0001, 0x0001,
+                "Minolta",
+                "Dimage S304",
+                US_SC_SCSI, US_PR_BULK, NULL,
+                US_FL_START_STOP ),
+
 UNUSUAL_DEV(  0x0693, 0x0002, 0x0100, 0x0100, 
 		"Hagiwara",
 		"FlashGate SmartMedia",
@@ -307,7 +356,7 @@
                 US_SC_QIC, US_PR_FREECOM, freecom_init, 0),
 #endif
 
-UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0100, 
+UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133, 
 		"Microtech",
 		"USB-SCSI-DB25",
 		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
@@ -374,16 +423,23 @@
 		"Simple Tech/Datafab CF+SM Reader",
 		US_SC_SCSI, US_PR_DATAFAB, NULL,
 		US_FL_MODE_XLATE | US_FL_START_STOP ),
+
+/* Submitted by Olaf Hering <olh@suse.de> */
+UNUSUAL_DEV(  0x07c4, 0xa109, 0x0000, 0xffff,
+		"Datafab Systems, Inc.",
+		"USB to CF + SM Combo (LC1)",
+		US_SC_SCSI, US_PR_DATAFAB, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP ),
 #endif
 
-/* Casio QV 2x00/3x00/8000 digital still cameras are not conformant
+/* Casio QV 2x00/3x00/4000/8000 digital still cameras are not conformant
  * to the USB storage specification in two ways:
  * - They tell us they are using transport protocol CBI. In reality they
  *   are using transport protocol CB.
  * - They don't like the INQUIRY command. So we must handle this command
  *   of the SCSI layer ourselves.
  */
-UNUSUAL_DEV( 0x07cf, 0x1001, 0x9009, 0x9009,
+UNUSUAL_DEV( 0x07cf, 0x1001, 0x1000, 0x9009,
                 "Casio",
                 "QV DigitalCamera",
                 US_SC_8070, US_PR_CB, NULL,
@@ -402,3 +458,20 @@
                 US_SC_ISD200, US_PR_BULK, isd200_Initialization,
                 0 ),
 #endif
+
+/* Submitted by Brian Hall <brihall@bigfoot.com>
+ * Needed for START_STOP flag */
+UNUSUAL_DEV(  0x0c76, 0x0003, 0x0100, 0x0100,
+		"JMTek",
+		"USBDrive",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_START_STOP ),
+
+/* Reported by Dan Pilone <pilone@slac.com>
+ * The device needs the flags only.
+ */
+UNUSUAL_DEV(  0x1065, 0x2136, 0x0000, 0x9999,
+		"CCYU TECHNOLOGY",
+		"EasyDisk Portable Device",
+		US_SC_SCSI, US_PR_BULK, NULL,
+		US_FL_MODE_XLATE | US_FL_START_STOP),
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/storage/usb.c linux/drivers/usb/storage/usb.c
--- linux.orig/drivers/usb/storage/usb.c	Sun Nov 11 18:01:32 2001
+++ linux/drivers/usb/storage/usb.c	Wed Jan 16 20:13:57 2002
@@ -1,6 +1,6 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: usb.c,v 1.69 2001/11/11 03:33:03 mdharm Exp $
+ * $Id: usb.c,v 1.70 2002/01/06 07:14:12 mdharm Exp $
  *
  * Current development and maintenance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
@@ -318,6 +318,13 @@
 	current->files = init_task.files;
 	atomic_inc(&current->files->count);
 	daemonize();
+
+	/* avoid getting signals */
+	spin_lock_irq(&current->sigmask_lock);
+	flush_signals(current);
+	sigfillset(&current->blocked);
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
 
 	/* set our name for identification purposes */
 	sprintf(current->comm, "usb-storage-%d", us->host_number);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/stv680.c linux/drivers/usb/stv680.c
--- linux.orig/drivers/usb/stv680.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/stv680.c	Mon Feb  4 19:00:37 2002
@@ -0,0 +1,1673 @@
+/*
+ *  STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
+ *  
+ * Thanks to STMicroelectronics for information on the usb commands, and 
+ * to Steve Miller at STM for his help and encouragement while I was 
+ * writing this driver.
+ *
+ * This driver is based heavily on the 
+ * Endpoints (formerly known as AOX) se401 USB Camera Driver
+ * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org)
+ *
+ * Still somewhat based on the Linux ov511 driver.
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * History: 
+ * ver 0.1 October, 2001. Initial attempt. 
+ *
+ * ver 0.2 November, 2001. Fixed asbility to resize, added brightness
+ *                         function, made more stable (?)
+ *
+ * ver 0.21 Nov, 2001.     Added gamma correction and white balance, 
+ *                         due to Alexander Schwartz. Still trying to 
+ *                         improve stablility. Moved stuff into stv680.h
+ *
+ * ver 0.22 Nov, 2001.	   Added sharpen function (by Michael Sweet, 
+ *                         mike@easysw.com) from GIMP, also used in pencam. 
+ *                         Simple, fast, good integer math routine.
+ *
+ * ver 0.23 Dec, 2001 (gkh)
+ * 			   Took out sharpen function, ran code through
+ * 			   Lindent, and did other minor tweaks to get
+ * 			   things to work properly with 2.5.1
+ *
+ * ver 0.24 Jan, 2002 (kjs) 
+ *                         Fixed the problem with webcam crashing after
+ *                         two pictures. Changed the way pic is halved to 
+ *                         improve quality. Got rid of green line around 
+ *                         frame. Fix brightness reset when changing size 
+ *                         bug. Adjusted gamma filters slightly.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/wrapper.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/videodev.h>
+#include <linux/usb.h>
+
+#include "stv680.h"
+
+static int video_nr = -1;
+static int swapRGB = 0;   /* default for auto sleect */
+static int swapRGB_on = 0; /* default to allow auto select; -1=swap never, +1= swap always */
+
+static unsigned int debug = 0;
+
+#define PDEBUG(level, fmt, args...) \
+	do { \
+	if (debug >= level)	\
+		info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args);	\
+	} while (0)
+
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.24"
+#define DRIVER_AUTHOR "Kevin Sisson <kjsisson@bellsouth.net>"
+#define DRIVER_DESC "STV0680 USB Camera Driver"
+
+MODULE_AUTHOR (DRIVER_AUTHOR);
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+MODULE_PARM (debug, "i");
+MODULE_PARM_DESC (debug, "Debug enabled or not");
+MODULE_PARM (swapRGB_on, "i");
+MODULE_PARM_DESC (swapRGB_on, "Red/blue swap: 1=always, 0=auto, -1=never");
+MODULE_PARM (video_nr, "i");
+EXPORT_NO_SYMBOLS;
+
+/********************************************************************
+ *
+ * Memory management
+ *
+ * This is a shameless copy from the USB-cpia driver (linux kernel
+ * version 2.3.29 or so, I have no idea what this code actually does ;).
+ * Actually it seems to be a copy of a shameless copy of the bttv-driver.
+ * Or that is a copy of a shameless copy of ... (To the powers: is there
+ * no generic kernel-function to do this sort of stuff?)
+ *
+ * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says
+ * there will be one, but apparentely not yet -jerdfelt
+ *
+ * So I copied it again for the ov511 driver -claudio
+ *
+ * Same for the se401 driver -Jeroen
+ *
+ * And the STV0680 driver - Kevin
+ ********************************************************************/
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva (pgd_t * pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none (*pgd)) {
+		pmd = pmd_offset (pgd, adr);
+		if (!pmd_none (*pmd)) {
+			ptep = pte_offset (pmd, adr);
+			pte = *ptep;
+			if (pte_present (pte)) {
+				ret = (unsigned long) page_address (pte_page (pte));
+				ret |= (adr & (PAGE_SIZE - 1));
+			}
+		}
+	}
+	return ret;
+}
+
+/* Here we want the physical address of the memory. This is used when 
+ * initializing the contents of the area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa (unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR (adr);
+	kva = uvirt_to_kva (pgd_offset_k (va), va);
+	ret = __pa (kva);
+	return ret;
+}
+
+static void *rvmalloc (unsigned long size)
+{
+	void *mem;
+	unsigned long adr, page;
+
+	/* Round it off to PAGE_SIZE */
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	mem = vmalloc_32 (size);
+	if (!mem)
+		return NULL;
+
+	memset (mem, 0, size);	/* Clear the ram out, no junk to the user */
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa (adr);
+		mem_map_reserve (virt_to_page (__va (page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	return mem;
+}
+
+static void rvfree (void *mem, unsigned long size)
+{
+	unsigned long adr, page;
+
+	if (!mem)
+		return;
+
+	size += (PAGE_SIZE - 1);
+	size &= ~(PAGE_SIZE - 1);
+
+	adr = (unsigned long) mem;
+	while (size > 0) {
+		page = kvirt_to_pa (adr);
+		mem_map_unreserve (virt_to_page (__va (page)));
+		adr += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	vfree (mem);
+}
+
+
+/*********************************************************************
+ * pencam read/write functions
+ ********************************************************************/
+
+static int stv_sndctrl (int set, struct usb_stv *stv680, unsigned short req, unsigned short value, unsigned char *buffer, int size)
+{
+	int ret = -1;
+
+	switch (set) {
+	case 0:		/*  0xc1  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_rcvctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 1:		/*  0x41  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_sndctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 2:		/*  0x80  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_rcvctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_IN | USB_RECIP_DEVICE),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	case 3:		/*  0x40  */
+		ret = usb_control_msg (stv680->udev,
+				       usb_sndctrlpipe (stv680->udev, 0),
+				       req,
+				       (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+				       value, 0, buffer, size, PENCAM_TIMEOUT);
+		break;
+
+	}
+	if ((ret < 0) && (req != 0x0a)) {
+		PDEBUG (1, "STV(e): usb_control_msg error %i, request = 0x%x, error = %i", set, req, ret);
+	}
+	return ret;
+}
+
+static int stv_set_config (struct usb_stv *dev, int configuration, int interface, int alternate)
+{
+
+	if (usb_set_configuration (dev->udev, configuration) < 0) {
+		PDEBUG (1, "STV(e): FAILED to set configuration %i", configuration);
+		return -1;
+	}
+	if (usb_set_interface (dev->udev, interface, alternate) < 0) {
+		PDEBUG (1, "STV(e): FAILED to set alternate interface %i", alternate);
+		return -1;
+	}
+	return 0;
+}
+
+static int stv_stop_video (struct usb_stv *dev)
+{
+	int i;
+	unsigned char *buf;
+
+	buf = kmalloc (40, GFP_KERNEL);
+	if (buf == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+
+	/* this is a high priority command; it stops all lower order commands */
+	if ((i = stv_sndctrl (1, dev, 0x04, 0x0000, buf, 0x0)) < 0) {
+		i = stv_sndctrl (0, dev, 0x80, 0, buf, 0x02);	/* Get Last Error; 2 = busy */
+		PDEBUG (1, "STV(i): last error: %i,  command = 0x%x", buf[0], buf[1]);
+	} else {
+		PDEBUG (1, "STV(i): Camera reset to idle mode.");
+	}
+
+	if ((i = stv_set_config (dev, 1, 0, 0)) < 0)
+		PDEBUG (1, "STV(e): Reset config during exit failed");
+
+	/*  get current mode  */
+	buf[0] = 0xf0;
+	if ((i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08)) != 0x08)	/* get mode */
+		PDEBUG (0, "STV(e): Stop_video: problem setting original mode");
+	if (dev->origMode != buf[0]) {
+		memset (buf, 0, 8);
+		buf[0] = (unsigned char) dev->origMode;
+		if ((i = stv_sndctrl (3, dev, 0x07, 0x0100, buf, 0x08)) != 0x08) {
+			PDEBUG (0, "STV(e): Stop_video: Set_Camera_Mode failed");
+			i = -1;
+		}
+		buf[0] = 0xf0;
+		i = stv_sndctrl (0, dev, 0x87, 0, buf, 0x08);
+		if ((i != 0x08) || (buf[0] != dev->origMode)) {
+			PDEBUG (0, "STV(e): camera NOT set to original resolution.");
+			i = -1;
+		} else
+			PDEBUG (0, "STV(i): Camera set to original resolution");
+	}
+	/* origMode */
+	kfree (buf);
+	return i;
+}
+
+static int stv_set_video_mode (struct usb_stv *dev)
+{
+	int i, stop_video = 1;
+	unsigned char *buf;
+
+	buf = kmalloc (40, GFP_KERNEL);
+	if (buf == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+
+	if ((i = stv_set_config (dev, 1, 0, 0)) < 0) {
+		kfree (buf);
+		return i;
+	}
+
+	i = stv_sndctrl (2, dev, 0x06, 0x0100, buf, 0x12);
+	if (!(i > 0) && (buf[8] == 0x53) && (buf[9] == 0x05)) {
+		PDEBUG (1, "STV(e): Could not get descriptor 0100.");
+		goto error;
+	}
+
+	/*  set alternate interface 1 */
+	if ((i = stv_set_config (dev, 1, 0, 1)) < 0)
+		goto error;
+
+	if ((i = stv_sndctrl (0, dev, 0x85, 0, buf, 0x10)) != 0x10)
+		goto error;
+	PDEBUG (1, "STV(i): Setting video mode.");
+	/*  Switch to Video mode: 0x0100 = VGA (640x480), 0x0000 = CIF (352x288) 0x0300 = QVGA (320x240)  */
+	if ((i = stv_sndctrl (1, dev, 0x09, dev->VideoMode, buf, 0x0)) < 0) {
+		stop_video = 0;
+		goto error;
+	}
+	goto exit;
+
+error:
+	kfree (buf);
+	if (stop_video == 1)
+		stv_stop_video (dev);
+	return -1;
+
+exit:
+	kfree (buf);
+	return 0;
+}
+
+static int stv_init (struct usb_stv *stv680)
+{
+	int i = 0;
+	unsigned char *buffer;
+	unsigned long int bufsize;
+
+	buffer = kmalloc (40, GFP_KERNEL);
+	if (buffer == NULL) {
+		PDEBUG (0, "STV(e): Out of (small buf) memory");
+		return -1;
+	}
+	memset (buffer, 0, 40);
+	udelay (100);
+
+	/* set config 1, interface 0, alternate 0 */
+	if ((i = stv_set_config (stv680, 1, 0, 0)) < 0) {
+		kfree (buffer);
+		PDEBUG (0, "STV(e): set config 1,0,0 failed");
+		return -1;
+	}
+	/* ping camera to be sure STV0680 is present */
+	if ((i = stv_sndctrl (0, stv680, 0x88, 0x5678, buffer, 0x02)) != 0x02)
+		goto error;
+	if ((buffer[0] != 0x56) || (buffer[1] != 0x78)) {
+		PDEBUG (1, "STV(e): camera ping failed!!");
+		goto error;
+	}
+
+	/* get camera descriptor */
+	if ((i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x09)) != 0x09)
+		goto error;
+	i = stv_sndctrl (2, stv680, 0x06, 0x0200, buffer, 0x22);
+	if (!(i >= 0) && (buffer[7] == 0xa0) && (buffer[8] == 0x23)) {
+		PDEBUG (1, "STV(e): Could not get descriptor 0200.");
+		goto error;
+	}
+	if ((i = stv_sndctrl (0, stv680, 0x8a, 0, buffer, 0x02)) != 0x02)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x8b, 0, buffer, 0x24)) != 0x24)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+		goto error;
+
+	stv680->SupportedModes = buffer[7];
+	i = stv680->SupportedModes;
+	stv680->CIF = 0;
+	stv680->VGA = 0;
+	stv680->QVGA = 0;
+	if (i & 1)
+		stv680->CIF = 1;
+	if (i & 2)
+		stv680->VGA = 1;
+	if (i & 8)
+		stv680->QVGA = 1;
+	if (stv680->SupportedModes == 0) {
+		PDEBUG (0, "STV(e): There are NO supported STV680 modes!!");
+		i = -1;
+		goto error;
+	} else {
+		if (stv680->CIF)
+			PDEBUG (0, "STV(i): CIF is supported");
+		if (stv680->QVGA)
+			PDEBUG (0, "STV(i): QVGA is supported");
+	}
+	/* FW rev, ASIC rev, sensor ID  */
+	PDEBUG (1, "STV(i): Firmware rev is %i.%i", buffer[0], buffer[1]);
+	PDEBUG (1, "STV(i): ASIC rev is %i.%i", buffer[2], buffer[3]);
+	PDEBUG (1, "STV(i): Sensor ID is %i", (buffer[4]*16) + (buffer[5]>>4));
+
+	/*  set alternate interface 1 */
+	if ((i = stv_set_config (stv680, 1, 0, 1)) < 0)
+		goto error;
+
+	if ((i = stv_sndctrl (0, stv680, 0x85, 0, buffer, 0x10)) != 0x10)
+		goto error;
+	if ((i = stv_sndctrl (0, stv680, 0x8d, 0, buffer, 0x08)) != 0x08)
+		goto error;
+	i = buffer[3];
+	PDEBUG (0, "STV(i): Camera has %i pictures.", i);
+
+	/*  get current mode */
+	if ((i = stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08)) != 0x08)
+		goto error;
+	stv680->origMode = buffer[0];	/* 01 = VGA, 03 = QVGA, 00 = CIF */
+
+	/* This will attemp CIF mode, if supported. If not, set to QVGA  */
+	memset (buffer, 0, 8);
+	if (stv680->CIF)
+		buffer[0] = 0x00;
+	else if (stv680->QVGA)
+		buffer[0] = 0x03;
+	if ((i = stv_sndctrl (3, stv680, 0x07, 0x0100, buffer, 0x08)) != 0x08) {
+		PDEBUG (0, "STV(i): Set_Camera_Mode failed");
+		i = -1;
+		goto error;
+	}
+	buffer[0] = 0xf0;
+	stv_sndctrl (0, stv680, 0x87, 0, buffer, 0x08);
+	if (((stv680->CIF == 1) && (buffer[0] != 0x00)) || ((stv680->QVGA == 1) && (buffer[0] != 0x03))) {
+		PDEBUG (0, "STV(e): Error setting camera video mode!");
+		i = -1;
+		goto error;
+	} else {
+		if (buffer[0] == 0) {
+			stv680->VideoMode = 0x0000;
+			PDEBUG (0, "STV(i): Video Mode set to CIF");
+		}
+		if (buffer[0] == 0x03) {
+			stv680->VideoMode = 0x0300;
+			PDEBUG (0, "STV(i): Video Mode set to QVGA");
+		}
+	}
+	if ((i = stv_sndctrl (0, stv680, 0x8f, 0, buffer, 0x10)) != 0x10)
+		goto error;
+	bufsize = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3]);
+	stv680->cwidth = (buffer[4] << 8) | (buffer[5]);	/* ->camera = 322, 356, 644  */
+	stv680->cheight = (buffer[6] << 8) | (buffer[7]);	/* ->camera = 242, 292, 484  */
+	stv680->origGain = buffer[12];
+
+	goto exit;
+
+error:
+	i = stv_sndctrl (0, stv680, 0x80, 0, buffer, 0x02);	/* Get Last Error */
+	PDEBUG (1, "STV(i): last error: %i,  command = 0x%x", buffer[0], buffer[1]);
+	kfree (buffer);
+	return -1;
+
+exit:
+	kfree (buffer);
+
+	/* video = 320x240, 352x288 */
+	if (stv680->CIF == 1) {
+		stv680->maxwidth = 352;
+		stv680->maxheight = 288;
+		stv680->vwidth = 352;
+		stv680->vheight = 288;
+	}
+	if (stv680->QVGA == 1) {
+		stv680->maxwidth = 320;
+		stv680->maxheight = 240;
+		stv680->vwidth = 320;
+		stv680->vheight = 240;
+	}
+
+	stv680->rawbufsize = bufsize;	/* must be ./. by 8 */
+	stv680->maxframesize = bufsize * 3;	/* RGB size */
+	PDEBUG (2, "STV(i): cwidth = %i, cheight = %i", stv680->cwidth, stv680->cheight);
+	PDEBUG (1, "STV(i): width = %i, height = %i, rawbufsize = %li", stv680->vwidth, stv680->vheight, stv680->rawbufsize);
+
+	/* some default values */
+	stv680->bulk_in_endpointAddr = 0x82;
+	stv680->dropped = 0;
+	stv680->error = 0;
+	stv680->framecount = 0;
+	stv680->readcount = 0;
+	stv680->streaming = 0;
+	/* bright, white, colour, hue, contrast are set by software, not in stv0680 */
+	stv680->brightness = 32767;
+	stv680->chgbright = 0;
+	stv680->whiteness = 0;	/* only for greyscale */
+	stv680->colour = 32767;
+	stv680->contrast = 32767;
+	stv680->hue = 32767;
+	stv680->palette = STV_VIDEO_PALETTE;
+	stv680->depth = 24;	/* rgb24 bits */
+	swapRGB = 0;
+	if ((swapRGB_on == 0) && (swapRGB == 0))
+		PDEBUG (1, "STV(i): swapRGB is (auto) OFF");
+	else if ((swapRGB_on == 1) && (swapRGB == 1))
+		PDEBUG (1, "STV(i): swapRGB is (auto) ON");
+	else if (swapRGB_on == 1)
+		PDEBUG (1, "STV(i): swapRGB is (forced) ON");
+	else if (swapRGB_on == -1)
+		PDEBUG (1, "STV(i): swapRGB is (forced) OFF");
+	
+	if (stv_set_video_mode (stv680) < 0) {
+		PDEBUG (0, "STV(e): Could not set video mode in stv_init");
+		return -1;
+	}
+
+	return 0;
+}
+
+/***************** last of pencam  routines  *******************/
+
+/********************************************************************
+ * /proc interface
+ *******************************************************************/
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+
+static struct proc_dir_entry *stv680_proc_entry = NULL;
+extern struct proc_dir_entry *video_proc_entry;
+
+#define YES_NO(x) ((x) ? "yes" : "no")
+#define ON_OFF(x) ((x) ? "(auto) on" : "(auto) off")
+
+static int stv680_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char *out = page;
+	int len;
+	struct usb_stv *stv680 = data;
+
+	/* Stay under PAGE_SIZE or else bla bla bla.... */
+
+	out += sprintf (out, "driver_version  : %s\n", DRIVER_VERSION);
+	out += sprintf (out, "model           : %s\n", stv680->camera_name);
+	out += sprintf (out, "in use          : %s\n", YES_NO (stv680->user));
+	out += sprintf (out, "streaming       : %s\n", YES_NO (stv680->streaming));
+	out += sprintf (out, "num_frames      : %d\n", STV680_NUMFRAMES);
+
+	out += sprintf (out, "Current size    : %ix%i\n", stv680->vwidth, stv680->vheight);
+	if (swapRGB_on == 0)
+		out += sprintf (out, "swapRGB         : %s\n", ON_OFF (swapRGB));
+	else if (swapRGB_on == 1)
+		out += sprintf (out, "swapRGB         : (forced) on\n");
+	else if (swapRGB_on == -1)
+		out += sprintf (out, "swapRGB         : (forced) off\n");
+
+	out += sprintf (out, "Palette         : %i", stv680->palette);
+
+	out += sprintf (out, "\n");
+
+	out += sprintf (out, "Frames total    : %d\n", stv680->readcount);
+	out += sprintf (out, "Frames read     : %d\n", stv680->framecount);
+	out += sprintf (out, "Packets dropped : %d\n", stv680->dropped);
+	out += sprintf (out, "Decoding Errors : %d\n", stv680->error);
+
+	len = out - page;
+	len -= off;
+	if (len < count) {
+		*eof = 1;
+		if (len <= 0)
+			return 0;
+	} else
+		len = count;
+
+	*start = page + off;
+	return len;
+}
+
+static int create_proc_stv680_cam (struct usb_stv *stv680)
+{
+	char name[9];
+	struct proc_dir_entry *ent;
+
+	if (!stv680_proc_entry || !stv680)
+		return -1;
+
+	sprintf (name, "video%d", stv680->vdev.minor);
+
+	ent = create_proc_entry (name, S_IFREG | S_IRUGO | S_IWUSR, stv680_proc_entry);
+	if (!ent)
+		return -1;
+
+	ent->data = stv680;
+	ent->read_proc = stv680_read_proc;
+	stv680->proc_entry = ent;
+	return 0;
+}
+
+static void destroy_proc_stv680_cam (struct usb_stv *stv680)
+{
+	/* One to much, just to be sure :) */
+	char name[9];
+
+	if (!stv680 || !stv680->proc_entry)
+		return;
+
+	sprintf (name, "video%d", stv680->vdev.minor);
+	remove_proc_entry (name, stv680_proc_entry);
+	stv680->proc_entry = NULL;
+}
+
+static int proc_stv680_create (void)
+{
+	if (video_proc_entry == NULL) {
+		PDEBUG (0, "STV(e): /proc/video/ doesn't exist!");
+		return -1;
+	}
+	stv680_proc_entry = create_proc_entry ("stv680", S_IFDIR, video_proc_entry);
+
+	if (stv680_proc_entry) {
+		stv680_proc_entry->owner = THIS_MODULE;
+	} else {
+		PDEBUG (0, "STV(e): Unable to initialize /proc/video/stv680");
+		return -1;
+	}
+	return 0;
+}
+
+static void proc_stv680_destroy (void)
+{
+	if (stv680_proc_entry == NULL)
+		return;
+
+	remove_proc_entry ("stv", video_proc_entry);
+}
+#endif				/* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */
+
+/********************************************************************
+ * Camera control
+ *******************************************************************/
+
+static int stv680_get_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+	/* This sets values for v4l interface. max/min = 65535/0  */
+
+	p->brightness = stv680->brightness;
+	p->whiteness = stv680->whiteness;	/* greyscale */
+	p->colour = stv680->colour;
+	p->contrast = stv680->contrast;
+	p->hue = stv680->hue;
+	p->palette = stv680->palette;
+	p->depth = stv680->depth;
+	return 0;
+}
+
+static int stv680_set_pict (struct usb_stv *stv680, struct video_picture *p)
+{
+	/* See above stv680_get_pict  */
+
+	if (p->palette != STV_VIDEO_PALETTE) {
+		PDEBUG (2, "STV(e): Palette set error in _set_pic");
+		return 1;
+	}
+
+	if (stv680->brightness != p->brightness) {
+		stv680->chgbright = 1;
+		stv680->brightness = p->brightness;
+	} 
+
+	stv680->whiteness = p->whiteness;	/* greyscale */
+	stv680->colour = p->colour;
+	stv680->contrast = p->contrast;
+	stv680->hue = p->hue;
+	stv680->palette = p->palette;
+	stv680->depth = p->depth;
+
+	return 0;
+}
+
+static void stv680_video_irq (struct urb *urb)
+{
+	struct usb_stv *stv680 = urb->context;
+	int length = urb->actual_length;
+
+	if (length < stv680->rawbufsize)
+		PDEBUG (2, "STV(i): Lost data in transfer: exp %li, got %i", stv680->rawbufsize, length);
+
+	/* ohoh... */
+	if (!stv680->streaming)
+		return;
+
+	if (!stv680->udev) {
+		PDEBUG (0, "STV(e): device vapourished in video_irq");
+		return;
+	}
+
+	/* 0 sized packets happen if we are to fast, but sometimes the camera
+	   keeps sending them forever...
+	 */
+	if (length && !urb->status) {
+		stv680->nullpackets = 0;
+		switch (stv680->scratch[stv680->scratch_next].state) {
+		case BUFFER_READY:
+		case BUFFER_BUSY:
+			stv680->dropped++;
+			break;
+
+		case BUFFER_UNUSED:
+			memcpy (stv680->scratch[stv680->scratch_next].data,
+			        (unsigned char *) urb->transfer_buffer, length);
+			stv680->scratch[stv680->scratch_next].state = BUFFER_READY;
+			stv680->scratch[stv680->scratch_next].length = length;
+			if (waitqueue_active (&stv680->wq)) {
+				wake_up_interruptible (&stv680->wq);
+			}
+			stv680->scratch_overflow = 0;
+			stv680->scratch_next++;
+			if (stv680->scratch_next >= STV680_NUMSCRATCH)
+				stv680->scratch_next = 0;;
+			break;
+		}		/* switch  */
+	} else {
+		stv680->nullpackets++;
+		if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+			if (waitqueue_active (&stv680->wq)) {
+				wake_up_interruptible (&stv680->wq);
+			}
+		}
+	}			/*  if - else */
+
+	/* Resubmit urb for new data */
+	urb->status = 0;
+	urb->dev = stv680->udev;
+	if (usb_submit_urb (urb))
+		PDEBUG (0, "STV(e): urb burned down in video irq");
+	return;
+}				/*  _video_irq  */
+
+static int stv680_start_stream (struct usb_stv *stv680)
+{
+	urb_t *urb;
+	int err = 0, i;
+
+	stv680->streaming = 1;
+
+	/* Do some memory allocation */
+	for (i = 0; i < STV680_NUMFRAMES; i++) {
+		stv680->frame[i].data = stv680->fbuf + i * stv680->maxframesize;
+		stv680->frame[i].curpix = 0;
+	}
+	/* packet size = 4096  */
+	for (i = 0; i < STV680_NUMSBUF; i++) {
+		stv680->sbuf[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+		if (stv680->sbuf[i].data == NULL) {
+			PDEBUG (0, "STV(e): Could not kmalloc raw data buffer %i", i);
+			return -1;
+		}
+	}
+
+	stv680->scratch_next = 0;
+	stv680->scratch_use = 0;
+	stv680->scratch_overflow = 0;
+	for (i = 0; i < STV680_NUMSCRATCH; i++) {
+		stv680->scratch[i].data = kmalloc (stv680->rawbufsize, GFP_KERNEL);
+		if (stv680->scratch[i].data == NULL) {
+			PDEBUG (0, "STV(e): Could not kmalloc raw scratch buffer %i", i);
+			return -1;
+		}
+		stv680->scratch[i].state = BUFFER_UNUSED;
+	}
+
+	for (i = 0; i < STV680_NUMSBUF; i++) {
+		urb = usb_alloc_urb (0);
+		if (!urb)
+			return ENOMEM;
+
+		/* sbuf is urb->transfer_buffer, later gets memcpyed to scratch */
+		usb_fill_bulk_urb (urb, stv680->udev,
+				   usb_rcvbulkpipe (stv680->udev, stv680->bulk_in_endpointAddr),
+				   stv680->sbuf[i].data, stv680->rawbufsize,
+				   stv680_video_irq, stv680);
+		urb->timeout = PENCAM_TIMEOUT * 2;
+		urb->transfer_flags |= USB_QUEUE_BULK;
+		stv680->urb[i] = urb;
+		err = usb_submit_urb (stv680->urb[i]);
+		if (err)
+			PDEBUG (0, "STV(e): urb burned down in start stream");
+	}			/* i STV680_NUMSBUF */
+
+	stv680->framecount = 0;
+	return 0;
+}
+
+static int stv680_stop_stream (struct usb_stv *stv680)
+{
+	int i;
+
+	if (!stv680->streaming || !stv680->udev)
+		return 1;
+
+	stv680->streaming = 0;
+
+	for (i = 0; i < STV680_NUMSBUF; i++)
+		if (stv680->urb[i]) {
+			stv680->urb[i]->next = NULL;
+			usb_unlink_urb (stv680->urb[i]);
+			usb_free_urb (stv680->urb[i]);
+			stv680->urb[i] = NULL;
+			kfree (stv680->sbuf[i].data);
+		}
+	for (i = 0; i < STV680_NUMSCRATCH; i++) {
+		kfree (stv680->scratch[i].data);
+		stv680->scratch[i].data = NULL;
+	}
+
+	return 0;
+}
+
+static int stv680_set_size (struct usb_stv *stv680, int width, int height)
+{
+	int wasstreaming = stv680->streaming;
+
+	/* Check to see if we need to change */
+	if ((stv680->vwidth == width) && (stv680->vheight == height))
+		return 0;
+
+	PDEBUG (1, "STV(i): size request for %i x %i", width, height);
+	/* Check for a valid mode */
+	if ((!width || !height) || ((width & 1) || (height & 1))) {
+		PDEBUG (1, "STV(e): set_size error: request: v.width = %i, v.height = %i  actual: stv.width = %i, stv.height = %i", width, height, stv680->vwidth, stv680->vheight);
+		return 1;
+	}
+
+	if ((width < (stv680->maxwidth / 2)) || (height < (stv680->maxheight / 2))) {
+		width = stv680->maxwidth / 2;
+		height = stv680->maxheight / 2;
+	} else if ((width >= 158) && (width <= 166)) {
+		width = 160;
+		height = 120;
+	} else if ((width >= 172) && (width <= 180)) {
+		width = 176;
+		height = 144;
+	} else if ((width >= 318) && (width <= 350)) {
+		width = 320;
+		height = 240;
+	} else if ((width >= 350) && (width <= 358)) {
+		width = 352;
+		height = 288;
+	}
+
+	/* Stop a current stream and start it again at the new size */
+	if (wasstreaming)
+		stv680_stop_stream (stv680);
+	stv680->vwidth = width;
+	stv680->vheight = height;
+	PDEBUG (1, "STV(i): size set to %i x %i", stv680->vwidth, stv680->vheight);
+	if (wasstreaming)
+		stv680_start_stream (stv680);
+
+	return 0;
+}
+
+/**********************************************************************
+ * Video Decoding
+ **********************************************************************/
+
+/*******  routines from the pencam program; hey, they work!  ********/
+
+/*
+ * STV0680 Vision Camera Chipset Driver
+ * Copyright (C) 2000 Adam Harrison <adam@antispin.org> 
+*/
+
+#define RED 0
+#define GREEN 1
+#define BLUE 2
+#define AD(x, y, w) (((y)*(w)+(x))*3)
+
+static void bayer_unshuffle (struct usb_stv *stv680, struct stv680_scratch *buffer)
+{
+	int x, y, i;
+	int w = stv680->cwidth;
+	int vw = stv680->cwidth, vh = stv680->cheight;
+	unsigned int p = 0;
+	int colour = 0, bayer = 0;
+	unsigned char *raw = buffer->data;
+	struct stv680_frame *frame = &stv680->frame[stv680->curframe];
+	unsigned char *output = frame->data;
+	unsigned char *temp = frame->data;
+	int offset = buffer->offset;
+
+	if (frame->curpix == 0) {
+		if (frame->grabstate == FRAME_READY) {
+			frame->grabstate = FRAME_GRABBING;
+		}
+	}
+	if (offset != frame->curpix) {	/* Regard frame as lost :( */
+		frame->curpix = 0;
+		stv680->error++;
+		return;
+	}
+
+	if ((stv680->vwidth == 320) || (stv680->vwidth == 160)) {
+		vw = 320;
+		vh = 240;
+	}
+	if ((stv680->vwidth == 352) || (stv680->vwidth == 176)) {
+		vw = 352;
+		vh = 288;
+	}
+
+	memset (output, 0, 3 * vw * vh);	/* clear output matrix. */
+
+	for (y = 0; y < vh; y++) {
+		for (x = 0; x < vw; x++) {
+			if (x & 1)
+				p = *(raw + y * w + (x >> 1));
+			else
+				p = *(raw + y * w + (x >> 1) + (w >> 1));
+
+			if (y & 1)
+				bayer = 2;
+			else
+				bayer = 0;
+			if (x & 1)
+				bayer++;
+
+			switch (bayer) {
+			case 0:
+			case 3:
+				colour = 1;
+				break;
+			case 1:
+				colour = 0;
+				break;
+			case 2:
+				colour = 2;
+				break;
+			}
+			i = (y * vw + x) * 3;	
+			*(output + i + colour) = (unsigned char) p;
+		}		/* for x */
+
+	}			/* for y */
+
+	/****** gamma correction plus hardcoded white balance */
+	/* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
+	   Correction values red[], green[], blue[], are generated by 
+	   (pow(i/256.0, GAMMA)*255.0)*white balanceRGB where GAMMA=0.55, 1<i<255. 
+	   White balance (RGB)= 1.0, 1.17, 1.48. Values are calculated as double float and 
+	   converted to unsigned char. Values are in stv680.h  */
+
+	for (y = 0; y < vh; y++) {
+		for (x = 0; x < vw; x++) {
+			i = (y * vw + x) * 3;
+			*(output + i) = red[*(output + i)];
+			*(output + i + 1) = green[*(output + i + 1)];
+			*(output + i + 2) = blue[*(output + i + 2)];
+		}
+	}
+
+	/******  bayer demosaic  ******/
+	for (y = 1; y < (vh - 1); y++) {
+		for (x = 1; x < (vw - 1); x++) {	/* work out pixel type */
+			if (y & 1)
+				bayer = 0;
+			else
+				bayer = 2;
+			if (!(x & 1))
+				bayer++;
+
+			switch (bayer) {
+			case 0:	/* green. blue lr, red tb */
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y, vw) + BLUE) + (int) *(output + AD (x + 1, y, vw) + BLUE)) >> 1;
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x, y - 1, vw) + RED) + (int) *(output + AD (x, y + 1, vw) + RED)) >> 1;
+				break;
+
+			case 1:	/* blue. green lrtb, red diagonals */
+				*(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y - 1, vw) + RED) + (int) *(output + AD (x - 1, y + 1, vw) + RED) + (int) *(output + AD (x + 1, y - 1, vw) + RED) + (int) *(output + AD (x + 1, y + 1, vw) + RED)) >> 2;
+				break;
+
+			case 2:	/* red. green lrtb, blue diagonals */
+				*(output + AD (x, y, vw) + GREEN) = ((int) *(output + AD (x - 1, y, vw) + GREEN) + (int) *(output + AD (x + 1, y, vw) + GREEN) + (int) *(output + AD (x, y - 1, vw) + GREEN) + (int) *(output + AD (x, y + 1, vw) + GREEN)) >> 2;
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x - 1, y - 1, vw) + BLUE) + (int) *(output + AD (x + 1, y - 1, vw) + BLUE) + (int) *(output + AD (x - 1, y + 1, vw) + BLUE) + (int) *(output + AD (x + 1, y + 1, vw) + BLUE)) >> 2;
+				break;
+
+			case 3:	/* green. red lr, blue tb */
+				*(output + AD (x, y, vw) + RED) = ((int) *(output + AD (x - 1, y, vw) + RED) + (int) *(output + AD (x + 1, y, vw) + RED)) >> 1;
+				*(output + AD (x, y, vw) + BLUE) = ((int) *(output + AD (x, y - 1, vw) + BLUE) + (int) *(output + AD (x, y + 1, vw) + BLUE)) >> 1;
+				break;
+			}	/* switch */
+		}		/* for x */
+	}			/* for y  - end demosaic  */
+
+	/* fix top and bottom row, left and right side */
+	i = vw * 3;
+	memcpy (output, (output + i), i);
+	memcpy ((output + (vh * i)), (output + ((vh - 1) * i)), i);
+	for (y = 0; y < vh; y++) {
+		i = y * vw * 3;
+		memcpy ((output + i), (output + i + 3), 3);
+		memcpy ((output + i + (vw * 3)), (output + i + (vw - 1) * 3), 3);
+	}
+
+	/*  process all raw data, then trim to size if necessary */
+	if ((stv680->vwidth == 160) || (stv680->vwidth == 176))  {
+		i = 0;
+		for (y = 0; y < vh; y++) {
+			if (!(y & 1)) {
+				for (x = 0; x < vw; x++) {
+					p = (y * vw + x) * 3;
+					if (!(x & 1)) {
+						*(output + i) = *(output + p);
+						*(output + i + 1) = *(output + p + 1);
+						*(output + i + 2) = *(output + p + 2);
+						i += 3;
+					}
+				}  /* for x */
+			}
+		}  /* for y */
+	}
+	/* reset to proper width */
+	if ((stv680->vwidth == 160)) {
+		vw = 160;
+		vh = 120;
+	}
+	if ((stv680->vwidth == 176)) {
+		vw = 176;
+		vh = 144;
+	}
+
+	/* output is RGB; some programs want BGR  */
+	/* swapRGB_on=0 -> program decides;  swapRGB_on=1, always swap */
+	/* swapRGB_on=-1, never swap */
+	if (((swapRGB == 1) && (swapRGB_on != -1)) || (swapRGB_on == 1)) {
+		for (y = 0; y < vh; y++) {
+			for (x = 0; x < vw; x++) {
+				i = (y * vw + x) * 3;
+				*(temp) = *(output + i);
+				*(output + i) = *(output + i + 2);
+				*(output + i + 2) = *(temp);
+			}
+		}
+	}
+	/* brightness */
+	if (stv680->chgbright == 1) {
+		if (stv680->brightness >= 32767) {
+			p = (stv680->brightness - 32767) / 256;
+			for (x = 0; x < (vw * vh * 3); x++) {
+				if ((*(output + x) + (unsigned char) p) > 255)
+					*(output + x) = 255;
+				else
+					*(output + x) += (unsigned char) p;
+			}	/* for */
+		} else {
+			p = (32767 - stv680->brightness) / 256;
+			for (x = 0; x < (vw * vh * 3); x++) {
+				if ((unsigned char) p > *(output + x))
+					*(output + x) = 0;
+				else
+					*(output + x) -= (unsigned char) p;
+			}	/* for */
+		}		/* else */
+	}
+	/* if */
+	frame->curpix = 0;
+	frame->curlinepix = 0;
+	frame->grabstate = FRAME_DONE;
+	stv680->framecount++;
+	stv680->readcount++;
+	if (stv680->frame[(stv680->curframe + 1) & (STV680_NUMFRAMES - 1)].grabstate == FRAME_READY) {
+		stv680->curframe = (stv680->curframe + 1) & (STV680_NUMFRAMES - 1);
+	}
+
+}				/* bayer_unshuffle */
+
+/*******  end routines from the pencam program  *********/
+
+static int stv680_newframe (struct usb_stv *stv680, int framenr)
+{
+	int errors = 0;
+
+	while (stv680->streaming && (stv680->frame[framenr].grabstate == FRAME_READY || stv680->frame[framenr].grabstate == FRAME_GRABBING)) {
+		if (!stv680->frame[framenr].curpix) {
+			errors++;
+		}
+		wait_event_interruptible (stv680->wq, (stv680->scratch[stv680->scratch_use].state == BUFFER_READY));
+
+		if (stv680->nullpackets > STV680_MAX_NULLPACKETS) {
+			stv680->nullpackets = 0;
+			PDEBUG (2, "STV(i): too many null length packets, restarting capture");
+			stv680_stop_stream (stv680);
+			stv680_start_stream (stv680);
+		} else {
+			if (stv680->scratch[stv680->scratch_use].state != BUFFER_READY) {
+				stv680->frame[framenr].grabstate = FRAME_ERROR;
+				PDEBUG (2, "STV(e): FRAME_ERROR in _newframe");
+				return -EIO;
+			}
+			stv680->scratch[stv680->scratch_use].state = BUFFER_BUSY;
+
+			bayer_unshuffle (stv680, &stv680->scratch[stv680->scratch_use]);
+
+			stv680->scratch[stv680->scratch_use].state = BUFFER_UNUSED;
+			stv680->scratch_use++;
+			if (stv680->scratch_use >= STV680_NUMSCRATCH)
+				stv680->scratch_use = 0;
+			if (errors > STV680_MAX_ERRORS) {
+				errors = 0;
+				PDEBUG (2, "STV(i): too many errors, restarting capture");
+				stv680_stop_stream (stv680);
+				stv680_start_stream (stv680);
+			}
+		}		/* else */
+	}			/* while */
+	return 0;
+}
+
+/*********************************************************************
+ * Video4Linux
+ *********************************************************************/
+
+static int stv_open (struct video_device *dev, int flags)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	int err = 0;
+
+	/* we are called with the BKL held */
+	MOD_INC_USE_COUNT;
+	stv680->user = 1;
+	err = stv_init (stv680);	/* main initialization routine for camera */
+
+	if (err >= 0) {
+		stv680->fbuf = rvmalloc (stv680->maxframesize * STV680_NUMFRAMES);
+		if (!stv680->fbuf) {
+			PDEBUG (0, "STV(e): Could not rvmalloc frame bufer");
+			err = -ENOMEM;
+		}
+	}
+	if (err) {
+		MOD_DEC_USE_COUNT;
+		stv680->user = 0;
+	}
+
+	return err;
+}
+
+static void stv_close (struct video_device *dev)
+{
+	/* called with BKL held */
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	int i;
+
+	for (i = 0; i < STV680_NUMFRAMES; i++)
+		stv680->frame[i].grabstate = FRAME_UNUSED;
+	if (stv680->streaming)
+		stv680_stop_stream (stv680);
+
+	if ((i = stv_stop_video (stv680)) < 0)
+		PDEBUG (1, "STV(e): stop_video failed in stv_close");
+
+	rvfree (stv680->fbuf, stv680->maxframesize * STV680_NUMFRAMES);
+	stv680->user = 0;
+
+	if (stv680->removed) {
+		video_unregister_device (&stv680->vdev);
+		kfree (stv680);
+		stv680 = NULL;
+		PDEBUG (0, "STV(i): device unregistered");
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+static long stv680_write (struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	return -EINVAL;
+}
+
+static int stv680_ioctl (struct video_device *vdev, unsigned int cmd, void *arg)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) vdev;
+
+	if (!stv680->udev)
+		return -EIO;
+
+	switch (cmd) {
+	case VIDIOCGCAP:{
+			struct video_capability b;
+
+			strcpy (b.name, stv680->camera_name);
+			b.type = VID_TYPE_CAPTURE;
+			b.channels = 1;
+			b.audios = 0;
+			b.maxwidth = stv680->maxwidth;
+			b.maxheight = stv680->maxheight;
+			b.minwidth = stv680->maxwidth / 2;
+			b.minheight = stv680->maxheight / 2;
+
+			if (copy_to_user (arg, &b, sizeof (b))) {
+				PDEBUG (2, "STV(e): VIDIOCGGAP failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCGCHAN:{
+			struct video_channel v;
+
+			if (copy_from_user (&v, arg, sizeof (v)))
+				return -EFAULT;
+			if (v.channel != 0)
+				return -EINVAL;
+
+			v.flags = 0;
+			v.tuners = 0;
+			v.type = VIDEO_TYPE_CAMERA;
+			strcpy (v.name, "STV Camera");
+
+			if (copy_to_user (arg, &v, sizeof (v))) {
+				PDEBUG (2, "STV(e): VIDIOCGCHAN failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCSCHAN:{
+			int v;
+
+			if (copy_from_user (&v, arg, sizeof (v))) {
+				PDEBUG (2, "STV(e): VIDIOCSCHAN failed");
+				return -EFAULT;
+			}
+			if (v != 0)
+				return -EINVAL;
+
+			return 0;
+		}
+	case VIDIOCGPICT:{
+			struct video_picture p;
+
+			stv680_get_pict (stv680, &p);
+			if (copy_to_user (arg, &p, sizeof (p))) {
+				PDEBUG (2, "STV(e): VIDIOCGPICT failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCSPICT:{
+			struct video_picture p;
+
+			if (copy_from_user (&p, arg, sizeof (p))) {
+				PDEBUG (2, "STV(e): VIDIOCSPICT failed");
+				return -EFAULT;
+			}
+			copy_from_user (&p, arg, sizeof (p));
+			PDEBUG (2, "STV(i): palette set to %i in VIDIOSPICT", p.palette);
+
+			if (stv680_set_pict (stv680, &p))
+				return -EINVAL;
+			return 0;
+		}
+	case VIDIOCSWIN:{
+			struct video_window vw;
+
+			if (copy_from_user (&vw, arg, sizeof (vw)))
+				return -EFAULT;
+			if (vw.flags)
+				return -EINVAL;
+			if (vw.clipcount)
+				return -EINVAL;
+			if (vw.width != stv680->vwidth) {
+				if (stv680_set_size (stv680, vw.width, vw.height)) {
+					PDEBUG (2, "STV(e): failed (from user) set size in VIDIOCSWIN");
+					return -EINVAL;
+				}
+			}
+			return 0;
+		}
+	case VIDIOCGWIN:{
+			struct video_window vw;
+
+			vw.x = 0;	/* FIXME */
+			vw.y = 0;
+			vw.chromakey = 0;
+			vw.flags = 0;
+			vw.clipcount = 0;
+			vw.width = stv680->vwidth;
+			vw.height = stv680->vheight;
+
+			if (copy_to_user (arg, &vw, sizeof (vw))) {
+				PDEBUG (2, "STV(e): VIDIOCGWIN failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCGMBUF:{
+			struct video_mbuf vm;
+			int i;
+
+			memset (&vm, 0, sizeof (vm));
+			vm.size = STV680_NUMFRAMES * stv680->maxframesize;
+			vm.frames = STV680_NUMFRAMES;
+			for (i = 0; i < STV680_NUMFRAMES; i++)
+				vm.offsets[i] = stv680->maxframesize * i;
+
+			if (copy_to_user ((void *) arg, (void *) &vm, sizeof (vm))) {
+				PDEBUG (2, "STV(e): VIDIOCGMBUF failed");
+				return -EFAULT;
+			}
+
+			return 0;
+		}
+	case VIDIOCMCAPTURE:{
+			struct video_mmap vm;
+
+			if (copy_from_user (&vm, arg, sizeof (vm))) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE failed");
+				return -EFAULT;
+			}
+			if (vm.format != STV_VIDEO_PALETTE) {
+				PDEBUG (2, "STV(i): VIDIOCMCAPTURE vm.format (%i) != VIDEO_PALETTE (%i)",
+					vm.format, STV_VIDEO_PALETTE);
+				if ((vm.format == 3) && (swapRGB_on == 0))  {
+					PDEBUG (2, "STV(i): VIDIOCMCAPTURE swapRGB is (auto) ON");
+					/* this may fix those apps (e.g., xawtv) that want BGR */
+					swapRGB = 1;
+				}
+				return -EINVAL;
+			}
+			if (vm.frame >= STV680_NUMFRAMES) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE vm.frame > NUMFRAMES");
+				return -EINVAL;
+			}
+			if ((stv680->frame[vm.frame].grabstate == FRAME_ERROR)
+			    || (stv680->frame[vm.frame].grabstate == FRAME_GRABBING)) {
+				PDEBUG (2, "STV(e): VIDIOCMCAPTURE grabstate (%i) error",
+					stv680->frame[vm.frame].grabstate);
+				return -EBUSY;
+			}
+			/* Is this according to the v4l spec??? */
+			if (stv680->vwidth != vm.width) {
+				if (stv680_set_size (stv680, vm.width, vm.height)) {
+					PDEBUG (2, "STV(e): VIDIOCMCAPTURE set_size failed");
+					return -EINVAL;
+				}
+			}
+			stv680->frame[vm.frame].grabstate = FRAME_READY;
+
+			if (!stv680->streaming)
+				stv680_start_stream (stv680);
+
+			return 0;
+		}
+	case VIDIOCSYNC:{
+			int frame, ret = 0;
+
+			if (copy_from_user ((void *) &frame, arg, sizeof (int))) {
+				PDEBUG (2, "STV(e): VIDIOCSYNC failed");
+				return -EFAULT;
+			}
+			if (frame < 0 || frame >= STV680_NUMFRAMES) {
+				PDEBUG (2, "STV(e): Bad frame # in VIDIOCSYNC");
+				return -EINVAL;
+			}
+			ret = stv680_newframe (stv680, frame);
+			stv680->frame[frame].grabstate = FRAME_UNUSED;
+			return ret;
+		}
+	case VIDIOCGFBUF:{
+			struct video_buffer vb;
+
+			memset (&vb, 0, sizeof (vb));
+			vb.base = NULL;	/* frame buffer not supported, not used */
+
+			if (copy_to_user ((void *) arg, (void *) &vb, sizeof (vb))) {
+				PDEBUG (2, "STV(e): VIDIOCSYNC failed");
+				return -EFAULT;
+			}
+			return 0;
+		}
+	case VIDIOCKEY:
+		return 0;
+	case VIDIOCCAPTURE:
+		{
+			PDEBUG (2, "STV(e): VIDIOCCAPTURE failed");
+			return -EINVAL;
+		}
+	case VIDIOCSFBUF:
+		return -EINVAL;
+	case VIDIOCGTUNER:
+	case VIDIOCSTUNER:
+		return -EINVAL;
+	case VIDIOCGFREQ:
+	case VIDIOCSFREQ:
+		return -EINVAL;
+	case VIDIOCGAUDIO:
+	case VIDIOCSAUDIO:
+		return -EINVAL;
+	default:
+		return -ENOIOCTLCMD;
+	}			/* end switch */
+
+	return 0;
+}
+
+static int stv680_mmap (struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	unsigned long start = (unsigned long) adr;
+	unsigned long page, pos;
+
+	down (&stv680->lock);
+
+	if (stv680->udev == NULL) {
+		up (&stv680->lock);
+		return -EIO;
+	}
+	if (size > (((STV680_NUMFRAMES * stv680->maxframesize) + PAGE_SIZE - 1)
+		    & ~(PAGE_SIZE - 1))) {
+		up (&stv680->lock);
+		return -EINVAL;
+	}
+	pos = (unsigned long) stv680->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa (pos);
+		if (remap_page_range (start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up (&stv680->lock);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	up (&stv680->lock);
+
+	return 0;
+}
+
+static long stv680_read (struct video_device *dev, char *buf, unsigned long count, int noblock)
+{
+	unsigned long int realcount = count;
+	int ret = 0;
+	struct usb_stv *stv680 = (struct usb_stv *) dev;
+	unsigned long int i;
+
+	if (STV680_NUMFRAMES != 2) {
+		PDEBUG (0, "STV(e): STV680_NUMFRAMES needs to be 2!");
+		return -1;
+	}
+	if (stv680->udev == NULL)
+		return -EIO;
+	if (realcount > (stv680->vwidth * stv680->vheight * 3))
+		realcount = stv680->vwidth * stv680->vheight * 3;
+
+	/* Shouldn't happen: */
+	if (stv680->frame[0].grabstate == FRAME_GRABBING) {
+		PDEBUG (2, "STV(e): FRAME_GRABBING in stv680_read");
+		return -EBUSY;
+	}
+	stv680->frame[0].grabstate = FRAME_READY;
+	stv680->frame[1].grabstate = FRAME_UNUSED;
+	stv680->curframe = 0;
+
+	if (!stv680->streaming)
+		stv680_start_stream (stv680);
+
+	if (!stv680->streaming) {
+		ret = stv680_newframe (stv680, 0);	/* ret should = 0 */
+	}
+
+	ret = stv680_newframe (stv680, 0);
+
+	if (!ret) {
+		if ((i = copy_to_user (buf, stv680->frame[0].data, realcount)) != 0) {
+			PDEBUG (2, "STV(e): copy_to_user frame 0 failed, ret count = %li", i);
+			return -EFAULT;
+		}
+	} else {
+		realcount = ret;
+	}
+	stv680->frame[0].grabstate = FRAME_UNUSED;
+	return realcount;
+}				/* stv680_read */
+
+static int stv_init_done (struct video_device *dev)
+{
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	if (create_proc_stv680_cam ((struct usb_stv *) dev) < 0)
+		return -1;
+#endif
+	return 0;
+}
+
+static struct video_device stv680_template = {
+	owner:		THIS_MODULE,
+	name:		"STV0680 USB camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_SE401,
+	open:		stv_open,
+	close:		stv_close,
+	read:		stv680_read,
+	write:		stv680_write,
+	ioctl:		stv680_ioctl,
+	mmap:		stv680_mmap,
+	initialize:	stv_init_done,
+};
+
+static void *__devinit stv680_probe (struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *id)
+{
+	struct usb_interface_descriptor *interface;
+	struct usb_stv *stv680;
+	char *camera_name = NULL;
+
+	/* We don't handle multi-config cameras */
+	if (dev->descriptor.bNumConfigurations != 1) {
+		PDEBUG (0, "STV(e): Number of Configurations != 1");
+		return NULL;
+	}
+
+	interface = &dev->actconfig->interface[ifnum].altsetting[0];
+	/* Is it a STV680? */
+	if ((dev->descriptor.idVendor == USB_PENCAM_VENDOR_ID) && (dev->descriptor.idProduct == USB_PENCAM_PRODUCT_ID)) {
+		camera_name = "STV0680";
+		PDEBUG (0, "STV(i): STV0680 camera found.");
+	} else {
+		PDEBUG (0, "STV(e): Vendor/Product ID do not match STV0680 values.");
+		PDEBUG (0, "STV(e): Check that the STV0680 camera is connected to the computer.");
+		return NULL;
+	}
+	/* We found one */
+	if ((stv680 = kmalloc (sizeof (*stv680), GFP_KERNEL)) == NULL) {
+		PDEBUG (0, "STV(e): couldn't kmalloc stv680 struct.");
+		return NULL;
+	}
+
+	memset (stv680, 0, sizeof (*stv680));
+
+	stv680->udev = dev;
+	stv680->camera_name = camera_name;
+
+	memcpy (&stv680->vdev, &stv680_template, sizeof (stv680_template));
+	memcpy (stv680->vdev.name, stv680->camera_name, strlen (stv680->camera_name));
+	init_waitqueue_head (&stv680->wq);
+	init_MUTEX (&stv680->lock);
+	wmb ();
+
+	if (video_register_device (&stv680->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+		kfree (stv680);
+		PDEBUG (0, "STV(e): video_register_device failed");
+		return NULL;
+	}
+	PDEBUG (0, "STV(i): registered new video device: video%d", stv680->vdev.minor);
+
+	return stv680;
+}
+
+static inline void usb_stv680_remove_disconnected (struct usb_stv *stv680)
+{
+	int i;
+
+	stv680->udev = NULL;
+	stv680->frame[0].grabstate = FRAME_ERROR;
+	stv680->frame[1].grabstate = FRAME_ERROR;
+	stv680->streaming = 0;
+
+	wake_up_interruptible (&stv680->wq);
+
+	for (i = 0; i < STV680_NUMSBUF; i++)
+		if (stv680->urb[i]) {
+			stv680->urb[i]->next = NULL;
+			usb_unlink_urb (stv680->urb[i]);
+			usb_free_urb (stv680->urb[i]);
+			stv680->urb[i] = NULL;
+			kfree (stv680->sbuf[i].data);
+		}
+	for (i = 0; i < STV680_NUMSCRATCH; i++)
+		if (stv680->scratch[i].data) {
+			kfree (stv680->scratch[i].data);
+		}
+	PDEBUG (0, "STV(i): %s disconnected", stv680->camera_name);
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	destroy_proc_stv680_cam (stv680);
+#endif
+	/* Free the memory */
+	kfree (stv680);
+}
+
+static void stv680_disconnect (struct usb_device *dev, void *ptr)
+{
+	struct usb_stv *stv680 = (struct usb_stv *) ptr;
+
+	lock_kernel ();
+	/* We don't want people trying to open up the device */
+	if (!stv680->user) {
+		video_unregister_device (&stv680->vdev);
+		usb_stv680_remove_disconnected (stv680);
+	} else {
+		stv680->removed = 1;
+	}
+	unlock_kernel ();
+}
+
+static struct usb_driver stv680_driver = {
+	name:		"stv680",
+	probe:		stv680_probe,
+	disconnect:	stv680_disconnect,
+	id_table:	device_table
+};
+
+/********************************************************************
+ *  Module routines
+ ********************************************************************/
+
+static int __init usb_stv680_init (void)
+{
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	if (proc_stv680_create () < 0)
+		return -1;
+#endif
+	if (usb_register (&stv680_driver) < 0) {
+		PDEBUG (0, "STV(e): Could not setup STV0680 driver");
+		return -1;
+	}
+	PDEBUG (0, "STV(i): usb camera driver version %s registering", DRIVER_VERSION);
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+}
+
+static void __exit usb_stv680_exit (void)
+{
+	usb_deregister (&stv680_driver);
+	PDEBUG (0, "STV(i): driver deregistered");
+
+#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
+	proc_stv680_destroy ();
+#endif
+}
+
+module_init (usb_stv680_init);
+module_exit (usb_stv680_exit);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/stv680.h linux/drivers/usb/stv680.h
--- linux.orig/drivers/usb/stv680.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/stv680.h	Mon Feb  4 19:00:37 2002
@@ -0,0 +1,223 @@
+/****************************************************************************
+ *
+ *  Filename: stv680.h
+ *
+ *  Description:
+ *     This is a USB driver for STV0680 based usb video cameras.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ * 
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ * 
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ ****************************************************************************/
+
+/* size of usb transfers */
+#define STV680_PACKETSIZE	4096
+
+/* number of queued bulk transfers to use, may have problems if > 1 */
+#define STV680_NUMSBUF		1
+
+/* number of frames supported by the v4l part */
+#define STV680_NUMFRAMES	2
+
+/* scratch buffers for passing data to the decoders: 2 or 4 are good */
+#define STV680_NUMSCRATCH	2
+
+/* number of nul sized packets to receive before kicking the camera */
+#define STV680_MAX_NULLPACKETS	200
+
+/* number of decoding errors before kicking the camera */
+#define STV680_MAX_ERRORS	100
+
+#define USB_PENCAM_VENDOR_ID	0x0553
+#define USB_PENCAM_PRODUCT_ID	0x0202
+#define PENCAM_TIMEOUT          1000
+/* fmt 4 */
+#define STV_VIDEO_PALETTE       VIDEO_PALETTE_RGB24
+
+static __devinitdata struct usb_device_id device_table[] = {
+	{USB_DEVICE (USB_PENCAM_VENDOR_ID, USB_PENCAM_PRODUCT_ID)},
+	{}
+};
+MODULE_DEVICE_TABLE (usb, device_table);
+
+struct stv680_sbuf {
+	unsigned char *data;
+};
+
+enum {
+	FRAME_UNUSED,		/* Unused (no MCAPTURE) */
+	FRAME_READY,		/* Ready to start grabbing */
+	FRAME_GRABBING,		/* In the process of being grabbed into */
+	FRAME_DONE,		/* Finished grabbing, but not been synced yet */
+	FRAME_ERROR,		/* Something bad happened while processing */
+};
+
+enum {
+	BUFFER_UNUSED,
+	BUFFER_READY,
+	BUFFER_BUSY,
+	BUFFER_DONE,
+};
+
+/* raw camera data <- sbuf (urb transfer buf) */
+struct stv680_scratch {
+	unsigned char *data;
+	volatile int state;
+	int offset;
+	int length;
+};
+
+/* processed data for display ends up here, after bayer */
+struct stv680_frame {
+	unsigned char *data;	/* Frame buffer */
+	volatile int grabstate;	/* State of grabbing */
+	unsigned char *curline;
+	int curlinepix;
+	int curpix;
+};
+
+/* this is almost the video structure uvd_t, with extra parameters for stv */
+struct usb_stv {
+	struct video_device vdev;
+
+	struct usb_device *udev;
+
+	unsigned char bulk_in_endpointAddr;	/* __u8  the address of the bulk in endpoint */
+	char *camera_name;
+
+	unsigned int VideoMode;	/* 0x0100 = VGA, 0x0000 = CIF, 0x0300 = QVGA */
+	int SupportedModes;
+	int CIF;
+	int VGA;
+	int QVGA;
+	int cwidth;		/* camera width */
+	int cheight;		/* camera height */
+	int maxwidth;		/* max video width */
+	int maxheight;		/* max video height */
+	int vwidth;		/* current width for video window */
+	int vheight;		/* current height for video window */
+	unsigned long int rawbufsize;
+	unsigned long int maxframesize;	/* rawbufsize * 3 for RGB */
+
+	int origGain;
+	int origMode;		/* original camera mode */
+
+	struct semaphore lock;	/* to lock the structure */
+	int user;		/* user count for exclusive use */
+	int removed;		/* device disconnected */
+	int streaming;		/* Are we streaming video? */
+	char *fbuf;		/* Videodev buffer area */
+	urb_t *urb[STV680_NUMSBUF];	/* # of queued bulk transfers */
+	int curframe;		/* Current receiving frame */
+	struct stv680_frame frame[STV680_NUMFRAMES];	/* # frames supported by v4l part */
+	int readcount;
+	int framecount;
+	int error;
+	int dropped;
+	int scratch_next;
+	int scratch_use;
+	int scratch_overflow;
+	struct stv680_scratch scratch[STV680_NUMSCRATCH];	/* for decoders */
+	struct stv680_sbuf sbuf[STV680_NUMSBUF];
+
+	unsigned int brightness;
+	unsigned int chgbright;
+	unsigned int whiteness;
+	unsigned int colour;
+	unsigned int contrast;
+	unsigned int hue;
+	unsigned int palette;
+	unsigned int depth;	/* rgb24 in bits */
+
+	wait_queue_head_t wq;	/* Processes waiting */
+
+	struct proc_dir_entry *proc_entry;	/* /proc/stv680/videoX */
+	int nullpackets;
+};
+
+
+unsigned char red[256] = { 
+	0, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 
+	18, 18, 18, 18, 18, 18, 18, 25, 30, 35, 38, 42, 
+	44, 47, 50, 53, 54, 57, 59, 61, 63, 65, 67, 69, 
+	71, 71, 73, 75, 77, 78, 80, 81, 82, 84, 85, 87, 
+	88, 89, 90, 91, 93, 94, 95, 97, 98, 98, 99, 101, 
+	102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 
+	114, 115, 116, 116, 117, 118, 119, 120, 121, 122, 123, 124, 
+	125, 125, 126, 127, 128, 129, 129, 130, 131, 132, 133, 134, 
+	134, 135, 135, 136, 137, 138, 139, 140, 140, 141, 142, 143, 
+	143, 143, 144, 145, 146, 147, 147, 148, 149, 150, 150, 151, 
+	152, 152, 152, 153, 154, 154, 155, 156, 157, 157, 158, 159, 
+	159, 160, 161, 161, 161, 162, 163, 163, 164, 165, 165, 166, 
+	167, 167, 168, 168, 169, 170, 170, 170, 171, 171, 172, 173, 
+	173, 174, 174, 175, 176, 176, 177, 178, 178, 179, 179, 179, 
+	180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186, 
+	187, 187, 188, 188, 188, 188, 189, 190, 190, 191, 191, 192, 
+	192, 193, 193, 194, 195, 195, 196, 196, 197, 197, 197, 197, 
+	198, 198, 199, 199, 200, 201, 201, 202, 202, 203, 203, 204, 
+	204, 205, 205, 206, 206, 206, 206, 207, 207, 208, 208, 209, 
+	209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, 
+	215, 215, 215, 216, 216, 217, 217, 218, 218, 218, 219, 219, 
+	220, 220, 221, 221 
+}; 
+
+unsigned char green[256] = {
+	0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 28, 34, 39, 43, 47, 
+	50, 53, 56, 59, 61, 64, 66, 68, 71, 73, 75, 77, 
+	79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 95, 97, 
+	98, 100, 101, 102, 104, 105, 106, 108, 109, 110, 111, 113, 
+	114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 
+	127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 
+	139, 140, 141, 142, 143, 144, 144, 145, 146, 147, 148, 149, 
+	150, 151, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, 
+	160, 160, 161, 162, 163, 164, 164, 165, 166, 167, 167, 168, 
+	169, 170, 170, 171, 172, 172, 173, 174, 175, 175, 176, 177, 
+	177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185, 
+	186, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 
+	193, 194, 194, 195, 196, 196, 197, 198, 198, 199, 199, 200, 
+	201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 
+	208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 
+	214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 
+	221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 
+	227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 
+	233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 
+	239, 240, 240, 241, 241, 242, 242, 243, 243, 243, 244, 244, 
+	245, 245, 246, 246 
+}; 
+
+unsigned char blue[256] = {
+	0, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 
+	23, 23, 23, 23, 23, 23, 23, 30, 37, 42, 47, 51, 
+	55, 58, 61, 64, 67, 70, 72, 74, 78, 80, 82, 84, 
+	86, 88, 90, 92, 94, 95, 97, 100, 101, 103, 104, 106, 
+	107, 110, 111, 112, 114, 115, 116, 118, 119, 121, 122, 124, 
+	125, 126, 127, 128, 129, 132, 133, 134, 135, 136, 137, 138, 
+	139, 140, 141, 143, 144, 145, 146, 147, 148, 149, 150, 151, 
+	152, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, 
+	165, 166, 166, 167, 168, 169, 170, 171, 171, 172, 173, 174, 
+	176, 176, 177, 178, 179, 180, 180, 181, 182, 183, 183, 184, 
+	185, 187, 187, 188, 189, 189, 190, 191, 192, 192, 193, 194, 
+	194, 195, 196, 196, 198, 199, 200, 200, 201, 202, 202, 203, 
+	204, 204, 205, 205, 206, 207, 207, 209, 210, 210, 211, 212, 
+	212, 213, 213, 214, 215, 215, 216, 217, 217, 218, 218, 220, 
+	221, 221, 222, 222, 223, 224, 224, 225, 225, 226, 226, 227, 
+	228, 228, 229, 229, 231, 231, 232, 233, 233, 234, 234, 235, 
+	235, 236, 236, 237, 238, 238, 239, 239, 240, 240, 242, 242, 
+	243, 243, 244, 244, 245, 246, 246, 247, 247, 248, 248, 249, 
+	249, 250, 250, 251, 251, 253, 253, 254, 254, 255, 255, 255, 
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
+	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
+	255, 255, 255, 255 
+}; 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/uhci.c linux/drivers/usb/uhci.c
--- linux.orig/drivers/usb/uhci.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/usb/uhci.c	Mon Feb  4 19:00:37 2002
@@ -520,7 +520,8 @@
 
 	lltd = list_entry(lurbp->td_list.prev, struct uhci_td, list);
 
-	uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1);
+	usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe),
+		uhci_fixup_toggle(urb, uhci_toggle(lltd->info) ^ 1));
 
 	/* All qh's in the queue need to link to the next queue */
 	urbp->qh->link = eurbp->qh->link;
@@ -556,6 +557,7 @@
 
 	/* Fix up the toggle for the next URB's */
 	if (!urbp->queued)
+		/* We set the toggle when we unlink */
 		toggle = usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 	else {
 		/* If we're in the middle of the queue, grab the toggle */
@@ -1683,8 +1685,8 @@
 
 		/* Control and Isochronous ignore the toggle, so this */
 		/* is safe for all types */
-		if (!(td->status & TD_CTRL_ACTIVE) &&
-		    (uhci_actual_length(td->status) < uhci_expected_length(td->info) ||
+		if ((!(td->status & TD_CTRL_ACTIVE) &&
+		    (uhci_actual_length(td->status) < uhci_expected_length(td->info)) ||
 		    tmp == head)) {
 			usb_settoggle(urb->dev, uhci_endpoint(td->info),
 				uhci_packetout(td->info),
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usb-ohci.c linux/drivers/usb/usb-ohci.c
--- linux.orig/drivers/usb/usb-ohci.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/usb-ohci.c	Tue Jan  8 18:08:45 2002
@@ -76,7 +76,8 @@
 
 
 #ifdef CONFIG_PMAC_PBOOK
-#include <asm/feature.h>
+#include <asm/machdep.h>
+#include <asm/pmac_feature.h>
 #include <asm/pci-bridge.h>
 #ifndef CONFIG_PM
 #define CONFIG_PM
@@ -2699,12 +2700,12 @@
 	pci_write_config_word (dev, PCI_COMMAND, cmd);
 #ifdef CONFIG_PMAC_PBOOK
 	{
-   	struct device_node	*of_node;
+	   	struct device_node	*of_node;
 
-	/* Disable USB PAD & cell clock */
-	of_node = pci_device_to_OF_node (ohci->ohci_dev);
-	if (of_node && _machine == _MACH_Pmac)
-		feature_set_usb_power (of_node, 0);
+		/* Disable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 0);
 	}
 #endif
 	return 0;
@@ -2729,12 +2730,12 @@
 
 #ifdef CONFIG_PMAC_PBOOK
 	{
-	struct device_node *of_node;
+		struct device_node *of_node;
 
-	/* Re-enable USB PAD & cell clock */
-	of_node = pci_device_to_OF_node (ohci->ohci_dev);
-	if (of_node && _machine == _MACH_Pmac)
-		feature_set_usb_power (of_node, 1);
+		/* Re-enable USB PAD & cell clock */
+		of_node = pci_device_to_OF_node (ohci->ohci_dev);
+		if (of_node)
+			pmac_call_feature(PMAC_FTR_USB_ENABLE, of_node, 0, 1);
 	}
 #endif
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usb-skeleton.c linux/drivers/usb/usb-skeleton.c
--- linux.orig/drivers/usb/usb-skeleton.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/usb-skeleton.c	Wed Jan  9 15:45:41 2002
@@ -138,9 +138,37 @@
 /* lock to protect the minor_table structure */
 static DECLARE_MUTEX (minor_table_mutex);
 
-/* file operations needed when we register this driver */
+/*
+ * File operations needed when we register this driver.
+ * This assumes that this driver NEEDS file operations,
+ * of course, which means that the driver is expected
+ * to have a node in the /dev directory. If the USB
+ * device were for a network interface then the driver
+ * would use "struct net_driver" instead, and a serial
+ * device would use "struct tty_driver". 
+ */
 static struct file_operations skel_fops = {
+	/*
+	 * The owner field is part of the module-locking
+	 * mechanism. The idea is that the kernel knows
+	 * which module to increment the use-counter of
+	 * BEFORE it calls the device's open() function.
+	 * This also means that the kernel can decrement
+	 * the use-counter again before calling release()
+	 * or should the open() function fail.
+	 *
+	 * Not all device structures have an "owner" field
+	 * yet. "struct file_operations" and "struct net_device"
+	 * do, while "struct tty_driver" does not. If the struct
+	 * has an "owner" field, then initialize it to the value
+	 * THIS_MODULE and the kernel will handle all module
+	 * locking for you automatically. Otherwise, you must
+	 * increment the use-counter in the open() function
+	 * and decrement it again in the release() function
+	 * yourself.
+	 */
 	owner:		THIS_MODULE,
+
 	read:		skel_read,
 	write:		skel_write,
 	ioctl:		skel_ioctl,
@@ -215,7 +243,11 @@
 		return -ENODEV;
 	}
 
-	/* increment our usage count for the module */
+	/* Increment our usage count for the module.
+	 * This is redundant here, because "struct file_operations"
+	 * has an "owner" field. This line is included here soley as
+	 * a reference for drivers using lesser structures... ;-)
+	 */
 	MOD_INC_USE_COUNT;
 
 	/* lock our minor table and get our local data for this minor */
@@ -278,8 +310,8 @@
 		/* the device was unplugged before the file was released */
 		up (&dev->sem);
 		skel_delete (dev);
-		MOD_DEC_USE_COUNT;
 		up (&minor_table_mutex);
+		MOD_DEC_USE_COUNT;
 		return 0;
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- linux.orig/drivers/usb/usb-uhci.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/usb/usb-uhci.c	Mon Feb  4 19:00:37 2002
@@ -16,7 +16,7 @@
  * (C) Copyright 1999 Randy Dunlap
  * (C) Copyright 1999 Gregory P. Smith
  *
- * $Id: usb-uhci.c,v 1.268 2001/08/29 14:08:43 acher Exp $
+ * $Id: usb-uhci.c,v 1.275 2002/01/19 20:57:33 acher Exp $
  */
 
 #include <linux/config.h>
@@ -34,6 +34,7 @@
 #include <linux/init.h>
 #include <linux/version.h>
 #include <linux/pm.h>
+#include <linux/timer.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -52,7 +53,7 @@
 /* This enables an extra UHCI slab for memory debugging */
 #define DEBUG_SLAB
 
-#define VERSTR "$Revision: 1.268 $ time " __TIME__ " " __DATE__
+#define VERSTR "$Revision: 1.275 $ time " __TIME__ " " __DATE__
 
 #include <linux/usb.h>
 #include "usb-uhci.h"
@@ -61,7 +62,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.268"
+#define DRIVER_VERSION "v1.275"
 #define DRIVER_AUTHOR "Georg Acher, Deti Fliegl, Thomas Sailer, Roman Weissgaerber"
 #define DRIVER_DESC "USB Universal Host Controller Interface driver"
 
@@ -71,6 +72,9 @@
 #define DEBUG_SYMBOLS
 #ifdef DEBUG_SYMBOLS
 	#define _static
+	#ifndef EXPORT_SYMTAB
+		#define EXPORT_SYMTAB
+	#endif
 #else
 	#define _static static
 #endif
@@ -1182,6 +1186,7 @@
 		// cleanup the rest
 		switch (usb_pipetype (urb->pipe)) {
 
+		case PIPE_INTERRUPT:
 		case PIPE_ISOCHRONOUS:
 			uhci_wait_ms(1);
 			uhci_clean_iso_step2(s, urb_priv);
@@ -1779,17 +1784,15 @@
 		type = usb_pipetype (urb->pipe);
 
 		hcpriv = (urb_priv_t*)urb->hcpriv;
-				
-		if ( urb->timeout && 
-			((hcpriv->started + urb->timeout) < jiffies)) {
+		
+		if ( urb->timeout && time_after(jiffies, hcpriv->started + urb->timeout)) {
 			urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK;
 			async_dbg("uhci_check_timeout: timeout for %p",urb);
 			uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
 		}
 #ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH
 		else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) &&  
-		     (hcpriv->use_loop) &&
-		     ((hcpriv->started + IDLE_TIMEOUT) < jiffies))
+			 (hcpriv->use_loop) && time_after(jiffies, hcpriv->started + IDLE_TIMEOUT))
 			disable_desc_loop(s, urb);
 #endif
 
@@ -2445,7 +2448,7 @@
 			break;
 		}
 
-		if (!desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC)) {
+		if (!(desc->hw.td.status & cpu_to_le32(TD_CTRL_IOC))) {
 			// do not process one-shot TDs, no recycling
 			break;
 		}
@@ -2509,6 +2512,8 @@
 			}
 			else {
 				uhci_unlink_urb_async(s, urb, UNLINK_ASYNC_STORE_URB);
+				// correct toggle after unlink
+				usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe));
 				clr_td_ioc(desc); // inactivate TD
 			}
 		}
@@ -2738,7 +2743,7 @@
 
 	if (status != 1) {
 		// Avoid too much error messages at a time
-		if ((jiffies - s->last_error_time > ERROR_SUPPRESSION_TIME)) {
+		if (time_after(jiffies, s->last_error_time + ERROR_SUPPRESSION_TIME)) {
 			warn("interrupt, status %x, frame# %i", status, 
 			     UHCI_GET_CURRENT_FRAME(s));
 			s->last_error_time = jiffies;
@@ -2786,7 +2791,7 @@
 				break;
 		}
 	}
-	if ((jiffies - s->timeout_check) > (HZ/30)) 
+	if (time_after(jiffies, s->timeout_check + (HZ/30)))
 		uhci_check_timeouts(s);
 
 	clean_descs(s, CLEAN_NOT_FORCED);
@@ -2815,7 +2820,7 @@
 _static void start_hc (uhci_t *s)
 {
 	unsigned int io_addr = s->io_addr;
-	int timeout = 1000;
+	int timeout = 10;
 
 	/*
 	 * Reset the HC - this will force us to get a
@@ -2830,6 +2835,7 @@
 			err("USBCMD_HCRESET timed out!");
 			break;
 		}
+		udelay(1);
 	}
 
 	/* Turn on all interrupts */
@@ -2845,7 +2851,8 @@
 	s->running = 1;
 }
 
-_static void __devexit
+/* No  __devexit, since it maybe called from alloc_uhci() */
+_static void
 uhci_pci_remove (struct pci_dev *dev)
 {
 	uhci_t *s = pci_get_drvdata(dev);
@@ -3070,7 +3077,7 @@
 	id_table:	&uhci_pci_ids [0],
 
 	probe:		uhci_pci_probe,
-	remove:		__devexit_p(uhci_pci_remove),
+	remove:		uhci_pci_remove,
 
 #ifdef	CONFIG_PM
 	suspend:	uhci_pci_suspend,
@@ -3130,4 +3137,3 @@
 MODULE_AUTHOR( DRIVER_AUTHOR );
 MODULE_DESCRIPTION( DRIVER_DESC );
 MODULE_LICENSE("GPL");
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux.orig/drivers/usb/usb.c	Wed Nov 21 17:59:11 2001
+++ linux/drivers/usb/usb.c	Wed Jan 16 20:13:57 2002
@@ -1952,6 +1952,14 @@
 		return -EINVAL;
 	}
 
+	/* 9.4.10 says devices don't need this, if the interface
+	   only has one alternate setting */
+	if (iface->num_altsetting == 1) {
+		warn("ignoring set_interface for dev %d, iface %d, alt %d",
+			dev->devnum, interface, alternate);
+		return 0;
+	}
+
 	if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 	    USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate,
 	    interface, NULL, 0, HZ * 5)) < 0)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/usbnet.c linux/drivers/usb/usbnet.c
--- linux.orig/drivers/usb/usbnet.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/usb/usbnet.c	Wed Jan  9 15:46:31 2002
@@ -1571,13 +1571,13 @@
 	struct urb		*urb = 0;
 	struct skb_data		*entry;
 	struct driver_info	*info = dev->driver_info;
-	int			flags;
+	unsigned long		flags;
 #ifdef	CONFIG_USB_NET1080
 	struct nc_header	*header = 0;
 	struct nc_trailer	*trailer = 0;
 #endif	/* CONFIG_USB_NET1080 */
 
-	flags = in_interrupt () ? GFP_ATOMIC : GFP_KERNEL;
+	flags = in_interrupt () ? GFP_ATOMIC : GFP_NOIO; /* might be used for nfs */
 
 	// some devices want funky USB-level framing, for
 	// win32 driver (usually) and/or hardware quirks
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicam.c linux/drivers/usb/vicam.c
--- linux.orig/drivers/usb/vicam.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicam.c	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,986 @@
+/* -*- linux-c -*-
+ * USB ViCAM driver
+ *
+ * Copyright (c) 2001 Christopher L Cheney (ccheney@cheney.cx)
+ * Copyright (c) 2001 Pavel Machek (pavel@suse.cz) sponsored by SuSE
+ *
+ *      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 driver is for the Vista Imaging ViCAM and 3Com HomeConnect USB
+ *
+ * Thanks to Greg Kroah-Hartman for the USB Skeleton driver
+ *
+ * TODO:
+ *	- find out the ids for the Vista Imaging ViCAM
+ *
+ * History:
+ *
+ * 2001_07_07 - 0.1 - christopher: first version
+ * 2001_08_28 - 0.2 - pavel: messed it up, but for some fun, try 
+ 			while true; do dd if=/dev/video of=/dev/fb0 bs=$[0x1e480] count=1 2> /dev/null; done
+		      yep, moving pictures.
+ * 2001_08_29 - 0.3 - pavel: played a little bit more. Experimental mmap support. For some fun,
+ 			get gqcam-0.9, compile it and run. Better than dd ;-).
+ * 2001_08_29 - 0.4 - pavel: added shutter speed control (not much functional)
+ 			kill update_params if it does not seem to work for you.
+ * 2001_08_30 - 0.5 - pavel: fixed stupid bug with update_params & vicam_bulk
+
+ *
+ * FIXME: It crashes on rmmod with camera plugged.
+ */
+#define DEBUG 1
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/usb.h>
+
+#include <asm/io.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+
+#include <linux/videodev.h>
+
+#include "vicam.h"
+#include "vicamurbs.h"
+
+/* Version Information */
+#define DRIVER_VERSION "v0"
+#define DRIVER_AUTHOR "Christopher L Cheney <ccheney@cheney.cx>, Pavel Machek <pavel@suse.cz>"
+#define DRIVER_DESC "USB ViCAM Driver"
+
+/* Define these values to match your device */
+#define USB_VICAM_VENDOR_ID	0x04C1
+#define USB_VICAM_PRODUCT_ID	0x009D
+
+/* table of devices that work with this driver */
+static struct usb_device_id vicam_table [] = {
+	{ USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID) },
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, vicam_table);
+
+static int video_nr = -1; 		/* next avail video device */
+static struct usb_driver vicam_driver;
+
+static char *buf, *buf2;
+static int change_pending = 0; 
+
+static int vicam_parameters(struct usb_vicam *vicam);
+
+/******************************************************************************
+ *
+ *  Memory management functions
+ *
+ *  Taken from bttv-drivers.c 2.4.7-pre3
+ *
+ ******************************************************************************/
+
+/* [DaveM] I've recoded most of this so that:
+ * 1) It's easier to tell what is happening
+ * 2) It's more portable, especially for translating things
+ *    out of vmalloc mapped areas in the kernel.
+ * 3) Less unnecessary translations happen.
+ *
+ * The code used to assume that the kernel vmalloc mappings
+ * existed in the page tables of every process, this is simply
+ * not guarenteed.  We now use pgd_offset_k which is the
+ * defined way to get at the kernel page tables.
+ */
+
+/* Given PGD from the address space's page table, return the kernel
+ * virtual mapping of the physical memory mapped at ADR.
+ */
+static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
+{
+	unsigned long ret = 0UL;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	if (!pgd_none(*pgd)) {
+		pmd = pmd_offset(pgd, adr);
+		if (!pmd_none(*pmd)) {
+			ptep = pte_offset(pmd, adr);
+			pte = *ptep;
+			if(pte_present(pte)) {
+				ret  = (unsigned long) page_address(pte_page(pte));
+				ret |= (adr & (PAGE_SIZE - 1));
+
+			}
+		}
+	}
+	return ret;
+}
+
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+	unsigned long kva, ret;
+
+	kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
+	ret = virt_to_bus((void *)kva);
+	return ret;
+}
+
+static inline unsigned long kvirt_to_bus(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = virt_to_bus((void *)kva);
+	return ret;
+}
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+	unsigned long va, kva, ret;
+
+	va = VMALLOC_VMADDR(adr);
+	kva = uvirt_to_kva(pgd_offset_k(va), va);
+	ret = __pa(kva);
+	return ret;
+}
+
+static void * rvmalloc(signed long size)
+{
+	void * mem;
+	unsigned long adr, page;
+
+	mem=vmalloc_32(size);
+	if (mem)
+	{
+		memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+		adr=(unsigned long) mem;
+		while (size > 0)
+		{
+			page = kvirt_to_pa(adr);
+			mem_map_reserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+	}
+	return mem;
+}
+
+static void rvfree(void * mem, signed long size)
+{
+	unsigned long adr, page;
+
+	if (mem)
+	{
+		adr=(unsigned long) mem;
+		while (size > 0)
+		{
+			page = kvirt_to_pa(adr);
+			mem_map_unreserve(virt_to_page(__va(page)));
+			adr+=PAGE_SIZE;
+			size-=PAGE_SIZE;
+		}
+		vfree(mem);
+	}
+}
+
+/******************************************************************************
+ *
+ *  Foo Bar
+ *
+ ******************************************************************************/
+
+/**
+ *	usb_vicam_debug_data
+ */
+static inline void usb_vicam_debug_data (const char *function, int size, const unsigned char *data)
+{
+	int i;
+
+	if (!debug)
+		return;
+
+	printk (KERN_DEBUG __FILE__": %s - length = %d, data = ",
+		function, size);
+	for (i = 0; i < size; ++i) {
+		printk ("%.2x ", data[i]);
+	}
+	printk ("\n");
+}
+
+/*****************************************************************************
+ *
+ *  Send command to vicam
+ *
+ *****************************************************************************/
+
+static int vicam_sndctrl(int set, struct usb_vicam *vicam, unsigned short req,
+	unsigned short value, unsigned char *cp, int size)
+{
+	int ret;
+	unsigned char *transfer_buffer = kmalloc (size, GFP_KERNEL);
+
+	/* Needs to return data I think, works for sending though */
+	memcpy(transfer_buffer, cp, size);
+	
+	ret = usb_control_msg ( vicam->udev, set ? usb_sndctrlpipe(vicam->udev, 0) : usb_rcvctrlpipe(vicam->udev, 0), req, (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, value, 0, transfer_buffer, size, HZ);
+
+	kfree(transfer_buffer);
+	if (ret)
+		printk("vicam: error: %d\n", ret);
+	mdelay(100);
+	return ret;
+}
+
+
+/*****************************************************************************
+ *
+ *  Video4Linux Helpers
+ * 
+ *****************************************************************************/
+
+static int vicam_get_capability(struct usb_vicam *vicam, struct video_capability *b)
+{
+	dbg("vicam_get_capability");
+
+	strcpy(b->name, vicam->camera_name);
+	b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME;
+	b->channels = 1;
+	b->audios = 0;
+
+	b->maxwidth = vicam->width[vicam->sizes-1];
+	b->maxheight = vicam->height[vicam->sizes-1];
+	b->minwidth = vicam->width[0];
+	b->minheight = vicam->height[0];
+
+	return 0;
+}
+		
+static int vicam_get_channel(struct usb_vicam *vicam, struct video_channel *v)
+{
+	dbg("vicam_get_channel");
+
+	if (v->channel != 0)
+		return -EINVAL;
+ 
+	v->flags = 0;
+	v->tuners = 0;
+	v->type = VIDEO_TYPE_CAMERA;
+	strcpy(v->name, "Camera");
+
+	return 0;
+} 
+		
+static int vicam_set_channel(struct usb_vicam *vicam, struct video_channel *v)
+{
+	dbg("vicam_set_channel");
+
+	if (v->channel != 0)
+		return -EINVAL;
+	
+	return 0;
+}
+		
+static int vicam_get_mmapbuffer(struct usb_vicam *vicam, struct video_mbuf *vm)
+{
+	int i;
+
+	dbg("vicam_get_mmapbuffer");
+
+	memset(vm, 0, sizeof(vm));
+	vm->size = VICAM_NUMFRAMES * vicam->maxframesize;
+	vm->frames = VICAM_NUMFRAMES;
+
+	for (i=0; i<VICAM_NUMFRAMES; i++)
+		vm->offsets[i] = vicam->maxframesize * i;
+
+	return 0;
+}
+
+static int vicam_get_picture(struct usb_vicam *vicam, struct video_picture *p)
+{
+	dbg("vicam_get_picture");
+
+	/* This is probably where that weird 0x56 call goes */
+	p->brightness = vicam->win.brightness;
+	p->hue = vicam->win.hue;
+	p->colour = vicam->win.colour;
+	p->contrast = vicam->win.contrast;
+	p->whiteness = vicam->win.whiteness;
+	p->depth = vicam->win.depth;
+	p->palette = vicam->win.palette;
+
+	return 0;
+}
+
+static void synchronize(struct usb_vicam *vicam)
+{
+	change_pending = 1;
+	interruptible_sleep_on(&vicam->wait);
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0);
+	mdelay(10);
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+	mdelay(10);
+}
+
+static void params_changed(struct usb_vicam *vicam)
+{
+#if 1
+	synchronize(vicam);
+	mdelay(10);
+	vicam_parameters(vicam);
+	printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb));
+#endif
+}
+
+static int vicam_set_picture(struct usb_vicam *vicam, struct video_picture *p)
+{
+	int changed = 0;
+	info("vicam_set_picture (%d)", p->brightness);
+
+
+#define SET(x) \
+	if (vicam->win.x != p->x) \
+		vicam->win.x = p->x, changed = 1;
+	SET(brightness);
+	SET(hue);
+	SET(colour);
+	SET(contrast);
+	SET(whiteness);
+	SET(depth);
+	SET(palette);
+	if (changed)
+		params_changed(vicam);
+
+	return 0;
+	/* Investigate what should be done maybe 0x56 type call */
+	if (p->depth != 8) return 1;
+	if (p->palette != VIDEO_PALETTE_GREY) return 1;
+
+	return 0;
+}
+
+/* FIXME - vicam_sync_frame - important */
+static int vicam_sync_frame(struct usb_vicam *vicam, int frame)
+{
+	dbg("vicam_sync_frame");
+
+	if(frame <0 || frame >= VICAM_NUMFRAMES)
+		return -EINVAL;
+
+	/* Probably need to handle various cases */
+/*	ret=vicam_newframe(vicam, frame);
+	vicam->frame[frame].grabstate=FRAME_UNUSED;
+*/
+	return 0;
+}
+	
+static int vicam_get_window(struct usb_vicam *vicam, struct video_window *vw)
+{
+	dbg("vicam_get_window");
+
+	vw->x = 0;
+	vw->y = 0;
+	vw->chromakey = 0;
+	vw->flags = 0;
+	vw->clipcount = 0;
+	vw->width = vicam->win.width;
+	vw->height = vicam->win.height;
+
+	return 0;
+}
+
+static int vicam_set_window(struct usb_vicam *vicam, struct video_window *vw)
+{
+	info("vicam_set_window");
+		
+	if (vw->flags)
+		return -EINVAL;
+	if (vw->clipcount)
+		return -EINVAL;
+
+	if (vicam->win.width == vw->width && vicam->win.height == vw->height)
+		return 0;
+
+	/* Pick largest mode that is smaller than specified res */
+	/* If specified res is too small reject                 */
+
+	/* Add urb send to device... */
+
+	vicam->win.width = vw->width;
+	vicam->win.height = vw->height;
+	params_changed(vicam);
+
+	return 0;
+}
+
+/* FIXME - vicam_mmap_capture - important */
+static int vicam_mmap_capture(struct usb_vicam *vicam, struct video_mmap *vm)
+{
+	dbg("vicam_mmap_capture");
+
+	/* usbvideo.c looks good for using here */
+
+	/* 
+	if (vm->frame >= VICAM_NUMFRAMES)
+		return -EINVAL;
+	if (vicam->frame[vm->frame].grabstate != FRAME_UNUSED)
+		return -EBUSY;
+	vicam->frame[vm->frame].grabstate=FRAME_READY;
+	*/
+
+	/* No need to vicam_set_window here according to Alan */
+
+	/*
+	if (!vicam->streaming)
+		vicam_start_stream(vicam);
+	*/
+
+	/* set frame as ready */
+
+	return 0;
+}
+
+/*****************************************************************************
+ *
+ *  Video4Linux
+ * 
+ *****************************************************************************/
+
+static int vicam_v4l_open(struct video_device *vdev, int flags)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+	int err = 0;
+	
+	dbg("vicam_v4l_open");
+
+	MOD_INC_USE_COUNT; 
+	down(&vicam->sem);
+
+	if (vicam->open_count)		/* Maybe not needed? */
+		err = -EBUSY;
+	else {
+		vicam->fbuf = rvmalloc(vicam->maxframesize * VICAM_NUMFRAMES);
+		if (!vicam->fbuf)
+			err=-ENOMEM;
+		else {
+			vicam->open_count = 1;
+		}
+#ifdef BLINKING
+		vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+		info ("led on");
+		vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+#endif
+	}
+
+	up(&vicam->sem);
+	if (err)
+		MOD_DEC_USE_COUNT;
+	return err;
+}
+
+static void vicam_v4l_close(struct video_device *vdev)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+
+	dbg("vicam_v4l_close");
+	
+	down(&vicam->sem);
+
+#ifdef BLINKING
+	info ("led off");
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+//	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0); Leave it on
+#endif
+
+	rvfree(vicam->fbuf, vicam->maxframesize * VICAM_NUMFRAMES);
+	vicam->fbuf = 0;
+	vicam->open_count=0;
+
+	up(&vicam->sem);
+	/* Why does se401.c have a usbdevice check here? */
+	/* If device is unplugged while open, I guess we only may unregister now */
+	MOD_DEC_USE_COUNT;
+}
+
+static long vicam_v4l_read(struct video_device *vdev, char *user_buf, unsigned long buflen, int noblock)
+{
+	//struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+
+	dbg("vicam_v4l_read(%ld)", buflen);
+
+	if (!vdev || !buf)
+		return -EFAULT;
+
+	if (copy_to_user(user_buf, buf2, buflen))
+		return -EFAULT;
+	return buflen;
+}
+
+static long vicam_v4l_write(struct video_device *dev, const char *buf, unsigned long count, int noblock)
+{
+	info("vicam_v4l_write");
+	return -EINVAL;
+}
+
+static int vicam_v4l_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)vdev;
+	int ret = -EL3RST;
+
+	if (!vicam->udev)
+		return -EIO;
+
+	down(&vicam->sem);
+
+	switch (cmd) {
+	case VIDIOCGCAP:
+	{
+		struct video_capability b;
+		ret = vicam_get_capability(vicam,&b);
+		dbg("name %s",b.name);
+		if (copy_to_user(arg, &b, sizeof(b)))
+			ret = -EFAULT;
+	}
+	case VIDIOCGFBUF:
+	{
+		struct video_buffer vb;
+		info("vicam_v4l_ioctl - VIDIOCGBUF - query frame buffer param");
+		/* frame buffer not supported, not used */
+		memset(&vb, 0, sizeof(vb));
+		vb.base = NULL;
+		
+		/* FIXME - VIDIOCGFBUF - why the void */
+		if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb)))
+			ret = -EFAULT;
+		ret = 0;
+	}
+	case VIDIOCGWIN:
+	{
+		struct video_window vw;
+		ret = vicam_get_window(vicam, &vw);
+		if (copy_to_user(arg, &vw, sizeof(vw)))
+			ret = -EFAULT;
+	}
+	case VIDIOCSWIN:
+	{
+		struct video_window vw;
+		if (copy_from_user(&vw, arg, sizeof(vw)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_window(vicam, &vw);
+		return ret;
+	}
+	case VIDIOCGCHAN:
+	{
+		struct video_channel v;
+
+		if (copy_from_user(&v, arg, sizeof(v)))
+			ret = -EFAULT;
+		else {
+			ret = vicam_get_channel(vicam,&v);
+			if (copy_to_user(arg, &v, sizeof(v)))
+				ret = -EFAULT;
+		}
+	}
+	case VIDIOCSCHAN:
+	{
+		struct video_channel v;
+		if (copy_from_user(&v, arg, sizeof(v)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_channel(vicam,&v);
+ 	}
+	case VIDIOCGPICT:
+	{
+		struct video_picture p;
+		ret = vicam_get_picture(vicam, &p);
+		if (copy_to_user(arg, &p, sizeof(p)))
+			ret = -EFAULT;
+	}
+	case VIDIOCSPICT:
+	{
+		struct video_picture p;
+		if (copy_from_user(&p, arg, sizeof(p)))
+			ret = -EFAULT;
+		else
+			ret = vicam_set_picture(vicam, &p);
+	}
+	case VIDIOCGMBUF:
+	{
+		struct video_mbuf vm;
+		ret = vicam_get_mmapbuffer(vicam,&vm);
+		/* FIXME - VIDIOCGMBUF - why the void */
+		if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+			ret = -EFAULT;
+	}
+	case VIDIOCMCAPTURE:
+	{
+		struct video_mmap vm;
+		ret = vicam_mmap_capture(vicam, &vm);
+		/* FIXME: This is probably not right */
+	}
+	case VIDIOCSYNC:
+	{
+		int frame;
+		/* FIXME - VIDIOCSYNC - why the void */
+		if (copy_from_user((void *)&frame, arg, sizeof(int)))
+			ret = -EFAULT;
+		else
+			ret = vicam_sync_frame(vicam,frame);
+	}
+
+	case VIDIOCKEY:
+		ret = 0;
+ 
+	case VIDIOCCAPTURE:
+	case VIDIOCSFBUF:
+	case VIDIOCGTUNER:
+	case VIDIOCSTUNER:
+	case VIDIOCGFREQ:
+	case VIDIOCSFREQ:
+	case VIDIOCGAUDIO:
+	case VIDIOCSAUDIO:
+	case VIDIOCGUNIT:
+		ret = -EINVAL;
+
+	default:
+	{
+		info("vicam_v4l_ioctl - %ui",cmd);
+		ret = -ENOIOCTLCMD;
+	}
+	} /* end switch */
+
+	up(&vicam->sem);
+        return ret;
+}
+
+static int vicam_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+	struct usb_vicam *vicam = (struct usb_vicam *)dev;
+	unsigned long start = (unsigned long)adr;
+	unsigned long page, pos;
+
+	down(&vicam->sem);
+	
+	if (vicam->udev == NULL) {
+		up(&vicam->sem);
+		return -EIO;
+	}
+#if 0
+	if (size > (((VICAM_NUMFRAMES * vicam->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
+		up(&vicam->sem);
+		return -EINVAL;
+	}
+#endif
+	pos = (unsigned long)vicam->fbuf;
+	while (size > 0) {
+		page = kvirt_to_pa(pos);
+		if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) {
+			up(&vicam->sem);
+			return -EAGAIN;
+		}
+		start += PAGE_SIZE;
+		pos += PAGE_SIZE;
+		if (size > PAGE_SIZE)
+			size -= PAGE_SIZE;
+		else
+			size = 0;
+	}
+	up(&vicam->sem);
+
+        return 0;
+}
+
+/* FIXME - vicam_v4l_init */
+static int vicam_v4l_init(struct video_device *dev)
+{
+	/* stick proc fs stuff in here if wanted */
+	dbg("vicam_v4l_init");
+	return 0;
+}
+
+/* FIXME - vicam_template - important */
+static struct video_device vicam_template = {
+	name:		"vicam USB camera",
+	type:		VID_TYPE_CAPTURE,
+	hardware:	VID_HARDWARE_SE401, /* need to ask for own id */
+	open:		vicam_v4l_open,
+	close:		vicam_v4l_close,
+	read:		vicam_v4l_read,
+	write:		vicam_v4l_write,
+	ioctl:		vicam_v4l_ioctl,
+	mmap:		vicam_v4l_mmap,
+	initialize:	vicam_v4l_init,
+};
+
+/******************************************************************************
+ *
+ *  Some Routines
+ *
+ ******************************************************************************/
+
+/*
+Flash the led
+vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+info ("led on");
+vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+info ("led off");
+vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x00, NULL, 0);
+vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x00, NULL, 0);
+*/
+
+static void vicam_bulk(struct urb *urb)
+{
+	struct usb_vicam *vicam = urb->context;
+
+	/*	if (!vicam || !vicam->dev || !vicam->used)
+		return;
+	*/
+
+	if (urb->status)
+		printk("vicam%d: nonzero read/write bulk status received: %d",
+			0, urb->status);
+
+	urb->actual_length = 0;
+	urb->dev = vicam->udev;
+
+	memcpy(buf2, buf+64, 0x1e480);
+	if (vicam->fbuf)
+		memcpy(vicam->fbuf, buf+64, 0x1e480);
+
+	if (!change_pending) {
+		if (usb_submit_urb(urb))
+			dbg("failed resubmitting read urb");
+	} else {
+		change_pending = 0;
+		wake_up_interruptible(&vicam->wait);
+	}
+}
+
+static int vicam_parameters(struct usb_vicam *vicam)
+{
+	unsigned char req[0x10];
+	unsigned int shutter;
+	shutter = 10;
+
+	switch (vicam->win.width) {
+	case 512:
+	default:
+		memcpy(req, s512x242bw, 0x10);
+		break;
+	case 256:
+		memcpy(req, s256x242bw, 0x10);
+		break;
+	case 128:
+		memcpy(req, s128x122bw, 0x10);
+		break;
+	}
+
+
+	mdelay(10);
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAMERA_POWER, 0x01, NULL, 0);
+	info ("led on");
+	vicam_sndctrl(1, vicam, VICAM_REQ_LED_CONTROL, 0x01, NULL, 0);
+
+	mdelay(10);
+
+	shutter = vicam->win.contrast / 256;
+	if (shutter == 0)
+		shutter = 1;
+	printk("vicam_parameters: brightness %d, shutter %d\n", vicam->win.brightness, shutter );
+	req[0] = vicam->win.brightness /256;
+	shutter = 15600/shutter - 1;
+	req[6] = shutter & 0xff;
+	req[7] = (shutter >> 8) & 0xff;
+	vicam_sndctrl(1, vicam, VICAM_REQ_CAPTURE, 0x80, req, 0x10);
+	mdelay(10);
+	vicam_sndctrl(0, vicam, VICAM_REQ_GET_SOMETHIN, 0, buf, 0x10);
+	mdelay(10);
+
+	return 0;
+}
+
+static int vicam_init(struct usb_vicam *vicam)
+{
+	int width[] = {128, 256, 512};
+	int height[] = {122, 242, 242};
+
+	dbg("vicam_init");
+	buf = kmalloc(0x1e480, GFP_KERNEL);
+	buf2 = kmalloc(0x1e480, GFP_KERNEL);
+	if ((!buf) || (!buf2)) {
+		printk("Not enough memory for vicam!\n");
+		goto error;
+	}
+
+	/* do we do aspect correction in kernel or not? */
+	vicam->sizes = 3;
+	vicam->width = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL);
+	vicam->height = kmalloc(vicam->sizes*sizeof(int), GFP_KERNEL);
+	memcpy(vicam->width, &width, sizeof(width));
+	memcpy(vicam->height, &height, sizeof(height));
+	vicam->maxframesize = vicam->width[vicam->sizes-1] * vicam->height[vicam->sizes-1];
+
+	/* Download firmware to camera */
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware1, sizeof(firmware1));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex1, sizeof(findex1));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, firmware2, sizeof(firmware2));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, findex2, sizeof(findex2));
+	vicam_sndctrl(1, vicam, VICAM_REQ_VENDOR, 0, fsetup, sizeof(fsetup));
+
+	vicam_parameters(vicam);
+
+	FILL_BULK_URB(&vicam->readurb, vicam->udev, usb_rcvbulkpipe(vicam->udev, 0x81),
+		      buf, 0x1e480, vicam_bulk, vicam);
+	printk("Submiting urb: %d\n", usb_submit_urb(&vicam->readurb));
+
+	return 0;
+error:
+	if (buf)
+		kfree(buf);
+	if (buf2)
+		kfree(buf2);
+	return 1;
+}
+
+static void * __devinit vicam_probe(struct usb_device *udev, unsigned int ifnum,
+	const struct usb_device_id *id)
+{
+	struct usb_vicam *vicam;
+	char *camera_name=NULL;
+
+	dbg("vicam_probe");
+
+	/* See if the device offered us matches what we can accept */
+	if ((udev->descriptor.idVendor != USB_VICAM_VENDOR_ID) ||
+	    (udev->descriptor.idProduct != USB_VICAM_PRODUCT_ID)) {
+		return NULL;
+	}
+	
+	camera_name="3Com HomeConnect USB";
+	info("ViCAM camera found: %s", camera_name);
+	
+	vicam = kmalloc (sizeof(struct usb_vicam), GFP_KERNEL);
+	if (vicam == NULL) {
+		err ("couldn't kmalloc vicam struct");
+		return NULL;
+	}
+	memset(vicam, 0, sizeof(*vicam));
+	
+	vicam->udev = udev;
+	vicam->camera_name = camera_name;
+	vicam->win.brightness = 128;
+	vicam->win.contrast = 10;
+
+	/* FIXME */
+	if (vicam_init(vicam))
+		return NULL;
+	memcpy(&vicam->vdev, &vicam_template, sizeof(vicam_template));
+	memcpy(vicam->vdev.name, vicam->camera_name, strlen(vicam->camera_name));
+	
+	if (video_register_device(&vicam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
+		err("video_register_device");
+		return NULL;
+	}
+
+	info("registered new video device: video%d", vicam->vdev.minor);
+	
+	init_MUTEX (&vicam->sem);
+	init_waitqueue_head(&vicam->wait);
+	
+	return vicam;
+}
+
+
+/* FIXME - vicam_disconnect - important */
+static void vicam_disconnect(struct usb_device *udev, void *ptr)
+{
+	struct usb_vicam *vicam;
+
+	vicam = (struct usb_vicam *) ptr;
+
+	if (!vicam->open_count)
+		video_unregister_device(&vicam->vdev);
+	vicam->udev = NULL;
+/*
+	vicam->frame[0].grabstate = FRAME_ERROR;
+	vicam->frame[1].grabstate = FRAME_ERROR;
+*/
+
+	/* Free buffers and shit */
+
+	info("%s disconnected", vicam->camera_name);
+	synchronize(vicam);
+
+	if (!vicam->open_count) {
+		/* Other random junk */
+		kfree(vicam);
+		vicam = NULL;
+	}
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver vicam_driver = {
+	name:		"vicam",
+	probe:		vicam_probe,
+	disconnect:	vicam_disconnect,
+	id_table:	vicam_table,
+};
+
+/******************************************************************************
+ *
+ *  Module Routines
+ *
+ ******************************************************************************/
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+static int __init usb_vicam_init(void)
+{
+	int result;
+
+	printk("VICAM: initializing\n");
+	/* register this driver with the USB subsystem */
+	result = usb_register(&vicam_driver);
+	if (result < 0) {
+		err("usb_register failed for the "__FILE__" driver. Error number %d",
+		    result);
+		return -1;
+	}
+
+	info(DRIVER_VERSION " " DRIVER_AUTHOR);
+	info(DRIVER_DESC);
+	return 0;
+}
+
+static void __exit usb_vicam_exit(void)
+{
+	/* deregister this driver with the USB subsystem */
+	usb_deregister(&vicam_driver);
+}
+
+module_init(usb_vicam_init);
+module_exit(usb_vicam_exit);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicam.h linux/drivers/usb/vicam.h
--- linux.orig/drivers/usb/vicam.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicam.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,81 @@
+/*
+ *
+ * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver
+ * Christopher L Cheney (C) 2001
+ * 
+ */
+
+#ifndef __LINUX_VICAM_H
+#define __LINUX_VICAM_H
+
+
+#ifdef CONFIG_USB_DEBUG
+	static int debug = 1;
+#else
+	static int debug;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(format, arg...) do { if (debug) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg); } while (0)
+
+#define VICAM_NUMFRAMES 30
+#define VICAM_NUMSBUF 1
+
+/* USB REQUEST NUMBERS */
+#define VICAM_REQ_VENDOR	0xff
+#define VICAM_REQ_CAMERA_POWER	0x50
+#define VICAM_REQ_CAPTURE	0x51
+#define VICAM_REQ_LED_CONTROL	0x55
+#define VICAM_REQ_GET_SOMETHIN	0x56
+ 
+/* not required but lets you know camera is on */
+/* camera must be on to turn on led */
+/* 0x01 always on  0x03 on when picture taken (flashes) */
+
+struct picture_parm
+{
+	int width;
+	int height;
+	int brightness;
+	int hue;
+	int colour;
+	int contrast;
+	int whiteness;
+	int depth;
+	int palette;
+};
+
+struct vicam_scratch {
+        unsigned char *data;
+        volatile int state;
+        int offset;
+        int length;
+};
+
+/* Structure to hold all of our device specific stuff */
+struct usb_vicam
+{
+	struct video_device vdev;
+	struct usb_device *udev;
+
+	int open_count;	/* number of times this port has been opened */
+	struct semaphore sem;			/* locks this structure */
+	wait_queue_head_t wait;			/* Processes waiting */ 
+
+	int streaming;
+
+	/* v4l stuff */
+	char *camera_name;
+	char *fbuf;
+	urb_t *urb[VICAM_NUMSBUF];
+	int sizes;
+	int *width;
+	int *height;
+	int maxframesize;
+	struct picture_parm win;
+	struct proc_dir_entry *proc_entry;      /* /proc/se401/videoX */
+	struct urb readurb;
+};
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/usb/vicamurbs.h linux/drivers/usb/vicamurbs.h
--- linux.orig/drivers/usb/vicamurbs.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/usb/vicamurbs.h	Wed Dec 26 15:09:47 2001
@@ -0,0 +1,330 @@
+/*
+ *
+ * Vista Imaging ViCAM / 3Com HomeConnect Usermode Driver
+ * Christopher L Cheney (C) 2001
+ * 
+ */
+
+
+#ifndef __LINUX_VICAMURBS_H
+#define __LINUX_VICAMURBS_H
+
+/* -------------------------------------------------------------------------- */
+
+/* FIXME - Figure out transfers so that this doesn't need to be here
+ *
+ * Notice: in pieces below, "0" means other code will fill it while "0x00" means this is zero */
+
+/* Request 0x51 Image Setup */
+
+/* 128x98 ? 0x3180 size */
+static unsigned char s128x98bw[] = {
+  0,    0x34, 0xC4, 0x00, 0x00, 0x00, 0,    0,
+  0x18, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 128x122 3D80 size */
+static unsigned char s128x122bw[] = {
+  0,    0x34, 0xF4, 0x00, 0x00, 0x00, 0,    0,
+  0x00, 0x02, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 256x242 ? 0xF280 size */
+static unsigned char s256x242bw[] = {
+  0,    0x03, 0xC8, 0x03, 0x00, 0x00, 0,    0,
+  0x00, 0x04, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* 512x242 0x1E480 size */
+static unsigned char s512x242bw[] = {
+  0,    0x05, 0x90, 0x07, 0x00, 0x00, 0,    0,
+  0x00, 0x08, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00
+};
+
+/* In s512x242:
+   byte 0: gain -- higher number means brighter image
+   byte 6, 7: shutter speed, little-endian; set this to 15600 * (shutter speed) - 1. (Where shutter speed is something like 1/1000).
+*/
+
+/* -------------------------------------------------------------------------- */
+
+static unsigned char fsetup[] = {
+  0xB6, 0xC3, 0x01, 0x00, 0x06, 0x64,
+
+  0x00, 0x00
+};
+
+static unsigned char firmware1[] = {
+  0xB6, 0xC3, 0x1F, 0x00, 0x02, 0x64,
+
+  0xE7, 0x67, 0xFD, 0xFF, 0x0E, 0xC0, 0xE7, 0x09,
+  0xDE, 0x00, 0x8E, 0x00, 0xC0, 0x09, 0x40, 0x03,
+  0xC0, 0x17, 0x44, 0x03, 0x4B, 0xAF, 0xC0, 0x07,
+  0x00, 0x00, 0x4B, 0xAF, 0x97, 0xCF, 0x00, 0x00
+};
+
+static unsigned char findex1[] = {
+  0xB6, 0xC3, 0x03, 0x00, 0x03, 0x64,
+
+  0x18, 0x00, 0x00, 0x00
+};
+
+static unsigned char firmware2[] = {
+  0xB6, 0xC3, 0x8F, 0x06, 0x02, 0x64,
+
+  0xE7, 0x07, 0x00, 0x00, 0x08, 0xC0, 0xE7, 0x07,
+  0x00, 0x00, 0x3E, 0xC0, 0xE7, 0x07, 0x54, 0x01,
+  0xAA, 0x00, 0xE7, 0x07, 0xC8, 0x05, 0xB6, 0x00,
+  0xE7, 0x07, 0x42, 0x01, 0xD2, 0x00, 0xE7, 0x07,
+  0x7C, 0x00, 0x16, 0x00, 0xE7, 0x07, 0x56, 0x00,
+  0x18, 0x00, 0xE7, 0x07, 0x06, 0x00, 0x92, 0xC0,
+  0xE7, 0x07, 0x00, 0x00, 0x1E, 0xC0, 0xE7, 0x07,
+  0xFF, 0xFF, 0x22, 0xC0, 0xE7, 0x07, 0x04, 0x00,
+  0x24, 0xC0, 0xE7, 0x07, 0xEC, 0x27, 0x28, 0xC0,
+  0xE7, 0x07, 0x16, 0x01, 0x8E, 0x00, 0xE7, 0x87,
+  0x01, 0x00, 0x0E, 0xC0, 0x97, 0xCF, 0xD7, 0x09,
+  0x00, 0xC0, 0xE7, 0x77, 0x01, 0x00, 0x92, 0xC0,
+  0x09, 0xC1, 0xE7, 0x09, 0xFE, 0x05, 0x24, 0x01,
+  0xE7, 0x09, 0x04, 0x06, 0x26, 0x01, 0xE7, 0x07,
+  0x07, 0x00, 0x92, 0xC0, 0xE7, 0x05, 0x00, 0xC0,
+  0xC0, 0xDF, 0x97, 0xCF, 0x17, 0x00, 0x57, 0x00,
+  0x17, 0x02, 0xD7, 0x09, 0x00, 0xC0, 0xE7, 0x77,
+  0x01, 0x00, 0x92, 0xC0, 0x0A, 0xC1, 0xE7, 0x57,
+  0xFF, 0xFF, 0xFA, 0x05, 0x0D, 0xC0, 0xE7, 0x57,
+  0x00, 0x00, 0xFA, 0x05, 0x0F, 0xC0, 0x9F, 0xAF,
+  0xC6, 0x00, 0xE7, 0x05, 0x00, 0xC0, 0xC8, 0x05,
+  0xC1, 0x05, 0xC0, 0x05, 0xC0, 0xDF, 0x97, 0xCF,
+  0x27, 0xDA, 0xFA, 0x05, 0xEF, 0x07, 0x01, 0x00,
+  0x0B, 0x06, 0x73, 0xCF, 0x9F, 0xAF, 0x78, 0x01,
+  0x9F, 0xAF, 0x1A, 0x03, 0x6E, 0xCF, 0xE7, 0x09,
+  0xFC, 0x05, 0x24, 0x01, 0xE7, 0x09, 0x02, 0x06,
+  0x26, 0x01, 0xE7, 0x07, 0x07, 0x00, 0x92, 0xC0,
+  0xE7, 0x09, 0xFC, 0x05, 0xFE, 0x05, 0xE7, 0x09,
+  0x02, 0x06, 0x04, 0x06, 0xE7, 0x09, 0x00, 0x06,
+  0xFC, 0x05, 0xE7, 0x09, 0xFE, 0x05, 0x00, 0x06,
+  0x27, 0xDA, 0xFA, 0x05, 0xE7, 0x57, 0x01, 0x00,
+  0xFA, 0x05, 0x02, 0xCA, 0x04, 0xC0, 0x97, 0xCF,
+  0x9F, 0xAF, 0x66, 0x05, 0x97, 0xCF, 0xE7, 0x07,
+  0x40, 0x00, 0x02, 0x06, 0xC8, 0x09, 0xFC, 0x05,
+  0x9F, 0xAF, 0xDA, 0x02, 0x97, 0xCF, 0xCF, 0x17,
+  0x02, 0x00, 0xEF, 0x57, 0x81, 0x00, 0x09, 0x06,
+  0x9F, 0xA0, 0xB6, 0x01, 0xEF, 0x57, 0x80, 0x00,
+  0x09, 0x06, 0x9F, 0xA0, 0x40, 0x02, 0xEF, 0x57,
+  0x01, 0x00, 0x0B, 0x06, 0x9F, 0xA0, 0x46, 0x03,
+  0xE7, 0x07, 0x01, 0x00, 0x0A, 0xC0, 0x46, 0xAF,
+  0x47, 0xAF, 0x9F, 0xAF, 0x40, 0x02, 0xE7, 0x07,
+  0x2E, 0x00, 0x0A, 0xC0, 0xEF, 0x87, 0x80, 0x00,
+  0x09, 0x06, 0x97, 0xCF, 0x00, 0x0E, 0x01, 0x00,
+  0xC0, 0x57, 0x51, 0x00, 0x9F, 0xC0, 0x9E, 0x02,
+  0xC0, 0x57, 0x50, 0x00, 0x20, 0xC0, 0xC0, 0x57,
+  0x55, 0x00, 0x12, 0xC0, 0xC0, 0x57, 0x56, 0x00,
+  0x9F, 0xC0, 0x72, 0x02, 0x9F, 0xCF, 0xD6, 0x02,
+  0xC1, 0x0B, 0x08, 0x06, 0x01, 0xD0, 0x6F, 0x90,
+  0x08, 0x06, 0xC0, 0x07, 0x08, 0x00, 0xC1, 0x0B,
+  0x08, 0x06, 0x9F, 0xAF, 0x28, 0x05, 0x97, 0xCF,
+  0x2F, 0x0E, 0x02, 0x00, 0x08, 0x06, 0xC0, 0x07,
+  0x08, 0x00, 0xC1, 0x0B, 0x08, 0x06, 0x9F, 0xAF,
+  0x28, 0x05, 0x9F, 0xCF, 0xD6, 0x02, 0x2F, 0x0E,
+  0x02, 0x00, 0x09, 0x06, 0xEF, 0x87, 0x80, 0x00,
+  0x09, 0x06, 0x9F, 0xCF, 0xD6, 0x02, 0xEF, 0x67,
+  0x7F, 0xFF, 0x09, 0x06, 0xE7, 0x67, 0xFF, 0xFD,
+  0x22, 0xC0, 0xE7, 0x67, 0xEF, 0xFF, 0x24, 0xC0,
+  0xE7, 0x87, 0x10, 0x00, 0x28, 0xC0, 0x9F, 0xAF,
+  0xB8, 0x05, 0xE7, 0x87, 0xE0, 0x21, 0x24, 0xC0,
+  0x9F, 0xAF, 0xA8, 0x05, 0xE7, 0x87, 0x08, 0x00,
+  0x24, 0xC0, 0xE7, 0x67, 0xDF, 0xFF, 0x24, 0xC0,
+  0xC8, 0x07, 0x0A, 0x00, 0xC0, 0x07, 0x00, 0x00,
+  0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0x9F, 0xAF, 0xB8, 0x05, 0xC0, 0x07, 0x9E, 0x00,
+  0x9F, 0xAF, 0x44, 0x05, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xC0, 0x09, 0x20, 0xC0, 0xE7, 0x87,
+  0x00, 0x01, 0x24, 0xC0, 0xC0, 0x77, 0x00, 0x02,
+  0x0F, 0xC1, 0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0,
+  0xE7, 0x67, 0xF7, 0xFF, 0x24, 0xC0, 0xE7, 0x87,
+  0x08, 0x00, 0x24, 0xC0, 0x08, 0xDA, 0x5E, 0xC1,
+  0xEF, 0x07, 0x80, 0x00, 0x09, 0x06, 0x97, 0xCF,
+  0xEF, 0x07, 0x01, 0x00, 0x0A, 0x06, 0x97, 0xCF,
+  0xEF, 0x07, 0x00, 0x00, 0x0B, 0x06, 0xEF, 0x07,
+  0x00, 0x00, 0x0A, 0x06, 0xEF, 0x67, 0x7F, 0xFF,
+  0x09, 0x06, 0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06,
+  0xE7, 0x67, 0xEF, 0xFF, 0x28, 0xC0, 0xE7, 0x67,
+  0x17, 0xD8, 0x24, 0xC0, 0xE7, 0x07, 0x00, 0x00,
+  0x1E, 0xC0, 0xE7, 0x07, 0xFF, 0xFF, 0x22, 0xC0,
+  0x97, 0xCF, 0xC8, 0x07, 0x0E, 0x06, 0x9F, 0xAF,
+  0xDA, 0x02, 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05,
+  0xE7, 0x07, 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07,
+  0x0E, 0x06, 0xF4, 0x05, 0xE7, 0x07, 0xD6, 0x02,
+  0xF8, 0x05, 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07,
+  0x00, 0x80, 0x50, 0xAF, 0x97, 0xCF, 0x2F, 0x0C,
+  0x02, 0x00, 0x07, 0x06, 0x2F, 0x0C, 0x04, 0x00,
+  0x06, 0x06, 0xE7, 0x07, 0x00, 0x00, 0xF2, 0x05,
+  0xE7, 0x07, 0x10, 0x00, 0xF6, 0x05, 0xE7, 0x07,
+  0xE2, 0x05, 0xF4, 0x05, 0xE7, 0x07, 0xCE, 0x02,
+  0xF8, 0x05, 0xC8, 0x07, 0xF2, 0x05, 0xC1, 0x07,
+  0x00, 0x80, 0x51, 0xAF, 0x97, 0xCF, 0x9F, 0xAF,
+  0x66, 0x04, 0x9F, 0xAF, 0x1A, 0x03, 0x59, 0xAF,
+  0x97, 0xCF, 0xC0, 0x07, 0x0E, 0x00, 0xC1, 0x0B,
+  0x0C, 0x06, 0x41, 0xD1, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x3C, 0x00, 0x9F, 0xAF, 0x44, 0x05,
+  0x68, 0x00, 0xC0, 0x07, 0x3B, 0x00, 0x9F, 0xAF,
+  0x44, 0x05, 0x6F, 0x00, 0x0C, 0x06, 0x68, 0x00,
+  0xE0, 0x07, 0x04, 0x01, 0xE8, 0x0B, 0x0A, 0x06,
+  0xE8, 0x07, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x02,
+  0xE0, 0x07, 0xEC, 0x01, 0xE0, 0x07, 0xFC, 0xFF,
+  0x97, 0xCF, 0xE7, 0x07, 0xFF, 0xFF, 0xFA, 0x05,
+  0xEF, 0x07, 0x00, 0x00, 0x0B, 0x06, 0xE7, 0x07,
+  0x0E, 0x06, 0x24, 0x01, 0xE7, 0x07, 0x0E, 0x06,
+  0xFE, 0x05, 0xE7, 0x07, 0x40, 0x00, 0x26, 0x01,
+  0xE7, 0x07, 0x40, 0x00, 0x04, 0x06, 0xE7, 0x07,
+  0x07, 0x00, 0x92, 0xC0, 0x97, 0xCF, 0xEF, 0x07,
+  0x02, 0x00, 0x0B, 0x06, 0x9F, 0xAF, 0x78, 0x01,
+  0xEF, 0x77, 0x80, 0x00, 0x07, 0x06, 0x9F, 0xC0,
+  0x14, 0x04, 0xEF, 0x77, 0x01, 0x00, 0x07, 0x06,
+  0x37, 0xC0, 0xEF, 0x77, 0x01, 0x00, 0x0D, 0x06,
+  0x0F, 0xC1, 0xEF, 0x07, 0x01, 0x00, 0x0D, 0x06,
+  0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07, 0x30, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x01, 0x00,
+  0xC1, 0x07, 0x02, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0xC8, 0x07, 0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05,
+  0xC0, 0x07, 0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05,
+  0xC1, 0x77, 0x03, 0x00, 0x02, 0xC1, 0x08, 0xDA,
+  0x75, 0xC1, 0xC1, 0x77, 0x01, 0x00, 0x0A, 0xC1,
+  0xC0, 0x07, 0x01, 0x00, 0xC1, 0x07, 0x02, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xEF, 0x07, 0x01, 0x00,
+  0x06, 0x06, 0x2C, 0xCF, 0xC0, 0x07, 0x01, 0x00,
+  0xC1, 0x07, 0x04, 0x00, 0x9F, 0xAF, 0x28, 0x05,
+  0xEF, 0x07, 0x00, 0x00, 0x06, 0x06, 0x22, 0xCF,
+  0xEF, 0x07, 0x00, 0x00, 0x0D, 0x06, 0xEF, 0x57,
+  0x01, 0x00, 0x06, 0x06, 0x1B, 0xC0, 0xC0, 0x07,
+  0x01, 0x00, 0xC1, 0x07, 0x01, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x02, 0x00, 0xC1, 0x07,
+  0x30, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC8, 0x07,
+  0xFF, 0x4F, 0x9F, 0xAF, 0xA8, 0x05, 0xC0, 0x07,
+  0x38, 0x00, 0x9F, 0xAF, 0x44, 0x05, 0xC1, 0x67,
+  0x03, 0x00, 0xC1, 0x57, 0x03, 0x00, 0x02, 0xC0,
+  0x08, 0xDA, 0x73, 0xC1, 0xC0, 0x07, 0x02, 0x00,
+  0xC1, 0x07, 0x12, 0x00, 0xEF, 0x57, 0x00, 0x00,
+  0x06, 0x06, 0x02, 0xC0, 0xC1, 0x07, 0x23, 0x00,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x14, 0x00,
+  0xC1, 0x0B, 0xEA, 0x05, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x3E, 0x00, 0x9F, 0xAF, 0x0A, 0x05,
+  0xE7, 0x09, 0xE4, 0x05, 0xFA, 0x05, 0x27, 0xD8,
+  0xFA, 0x05, 0xE7, 0x07, 0x0E, 0x06, 0xFC, 0x05,
+  0xE7, 0x07, 0x4E, 0x06, 0x00, 0x06, 0xE7, 0x07,
+  0x40, 0x00, 0x02, 0x06, 0x9F, 0xAF, 0x66, 0x05,
+  0x9F, 0xAF, 0xC6, 0x00, 0x97, 0xCF, 0xC1, 0x0B,
+  0xE2, 0x05, 0x41, 0xD0, 0x01, 0xD2, 0xC1, 0x17,
+  0x23, 0x00, 0x9F, 0xAF, 0xDC, 0x04, 0xC0, 0x07,
+  0x04, 0x00, 0xC1, 0x0B, 0xE3, 0x05, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x06, 0x00, 0xC1, 0x09,
+  0xE6, 0x05, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x07, 0x00, 0xC1, 0x09, 0xE6, 0x05, 0xC1, 0xD1,
+  0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07, 0x0B, 0x00,
+  0xC1, 0x09, 0xE8, 0x05, 0x9F, 0xAF, 0x28, 0x05,
+  0xC0, 0x07, 0x0C, 0x00, 0xC1, 0x09, 0xE8, 0x05,
+  0xC1, 0xD1, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x0D, 0x00, 0xC1, 0x07, 0x09, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0xC0, 0x07, 0x03, 0x00, 0xC1, 0x07,
+  0x32, 0x00, 0x9F, 0xAF, 0x28, 0x05, 0xC0, 0x07,
+  0x0F, 0x00, 0xC1, 0x07, 0x00, 0x00, 0x9F, 0xAF,
+  0x28, 0x05, 0x97, 0xCF, 0xE7, 0x67, 0xFF, 0xD9,
+  0x24, 0xC0, 0xC8, 0x07, 0x0A, 0x00, 0x40, 0x00,
+  0xC0, 0x67, 0x00, 0x02, 0x27, 0x80, 0x24, 0xC0,
+  0xE7, 0x87, 0x00, 0x04, 0x24, 0xC0, 0xE7, 0x67,
+  0xFF, 0xF9, 0x24, 0xC0, 0x01, 0xD2, 0x08, 0xDA,
+  0x72, 0xC1, 0xE7, 0x87, 0x00, 0x20, 0x24, 0xC0,
+  0x97, 0xCF, 0x27, 0x00, 0x1E, 0xC0, 0xE7, 0x87,
+  0xFF, 0x00, 0x22, 0xC0, 0xE7, 0x67, 0x7F, 0xFF,
+  0x24, 0xC0, 0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0,
+  0xE7, 0x87, 0x80, 0x00, 0x24, 0xC0, 0x97, 0xCF,
+  0x9F, 0xAF, 0x0A, 0x05, 0x67, 0x00, 0x1E, 0xC0,
+  0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0xE7, 0x87,
+  0x40, 0x00, 0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00,
+  0x24, 0xC0, 0x97, 0xCF, 0x9F, 0xAF, 0x0A, 0x05,
+  0xE7, 0x67, 0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67,
+  0xFF, 0xFE, 0x24, 0xC0, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xC1, 0x09, 0x20, 0xC0, 0xE7, 0x87,
+  0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF, 0xC0, 0x07,
+  0x40, 0x00, 0xC8, 0x09, 0xFC, 0x05, 0xE7, 0x67,
+  0x00, 0xFF, 0x22, 0xC0, 0xE7, 0x67, 0xFF, 0xFE,
+  0x24, 0xC0, 0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0,
+  0xE7, 0x67, 0xBF, 0xFF, 0x24, 0xC0, 0x00, 0xDA,
+  0xE8, 0x09, 0x20, 0xC0, 0xE7, 0x87, 0x40, 0x00,
+  0x24, 0xC0, 0xE7, 0x87, 0x40, 0x00, 0x24, 0xC0,
+  0x00, 0xDA, 0xE8, 0x09, 0x20, 0xC0, 0x6D, 0xC1,
+  0xE7, 0x87, 0x00, 0x01, 0x24, 0xC0, 0x97, 0xCF,
+  0xE7, 0x07, 0x32, 0x00, 0x12, 0xC0, 0xE7, 0x77,
+  0x00, 0x80, 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF,
+  0xE7, 0x07, 0x20, 0x4E, 0x12, 0xC0, 0xE7, 0x77,
+  0x00, 0x80, 0x12, 0xC0, 0x7C, 0xC0, 0x97, 0xCF,
+  0x09, 0x02, 0x19, 0x00, 0x01, 0x01, 0x00, 0x80,
+  0x96, 0x09, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+static unsigned char findex2[] = {
+  0xB6, 0xC3, 0x2F, 0x01, 0x03, 0x64,
+
+  0x0E, 0x00, 0x14, 0x00, 0x1A, 0x00, 0x20, 0x00,
+  0x26, 0x00, 0x4A, 0x00, 0x64, 0x00, 0x6A, 0x00,
+  0x92, 0x00, 0x9A, 0x00, 0xA0, 0x00, 0xB2, 0x00,
+  0xB8, 0x00, 0xBE, 0x00, 0xC2, 0x00, 0xC8, 0x00,
+  0xCE, 0x00, 0xDC, 0x00, 0xDA, 0x00, 0xE2, 0x00,
+  0xE0, 0x00, 0xE8, 0x00, 0xE6, 0x00, 0xEE, 0x00,
+  0xEC, 0x00, 0xF2, 0x00, 0xF8, 0x00, 0x02, 0x01,
+  0x0A, 0x01, 0x0E, 0x01, 0x12, 0x01, 0x1E, 0x01,
+  0x22, 0x01, 0x28, 0x01, 0x2C, 0x01, 0x32, 0x01,
+  0x36, 0x01, 0x44, 0x01, 0x50, 0x01, 0x5E, 0x01,
+  0x72, 0x01, 0x76, 0x01, 0x7A, 0x01, 0x80, 0x01,
+  0x88, 0x01, 0x8C, 0x01, 0x94, 0x01, 0x9C, 0x01,
+  0xA0, 0x01, 0xA4, 0x01, 0xAA, 0x01, 0xB0, 0x01,
+  0xB4, 0x01, 0xBA, 0x01, 0xD0, 0x01, 0xDA, 0x01,
+  0xF6, 0x01, 0xFA, 0x01, 0x02, 0x02, 0x34, 0x02,
+  0x3C, 0x02, 0x44, 0x02, 0x4A, 0x02, 0x50, 0x02,
+  0x56, 0x02, 0x74, 0x02, 0x78, 0x02, 0x7E, 0x02,
+  0x84, 0x02, 0x8A, 0x02, 0x88, 0x02, 0x90, 0x02,
+  0x8E, 0x02, 0x94, 0x02, 0xA2, 0x02, 0xA8, 0x02,
+  0xAE, 0x02, 0xB4, 0x02, 0xBA, 0x02, 0xB8, 0x02,
+  0xC0, 0x02, 0xBE, 0x02, 0xC4, 0x02, 0xD0, 0x02,
+  0xD4, 0x02, 0xE0, 0x02, 0xE6, 0x02, 0xEE, 0x02,
+  0xF8, 0x02, 0xFC, 0x02, 0x06, 0x03, 0x1E, 0x03,
+  0x24, 0x03, 0x28, 0x03, 0x30, 0x03, 0x2E, 0x03,
+  0x3C, 0x03, 0x4A, 0x03, 0x4E, 0x03, 0x54, 0x03,
+  0x58, 0x03, 0x5E, 0x03, 0x66, 0x03, 0x6E, 0x03,
+  0x7A, 0x03, 0x86, 0x03, 0x8E, 0x03, 0x96, 0x03,
+  0xB2, 0x03, 0xB8, 0x03, 0xC6, 0x03, 0xCC, 0x03,
+  0xD4, 0x03, 0xDA, 0x03, 0xE8, 0x03, 0xF4, 0x03,
+  0xFC, 0x03, 0x04, 0x04, 0x20, 0x04, 0x2A, 0x04,
+  0x32, 0x04, 0x36, 0x04, 0x3E, 0x04, 0x44, 0x04,
+  0x42, 0x04, 0x48, 0x04, 0x4E, 0x04, 0x4C, 0x04,
+  0x54, 0x04, 0x52, 0x04, 0x5A, 0x04, 0x5E, 0x04,
+  0x62, 0x04, 0x68, 0x04, 0x74, 0x04, 0x7C, 0x04,
+  0x80, 0x04, 0x88, 0x04, 0x8C, 0x04, 0x94, 0x04,
+  0x9A, 0x04, 0xA2, 0x04, 0xA6, 0x04, 0xAE, 0x04,
+  0xB4, 0x04, 0xC0, 0x04, 0xCC, 0x04, 0xD8, 0x04,
+  0x2A, 0x05, 0x46, 0x05, 0x6C, 0x05, 0x00, 0x00
+};
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/Config.in linux/drivers/video/Config.in
--- linux.orig/drivers/video/Config.in	Wed Nov 14 23:16:31 2001
+++ linux/drivers/video/Config.in	Tue Feb  5 19:09:31 2002
@@ -121,14 +121,14 @@
 	 if [ "$CONFIG_FB_MATROX" != "n" ]; then
 	    bool '    Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM
 	    bool '    Mystique support' CONFIG_FB_MATROX_MYSTIQUE
-	    bool '    G100/G200/G400/G450 support' CONFIG_FB_MATROX_G100
+	    bool '    G100/G200/G400/G450/G550 support' CONFIG_FB_MATROX_G100
             if [ "$CONFIG_I2C" != "n" ]; then
 	       dep_tristate '      Matrox I2C support' CONFIG_FB_MATROX_I2C $CONFIG_FB_MATROX $CONFIG_I2C_ALGOBIT
 	       if [ "$CONFIG_FB_MATROX_G100" = "y" ]; then
 	          dep_tristate '      G400 second head support' CONFIG_FB_MATROX_MAVEN $CONFIG_FB_MATROX_I2C
 	       fi
             fi
-            dep_tristate '      G450 second head support' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100
+            dep_tristate '      G450/G550 second head support' CONFIG_FB_MATROX_G450 $CONFIG_FB_MATROX_G100
 	    bool '    Multihead support' CONFIG_FB_MATROX_MULTIHEAD
 	 fi
 	 tristate '  ATI Mach64 display support (EXPERIMENTAL)' CONFIG_FB_ATY
@@ -145,6 +145,7 @@
 	 fi
 	 tristate '  3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX
 	 tristate '  3Dfx Voodoo Graphics (sst1) support (EXPERIMENTAL)' CONFIG_FB_VOODOO1
+	 tristate '  Trident support (EXPERIMENTAL)' CONFIG_FB_TRIDENT
       fi
    fi
    if [ "$ARCH" = "sparc" -o "$ARCH" = "sparc64" ]; then
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/Makefile linux/drivers/video/Makefile
--- linux.orig/drivers/video/Makefile	Thu Oct 25 20:53:52 2001
+++ linux/drivers/video/Makefile	Tue Feb  5 19:09:31 2002
@@ -64,6 +64,7 @@
 obj-$(CONFIG_FB_IMSTT)            += imsttfb.o
 obj-$(CONFIG_FB_RETINAZ3)         += retz3fb.o
 obj-$(CONFIG_FB_CLGEN)            += clgenfb.o fbgen.o
+obj-$(CONFIG_FB_TRIDENT)          += tridentfb.o fbgen.o
 obj-$(CONFIG_FB_S3TRIO)           += S3triofb.o
 obj-$(CONFIG_FB_TGA)              += tgafb.o fbgen.o
 obj-$(CONFIG_FB_VESA)             += vesafb.o 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty/atyfb_base.c linux/drivers/video/aty/atyfb_base.c
--- linux.orig/drivers/video/aty/atyfb_base.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/aty/atyfb_base.c	Wed Jan 16 20:54:41 2002
@@ -350,7 +350,7 @@
     { 0x4751, 0x4751, 0x00, 0x00, m64n_gtc_ppl, 230, 100, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | M64F_EXTRA_BRIGHT },
 
     /* 3D RAGE XL */
-    { 0x4752, 0x4752, 0x00, 0x00, m64n_xl, 230, 120, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | M64F_EXTRA_BRIGHT | M64F_XL_DLL },
+    { 0x4752, 0x4752, 0x00, 0x00, m64n_xl, 230, 100, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP | M64F_SDRAM_MAGIC_PLL | M64F_EXTRA_BRIGHT | M64F_XL_DLL },
 
     /* Mach64 LT PRO */
     { 0x4c42, 0x4c42, 0x00, 0x00, m64n_ltp_a,   230, 100, M64F_GT | M64F_INTEGRATED | M64F_RESET_3D | M64F_GTB_DSP },
@@ -366,6 +366,7 @@
 
 #if defined(CONFIG_FB_ATY_GX) || defined(CONFIG_FB_ATY_CT)
 static char ram_dram[] __initdata = "DRAM";
+static char ram_resv[] __initdata = "RESV";
 #endif /* CONFIG_FB_ATY_GX || CONFIG_FB_ATY_CT */
 
 #ifdef CONFIG_FB_ATY_GX
@@ -378,7 +379,6 @@
 static char ram_sgram[] __initdata = "SGRAM";
 static char ram_wram[] __initdata = "WRAM";
 static char ram_off[] __initdata = "OFF";
-static char ram_resv[] __initdata = "RESV";
 #endif /* CONFIG_FB_ATY_CT */
 
 #ifdef CONFIG_FB_ATY_GX
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty/mach64_accel.c linux/drivers/video/aty/mach64_accel.c
--- linux.orig/drivers/video/aty/mach64_accel.c	Tue Jul 31 21:43:29 2001
+++ linux/drivers/video/aty/mach64_accel.c	Wed Jan 16 20:54:41 2002
@@ -326,7 +326,7 @@
 		     fbcon_cfb##width##_clear_margins(conp, p, bottom_only), \
 		     int bottom_only) \
  \
-const struct display_switch fbcon_aty##width## = { \
+const struct display_switch fbcon_aty##width = { \
     setup:		fbcon_cfb##width##_setup, \
     bmove:		fbcon_aty_bmove, \
     clear:		fbcon_aty_clear, \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty128.h linux/drivers/video/aty128.h
--- linux.orig/drivers/video/aty128.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/aty128.h	Wed Dec 26 18:13:49 2001
@@ -13,6 +13,7 @@
 #define CLOCK_CNTL_DATA				0x000c
 #define BIOS_0_SCRATCH				0x0010
 #define BUS_CNTL				0x0030
+#define BUS_CNTL1				0x0034
 #define GEN_INT_CNTL				0x0040
 #define CRTC_GEN_CNTL				0x0050
 #define CRTC_EXT_CNTL				0x0054
@@ -24,6 +25,7 @@
 #define GEN_RESET_CNTL				0x00f0
 #define CONFIG_MEMSIZE				0x00f8
 #define MEM_CNTL				0x0140
+#define MEM_POWER_MISC				0x015c
 #define AGP_BASE				0x0170
 #define AGP_CNTL				0x0174
 #define AGP_APER_OFFSET				0x0178
@@ -37,6 +39,9 @@
 #define CRTC_H_SYNC_STRT_WID			0x0204
 #define CRTC_V_TOTAL_DISP			0x0208
 #define CRTC_V_SYNC_STRT_WID			0x020c
+#define CRTC_VLINE_CRNT_VLINE			0x0210
+#define CRTC_CRNT_FRAME				0x0214
+#define CRTC_GUI_TRIG_VLINE			0x0218
 #define CRTC_OFFSET				0x0224
 #define CRTC_OFFSET_CNTL			0x0228
 #define CRTC_PITCH				0x022c
@@ -48,6 +53,20 @@
 #define DDA_ON_OFF				0x02e4
 #define VGA_DDA_CONFIG				0x02e8
 #define VGA_DDA_ON_OFF				0x02ec
+#define CRTC2_H_TOTAL_DISP			0x0300
+#define CRTC2_H_SYNC_STRT_WID			0x0304
+#define CRTC2_V_TOTAL_DISP			0x0308
+#define CRTC2_V_SYNC_STRT_WID			0x030c
+#define CRTC2_VLINE_CRNT_VLINE			0x0310
+#define CRTC2_CRNT_FRAME			0x0314
+#define CRTC2_GUI_TRIG_VLINE			0x0318
+#define CRTC2_OFFSET				0x0324
+#define CRTC2_OFFSET_CNTL			0x0328
+#define CRTC2_PITCH				0x032c
+#define DDA2_CONFIG				0x03e0
+#define DDA2_ON_OFF				0x03e4
+#define CRTC2_GEN_CNTL				0x03f8
+#define CRTC2_STATUS				0x03fc
 #define OV0_SCALE_CNTL				0x0420
 #define SUBPIC_CNTL				0x0540
 #define PM4_BUFFER_OFFSET			0x0700
@@ -237,6 +256,10 @@
 #define AGP_PLL_CNTL				0x0010
 #define FCP_CNTL				0x0012
 #define PLL_TEST_CNTL				0x0013
+#define P2PLL_CNTL				0x002a
+#define P2PLL_REF_DIV				0x002b
+#define P2PLL_DIV_0				0x002b
+#define POWER_MANAGEMENT			0x002f
 
 #define PPLL_RESET				0x01
 #define PPLL_ATOMIC_UPDATE_EN			0x10000
@@ -254,6 +277,14 @@
 /* CRTC control values (CRTC_GEN_CNTL) */
 #define CRTC_CSYNC_EN				0x00000010
 
+#define CRTC2_DBL_SCAN_EN			0x00000001
+#define CRTC2_DISPLAY_DIS			0x00800000
+#define CRTC2_FIFO_EXTSENSE			0x00200000
+#define CRTC2_ICON_EN				0x00100000
+#define CRTC2_CUR_EN				0x00010000
+#define CRTC2_EN				0x02000000
+#define CRTC2_DISP_REQ_EN_B			0x04000000
+
 #define CRTC_PIX_WIDTH_MASK			0x00000700
 #define CRTC_PIX_WIDTH_4BPP			0x00000100
 #define CRTC_PIX_WIDTH_8BPP			0x00000200
@@ -267,10 +298,14 @@
 #define DAC_MASK				0xFF000000
 #define DAC_BLANKING				0x00000004
 #define DAC_RANGE_CNTL				0x00000003
-#define DAC_RANGE_CNTL				0x00000003
+#define DAC_CLK_SEL				0x00000010
 #define DAC_PALETTE_ACCESS_CNTL			0x00000020
+#define DAC_PALETTE2_SNOOP_EN			0x00000040
 #define DAC_PDWN				0x00008000
 
+/* CRTC_EXT_CNTL */
+#define CRT_CRTC_ON				0x00008000
+
 /* GEN_RESET_CNTL bit constants */
 #define SOFT_RESET_GUI				0x00000001
 #define SOFT_RESET_VCLK				0x00000100
@@ -348,5 +383,37 @@
 #define LVDS_BL_MOD_EN				0x00010000
 #define LVDS_DIGION				0x00040000
 #define LVDS_BLON				0x00080000
+#define LVDS_ON					0x00000001
+#define LVDS_DISPLAY_DIS			0x00000002
+#define LVDS_PANEL_TYPE_2PIX_PER_CLK		0x00000004
+#define LVDS_PANEL_24BITS_TFT			0x00000008
+#define LVDS_FRAME_MOD_NO			0x00000000
+#define LVDS_FRAME_MOD_2_LEVELS			0x00000010
+#define LVDS_FRAME_MOD_4_LEVELS			0x00000020
+#define LVDS_RST_FM				0x00000040
+#define LVDS_EN					0x00000080
+
+/* CRTC2_GEN_CNTL constants */
+#define CRTC2_EN				0x02000000
+
+/* POWER_MANAGEMENT constants */
+#define	PWR_MGT_ON				0x00000001
+#define PWR_MGT_MODE_MASK			0x00000006
+#define PWR_MGT_MODE_PIN			0x00000000
+#define PWR_MGT_MODE_REGISTER			0x00000002
+#define PWR_MGT_MODE_TIMER			0x00000004
+#define PWR_MGT_MODE_PCI			0x00000006
+#define PWR_MGT_AUTO_PWR_UP_EN			0x00000008
+#define PWR_MGT_ACTIVITY_PIN_ON			0x00000010
+#define PWR_MGT_STANDBY_POL			0x00000020
+#define PWR_MGT_SUSPEND_POL			0x00000040
+#define PWR_MGT_SELF_REFRESH			0x00000080
+#define PWR_MGT_ACTIVITY_PIN_EN			0x00000100
+#define PWR_MGT_KEYBD_SNOOP			0x00000200
+#define PWR_MGT_TRISTATE_MEM_EN			0x00000800
+#define PWR_MGT_SELW4MS				0x00001000
+#define PWR_MGT_SLOWDOWN_MCLK			0x00002000
+
+#define PMI_PMSCR_REG				0x60
 
 #endif /* REG_RAGE128_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/aty128fb.c linux/drivers/video/aty128fb.c
--- linux.orig/drivers/video/aty128fb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/aty128fb.c	Wed Dec 26 18:13:49 2001
@@ -7,13 +7,19 @@
  *                Ani Joshi / Jeff Garzik
  *                      - Code cleanup
  *
+ *                Michel Dänzer <michdaen@iiic.ethz.ch>
+ *                      - 15/16 bit cleanup
+ *                      - fix panning
+ *                      
+ *                Benjamin Herrenschmidt
+ *                      - pmac-specific PM stuff
+ *
  *                Andreas Hundt <andi@convergence.de>
  *                      - FB_ACTIVATE fixes
  *
  *  Based off of Geert's atyfb.c and vfb.c.
  *
  *  TODO:
- *		- panning
  *		- monitor sensing (DDC)
  *              - virtual display
  *		- other platform support (only ppc/x86 supported)
@@ -70,6 +76,9 @@
 #ifdef CONFIG_FB_COMPAT_XPMAC
 #include <asm/vc_ioctl.h>
 #endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif /* CONFIG_BOOTX_TEXT */
 
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
@@ -154,6 +163,7 @@
     {"Rage128 RL (AGP)", PCI_DEVICE_ID_ATI_RAGE128_RL, rage_128},
     {"Rage128 Pro PF (AGP)", PCI_DEVICE_ID_ATI_RAGE128_PF, rage_128_pro},
     {"Rage128 Pro PR (PCI)", PCI_DEVICE_ID_ATI_RAGE128_PR, rage_128_pro},
+    {"Rage128 Pro TR (AGP)", PCI_DEVICE_ID_ATI_RAGE128_U3, rage_128_pro},
     {"Rage Mobility M3 (PCI)", PCI_DEVICE_ID_ATI_RAGE128_LE, rage_M3},
     {"Rage Mobility M3 (AGP)", PCI_DEVICE_ID_ATI_RAGE128_LF, rage_M3},
     {NULL, 0, rage_128}
@@ -227,6 +237,11 @@
 static int default_cmode __initdata = CMODE_8;
 #endif
 
+#ifdef CONFIG_PMAC_PBOOK
+static int default_crt_on __initdata = 0;
+static int default_lcd_on __initdata = 1;
+#endif
+
 #ifdef CONFIG_MTRR
 static int mtrr = 1;
 #endif
@@ -251,7 +266,7 @@
     u32 offset, offset_cntl;
     u32 xoffset, yoffset;
     u32 vxres, vyres;
-    u32 bpp;
+    u32 depth, bpp;
 };
 
 struct aty128_pll {
@@ -307,10 +322,23 @@
     int currcon;
     int blitter_may_be_busy;
     int fifo_slots;                 /* free slots in FIFO (64 max) */
+#ifdef CONFIG_PMAC_PBOOK
+    unsigned char *save_framebuffer;
+    int	pm_reg;
+    int crt_on, lcd_on;
+    u32 save_lcd_gen_cntl;
+#endif
 };
 
 static struct fb_info_aty128 *board_list = NULL;
 
+#ifdef CONFIG_PMAC_PBOOK
+  int aty128_sleep_notify(struct pmu_sleep_notifier *self, int when);
+  static struct pmu_sleep_notifier aty128_sleep_notifier = {
+  	aty128_sleep_notify, SLEEP_LEVEL_VIDEO,
+  };
+#endif
+
 #define round_div(n, d) ((n+(d/2))/d)
 
     /*
@@ -331,6 +359,8 @@
 			struct fb_info *info);
 static int aty128fb_pan_display(struct fb_var_screeninfo *var, int con,
 			   struct fb_info *fb);
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info);
 static int aty128fb_rasterimg(struct fb_info *info, int start);
 
 
@@ -379,7 +409,7 @@
 static void do_wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
 static void wait_for_fifo(u16 entries, struct fb_info_aty128 *info);
 static void wait_for_idle(struct fb_info_aty128 *info);
-static u32 bpp_to_depth(u32 bpp);
+static u32 depth_to_dst(u32 depth);
 
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_aty128_8;
@@ -422,6 +452,7 @@
 	fb_get_cmap:	aty128fb_get_cmap,
 	fb_set_cmap:	aty128fb_set_cmap,
 	fb_pan_display:	aty128fb_pan_display,
+	fb_ioctl:	aty128fb_ioctl,
 	fb_rasterimg:	aty128fb_rasterimg,
 };
 
@@ -496,7 +527,7 @@
 _aty_ld_pll(unsigned int pll_index,
 			const struct fb_info_aty128 *info)
 {       
-    aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x1F);
+    aty_st_8(CLOCK_CNTL_INDEX, pll_index & 0x3F);
     return aty_ld_le32(CLOCK_CNTL_DATA);
 }
 
@@ -505,7 +536,7 @@
 _aty_st_pll(unsigned int pll_index, u32 val,
 			const struct fb_info_aty128 *info)
 {
-    aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x1F) | PLL_WR_EN);
+    aty_st_8(CLOCK_CNTL_INDEX, (pll_index & 0x3F) | PLL_WR_EN);
     aty_st_le32(CLOCK_CNTL_DATA, val);
 }
 
@@ -698,7 +729,7 @@
 		GMC_SRC_CLIP_DEFAULT			|
 		GMC_DST_CLIP_DEFAULT			|
 		GMC_BRUSH_SOLIDCOLOR			|
-		(bpp_to_depth(par->crtc.bpp) << 8)	|
+		(depth_to_dst(par->crtc.depth) << 8)	|
 		GMC_SRC_DSTCOLOR			|
 		GMC_BYTE_ORDER_MSB_TO_LSB		|
 		GMC_DP_CONVERSION_TEMP_6500		|
@@ -731,18 +762,20 @@
 }
 
 
-/* convert bpp values to their register representation */
+/* convert depth values to their register representation */
 static u32
-bpp_to_depth(u32 bpp)
-{
-    if (bpp <= 8)
-	return DST_8BPP;
-    else if (bpp <= 16)
-        return DST_15BPP;
-    else if (bpp <= 24)
-	return DST_24BPP;
-    else if (bpp <= 32)
-	return DST_32BPP;
+depth_to_dst(u32 depth)
+ {
+    if (depth <= 8)
+ 	return DST_8BPP;
+    else if (depth <= 15)
+         return DST_15BPP;
+    else if (depth == 16)
+        return DST_16BPP;
+    else if (depth <= 24)
+ 	return DST_24BPP;
+    else if (depth <= 32)
+ 	return DST_32BPP;
 
     return -EINVAL;
 }
@@ -765,12 +798,8 @@
     aty_st_le32(CRTC_PITCH, crtc->pitch);
     aty_st_le32(CRTC_OFFSET, crtc->offset);
     aty_st_le32(CRTC_OFFSET_CNTL, crtc->offset_cntl);
-    /* Disable ATOMIC updating.  Is this the right place?
-     * -- BenH: Breaks on my G4
-     */
-#if 0
-    aty_st_le32(PPLL_CNTL, aty_ld_le32(PPLL_CNTL) & ~(0x00030000));
-#endif
+    /* Disable ATOMIC updating.  Is this the right place? */
+    aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~(0x00030000));
 }
 
 
@@ -779,7 +808,7 @@
 			struct aty128_crtc *crtc,
 			const struct fb_info_aty128 *info)
 {
-    u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp;
+    u32 xres, yres, vxres, vyres, xoffset, yoffset, bpp, dst;
     u32 left, right, upper, lower, hslen, vslen, sync, vmode;
     u32 h_total, h_disp, h_sync_strt, h_sync_wid, h_sync_pol;
     u32 v_total, v_disp, v_sync_strt, v_sync_wid, v_sync_pol, c_sync;
@@ -804,6 +833,11 @@
     sync  = var->sync;
     vmode = var->vmode;
 
+    if (bpp != 16)
+        depth = bpp;
+    else
+        depth = (var->green.length == 6) ? 16 : 15;
+
     /* check for mode eligibility
      * accept only non interlaced modes */
     if ((vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
@@ -819,17 +853,16 @@
     if (vyres < yres + yoffset)
 	vyres = yres + yoffset;
 
-    /* convert bpp into ATI register depth */
-    depth = bpp_to_depth(bpp);
+    /* convert depth into ATI register depth */
+    dst = depth_to_dst(depth);
 
-    /* make sure we didn't get an invalid depth */
-    if (depth == -EINVAL) {
-        printk(KERN_ERR "aty128fb: Invalid depth\n");
+    if (dst == -EINVAL) {
+        printk(KERN_ERR "aty128fb: Invalid depth or RGBA\n");
         return -EINVAL;
     }
 
-    /* convert depth to bpp */
-    bytpp = mode_bytpp[depth];
+    /* convert register depth to bytes per pixel */
+    bytpp = mode_bytpp[dst];
 
     /* make sure there is enough video ram for the mode */
     if ((u32)(vxres * vyres * bytpp) > info->vram_size) {
@@ -870,7 +903,7 @@
     
     c_sync = sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-    crtc->gen_cntl = 0x3000000L | c_sync | (depth << 8);
+    crtc->gen_cntl = 0x3000000L | c_sync | (dst << 8);
 
     crtc->h_total = h_total | (h_disp << 16);
     crtc->v_total = v_total | (v_disp << 16);
@@ -893,6 +926,7 @@
     crtc->vyres = vyres;
     crtc->xoffset = xoffset;
     crtc->yoffset = yoffset;
+    crtc->depth = depth;
     crtc->bpp = bpp;
 
     return 0;
@@ -900,7 +934,7 @@
 
 
 static int
-aty128_bpp_to_var(int pix_width, struct fb_var_screeninfo *var)
+aty128_pix_width_to_var(int pix_width, struct fb_var_screeninfo *var)
 {
 
     /* fill in pixel info */
@@ -917,7 +951,6 @@
 	var->transp.length = 0;
 	break;
     case CRTC_PIX_WIDTH_15BPP:
-    case CRTC_PIX_WIDTH_16BPP:
 	var->bits_per_pixel = 16;
 	var->red.offset = 10;
 	var->red.length = 5;
@@ -928,6 +961,17 @@
 	var->transp.offset = 0;
 	var->transp.length = 0;
 	break;
+    case CRTC_PIX_WIDTH_16BPP:
+	var->bits_per_pixel = 16;
+	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;
+	break;
     case CRTC_PIX_WIDTH_24BPP:
         var->bits_per_pixel = 24;
         var->red.offset = 16;
@@ -996,7 +1040,7 @@
             (v_sync_pol ? 0 : FB_SYNC_VERT_HIGH_ACT) |
             (c_sync ? FB_SYNC_COMP_HIGH_ACT : 0);
 
-    aty128_bpp_to_var(pix_width, var);
+    aty128_pix_width_to_var(pix_width, var);
 
     var->xres = xres;
     var->yres = yres;
@@ -1017,6 +1061,42 @@
 }
 
 static void
+aty128_set_crt_enable(struct fb_info_aty128 *info, int on)
+{
+    if (on) {
+	aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) | CRT_CRTC_ON);
+	aty_st_le32(DAC_CNTL, (aty_ld_le32(DAC_CNTL) | DAC_PALETTE2_SNOOP_EN));
+    } else
+	aty_st_le32(CRTC_EXT_CNTL, aty_ld_le32(CRTC_EXT_CNTL) & ~CRT_CRTC_ON);
+}
+
+static void
+aty128_set_lcd_enable(struct fb_info_aty128 *info, int on)
+{
+    u32 reg;
+    
+    if (on) {
+	reg = aty_ld_le32(LVDS_GEN_CNTL);
+	reg |= LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION;
+	reg &= ~LVDS_DISPLAY_DIS;
+	aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef CONFIG_PMAC_BACKLIGHT
+	aty128_set_backlight_enable(get_backlight_enable(), get_backlight_level(), info);
+#endif	
+    } else {
+#ifdef CONFIG_PMAC_BACKLIGHT
+	aty128_set_backlight_enable(0, 0, info);
+#endif	
+	reg = aty_ld_le32(LVDS_GEN_CNTL);
+	reg |= LVDS_DISPLAY_DIS;
+        aty_st_le32(LVDS_GEN_CNTL, reg);
+        mdelay(100);
+	reg &= ~(LVDS_ON /*| LVDS_EN*/);
+	aty_st_le32(LVDS_GEN_CNTL, reg);
+    }
+}
+
+static void
 aty128_set_pll(struct aty128_pll *pll, const struct fb_info_aty128 *info)
 {
     u32 div3;
@@ -1053,6 +1133,23 @@
 
     /* clear the reset, just in case */
     aty_st_pll(PPLL_CNTL, aty_ld_pll(PPLL_CNTL) & ~PPLL_RESET);
+
+#if 0
+    if (info->chip_gen == rage_M3) {
+	/* XXX energy saving, disable VCLK during blanking */
+	aty_pll_wait_readupdate(info);
+    	aty_st_pll(VCLK_ECP_CNTL, aty_ld_pll(VCLK_ECP_CNTL) | 0xc0);
+		aty_pll_writeupdate(info);
+
+	/* Set PM clocks */
+	aty_pll_wait_readupdate(info);
+	aty_st_pll(XCLK_CNTL, aty_ld_pll(XCLK_CNTL) | 0x00330000);
+	aty_pll_writeupdate(info);
+	aty_pll_wait_readupdate(info);
+	aty_st_pll(MCLK_CNTL, aty_ld_pll(MCLK_CNTL) | 0x00000700);
+	aty_pll_writeupdate(info);
+    }
+#endif    
 }
 
 
@@ -1121,7 +1218,7 @@
 static int
 aty128_ddafifo(struct aty128_ddafifo *dsp,
 		const struct aty128_pll *pll,
-		u32 bpp,
+		u32 depth,
 		const struct fb_info_aty128 *info)
 {
     const struct aty128_meminfo *m = info->mem;
@@ -1129,11 +1226,10 @@
     u32 fifo_width = info->constants.fifo_width;
     u32 fifo_depth = info->constants.fifo_depth;
     s32 x, b, p, ron, roff;
-    u32 n, d;
+    u32 n, d, bpp;
 
-    /* 15bpp is really 16bpp */
-    if (bpp == 15)
-	bpp = 16;
+    /* round up to multiple of 8 */
+    bpp = (depth+7) & ~7;
 
     n = xclk * fifo_width;
     d = pll->vclk * bpp;
@@ -1214,15 +1310,21 @@
     config = aty_ld_le32(CONFIG_CNTL) & ~3;
 
 #if defined(__BIG_ENDIAN)
-    if (par->crtc.bpp >= 24)
-	config |= 2;	/* make aperture do 32 byte swapping */
-    else if (par->crtc.bpp > 8)
-	config |= 1;	/* make aperture do 16 byte swapping */
+    if (par->crtc.bpp == 32)
+	config |= 2;	/* make aperture do 32 bit swapping */
+    else if (par->crtc.bpp == 16)
+	config |= 1;	/* make aperture do 16 bit swapping */
 #endif
 
     aty_st_le32(CONFIG_CNTL, config);
     aty_st_8(CRTC_EXT_CNTL + 1, 0);	/* turn the video back on */
 
+#ifdef CONFIG_PMAC_PBOOK
+    if (info->chip_gen == rage_M3) {
+	aty128_set_crt_enable(info, info->crt_on);
+	aty128_set_lcd_enable(info, info->lcd_on);
+    }
+#endif
     if (par->accel_flags & FB_ACCELF_TEXT)
         aty128_init_engine(par, info);
 
@@ -1247,6 +1349,13 @@
 	display_info.disp_reg_address = info->regbase_phys;
     }
 #endif /* CONFIG_FB_COMPAT_XPMAC */
+#if defined(CONFIG_BOOTX_TEXT)
+    btext_update_display(info->frame_buffer_phys,
+		    (((par->crtc.h_total>>16) & 0xff)+1)*8,
+		    ((par->crtc.v_total>>16) & 0x7ff)+1,
+		    par->crtc.bpp,
+		    par->crtc.vxres*par->crtc.bpp/8);
+#endif /* CONFIG_BOOTX_TEXT */
 }
 
     /*
@@ -1265,7 +1374,7 @@
     if ((err = aty128_var_to_pll(var->pixclock, &par->pll, info)))
 	return err;
 
-    if ((err = aty128_ddafifo(&par->fifo_reg, &par->pll, par->crtc.bpp, info)))
+    if ((err = aty128_ddafifo(&par->fifo_reg, &par->pll, par->crtc.depth, info)))
 	return err;
 
     if (var->accel_flags & FB_ACCELF_TEXT)
@@ -1333,7 +1442,7 @@
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
     struct aty128fb_par par;
     struct display *display;
-    int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldaccel;
+    int oldxres, oldyres, oldvxres, oldvyres, oldbpp, oldgreen, oldaccel;
     int accel, err;
 
     display = (con >= 0) ? &fb_display[con] : fb->disp;
@@ -1378,11 +1487,13 @@
     oldvxres = display->var.xres_virtual;
     oldvyres = display->var.yres_virtual;
     oldbpp = display->var.bits_per_pixel;
+    oldgreen = display->var.green.length;
     oldaccel = display->var.accel_flags;
     display->var = *var;
     if (oldxres != var->xres || oldyres != var->yres ||
 	oldvxres != var->xres_virtual || oldvyres != var->yres_virtual ||
-	oldbpp != var->bits_per_pixel || oldaccel != var->accel_flags) {
+	oldgreen != var->green.length || oldbpp != var->bits_per_pixel ||
+	oldaccel != var->accel_flags) {
 
 	struct fb_fix_screeninfo fix;
 
@@ -1412,7 +1523,7 @@
     if (!info->fb_info.display_fg || info->fb_info.display_fg->vc_num == con)
 	aty128_set_par(&par, info);
 
-    if (oldbpp != var->bits_per_pixel) {
+    if (oldbpp != var->bits_per_pixel || oldgreen != var->green.length) {
 	if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
 	    return err;
 	do_install_cmap(con, &info->fb_info);
@@ -1433,7 +1544,6 @@
 	break;
 #endif
 #ifdef FBCON_HAS_CFB16
-    case 15:
     case 16:
 	disp->dispsw = accel ? &fbcon_aty128_16 : &fbcon_cfb16;
 	disp->dispsw_data = info->fbcon_cmap.cfb16;
@@ -1475,7 +1585,7 @@
     fix->type        = FB_TYPE_PACKED_PIXELS;
     fix->type_aux    = 0;
     fix->line_length = (par->crtc.vxres * par->crtc.bpp) >> 3;
-    fix->visual      = par->crtc.bpp <= 8 ? FB_VISUAL_PSEUDOCOLOR
+    fix->visual      = par->crtc.bpp == 8 ? FB_VISUAL_PSEUDOCOLOR
                                           : FB_VISUAL_DIRECTCOLOR;
     fix->ywrapstep = 0;
     fix->xpanstep  = 8;
@@ -1509,8 +1619,6 @@
 
     /*
      *  Pan or Wrap the Display
-     *
-     *  Not supported (yet!)
      */
 static int
 aty128fb_pan_display(struct fb_var_screeninfo *var, int con,
@@ -1534,7 +1642,10 @@
     par->crtc.xoffset = xoffset;
     par->crtc.yoffset = yoffset;
 
-    offset = ((yoffset * par->crtc.vxres + xoffset) * par->crtc.bpp) >> 6;
+    offset = ((yoffset * par->crtc.vxres + xoffset)*(par->crtc.bpp >> 3)) & ~7;
+
+    if (par->crtc.bpp == 24)
+        offset += 8 * (offset % 3); /* Must be multiple of 8 and 3 */
 
     aty_st_le32(CRTC_OFFSET, offset);
 
@@ -1550,20 +1661,16 @@
 aty128fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-#if 1
-    fb_copy_cmap(&info->cmap, cmap, kspc ? 0 : 2);
-#else
-    struct fb_info_aty128 fb = (struct fb_info_aty128 *)info;
+    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
 
     if (con == fb->currcon) /* current console? */
-	return fb_get_cmap(cmap, kspc, aty128_getcolreg, info);
-    else if (fb_display[con].cmap.len) /* non default colormap? */
-	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
-    else {  
-	int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 32;
-	fb_copy_cmap(fb_default_cmap(size), cmap, kspc ? 0 : 2);
-    }
-#endif
+        return fb_get_cmap(cmap, kspc, aty128_getcolreg, info);
+    else if (disp->cmap.len) /* non default colormap? */
+        fb_copy_cmap(&disp->cmap, cmap, kspc ? 0 : 2);
+    else
+        fb_copy_cmap(fb_default_cmap((disp->var.bits_per_pixel==8) ? 256 : 32),
+                     cmap, kspc ? 0 : 2);
 
     return 0;
 }
@@ -1576,19 +1683,19 @@
 aty128fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 			struct fb_info *info)
 {
-    int err;
     struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
-    struct display *disp;  
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
+    unsigned int cmap_len = (disp->var.bits_per_pixel==8) ? 256 : 32;
 
-    if (con >= 0)
-	disp = &fb_display[con];
-    else
-	disp = info->disp;
+    if (disp->cmap.len != cmap_len) {
+        int err = fb_alloc_cmap(&disp->cmap, cmap_len, 0);
 
-    if (!disp->cmap.len) {      /* no colormap allocated? */
-        int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
-	if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
-	    return err;
+    	if (!disp->cmap.len) {      /* no colormap allocated? */
+        	int size = (disp->var.bits_per_pixel <= 8) ? 256 : 32;
+		if ((err = fb_alloc_cmap(&disp->cmap, size, 0)))
+		    return err;
+	}
+        if (err) return err;
     }
 
     if (con == fb->currcon) /* current console? */
@@ -1599,6 +1706,31 @@
     return 0;                
 }
 
+    /*
+     *  Helper function to store a single palette register
+     */
+static __inline__ void
+aty128_st_pal(u_int regno, u_int red, u_int green, u_int blue,
+	      struct fb_info_aty128 *info)
+{
+    /* Note: For now, on M3, we set palette on both heads, which may
+     * be useless. Can someone with a M3 check this ?
+     * 
+     * This code would still be useful if using the second CRTC to 
+     * do mirroring
+     */
+
+    if (info->chip_gen == rage_M3) {
+#if 0
+        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
+        aty_st_8(PALETTE_INDEX, regno);
+        aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+#endif
+        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+    }
+    aty_st_8(PALETTE_INDEX, regno);
+    aty_st_le32(PALETTE_DATA, (red<<16)|(green<<8)|blue);
+}
 
 static int
 aty128fb_rasterimg(struct fb_info *info, int start)
@@ -1620,7 +1752,7 @@
     if (!options || !*options)
 	return 0;
 
-    while (this_opt = strsep(&options, ",")) {
+    while ((this_opt = strsep(&options, ",")) != 0) {
 	if (!strncmp(this_opt, "font:", 5)) {
 	    char *p;
 	    int i;
@@ -1633,6 +1765,12 @@
 	    fontname[i] = 0;
 	} else if (!strncmp(this_opt, "noaccel", 7)) {
 	    noaccel = 1;
+#ifdef CONFIG_PMAC_PBOOK
+        } else if (!strncmp(this_opt, "lcd:", 4)) {
+            default_lcd_on = simple_strtoul(this_opt+4, NULL, 0);
+        } else if (!strncmp(this_opt, "crt:", 4)) {
+            default_crt_on = simple_strtoul(this_opt+4, NULL, 0);
+#endif
         }
 #ifdef CONFIG_MTRR
         else if(!strncmp(this_opt, "nomtrr", 6)) {
@@ -1713,7 +1851,11 @@
     info->fb_info.updatevar  = NULL;
     info->fb_info.blank = &aty128fbcon_blank;
     info->fb_info.flags = FBINFO_FLAG_DEFAULT;
-
+#ifdef CONFIG_PMAC_PBOOK
+    info->lcd_on = default_lcd_on;
+    info->crt_on = default_crt_on;
+#endif
+    
     var = default_var;
 #ifdef CONFIG_PPC
     if (_machine == _MACH_Pmac) {
@@ -1724,6 +1866,29 @@
             if (default_vmode <= 0 || default_vmode > VMODE_MAX)
                 default_vmode = VMODE_1024_768_60;
 
+	    /* iMacs need that resolution
+	     * PowerMac2,1 first r128 iMacs
+	     * PowerMac2,2 summer 2000 iMacs
+	     * PowerMac4,1 january 2001 iMacs "flower power"
+	     */
+	    if (machine_is_compatible("PowerMac2,1") ||
+		machine_is_compatible("PowerMac2,2") ||
+		machine_is_compatible("PowerMac4,1"))
+		default_vmode = VMODE_1024_768_75;
+
+	    /* iBook SE */
+	    if (machine_is_compatible("PowerBook2,2"))
+		default_vmode = VMODE_800_600_60;
+ 
+	    /* PowerBook Firewire (Pismo), iBook Dual USB */
+	    if (machine_is_compatible("PowerBook3,1") ||
+	    	machine_is_compatible("PowerBook4,1"))
+		default_vmode = VMODE_1024_768_60;
+
+	    /* PowerBook Titanium */
+	    if (machine_is_compatible("PowerBook3,2"))
+		default_vmode = VMODE_1152_768_60;
+
             if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
                 default_cmode = CMODE_8;
 
@@ -1760,6 +1925,8 @@
     dac = aty_ld_le32(DAC_CNTL);
     dac |= (DAC_8BIT_EN | DAC_RANGE_CNTL);
     dac |= DAC_MASK;
+    if (info->chip_gen == rage_M3)
+    	dac |= DAC_PALETTE2_SNOOP_EN;
     aty_st_le32(DAC_CNTL, dac);
 
     /* turn off bus mastering, just in case */
@@ -1778,6 +1945,14 @@
     if (info->chip_gen == rage_M3)
     	register_backlight_controller(&aty128_backlight_controller, info, "ati");
 #endif /* CONFIG_PMAC_BACKLIGHT */
+#ifdef CONFIG_PMAC_PBOOK
+    if (!info->pdev)
+    	printk(KERN_WARNING "aty128fb: Not a PCI card, can't enable power management\n");
+    else {
+	    info->pm_reg = pci_find_capability(info->pdev, PCI_CAP_ID_PM);
+	    pmu_register_sleep_notifier(&aty128_sleep_notifier);
+    }
+#endif
 
     printk(KERN_INFO "fb%d: %s frame buffer device on %s\n",
 	   GET_FB_IDX(info->fb_info.node), aty128fb_name, name);
@@ -1843,7 +2018,7 @@
 	if ((err = pci_enable_device(pdev))) {
 		printk(KERN_ERR "aty128fb: Cannot enable PCI device: %d\n",
 				err);
-		goto err_out;
+		return -ENODEV;
 	}
 
 	fb_addr = pci_resource_start(pdev, 0);
@@ -2162,6 +2337,12 @@
 
     aty_st_8(CRTC_EXT_CNTL+1, state);
 
+#ifdef CONFIG_PMAC_PBOOK
+    if (info->chip_gen == rage_M3) {
+	aty128_set_crt_enable(info, info->crt_on && !blank);
+	aty128_set_lcd_enable(info, info->lcd_on && !blank);
+    }
+#endif	
 #ifdef CONFIG_PMAC_BACKLIGHT
     if ((_machine == _MACH_Pmac) && !blank)
     	set_backlight_enable(1);
@@ -2200,7 +2381,7 @@
                          u_int transp, struct fb_info *fb)
 {
     struct fb_info_aty128 *info = (struct fb_info_aty128 *)fb;
-    u32 col;
+    u32 palreg;
 
     if (regno > 255)
 	return 1;
@@ -2220,65 +2401,47 @@
     if ((info->current_par.crtc.bpp > 8) && (regno == 0)) {
         int i;
 
-        if (info->chip_gen == rage_M3)
-            aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
-
-        for (i=16; i<256; i++) {
-            aty_st_8(PALETTE_INDEX, i);
-            col = (i << 16) | (i << 8) | i;
-            aty_st_le32(PALETTE_DATA, col);
-        }
-
-        if (info->chip_gen == rage_M3) {
-            aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
-
-            for (i=16; i<256; i++) {
-                aty_st_8(PALETTE_INDEX, i);
-                col = (i << 16) | (i << 8) | i;
-                aty_st_le32(PALETTE_DATA, col);
-            }
-        }
+        for (i=0; i<256; i++)
+            aty128_st_pal(i, i, i, i, info);
     }
 
     /* initialize palette */
 
-    if (info->chip_gen == rage_M3)
-        aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & ~DAC_PALETTE_ACCESS_CNTL);
+    palreg = regno;
 
     if (info->current_par.crtc.bpp == 16)
-        aty_st_8(PALETTE_INDEX, (regno << 3));
-    else
-        aty_st_8(PALETTE_INDEX, regno);
-    col = (red << 16) | (green << 8) | blue;
-    aty_st_le32(PALETTE_DATA, col);
-    if (info->chip_gen == rage_M3) {
-    	aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PALETTE_ACCESS_CNTL);
-        if (info->current_par.crtc.bpp == 16)
-            aty_st_8(PALETTE_INDEX, (regno << 3));
-        else
-            aty_st_8(PALETTE_INDEX, regno);
-        aty_st_le32(PALETTE_DATA, col);
+        palreg = regno * 8;
+
+    if (info->current_par.crtc.depth == 16) {
+        aty128_st_pal(palreg/2, info->palette[regno/2].red, green,
+                      info->palette[regno/2].blue, info);
+        green = info->palette[regno*2].green;
     }
 
+    if (info->current_par.crtc.bpp == 8 || regno < 32)
+        aty128_st_pal(palreg, red, green, blue, info);
+        
     if (regno < 16)
-	switch (info->current_par.crtc.bpp) {
+	switch (info->current_par.crtc.depth) {
 #ifdef FBCON_HAS_CFB16
-	case 9 ... 16:
+	case 15:
 	    info->fbcon_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
                 regno;
 	    break;
+	case 16:
+	    info->fbcon_cmap.cfb16[regno] = (regno << 11) | (regno << 5) |
+                regno;
+	    break;
 #endif
 #ifdef FBCON_HAS_CFB24
-	case 17 ... 24:
+	case 24:
 	    info->fbcon_cmap.cfb24[regno] = (regno << 16) | (regno << 8) |
 		regno;
 	    break;
 #endif
 #ifdef FBCON_HAS_CFB32
-	case 25 ... 32: {
-            u32 i;
-
-            i = (regno << 8) | regno;
+	case 32: {
+            u32 i = (regno << 8) | regno;
             info->fbcon_cmap.cfb32[regno] = (i << 16) | i;
 	    break;
         }
@@ -2291,41 +2454,115 @@
 static void
 do_install_cmap(int con, struct fb_info *info)
 {
-    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    struct display *disp = (con < 0) ? info->disp : (fb_display + con);
+
+    if (disp->cmap.len)
+	fb_set_cmap(&disp->cmap, 1, aty128_setcolreg, info);
+    else
+	fb_set_cmap(fb_default_cmap((disp->var.bits_per_pixel==8) ? 256 :32),
+		    1, aty128_setcolreg, info);
+}
 
-    if (con != fb->currcon)
-	return;
+#define ATY_MIRROR_LCD_ON	0x00000001
+#define ATY_MIRROR_CRT_ON	0x00000002
 
-    if (fb_display[con].cmap.len)
-	fb_set_cmap(&fb_display[con].cmap, 1, aty128_setcolreg, info);
-    else {
-	int size = (fb_display[con].var.bits_per_pixel <= 8) ? 256 : 16;
-	fb_set_cmap(fb_default_cmap(size), 1, aty128_setcolreg, info);
+/* out param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_GET_MIRROR	_IOR('@', 1, sizeof(__u32*))
+/* in param: u32*	backlight value: 0 to 15 */
+#define FBIO_ATY128_SET_MIRROR	_IOW('@', 2, sizeof(__u32*))
+
+static int aty128fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+		       u_long arg, int con, struct fb_info *info)
+{
+    struct fb_info_aty128 *fb = (struct fb_info_aty128 *)info;
+    u32 value;
+    int rc;
+    
+    switch (cmd) {
+#ifdef CONFIG_PMAC_PBOOK
+    case FBIO_ATY128_SET_MIRROR:
+    	if (fb->chip_gen != rage_M3)
+    		return -EINVAL;
+    	rc = get_user(value, (__u32*)arg);
+    	if (rc)
+    		return rc;
+    	fb->lcd_on = (value & 0x01) != 0;
+    	fb->crt_on = (value & 0x02) != 0;
+    	if (!fb->crt_on && !fb->lcd_on)
+    		fb->lcd_on = 1;
+    	aty128_set_crt_enable(fb, fb->crt_on);	
+    	aty128_set_lcd_enable(fb, fb->lcd_on);	
+	break;
+    case FBIO_ATY128_GET_MIRROR:
+    	if (fb->chip_gen != rage_M3)
+    		return -EINVAL;
+	value = (fb->crt_on << 1) | fb->lcd_on;
+    	return put_user(value, (__u32*)arg);
+#endif
+    default:
+	return -EINVAL;
     }
+    return 0;
 }
 
-
 #ifdef CONFIG_PMAC_BACKLIGHT
 static int backlight_conv[] = {
 	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
 	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
 };
 
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides greater power saving and the display is useless without
+ * backlight anyway
+ */
+#define BACKLIGHT_LVDS_OFF
+/* That one prevents proper CRT output with LCD off */
+#undef BACKLIGHT_DAC_OFF
+
 static int
 aty128_set_backlight_enable(int on, int level, void* data)
 {
 	struct fb_info_aty128 *info = (struct fb_info_aty128 *)data;
 	unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL);
-	
+
+	if (!info->lcd_on)
+		on = 0;
 	reg |= LVDS_BL_MOD_EN | LVDS_BLON;
 	if (on && level > BACKLIGHT_OFF) {
+		reg |= LVDS_DIGION;
+		if (!reg & LVDS_ON) {
+			reg &= ~LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+			(void)aty_ld_le32(LVDS_GEN_CNTL);
+			mdelay(10);
+			reg |= LVDS_BLON;
+			aty_st_le32(LVDS_GEN_CNTL, reg);
+		}
 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
 		reg |= (backlight_conv[level] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_ON | LVDS_EN;
+		reg &= ~LVDS_DISPLAY_DIS;
+#endif
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) & (~DAC_PDWN));
+#endif		
 	} else {
 		reg &= ~LVDS_BL_MOD_LEVEL_MASK;
 		reg |= (backlight_conv[0] << LVDS_BL_MOD_LEVEL_SHIFT);
+#ifdef BACKLIGHT_LVDS_OFF
+		reg |= LVDS_DISPLAY_DIS;
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+		(void)aty_ld_le32(LVDS_GEN_CNTL);
+		udelay(10);
+		reg &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGION);
+#endif		
+		aty_st_le32(LVDS_GEN_CNTL, reg);
+#ifdef BACKLIGHT_DAC_OFF
+		aty_st_le32(DAC_CNTL, aty_ld_le32(DAC_CNTL) | DAC_PDWN);
+#endif		
 	}
-	aty_st_le32(LVDS_GEN_CNTL, reg);
 
 	return 0;
 }
@@ -2346,18 +2583,18 @@
 		u_int width, u_int height,
 		struct fb_info_aty128 *info)
 {
-    u32 save_dp_datatype, save_dp_cntl, bppval;
+    u32 save_dp_datatype, save_dp_cntl, dstval;
 
     if (!width || !height)
         return;
 
-    bppval = bpp_to_depth(info->current_par.crtc.bpp);
-    if (bppval == DST_24BPP) {
+    dstval = depth_to_dst(info->current_par.crtc.depth);
+    if (dstval == DST_24BPP) {
         srcx *= 3;
         dstx *= 3;
         width *= 3;
-    } else if (bppval == -EINVAL) {
-        printk("aty128fb: invalid depth\n");
+    } else if (dstval == -EINVAL) {
+        printk("aty128fb: invalid depth or RGBA\n");
         return;
     }
 
@@ -2369,7 +2606,7 @@
     aty_st_le32(SRC_Y_X, (srcy << 16) | srcx);
     aty_st_le32(DP_MIX, ROP3_SRCCOPY | DP_SRC_RECT);
     aty_st_le32(DP_CNTL, DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM);
-    aty_st_le32(DP_DATATYPE, save_dp_datatype | bppval | SRC_DSTCOLOR);
+    aty_st_le32(DP_DATATYPE, save_dp_datatype | dstval | SRC_DSTCOLOR);
 
     aty_st_le32(DST_Y_X, (dsty << 16) | dstx);
     aty_st_le32(DST_HEIGHT_WIDTH, (height << 16) | width);
@@ -2594,6 +2831,139 @@
     fontwidthmask:	FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
 };
 #endif
+
+#ifdef CONFIG_PMAC_PBOOK
+static void
+aty128_set_suspend(struct fb_info_aty128 *info, int suspend)
+{
+	u32	pmgt;
+	u16	pwr_command;
+
+	if (!info->pm_reg)
+		return;
+		
+	/* Set the chip into the appropriate suspend mode (we use D2,
+	 * D3 would require a complete re-initialisation of the chip,
+	 * including PCI config registers, clocks, AGP configuration, ...)
+	 */
+	if (suspend) {
+		/* Make sure CRTC2 is reset. Remove that the day we decide to
+		 * actually use CRTC2 and replace it with real code for disabling
+		 * the CRTC2 output during sleep
+		 */
+		aty_st_le32(CRTC2_GEN_CNTL, aty_ld_le32(CRTC2_GEN_CNTL) &
+			~(CRTC2_EN));
+
+		/* Set the power management mode to be PCI based */
+		pmgt = aty_ld_pll(POWER_MANAGEMENT);
+#if 0
+		pmgt &= ~PWR_MGT_MODE_MASK;
+		pmgt |= PWR_MGT_MODE_PCI | PWR_MGT_ON | PWR_MGT_TRISTATE_MEM_EN | PWR_MGT_AUTO_PWR_UP_EN;
+#else		/* Use this magic value for now */
+		pmgt = 0x0c005407;
+#endif
+		aty_st_pll(POWER_MANAGEMENT, pmgt);
+		(void)aty_ld_pll(POWER_MANAGEMENT);
+		aty_st_le32(BUS_CNTL1, 0x00000010);
+		aty_st_le32(MEM_POWER_MISC, 0x0c830000);
+		mdelay(100);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+		/* Switch PCI power management to D2 */
+		pci_write_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL,
+			(pwr_command & ~PCI_PM_CTRL_STATE_MASK) | 2);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+	} else {
+		/* Switch back PCI power management to D0 */
+		mdelay(100);
+		pci_write_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(100);
+		pci_read_config_word(info->pdev, info->pm_reg+PCI_PM_CTRL, &pwr_command);
+		mdelay(100);
+	}
+}
+
+extern struct display_switch fbcon_dummy;
+
+/*
+ * Save the contents of the frame buffer when we go to sleep,
+ * and restore it when we wake up again.
+ */
+int
+aty128_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	struct fb_info_aty128 *info;
+ 	int result;
+
+	result = PBOOK_SLEEP_OK;
+
+	for (info = board_list; info != NULL; info = info->next) {
+		struct fb_fix_screeninfo fix;
+		int nb;
+
+		aty128fb_get_fix(&fix, fg_console, (struct fb_info *)info);
+		nb = fb_display[fg_console].var.yres * fix.line_length;
+
+		switch (when) {
+		case PBOOK_SLEEP_REQUEST:
+			info->save_framebuffer = vmalloc(nb);
+			if (info->save_framebuffer == NULL)
+				return PBOOK_SLEEP_REFUSE;
+			break;
+		case PBOOK_SLEEP_REJECT:
+			if (info->save_framebuffer) {
+				vfree(info->save_framebuffer);
+				info->save_framebuffer = 0;
+			}
+			break;
+		case PBOOK_SLEEP_NOW:
+			if (info->currcon >= 0)
+				fb_display[info->currcon].dispsw = &fbcon_dummy;
+			
+			wait_for_idle(info);
+			aty128_reset_engine(info);
+			wait_for_idle(info);
+
+			/* Backup fb content */	
+			if (info->save_framebuffer)
+				memcpy_fromio(info->save_framebuffer,
+				       (void *)info->frame_buffer, nb);
+
+			/* Blank display and LCD */
+			aty128fbcon_blank(VESA_POWERDOWN+1, (struct fb_info *)info);
+			
+			/* Sleep the chip */
+			aty128_set_suspend(info, 1);
+
+			break;
+		case PBOOK_WAKE:
+			/* Wake the chip */
+			aty128_set_suspend(info, 0);
+			
+			aty128_reset_engine(info);
+			wait_for_idle(info);
+
+			/* Restore fb content */			
+			if (info->save_framebuffer) {
+				memcpy_toio((void *)info->frame_buffer,
+				       info->save_framebuffer, nb);
+				vfree(info->save_framebuffer);
+				info->save_framebuffer = 0;
+			}
+
+			if (info->currcon >= 0) {
+				aty128_set_dispsw(
+					&fb_display[info->currcon],
+					info,
+					info->current_par.crtc.bpp,
+					info->current_par.accel_flags & FB_ACCELF_TEXT);
+			}
+			aty128fbcon_blank(0, (struct fb_info *)info);
+			break;
+		}
+	}
+	return result;
+}
+#endif /* CONFIG_PMAC_PBOOK */
 
 #ifdef MODULE
 MODULE_AUTHOR("(c)1999-2000 Brad Douglas <brad@neruo.com>");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/clgenfb.c linux/drivers/video/clgenfb.c
--- linux.orig/drivers/video/clgenfb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/clgenfb.c	Mon Jan 14 17:27:24 2002
@@ -413,6 +413,7 @@
 static struct clgenfb_info boards[MAX_NUM_BOARDS];	/* the boards */
 
 static unsigned clgen_def_mode = 1;
+static int noaccel = 0;
 
 static int release_io_ports = 0;
 
@@ -1405,7 +1406,9 @@
 			break;
 
 		case BT_PICASSO4:
+#ifdef CONFIG_ZORRO
 			vga_wseq (fb_info->regs, CL_SEQRF, 0xb8);	/* ### INCOMPLETE!! */
+#endif
 /*          vga_wseq (fb_info->regs, CL_SEQR1F, 0x1c); */
 			break;
 
@@ -2748,6 +2751,23 @@
 
 	DPRINTK ("clgen: (RAM start set to: 0x%p)\n", fb_info->fbmem);
 
+	if (noaccel)
+	{
+		printk("clgen: disabling text acceleration support\n");
+#ifdef FBCON_HAS_CFB8
+		fbcon_clgen_8.bmove = fbcon_cfb8_bmove;
+		fbcon_clgen_8.clear = fbcon_cfb8_clear;
+#endif
+#ifdef FBCON_HAS_CFB16
+		fbcon_clgen_16.bmove = fbcon_cfb16_bmove;
+		fbcon_clgen_16.clear = fbcon_cfb16_clear;
+#endif
+#ifdef FBCON_HAS_CFB32
+		fbcon_clgen_32.bmove = fbcon_cfb32_bmove;
+		fbcon_clgen_32.clear = fbcon_cfb32_clear;
+#endif
+	}
+
 	init_vgachip (fb_info);
 
 	/* set up a few more things, register framebuffer driver etc */
@@ -2851,6 +2871,8 @@
 			if (strcmp (this_opt, s) == 0)
 				clgen_def_mode = i;
 		}
+		if (!strcmp(this_opt, "noaccel"))
+			noaccel = 1;
 	}
 	return 0;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/controlfb.c linux/drivers/video/controlfb.c
--- linux.orig/drivers/video/controlfb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/controlfb.c	Wed Dec 26 16:50:52 2001
@@ -621,14 +621,10 @@
 
 	full = p->total_vram == 0x400000;
 
+#ifdef CONFIG_NVRAM
 	/* Try to pick a video mode out of NVRAM if we have one. */
-	if (default_cmode == CMODE_NVRAM){
+	if (default_cmode == CMODE_NVRAM)
 		cmode = nvram_read_byte(NV_CMODE);
-		if(cmode < CMODE_8 || cmode > CMODE_32)
-			cmode = CMODE_8;
-	} else
-		cmode=default_cmode;
-
 	if (default_vmode == VMODE_NVRAM) {
 		vmode = nvram_read_byte(NV_VMODE);
 		if (vmode < 1 || vmode > VMODE_MAX ||
@@ -639,15 +635,16 @@
 			if (control_mac_modes[vmode - 1].m[full] < cmode)
 				vmode = VMODE_640_480_60;
 		}
-	} else {
-		vmode=default_vmode;
-		if (control_mac_modes[vmode - 1].m[full] < cmode) {
-			if (cmode > CMODE_8)
-				cmode--;
-			else
-				vmode = VMODE_640_480_60;
-		}
 	}
+#endif
+
+	/* If we didn't get something from NVRAM, pick a
+	 * sane default.
+	 */
+	if (vmode <= 0 || vmode > VMODE_MAX)
+		vmode = VMODE_640_480_67;
+	if (cmode < CMODE_8 || cmode > CMODE_32)
+		cmode = CMODE_8;
 
 	if (mac_vmode_to_var(vmode, cmode, &var) < 0) {
 		/* This shouldn't happen! */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/fbcon.c linux/drivers/video/fbcon.c
--- linux.orig/drivers/video/fbcon.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/fbcon.c	Wed Dec 26 16:50:52 2001
@@ -75,6 +75,7 @@
 #include <linux/selection.h>
 #include <linux/smp.h>
 #include <linux/init.h>
+#include <linux/pm.h>
 
 #include <asm/irq.h>
 #include <asm/system.h>
@@ -137,6 +138,12 @@
 static void fbcon_free_font(struct display *);
 static int fbcon_set_origin(struct vc_data *);
 
+#ifdef CONFIG_PM
+static int pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data);
+static struct pm_dev *pm_fbcon;
+static int fbcon_sleeping;
+#endif
+
 /*
  * Emmanuel: fbcon will now use a hardware cursor if the
  * low-level driver provides a non-NULL dispsw->cursor pointer,
@@ -233,6 +240,7 @@
 static struct timer_list cursor_timer = {
     function: cursor_timer_handler
 };
+static int use_timer_cursor;
 
 static void cursor_timer_handler(unsigned long dev_addr)
 {
@@ -457,11 +465,16 @@
 #endif
 
     if (irqres) {
+	use_timer_cursor = 1;
 	cursor_blink_rate = DEFAULT_CURSOR_BLINK_RATE;
 	cursor_timer.expires = jiffies+HZ/50;
 	add_timer(&cursor_timer);
     }
 
+#ifdef CONFIG_PM
+    pm_fbcon = pm_register(PM_SYS_DEV, PM_SYS_VGA, pm_fbcon_request);
+#endif
+
     return display_desc;
 }
 
@@ -1558,6 +1571,10 @@
 
     if (blank < 0)	/* Entering graphics mode */
 	return 0;
+#ifdef CONFIG_PM
+    if (fbcon_sleeping)
+    	return 0;
+#endif /* CONFIG_PM */
 
     fbcon_cursor(p->conp, blank ? CM_ERASE : CM_DRAW);
 
@@ -2446,6 +2463,39 @@
 
     return done ? (LOGO_H + fontheight(p) - 1) / fontheight(p) : 0 ;
 }
+
+#ifdef CONFIG_PM
+/* console.c doesn't do enough here */
+static int
+pm_fbcon_request(struct pm_dev *dev, pm_request_t rqst, void *data)
+{
+	unsigned long flags;
+	
+	switch (rqst)
+	{
+	case PM_RESUME:
+		acquire_console_sem();
+		fbcon_sleeping = 0;
+		if (use_timer_cursor) {
+			cursor_timer.expires = jiffies+HZ/50;
+			add_timer(&cursor_timer);
+		}
+		release_console_sem();
+		break;
+	case PM_SUSPEND:
+		acquire_console_sem();
+		save_flags(flags);
+		cli();
+		if (use_timer_cursor)
+			del_timer(&cursor_timer);
+		fbcon_sleeping = 1;
+		restore_flags(flags);
+		release_console_sem();
+		break;
+	}
+	return 0;
+}
+#endif /* CONFIG_PM */
 
 /*
  *  The console `switch' structure for the frame buffer based console
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- linux.orig/drivers/video/fbmem.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/video/fbmem.c	Wed Feb 13 16:54:36 2002
@@ -114,6 +114,8 @@
 extern int rivafb_setup(char*);
 extern int tdfxfb_init(void);
 extern int tdfxfb_setup(char*);
+extern int tridentfb_init(void);
+extern int tridentfb_setup(char*);
 extern int sisfb_init(void);
 extern int sisfb_setup(char*);
 extern int stifb_init(void);
@@ -204,6 +206,9 @@
 #endif 
 #ifdef CONFIG_FB_SIS
 	{ "sisfb", sisfb_init, sisfb_setup },
+#endif
+#ifdef CONFIG_FB_TRIDENT
+	{ "trident", tridentfb_init, tridentfb_setup },
 #endif
 
 	/*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/imsttfb.c linux/drivers/video/imsttfb.c
--- linux.orig/drivers/video/imsttfb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/imsttfb.c	Wed Dec 26 16:50:52 2001
@@ -371,7 +371,6 @@
 	TVP = 1
 };
 
-#define USE_NV_MODES		1
 #define INIT_BPP		8
 #define INIT_XRES		640
 #define INIT_YRES		480
@@ -384,7 +383,8 @@
 static char curblink __initdata = 1;
 static char noaccel __initdata = 0;
 #if defined(CONFIG_PPC)
-static signed char init_vmode __initdata = -1, init_cmode __initdata = -1;
+static signed char init_vmode __initdata = VMODE_NVRAM;
+static signed char init_cmode __initdata = CMODE_NVRAM;
 #endif
 
 static struct imstt_regvals tvp_reg_init_2 = {
@@ -1804,20 +1804,25 @@
 		}
 	}
 
-#if USE_NV_MODES && defined(CONFIG_PPC)
+#ifdef CONFIG_ALL_PPC
 	{
 		int vmode = init_vmode, cmode = init_cmode;
 
-		if (vmode == -1) {
+#ifdef CONFIG_NVRAM
+		/* Attempt to read vmode/cmode from NVRAM */
+		if (vmode == VMODE_NVRAM)
 			vmode = nvram_read_byte(NV_VMODE);
-			if (vmode <= 0 || vmode > VMODE_MAX)
-				vmode = VMODE_640_480_67;
-		}
-		if (cmode == -1) {
+		if (cmode == CMODE_NVRAM)
 			cmode = nvram_read_byte(NV_CMODE);
-			if (cmode < CMODE_8 || cmode > CMODE_32)
-				cmode = CMODE_8;
-		}
+#endif
+		/* If we didn't get something from NVRAM, pick a
+		 * sane default.
+		 */
+		if (vmode <= 0 || vmode > VMODE_MAX)
+			vmode = VMODE_640_480_67;
+		if (cmode < CMODE_8 || cmode > CMODE_32)
+			cmode = CMODE_8;
+
 		if (mac_vmode_to_var(vmode, cmode, &p->disp.var)) {
 			p->disp.var.xres = p->disp.var.xres_virtual = INIT_XRES;
 			p->disp.var.yres = p->disp.var.yres_virtual = INIT_YRES;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/offb.c linux/drivers/video/offb.c
--- linux.orig/drivers/video/offb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/offb.c	Wed Dec 26 16:50:52 2001
@@ -52,7 +52,8 @@
 	cmap_r128,	/* ATI Rage128 */
 	cmap_M3A,	/* ATI Rage Mobility M3 Head A */
 	cmap_M3B,	/* ATI Rage Mobility M3 Head B */
-	cmap_radeon	/* ATI Radeon */
+	cmap_radeon,	/* ATI Radeon */
+	cmap_gxt2000	/* IBM GXT2000 */
 };
 
 struct fb_info_offb {
@@ -64,6 +65,7 @@
     volatile unsigned char *cmap_adr;
     volatile unsigned char *cmap_data;
     int cmap_type;
+    int blanked;
     union {
 #ifdef FBCON_HAS_CFB16
 	u16 cfb16[16];
@@ -210,9 +212,11 @@
 static int offb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			 struct fb_info *info)
 {
-    if (con == currcon) /* current console? */
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
+    if (con == currcon && !info2->blanked) /* current console? */
 	return fb_get_cmap(cmap, kspc, offb_getcolreg, info);
-    else if (fb_display[con].cmap.len) /* non default colormap? */
+    if (fb_display[con].cmap.len) /* non default colormap? */
 	fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
     else
     {
@@ -240,7 +244,7 @@
 	if ((err = fb_alloc_cmap(&fb_display[con].cmap, size, 0)))
 	    return err;
     }
-    if (con == currcon)			/* current console? */
+    if (con == currcon && !info2->blanked)		/* current console? */
 	return fb_set_cmap(cmap, kspc, offb_setcolreg, info);
     else
 	fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
@@ -426,7 +430,7 @@
     info->cmap_type = cmap_unknown;
     if (depth == 8)
     {
-    	/* XXX kludge for ati */
+    	/* XXX kludge for ati's */
 	if (dp && !strncmp(name, "ATY,Rage128", 11)) {
 		unsigned long regbase = dp->addrs[2].address;
 		info->cmap_adr = ioremap(regbase, 0x1FFF);
@@ -445,10 +449,19 @@
 		info->cmap_adr = ioremap(regbase, 0x1FFF);
 		info->cmap_type = cmap_radeon;
 	} else if (!strncmp(name, "ATY,", 4)) {
+		/* Hrm... this is bad... any recent ATI not covered
+		 * by the previous cases will get there, while this
+		 * cose is only good for mach64's. Gotta figure out
+		 * a proper fix... --BenH.
+		 */
 		unsigned long base = address & 0xff000000UL;
 		info->cmap_adr = ioremap(base + 0x7ff000, 0x1000) + 0xcc0;
 		info->cmap_data = info->cmap_adr + 1;
 		info->cmap_type = cmap_m64;
+	} else if (dp && device_is_compatible(dp, "pci1014,b7")) {
+		unsigned long regbase = dp->addrs[0].address;
+		info->cmap_adr = ioremap(regbase + 0x6000, 0x1000);
+		info->cmap_type = cmap_gxt2000;
 	}
         fix->visual = info->cmap_adr ? FB_VISUAL_PSEUDOCOLOR
 				     : FB_VISUAL_STATIC_PSEUDOCOLOR;
@@ -620,8 +633,10 @@
 
 static int offbcon_switch(int con, struct fb_info *info)
 {
+    struct fb_info_offb *info2 = (struct fb_info_offb *)info;
+
     /* Do we have to save the colormap? */
-    if (fb_display[currcon].cmap.len)
+    if (fb_display[currcon].cmap.len && !info2->blanked)
 	fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
 
     currcon = con;
@@ -652,6 +667,15 @@
     if (!info2->cmap_adr)
 	return;
 
+    if (!info2->blanked) {
+	if (!blank)
+	    return;
+	if (fb_display[currcon].cmap.len)
+	    fb_get_cmap(&fb_display[currcon].cmap, 1, offb_getcolreg, info);
+    }
+
+    info2->blanked = blank;
+
     if (blank)
 	for (i = 0; i < 256; i++) {
 	    switch(info2->cmap_type) {
@@ -664,26 +688,29 @@
 	    	}
 	    	break;
 	    case cmap_M3A:
-	        /* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
+		/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
+		out_le32((unsigned *)(info2->cmap_adr + 0x58),
+			in_le32((unsigned *)(info2->cmap_adr + 0x58)) & ~0x20);
 	    case cmap_r128:
-	    	/* Set palette index & data */
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		/* Set palette index & data */
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
 	    case cmap_M3B:
-	        /* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
-	    	out_le32((unsigned *)(info2->cmap_adr + 0x58),
-	    		in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
-	    	/* Set palette index & data */
-	    	out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
+		out_le32((unsigned *)(info2->cmap_adr + 0x58),
+			in_le32((unsigned *)(info2->cmap_adr + 0x58)) | 0x20);
+		/* Set palette index & data */
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
 	    case cmap_radeon:
-    	        out_8(info2->cmap_adr + 0xb0, i);
-	    	out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
-	    	break;
+		out_8(info2->cmap_adr + 0xb0, i);
+		out_le32((unsigned *)(info2->cmap_adr + 0xb4), 0);
+		break;
+	    case cmap_gxt2000:
+		out_le32((unsigned *)info2->cmap_adr + i, 0);
+		break;
 	    }
 	}
     else
@@ -768,6 +795,10 @@
 	out_8(info2->cmap_adr + 0xb0, regno);
   	out_le32((unsigned *)(info2->cmap_adr + 0xb4),
     		(red << 16 | green << 8 | blue));
+	break;
+    case cmap_gxt2000:
+	out_le32((unsigned *)info2->cmap_adr + regno,
+		 (red << 16 | green << 8 | blue));
 	break;
     }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/platinumfb.c linux/drivers/video/platinumfb.c
--- linux.orig/drivers/video/platinumfb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/platinumfb.c	Wed Dec 26 16:50:52 2001
@@ -126,7 +126,9 @@
  */
 
 static void platinum_of_init(struct device_node *dp);
-static inline int platinum_vram_reqd(int video_mode, int color_mode);
+static inline int platinum_vram_reqd(const struct fb_info_platinum* info,
+					int video_mode,
+					int color_mode);
 static int read_platinum_sense(struct fb_info_platinum *info);
 static void set_platinum_clock(struct fb_info_platinum *info);
 static void platinum_set_par(const struct fb_par_platinum *par, struct fb_info_platinum *info);
@@ -443,10 +445,12 @@
 	}
 }
 
-static inline int platinum_vram_reqd(int video_mode, int color_mode)
+static inline int platinum_vram_reqd(const struct fb_info_platinum *info, int video_mode, int color_mode)
 {
-	return vmode_attrs[video_mode-1].vres *
-	       (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20) +0x1000;
+	unsigned int pitch = 
+	       (vmode_attrs[video_mode-1].hres * (1<<color_mode) + 0x20);
+	fixup_pitch(pitch, info, color_mode);
+	return vmode_attrs[video_mode-1].vres * pitch;
 }
 
 #define STORE_D2(a, d) { \
@@ -487,7 +491,7 @@
 	volatile struct cmap_regs	*cmap_regs = info->cmap_regs;
 	struct platinum_regvals		*init;
 	int				i;
-	int				vmode, cmode;
+	int				vmode, cmode, pitch;
 	
 	info->current_par = *par;
 
@@ -506,7 +510,9 @@
 						init->offset[cmode] + 4 - cmode :
 						init->offset[cmode]));
 	out_be32(&platinum_regs->reg[16].r, (unsigned) info->frame_buffer_phys+init->fb_offset+0x10);
-	out_be32(&platinum_regs->reg[18].r, init->pitch[cmode]);
+	pitch = init->pitch[cmode];
+	fixup_pitch(pitch, info, cmode);
+	out_be32(&platinum_regs->reg[18].r, pitch);
 	out_be32(&platinum_regs->reg[19].r, (info->total_vram == 0x100000 ?
 					     init->mode[cmode+1] :
 					     init->mode[cmode]));
@@ -535,6 +541,7 @@
 		display_info.depth = ( (cmode == CMODE_32) ? 32 :
 				      ((cmode == CMODE_16) ? 16 : 8));
 		display_info.pitch = vmode_attrs[vmode-1].hres * (1<<cmode) + 0x20;
+		fixup_pitch(display_info.pitch, info, cmode);
 		display_info.mode = vmode;
 		strncpy(display_info.name, "platinum",
 			sizeof(display_info.name));
@@ -558,25 +565,27 @@
 	sense = read_platinum_sense(info);
 	printk(KERN_INFO "Monitor sense value = 0x%x, ", sense);
 
+#ifdef CONFIG_NVRAM
 	if (default_vmode == VMODE_NVRAM) {
 		default_vmode = nvram_read_byte(NV_VMODE);
 		if (default_vmode <= 0 || default_vmode > VMODE_MAX ||
 		    !platinum_reg_init[default_vmode-1])
 			default_vmode = VMODE_CHOOSE;
 	}
-	if (default_vmode == VMODE_CHOOSE) {
+	if (default_cmode == CMODE_NVRAM)
+		default_cmode = nvram_read_byte(NV_CMODE);
+#endif
+	if (default_vmode == VMODE_CHOOSE)
 		default_vmode = mac_map_monitor_sense(sense);
-	}
 	if (default_vmode <= 0 || default_vmode > VMODE_MAX)
 		default_vmode = VMODE_640_480_60;
-	if (default_cmode == CMODE_NVRAM)
-		default_cmode = nvram_read_byte(NV_CMODE);
 	if (default_cmode < CMODE_8 || default_cmode > CMODE_32)
 		default_cmode = CMODE_8;
 	/*
 	 * Reduce the pixel size if we don't have enough VRAM.
 	 */
-	while(default_cmode > CMODE_8 && platinum_vram_reqd(default_vmode, default_cmode) > info->total_vram)
+	while(default_cmode > CMODE_8 && platinum_vram_reqd(info, default_vmode, default_cmode)
+	    > info->total_vram)
 		default_cmode--;
 
 	printk("using video mode %d and color mode %d.\n", default_vmode, default_cmode);
@@ -782,7 +791,7 @@
 		return -EINVAL;
 	}
 
-	if (platinum_vram_reqd(par->vmode, par->cmode) > info->total_vram) {
+	if (platinum_vram_reqd(info, par->vmode, par->cmode) > info->total_vram) {
 		printk(KERN_ERR "platinum_var_to_par, not enough ram for vmode %d, cmode %d.\n", par->vmode, par->cmode);
 		return -EINVAL;
 	}
@@ -826,7 +835,8 @@
 	fix->visual = (par->cmode == CMODE_8) ?
 		FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
 	fix->line_length = vmode_attrs[par->vmode-1].hres * (1<<par->cmode) + 0x20;
-
+	fixup_pitch(fix->line_length, info, par->cmode);
+	
 	return 0;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/platinumfb.h linux/drivers/video/platinumfb.h
--- linux.orig/drivers/video/platinumfb.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/platinumfb.h	Wed Dec 26 16:50:52 2001
@@ -158,7 +158,7 @@
 /* 832x624, 75Hz (13) */
 static struct platinum_regvals platinum_reg_init_13 = {
 	0x70,
-	{ 864, 1680, 3360 },	/* MacOS does 1680 instead of 1696 to fit 16bpp in 1MB */
+	{ 864, 1696, 3360 },
 	{ 0xff0, 4, 0, 0, 0, 0, 0x299, 0,
 	  0, 0x21e, 0x120, 0x10, 0x23f, 0x1f, 0x25, 0x37,
 	  0x8a, 0x22a, 0x23e, 0x536, 0x534, 4, 9, 0x52,
@@ -310,6 +310,13 @@
 	{ 2, 0, 0xff }, { 0x11, 0x15, 0x19 },
 	{{ 94, 5 + DIV16 }, { 48, 7 + DIV8 }}
 };
+
+/* MacOS does 1680 instead of 1696 to fit 832x624@75-16bpp in 1MB */
+#define fixup_pitch(ll, info, cmode) \
+	do { \
+	  if ((cmode) == CMODE_16 && (ll) == 1696 && info->total_vram == 0x100000) \
+	      (ll) = 1680; \
+	} while(0)
 
 static struct platinum_regvals *platinum_reg_init[VMODE_MAX] = {
 	&platinum_reg_init_1,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/radeon.h linux/drivers/video/radeon.h
--- linux.orig/drivers/video/radeon.h	Thu Oct 25 20:53:52 2001
+++ linux/drivers/video/radeon.h	Wed Jan 16 17:26:48 2002
@@ -96,7 +96,9 @@
 #define MEM_VGA_WP_SEL                         0x0038  
 #define MEM_VGA_RP_SEL                         0x003C  
 #define HDP_DEBUG                              0x0138  
-#define SW_SEMAPHORE                           0x013C  
+#define SW_SEMAPHORE                           0x013C
+#define CRTC2_GEN_CNTL                         0x03f8  
+#define CRTC2_DISPLAY_BASE_ADDR                0x033c
 #define SURFACE_CNTL                           0x0B00  
 #define SURFACE0_LOWER_BOUND                   0x0B04  
 #define SURFACE1_LOWER_BOUND                   0x0B14  
@@ -337,6 +339,7 @@
 #define DST_Y_X				       0x1438
 #define DST_WIDTH_HEIGHT		       0x1598
 #define DST_HEIGHT_WIDTH		       0x143c
+#define DST_OFFSET                             0x1404
 #define SRC_CLUT_ADDRESS                       0x1780  
 #define SRC_CLUT_DATA                          0x1784  
 #define SRC_CLUT_DATA_RD                       0x1788  
@@ -380,6 +383,7 @@
 #define LVDS_GEN_CNTL			       0x02d0
 #define LVDS_PLL_CNTL			       0x02d4
 #define TMDS_CRC			       0x02a0
+#define TMDS_TRANSMITTER_CNTL		       0x02a4
 
 #define RADEON_BASE_CODE		       0x0f0b
 #define RADEON_BIOS_0_SCRATCH		       0x0010
@@ -406,11 +410,11 @@
 #define SPLL_CNTL                                  0x000c
 #define SCLK_CNTL                                  0x000d
 #define MPLL_CNTL                                  0x000e
+#define MDLL_CKO                                   0x000f
 #define MCLK_CNTL                                  0x0012
 #define AGP_PLL_CNTL                               0x000b
 #define PLL_TEST_CNTL                              0x0013
 
-
 /* MCLK_CNTL bit constants */
 #define FORCEON_MCLKA				   (1 << 16)
 #define FORCEON_MCLKB         		   	   (1 << 17)
@@ -474,10 +478,17 @@
 #define CRTC_INTERLACE_EN			   (1 << 1)
 #define CRTC_EXT_DISP_EN      			   (1 << 24)
 #define CRTC_EN					   (1 << 25)
+#define CRTC_DISP_REQ_EN_B                         (1 << 26)
 
 /* CRTC_STATUS bit constants */
 #define CRTC_VBLANK                                0x00000001
 
+/* CRTC2_GEN_CNTL bit constants */
+#define CRT2_ON                                    (1 << 7)
+#define CRTC2_DISPLAY_DIS                          (1 << 23)
+#define CRTC2_EN                                   (1 << 25)
+#define CRTC2_DISP_REQ_EN_B                        (1 << 26)
+
 /* CUR_OFFSET, CUR_HORZ_VERT_POSN, CUR_HORZ_VERT_OFF bit constants */
 #define CUR_LOCK                                   0x80000000
 
@@ -523,14 +534,26 @@
 #define LVDS_PANEL_TYPE				   (1 << 2)
 #define LVDS_PANEL_FORMAT			   (1 << 3)
 #define LVDS_EN					   (1 << 7)
+#define LVDS_BL_MOD_LEVEL_MASK			   0x0000ff00
+#define LVDS_BL_MOD_LEVEL_SHIFT			   8
+#define LVDS_BL_MOD_EN				   (1 << 16)
 #define LVDS_DIGON				   (1 << 18)
 #define LVDS_BLON				   (1 << 19)
 #define LVDS_SEL_CRTC2				   (1 << 23)
+#define LVDS_STATE_MASK	\
+	(LVDS_ON | LVDS_DISPLAY_DIS | LVDS_BL_MOD_LEVEL_MASK | \
+	 LVDS_EN | LVDS_DIGON | LVDS_BLON)
 
 /* LVDS_PLL_CNTL bit constatns */
 #define HSYNC_DELAY_SHIFT			   0x1c
 #define HSYNC_DELAY_MASK			   (0xf << 0x1c)
 
+/* TMDS_TRANSMITTER_CNTL bit constants */
+#define TMDS_PLL_EN				   (1 << 0)
+#define TMDS_PLLRST				   (1 << 1)
+#define TMDS_RAN_PAT_RST			   (1 << 7)
+#define ICHCSEL					   (1 << 28)
+
 /* FP_HORZ_STRETCH bit constants */
 #define HORZ_STRETCH_RATIO_MASK			   0xffff
 #define HORZ_STRETCH_RATIO_MAX			   4096
@@ -561,6 +584,7 @@
 #define DAC_4BPP_PIX_ORDER                         0x00000200
 #define DAC_CRC_EN                                 0x00080000
 #define DAC_MASK_ALL				   (0xff << 24)
+#define DAC_EXPAND_MODE				   (1 << 14)
 #define DAC_VGA_ADR_EN				   (1 << 13)
 #define DAC_RANGE_CNTL				   (3 << 0)
 #define DAC_BLANKING				   (1 << 2)
@@ -742,6 +766,15 @@
 #define DP_SRC_HOST                                0x00000300
 #define DP_SRC_HOST_BYTEALIGN                      0x00000400
 
+/* MPLL_CNTL bit constants */
+#define MPLL_RESET                                 0x00000001
+
+/* MDLL_CKO bit constants */
+#define MDLL_CKO__MCKOA_RESET                      0x00000002
+
+/* VCLK_ECP_CNTL constants */
+#define PIXCLK_ALWAYS_ONb                          0x00000040
+#define PIXCLK_DAC_ALWAYS_ONb                      0x00000080
 
 /* masks */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/radeonfb.c linux/drivers/video/radeonfb.c
--- linux.orig/drivers/video/radeonfb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/radeonfb.c	Wed Feb 13 17:37:44 2002
@@ -15,13 +15,17 @@
  *			blanking, pan_display, and cmap fixes, 0.1.0
  *	2001-10-10	Radeon 7500 and 8500 support, and experimental
  *			flat panel support, 0.1.1
+ *	2001-11-17	Radeon M6 (ppc) support, Daniel Berlin, 0.1.2
+ *	2001-11-18	DFP fixes, Kevin Hendricks, 0.1.3
+ *	2001-11-29	more cmap, backlight fixes, Benjamin Herrenschmidt
+ *	2002-01-18	DFP panel detection via BIOS, Michael Clark, 0.1.4
  *
  *	Special thanks to ATI DevRel team for their hardware donations.
  *
  */
 
 
-#define RADEON_VERSION	"0.1.1"
+#define RADEON_VERSION	"0.1.4"
 
 
 #include <linux/config.h>
@@ -39,12 +43,33 @@
 #include <linux/ioport.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/vmalloc.h>
 
 #include <asm/io.h>
 #if defined(__powerpc__)
 #include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <video/macmodes.h>
+
+#ifdef CONFIG_NVRAM
+#include <linux/nvram.h>
+#endif
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
 #endif
 
+#ifdef CONFIG_ADB_PMU
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#endif
+
+#endif /* __powerpc__ */
+
 #include <video/fbcon.h> 
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb16.h>
@@ -65,17 +90,17 @@
 
 
 enum radeon_chips {
-	RADEON_QD,
-	RADEON_QE,
-	RADEON_QF,
-	RADEON_QG,
-	RADEON_QY,
-	RADEON_QZ,
-	RADEON_QL,
-	RADEON_QW,
-	RADEON_LW,
-	RADEON_LY,
-	RADEON_LZ
+	RADEON_QD,	/* Radeon R100 */
+	RADEON_QE,	/* Radeon R100 */
+	RADEON_QF,	/* Radeon R100 */
+	RADEON_QG,	/* Radeon R100 */
+	RADEON_QY,	/* Radeon RV100 (VE) */
+	RADEON_QZ,	/* Radeon RV100 (VE) */
+	RADEON_QL,	/* Radeon R200 (8500) */
+	RADEON_QW,	/* Radeon RV200 (7500) */
+	RADEON_LW,	/* Radeon Mobility M7 */
+	RADEON_LY,	/* Radeon Mobility M6 */
+	RADEON_LZ	/* Radeon Mobility M6 */
 };
 
 
@@ -192,7 +217,6 @@
 	u32 flags;
 	u32 pix_clock;
 	int xres, yres;
-	int bpp;
 
 	/* DDA regs */
 	u32 dda_config;
@@ -214,6 +238,7 @@
 	u32 lvds_gen_cntl;
 	u32 lvds_pll_cntl;
 	u32 tmds_crc;
+	u32 tmds_transmitter_cntl;
 
 #if defined(__BIG_ENDIAN)
 	u32 surface_cntl;
@@ -238,6 +263,9 @@
 
 	struct pci_dev *pdev;
 
+	unsigned char *EDID;
+	unsigned char *bios_seg;
+
 	struct display disp;
 	int currcon;
 	struct display *currcon_display;
@@ -250,13 +278,19 @@
 	int pitch, bpp, depth;
 	int xres, yres, pixclock;
 
+	int use_default_var;
+	int got_dfpinfo;
+
 	int hasCRTC2;
 	int crtDisp_type;
 	int dviDisp_type;
 
 	int panel_xres, panel_yres;
+	int clock;
 	int hOver_plus, hSync_width, hblank;
 	int vOver_plus, vSync_width, vblank;
+	int hAct_high, vAct_high, interlaced;
+	int synct, misc;
 
 	u32 dp_gui_master_cntl;
 
@@ -281,6 +315,13 @@
 #endif  
         } con_cmap;
 #endif  
+
+#ifdef CONFIG_PMAC_PBOOK
+	unsigned char *save_framebuffer;
+	int pm_reg;
+#endif
+
+	struct radeonfb_info *next;
 };
 
 
@@ -433,6 +474,14 @@
 }
 
 
+static inline int var_to_depth(const struct fb_var_screeninfo *var)
+{
+	if (var->bits_per_pixel != 16)
+		return var->bits_per_pixel;
+	return (var->green.length == 6) ? 16 : 15;
+}
+
+
 static void _radeon_engine_reset(struct radeonfb_info *rinfo)
 {
 	u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
@@ -546,7 +595,9 @@
 static char fontname[40] __initdata;
 static char *mode_option __initdata;
 static char noaccel __initdata = 0;
-static char panel_yres __initdata = 0;
+static int panel_yres __initdata = 0;
+static char force_dfp __initdata = 0;
+static struct radeonfb_info *board_list = NULL;
 
 #ifdef FBCON_HAS_CFB8
 static struct display_switch fbcon_radeon8;
@@ -598,11 +649,31 @@
 static void radeon_get_pllinfo(struct radeonfb_info *rinfo, char *bios_seg);
 static void radeon_get_moninfo (struct radeonfb_info *rinfo);
 static int radeon_get_dfpinfo (struct radeonfb_info *rinfo);
-static int radeon_read_OF (struct radeonfb_info *rinfo);
+static int radeon_get_dfpinfo_BIOS(struct radeonfb_info *rinfo);
+static void radeon_get_EDID(struct radeonfb_info *rinfo);
+static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo);
+static void radeon_update_default_var(struct radeonfb_info *rinfo);
 
-#if defined(__powerpc__)
+
+#ifdef CONFIG_ALL_PPC
+static int radeon_read_OF (struct radeonfb_info *rinfo);
+static int radeon_get_EDID_OF(struct radeonfb_info *rinfo);
 extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev);
-#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when);
+static struct pmu_sleep_notifier radeon_sleep_notifier = {
+	radeon_sleep_notify, SLEEP_LEVEL_VIDEO,
+};
+static int radeon_set_backlight_enable(int on, int level, void *data);
+static int radeon_set_backlight_level(int level, void *data);
+static struct backlight_controller radeon_backlight_controller = {
+	radeon_set_backlight_enable,
+	radeon_set_backlight_level
+};
+#endif /* CONFIG_PMAC_PBOOK */
+
+#endif /* CONFIG_ALL_PPC */
 
 static struct fb_ops radeon_fb_ops = {
 	fb_get_fix:		radeonfb_get_fix,
@@ -656,6 +727,8 @@
                         memcpy(fontname, this_opt + 5, i);
                 } else if (!strncmp(this_opt, "noaccel", 7)) {
 			noaccel = 1;
+		} else if (!strncmp(this_opt, "dfp", 3)) {
+			force_dfp = 1;
 		} else if (!strncmp(this_opt, "panel_yres:", 11)) {
 			panel_yres = simple_strtoul((this_opt+11), NULL, 0);
                 } else
@@ -681,7 +754,6 @@
 	struct radeonfb_info *rinfo;
 	u32 tmp;
 	int i, j;
-	char *bios_seg = NULL;
 
 	RTRACE("radeonfb_pci_register BEGIN\n");
 
@@ -840,12 +912,28 @@
 			break;
 	}
 
-	bios_seg = radeon_find_rom(rinfo);
-	radeon_get_pllinfo(rinfo, bios_seg);
+	rinfo->bios_seg = radeon_find_rom(rinfo);
+	radeon_get_pllinfo(rinfo, rinfo->bios_seg);
 
 	RTRACE("radeonfb: probed %s %dk videoram\n", (rinfo->ram_type), (rinfo->video_ram/1024));
 
+#if !defined(__powerpc__)
 	radeon_get_moninfo(rinfo);
+#else
+	switch (pdev->device) {
+		case PCI_DEVICE_ID_RADEON_LW:
+		case PCI_DEVICE_ID_RADEON_LY:
+		case PCI_DEVICE_ID_RADEON_LZ:
+			rinfo->dviDisp_type = MT_LCD;
+			break;
+		default:
+			radeon_get_moninfo(rinfo);
+			break;
+	}
+#endif
+
+	radeon_get_EDID(rinfo);
+
 	if ((rinfo->dviDisp_type == MT_DFP) || (rinfo->dviDisp_type == MT_LCD) ||
 	    (rinfo->crtDisp_type == MT_DFP)) {
 		if (!radeon_get_dfpinfo(rinfo)) {
@@ -875,6 +963,9 @@
 	/* XXX turn off accel for now, blts aren't working right */
 	noaccel = 1;
 
+	/* currcon not yet configured, will be set by first switch */
+	rinfo->currcon = -1;
+
 	/* set all the vital stuff */
 	radeon_set_fbinfo (rinfo);
 
@@ -892,6 +983,8 @@
 	}
 
 	pci_set_drvdata(pdev, rinfo);
+	rinfo->next = board_list;
+	board_list = rinfo;
 
 	if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
 		printk ("radeonfb: could not register framebuffer\n");
@@ -910,6 +1003,19 @@
 		radeon_engine_init (rinfo);
 	}
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (rinfo->dviDisp_type == MT_LCD)
+		register_backlight_controller(&radeon_backlight_controller,
+					      rinfo, "ati");
+#endif
+
+#ifdef CONFIG_PMAC_PBOOK
+	if (rinfo->dviDisp_type == MT_LCD) {
+		rinfo->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
+		pmu_register_sleep_notifier(&radeon_sleep_notifier);
+	}
+#endif
+
 	printk ("radeonfb: ATI %s %s %d MB\n", rinfo->name, rinfo->ram_type,
 		(rinfo->video_ram/(1024*1024)));
 
@@ -1054,7 +1160,7 @@
 		printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d from BIOS\n",
 			rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
 	} else {
-#if defined(__powerpc__)
+#ifdef CONFIG_ALL_PPC
 		if (radeon_read_OF(rinfo)) {
 			unsigned int tmp, Nx, M, ref_div, xclk;
 
@@ -1116,6 +1222,11 @@
 {
 	unsigned int tmp;
 
+	if (force_dfp) {
+		rinfo->dviDisp_type = MT_DFP;
+		return;
+	}
+
 	tmp = INREG(RADEON_BIOS_4_SCRATCH);
 
 	if (rinfo->hasCRTC2) {
@@ -1155,74 +1266,251 @@
 }
 
 
-static int radeon_get_dfpinfo (struct radeonfb_info *rinfo)
+
+static void radeon_get_EDID(struct radeonfb_info *rinfo)
 {
-	unsigned int tmp;
-	unsigned short a, b;
+#ifdef CONFIG_ALL_PPC
+	if (!radeon_get_EDID_OF(rinfo))
+		RTRACE("radeonfb: could not retrieve EDID from OF\n");
+#else
+	/* XXX use other methods later */
+#endif
+}
 
-	if (panel_yres) {
-		rinfo->panel_yres = panel_yres;
-	} else {
-		tmp = INREG(FP_VERT_STRETCH);
-		tmp &= 0x00fff000;
-		rinfo->panel_yres = (unsigned short)(tmp >> 0x0c) + 1;
-	}
 
-	switch (rinfo->panel_yres) {
-		case 480:
-			rinfo->panel_xres = 640;
-			break;
-		case 600:
-			rinfo->panel_xres = 800;
-			break;
-		case 786:
-			rinfo->panel_xres = 1024;
-			break;
-		case 1024:
-			rinfo->panel_xres = 1280;
-			break;
-		case 1050:
-			rinfo->panel_xres = 1400;
-			break;
-		case 1200:
-			rinfo->panel_xres = 1600;
-			break;
-		default:
-			printk("radeonfb: Failed to detect DFP panel size\n");
-			return 0;
+#ifdef CONFIG_ALL_PPC
+static int radeon_get_EDID_OF(struct radeonfb_info *rinfo)
+{
+	struct device_node *dp;
+	unsigned char *pedid = NULL;
+
+	dp = pci_device_to_OF_node(rinfo->pdev);
+	pedid = (unsigned char *) get_property(dp, "DFP,EDID", 0);
+	if (!pedid)
+		pedid = (unsigned char *) get_property(dp, "LCD,EDID", 0);
+	if (!pedid)
+		pedid = (unsigned char *) get_property(dp, "EDID", 0);
+
+	if (pedid) {
+		rinfo->EDID = pedid;
+		return 1;
+	} else
+		return 0;
+}
+#endif /* CONFIG_ALL_PPC */
+
+
+static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo)
+{
+	unsigned char *block = rinfo->EDID;
+
+	if (!block)
+		return 0;
+
+	/* jump to the detailed timing block section */
+	block += 54;
+
+	rinfo->clock = (block[0] + (block[1] << 8));
+	rinfo->panel_xres = (block[2] + ((block[4] & 0xf0) << 4));
+	rinfo->hblank = (block[3] + ((block[4] & 0x0f) << 8));
+	rinfo->panel_yres = (block[5] + ((block[7] & 0xf0) << 4));
+	rinfo->vblank = (block[6] + ((block[7] & 0x0f) << 8));
+	rinfo->hOver_plus = (block[8] + ((block[11] & 0xc0) << 2));
+	rinfo->hSync_width = (block[9] + ((block[11] & 0x30) << 4));
+	rinfo->vOver_plus = ((block[10] >> 4) + ((block[11] & 0x0c) << 2));
+	rinfo->vSync_width = ((block[10] & 0x0f) + ((block[11] & 0x03) << 4));
+	rinfo->interlaced = ((block[17] & 0x80) >> 7);
+	rinfo->synct = ((block[17] & 0x18) >> 3);
+	rinfo->misc = ((block[17] & 0x06) >> 1);
+	rinfo->hAct_high = rinfo->vAct_high = 0;
+	if (rinfo->synct == 3) {
+		if (rinfo->misc & 2)
+			rinfo->hAct_high = 1;
+		if (rinfo->misc & 1)
+			rinfo->vAct_high = 1;
 	}
 
-	printk("radeonfb: detected DFP panel size: %dx%d\n",
+	printk("radeonfb: detected DFP panel size from EDID: %dx%d\n",
 		rinfo->panel_xres, rinfo->panel_yres);
 
-	tmp = INREG(FP_CRTC_H_TOTAL_DISP);
-	a = (tmp & FP_CRTC_H_TOTAL_MASK) + 4;
-	b = (tmp & 0x01ff0000) >> FP_CRTC_H_DISP_SHIFT;
-	rinfo->hblank = (a - b + 1) * 8;
-
-	tmp = INREG(FP_H_SYNC_STRT_WID);
-	rinfo->hOver_plus = (unsigned short) ((tmp & FP_H_SYNC_STRT_CHAR_MASK) >>
-				FP_H_SYNC_STRT_CHAR_SHIFT) - b - 1;
-	rinfo->hOver_plus *= 8;
-	rinfo->hSync_width = (unsigned short) ((tmp & FP_H_SYNC_WID_MASK) >>
-				FP_H_SYNC_WID_SHIFT);
-	rinfo->hSync_width *= 8;
-	tmp = INREG(FP_CRTC_V_TOTAL_DISP);
-	a = (tmp & FP_CRTC_V_TOTAL_MASK) + 1;
-	b = (tmp & FP_CRTC_V_DISP_MASK) >> FP_CRTC_V_DISP_SHIFT;
-	rinfo->vblank = a - b /* + 24 */ ;
-
-	tmp = INREG(FP_V_SYNC_STRT_WID);
-	rinfo->vOver_plus = (unsigned short) (tmp & FP_V_SYNC_STRT_MASK)
-				- b + 1;
-	rinfo->vSync_width = (unsigned short) ((tmp & FP_V_SYNC_WID_MASK) >>
-				FP_V_SYNC_WID_SHIFT);
+	rinfo->got_dfpinfo = 1;
 
 	return 1;
 }
 
 
+static void radeon_update_default_var(struct radeonfb_info *rinfo)
+{
+	struct fb_var_screeninfo *var = &radeonfb_default_var;
+
+	var->xres = rinfo->panel_xres;
+	var->yres = rinfo->panel_yres;
+	var->xres_virtual = rinfo->panel_xres;
+	var->yres_virtual = rinfo->panel_yres;
+	var->xoffset = var->yoffset = 0;
+	var->bits_per_pixel = 8;
+	var->pixclock = 100000000 / rinfo->clock;
+	var->left_margin = (rinfo->hblank - rinfo->hOver_plus - rinfo->hSync_width);
+	var->right_margin = rinfo->hOver_plus;
+	var->upper_margin = (rinfo->vblank - rinfo->vOver_plus - rinfo->vSync_width);
+	var->lower_margin = rinfo->vOver_plus;
+	var->hsync_len = rinfo->hSync_width;
+	var->vsync_len = rinfo->vSync_width;
+	var->sync = 0;
+	if (rinfo->synct == 3) {
+		if (rinfo->hAct_high)
+			var->sync |= FB_SYNC_HOR_HIGH_ACT;
+		if (rinfo->vAct_high)
+			var->sync |= FB_SYNC_VERT_HIGH_ACT;
+	}
+
+	var->vmode = 0;
+	if (rinfo->interlaced)
+		var->vmode |= FB_VMODE_INTERLACED;
+
+	rinfo->use_default_var = 1;
+}
+
+
+static int radeon_get_dfpinfo_BIOS(struct radeonfb_info *rinfo)
+{
+	char *fpbiosstart, *tmp, *tmp0;
+	char stmp[30];
+	int i;
+
+	if (!rinfo->bios_seg)
+		return 0;
+
+	if (!(fpbiosstart = rinfo->bios_seg + readw(rinfo->bios_seg + 0x48))) {
+		printk("radeonfb: Failed to detect DFP panel info using BIOS\n");
+		return 0;
+	}
+
+	if (!(tmp = rinfo->bios_seg + readw(fpbiosstart + 0x40))) {
+		printk("radeonfb: Failed to detect DFP panel info using BIOS\n");
+		return 0;
+	}
+
+	for(i=0; i<24; i++)
+		stmp[i] = readb(tmp+i+1);
+	stmp[24] = 0;
+	printk("radeonfb: panel ID string: %s\n", stmp);
+	rinfo->panel_xres = readw(tmp + 25);
+	rinfo->panel_yres = readw(tmp + 27);
+	printk("radeonfb: detected DFP panel size from BIOS: %dx%d\n",
+		rinfo->panel_xres, rinfo->panel_yres);
+
+	for(i=0; i<20; i++) {
+		tmp0 = rinfo->bios_seg + readw(tmp+64+i*2);
+		if (tmp0 == 0)
+			break;
+		if ((readw(tmp0) == rinfo->panel_xres) &&
+		    (readw(tmp0+2) == rinfo->panel_yres)) {
+			rinfo->hblank = (readw(tmp0+17) - readw(tmp0+19)) * 8;
+			rinfo->hOver_plus = ((readw(tmp0+21) - readw(tmp0+19) -1) * 8) & 0x7fff;
+			rinfo->hSync_width = readb(tmp0+23) * 8;
+			rinfo->vblank = readw(tmp0+24) - readw(tmp0+26);
+			rinfo->vOver_plus = (readw(tmp0+28) & 0x7ff) - readw(tmp0+26);
+			rinfo->vSync_width = (readw(tmp0+28) & 0xf800) >> 11;
+			rinfo->clock = readw(tmp0+9);
+
+			rinfo->got_dfpinfo = 1;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+
+
+static int radeon_get_dfpinfo (struct radeonfb_info *rinfo)
+{
+	unsigned int tmp;
+	unsigned short a, b;
+
+	if (radeon_get_dfpinfo_BIOS(rinfo))
+		radeon_update_default_var(rinfo);
+
+	if (radeon_dfp_parse_EDID(rinfo))
+		radeon_update_default_var(rinfo);
+
+	if (!rinfo->got_dfpinfo) {
+		/*
+		 * it seems all else has failed now and we
+		 * resort to probing registers for our DFP info
+	         */
+		if (panel_yres) {
+			rinfo->panel_yres = panel_yres;
+		} else {
+			tmp = INREG(FP_VERT_STRETCH);
+			tmp &= 0x00fff000;
+			rinfo->panel_yres = (unsigned short)(tmp >> 0x0c) + 1;
+		}
+
+		switch (rinfo->panel_yres) {
+			case 480:
+				rinfo->panel_xres = 640;
+				break;
+			case 600:
+				rinfo->panel_xres = 800;
+				break;
+			case 768:
 #if defined(__powerpc__)
+				if (rinfo->dviDisp_type == MT_LCD)
+					rinfo->panel_xres = 1152;
+				else
+#endif
+				rinfo->panel_xres = 1024;
+				break;
+			case 1024:
+				rinfo->panel_xres = 1280;
+				break;
+			case 1050:
+				rinfo->panel_xres = 1400;
+				break;
+			case 1200:
+				rinfo->panel_xres = 1600;
+				break;
+			default:
+				printk("radeonfb: Failed to detect DFP panel size\n");
+				return 0;
+		}
+
+		printk("radeonfb: detected DFP panel size from registers: %dx%d\n",
+			rinfo->panel_xres, rinfo->panel_yres);
+
+		tmp = INREG(FP_CRTC_H_TOTAL_DISP);
+		a = (tmp & FP_CRTC_H_TOTAL_MASK) + 4;
+		b = (tmp & 0x01ff0000) >> FP_CRTC_H_DISP_SHIFT;
+		rinfo->hblank = (a - b + 1) * 8;
+
+		tmp = INREG(FP_H_SYNC_STRT_WID);
+		rinfo->hOver_plus = (unsigned short) ((tmp & FP_H_SYNC_STRT_CHAR_MASK) >>
+					FP_H_SYNC_STRT_CHAR_SHIFT) - b - 1;
+		rinfo->hOver_plus *= 8;
+		rinfo->hSync_width = (unsigned short) ((tmp & FP_H_SYNC_WID_MASK) >>
+					FP_H_SYNC_WID_SHIFT);
+		rinfo->hSync_width *= 8;
+		tmp = INREG(FP_CRTC_V_TOTAL_DISP);
+		a = (tmp & FP_CRTC_V_TOTAL_MASK) + 1;
+		b = (tmp & FP_CRTC_V_DISP_MASK) >> FP_CRTC_V_DISP_SHIFT;
+		rinfo->vblank = a - b /* + 24 */ ;
+
+		tmp = INREG(FP_V_SYNC_STRT_WID);
+		rinfo->vOver_plus = (unsigned short) (tmp & FP_V_SYNC_STRT_MASK)
+					- b + 1;
+		rinfo->vSync_width = (unsigned short) ((tmp & FP_V_SYNC_WID_MASK) >>
+					FP_V_SYNC_WID_SHIFT);
+
+		return 1;
+	}
+
+	return 1;
+}
+
+
+#ifdef CONFIG_ALL_PPC
 static int radeon_read_OF (struct radeonfb_info *rinfo)
 {
 	struct device_node *dp;
@@ -1255,7 +1543,7 @@
 	OUTREG(DSTCACHE_MODE, 0);
 
 	/* XXX */
-	rinfo->pitch = ((rinfo->xres * (rinfo->depth / 8) + 0x3f)) >> 6;
+	rinfo->pitch = ((rinfo->xres * (rinfo->bpp / 8) + 0x3f)) >> 6;
 
 	radeon_fifo_wait (1);
 	temp = INREG(DEFAULT_PITCH_OFFSET);
@@ -1333,6 +1621,16 @@
         disp = &rinfo->disp;
         
         disp->var = radeonfb_default_var;
+#if defined(__powerpc__)
+	if (rinfo->dviDisp_type == MT_LCD) {
+		if (mac_vmode_to_var(VMODE_1152_768_60, CMODE_8, &disp->var))
+			disp->var = radeonfb_default_var;
+	}
+#endif
+
+	rinfo->depth = var_to_depth(&disp->var);
+	rinfo->bpp = disp->var.bits_per_pixel;
+
         info->disp = disp;
 
         radeon_set_dispsw (rinfo, disp);
@@ -1360,6 +1658,18 @@
                               NULL, 0, NULL, 8);
         else
 #endif
+#if defined(__powerpc__)
+	if (rinfo->dviDisp_type == MT_LCD) {
+		if (mac_vmode_to_var(VMODE_1152_768_60, CMODE_8, &rinfo->disp.var))
+			rinfo->disp.var = radeonfb_default_var;
+	}
+	else
+#endif
+	if (rinfo->use_default_var)
+		/* We will use the modified default far */
+		rinfo->disp.var = radeonfb_default_var;
+	else
+
                 fb_find_mode (&rinfo->disp.var, &rinfo->info, "640x480-8@60",
                               NULL, 0, NULL, 0);
 
@@ -1390,7 +1700,6 @@
         disp->can_soft_blank = 1;
         disp->inverse = 0;
 
-        rinfo->depth = disp->var.bits_per_pixel;
         switch (disp->var.bits_per_pixel) {
 #ifdef FBCON_HAS_CFB8
                 case 8:
@@ -1443,7 +1752,7 @@
         if (fb_display[con].cmap.len)
                 fb_set_cmap(&fb_display[con].cmap, 1, radeon_setcolreg, info);
         else {
-                int size = fb_display[con].var.bits_per_pixel == 8 ? 256 : 32;
+		int size = radeon_get_cmap_len(&fb_display[con].var);
                 fb_set_cmap(fb_default_cmap(size), 1, radeon_setcolreg, info);
         }
 }
@@ -1535,7 +1844,7 @@
         fix->type_aux = disp->type_aux;
         fix->visual = disp->visual;
 
-        fix->xpanstep = 1;
+        fix->xpanstep = 8;
         fix->ypanstep = 1;
         fix->ywrapstep = 0;
         
@@ -1592,6 +1901,23 @@
         memcpy (&v, var, sizeof (v));
 
         switch (v.bits_per_pixel) {
+		case 0 ... 8:
+			v.bits_per_pixel = 8;
+			break;
+		case 9 ... 16:
+			v.bits_per_pixel = 16;
+			break;
+		case 17 ... 24:
+			v.bits_per_pixel = 24;
+			break;
+		case 25 ... 32:
+			v.bits_per_pixel = 32;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	switch (var_to_depth(&v)) {
 #ifdef FBCON_HAS_CFB8
                 case 8:
                         nom = den = 1;
@@ -1604,6 +1930,17 @@
 #endif
                         
 #ifdef FBCON_HAS_CFB16
+		case 15:
+			nom = 2;
+			den = 1;
+			disp->line_length = v.xres_virtual * 2;
+			disp->visual = FB_VISUAL_DIRECTCOLOR;
+			v.red.offset = 10;
+			v.green.offset = 5;
+			v.red.offset = 0;
+			v.red.length = v.green.length = v.blue.length = 5;
+			v.transp.offset = v.transp.length = 0;
+			break;
                 case 16:
                         nom = 2;
                         den = 1;
@@ -1785,7 +2122,7 @@
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
         struct display *disp;
         struct fb_cmap *cmap;
-        int switchcon = 0;
+        int switchmode = 0;
         
         disp = (con < 0) ? rinfo->info.disp : &fb_display[con];
                 
@@ -1795,17 +2132,13 @@
                         fb_get_cmap (cmap, 1, radeon_getcolreg, info);
         }   
                 
-        if ((disp->var.xres != rinfo->xres) ||
-            (disp->var.yres != rinfo->yres) ||
-            (disp->var.pixclock != rinfo->pixclock) ||
-            (disp->var.bits_per_pixel != rinfo->depth))
-                switchcon = 1;
-        
-        if (switchcon) {
-                rinfo->currcon = con;
-                rinfo->currcon_display = disp;
-                disp->var.activate = FB_ACTIVATE_NOW;
+	switchmode = (con != rinfo->currcon);
+
+	rinfo->currcon = con;
+	rinfo->currcon_display = disp;
+	disp->var.activate = FB_ACTIVATE_NOW;
         
+        if (switchmode) {
                 radeonfb_set_var (&disp->var, con, info);
                 radeon_set_dispsw (rinfo, disp);
                 do_install_cmap(con, info);
@@ -1837,11 +2170,20 @@
 {
         struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
         u32 val = INREG(CRTC_EXT_CNTL);
+	u32 val2 = INREG(LVDS_GEN_CNTL);
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+	if (rinfo->dviDisp_type == MT_LCD && _machine == _MACH_Pmac) {
+		set_backlight_enable(!blank);
+		return;
+	}
+#endif
                         
         /* reset it */
         val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
                  CRTC_VSYNC_DIS);
-                
+	val2 &= ~(LVDS_DISPLAY_DIS);
+
         switch (blank) {
                 case VESA_NO_BLANKING:
                         break;
@@ -1854,25 +2196,33 @@
                 case VESA_POWERDOWN:
                         val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | 
                                 CRTC_HSYNC_DIS);
+			val2 |= (LVDS_DISPLAY_DIS);
                         break;
         }
-        
-        OUTREG(CRTC_EXT_CNTL, val);
-}
 
+	switch (rinfo->dviDisp_type) {
+		case MT_LCD:
+			OUTREG(LVDS_GEN_CNTL, val2);
+			break;
+		case MT_CRT:
+		default:
+		        OUTREG(CRTC_EXT_CNTL, val);
+			break;
+	}
+}
 
 
 static int radeon_get_cmap_len (const struct fb_var_screeninfo *var)
 {
-        int rc = 16;            /* reasonable default */
+        int rc = 256;            /* reasonable default */
         
-        switch (var->bits_per_pixel) {
-                case 8:
-                        rc = 256;
-                        break;
-                default:
+        switch (var_to_depth(var)) {
+                case 15:
                         rc = 32;
                         break;
+		case 16:
+			rc = 64;
+			break;
         }
                 
         return rc;
@@ -1915,40 +2265,38 @@
 	rinfo->palette[regno].green = green;
 	rinfo->palette[regno].blue = blue;
 
-        /* init gamma for hicolor */
-        if ((rinfo->depth > 8) && (regno == 0)) {
-                int i;
-                
-                for (i=0; i<255; i++) {
-                        OUTREG(PALETTE_INDEX, i);
-                        OUTREG(PALETTE_DATA, (i << 16) | (i << 8) | i);
-                }
-        }
-         
         /* default */
         pindex = regno;
         
-        /* XXX actually bpp, fixme */
-        if (rinfo->depth == 16)     
-                pindex  = regno * 8;
-                
-        if (rinfo->depth == 16) {
-                OUTREG(PALETTE_INDEX, pindex/2);
-                OUTREG(PALETTE_DATA, (rinfo->palette[regno/2].red << 16) |
-                        (green << 8) | (rinfo->palette[regno/2].blue));
-                green = rinfo->palette[regno/2].green;
-        }
-                
-        if ((rinfo->depth == 8) || (regno < 32)) {
-                OUTREG(PALETTE_INDEX, pindex);
-                OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
-        }
+	if (rinfo->bpp == 16) {
+		pindex = regno * 8;
 
+		if (rinfo->depth == 16 && regno > 63)
+			return 1;
+		if (rinfo->depth == 15 && regno > 31)
+			return 1;
 
-#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB32)
- 	if (regno < 32) {
+		/* For 565, the green component is mixed one order below */
+		if (rinfo->depth == 16) {
+	                OUTREG(PALETTE_INDEX, pindex>>1);
+       	         	OUTREG(PALETTE_DATA, (rinfo->palette[regno>>1].red << 16) |
+                        	(green << 8) | (rinfo->palette[regno>>1].blue));
+                	green = rinfo->palette[regno<<1].green;
+        	}
+	}
+
+	if (rinfo->depth != 16 || regno < 32) {
+		OUTREG(PALETTE_INDEX, pindex);
+		OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
+	}
+
+ 	if (regno < 16) {
         	switch (rinfo->depth) {
 #ifdef FBCON_HAS_CFB16
+		        case 15:
+        			rinfo->con_cmap.cfb16[regno] = (regno << 10) | (regno << 5) |
+				                       	 	  regno;   
+			        break;
 		        case 16:
         			rinfo->con_cmap.cfb16[regno] = (regno << 11) | (regno << 5) |
 				                       	 	  regno;   
@@ -1970,7 +2318,6 @@
 #endif
 		}
         }
-#endif
 	return 0;
 }
 
@@ -2003,6 +2350,7 @@
 	save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
 	save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL);
 	save->tmds_crc = INREG(TMDS_CRC);
+	save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL);
 }
 
 
@@ -2023,6 +2371,7 @@
         int min_bits, format = 0;
 	int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid;
 	int primary_mon = PRIMARY_MONITOR(rinfo);
+	int depth = var_to_depth(mode);
 
 	rinfo->xres = mode->xres;
 	rinfo->yres = mode->yres;
@@ -2042,13 +2391,13 @@
 		if (rinfo->panel_yres < mode->yres)
 			rinfo->yres = mode->yres = rinfo->panel_yres;
 
-		hTotal = mode->xres + rinfo->hblank + mode->left_margin;
-		hSyncStart = mode->xres + rinfo->hOver_plus + mode->right_margin;
-		hSyncEnd = hSyncStart + rinfo->hOver_plus + mode->hsync_len;
-
-		vTotal = mode->yres + rinfo->vblank + mode->upper_margin;
-		vSyncStart = mode->yres + rinfo->vOver_plus + mode->lower_margin;
-		vSyncEnd = vSyncStart + rinfo->vSync_width + mode->vsync_len;
+		hTotal = mode->xres + rinfo->hblank;
+		hSyncStart = mode->xres + rinfo->hOver_plus;
+		hSyncEnd = hSyncStart + rinfo->hSync_width;
+
+		vTotal = mode->yres + rinfo->vblank;
+		vSyncStart = mode->yres + rinfo->vOver_plus;
+		vSyncEnd = vSyncStart + rinfo->vSync_width;
 	}
 
 	sync = mode->sync;
@@ -2066,7 +2415,7 @@
 		hsync_wid = 1;
 	else if (hsync_wid > 0x3f)	/* max */
 		hsync_wid = 0x3f;
-	vsync_wid = mode->vsync_len;
+
 	if (vsync_wid == 0)
 		vsync_wid = 1;
 	else if (vsync_wid > 0x1f)	/* max */
@@ -2077,24 +2426,8 @@
 
 	cSync = mode->sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
 
-	switch (mode->bits_per_pixel) {
-		case 8:
-			format = DST_8BPP;
-			bytpp = 1;
-			break;
-		case 16:
-			format = DST_16BPP;
-			bytpp = 2;
-			break;
-		case 24:
-			format = DST_24BPP;
-			bytpp = 3;
-			break;
-		case 32:
-			format = DST_32BPP;
-			bytpp = 4;
-			break;
-	}
+	format = radeon_get_dstbpp(depth);
+	bytpp = mode->bits_per_pixel >> 3;
 
 	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
 		hsync_fudge = hsync_fudge_fp[format-1];
@@ -2118,7 +2451,7 @@
 	newmode.dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
 			   DAC_8BIT_EN;
 
-	newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0xffff) |
+	newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
 				     (((mode->xres / 8) - 1) << 16));
 
 	newmode.crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
@@ -2158,6 +2491,7 @@
 	newmode.yres = mode->yres;
 
 	rinfo->bpp = mode->bits_per_pixel;
+	rinfo->depth = depth;
 
 	rinfo->hack_crtc_ext_cntl = newmode.crtc_ext_cntl;
 	rinfo->hack_crtc_v_sync_strt_wid = newmode.crtc_v_sync_strt_wid;
@@ -2234,50 +2568,38 @@
 	newmode.dda_on_off = (ron << 16) | roff;
 
 	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
-		int hRatio, vRatio;
-
-		if ((rinfo->panel_xres == 0) || (rinfo->panel_yres == 0)) {
-			hRatio = vRatio = 1;
-		} else {
-			if (mode->xres > rinfo->panel_xres)
-				mode->xres = rinfo->panel_xres;
-			if (mode->yres > rinfo->panel_yres)
-				mode->yres = rinfo->panel_yres;
-	
-			hRatio = mode->xres / rinfo->panel_xres;
-			vRatio = mode->yres / rinfo->panel_yres;
-		}
+		unsigned int hRatio, vRatio;
 
-		if (hRatio == 1) {
-			newmode.fp_horz_stretch =
-				rinfo->init_state.fp_horz_stretch;
-			newmode.fp_horz_stretch &= ~(HORZ_STRETCH_BLEND |
-						     HORZ_STRETCH_ENABLE);
-		} else {
-			newmode.fp_horz_stretch =
-				((((unsigned long)(hRatio * HORZ_STRETCH_RATIO_MAX +
-				(int)0.5)) & HORZ_STRETCH_RATIO_MASK)) |
-				(rinfo->init_state.fp_horz_stretch &
-				(HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
-				HORZ_AUTO_RATIO_INC));
+		if (mode->xres > rinfo->panel_xres)
+			mode->xres = rinfo->panel_xres;
+		if (mode->yres > rinfo->panel_yres)
+			mode->yres = rinfo->panel_yres;
+
+		newmode.fp_horz_stretch = (((rinfo->panel_xres / 8) - 1)
+					   << HORZ_PANEL_SHIFT);
+		newmode.fp_vert_stretch = ((rinfo->panel_yres - 1)
+					   << VERT_PANEL_SHIFT);
+
+		if (mode->xres != rinfo->panel_xres) {
+			hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
+					   rinfo->panel_xres);
+			newmode.fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
+						   (newmode.fp_horz_stretch &
+						    (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
+						     HORZ_AUTO_RATIO_INC)));
 			newmode.fp_horz_stretch |= (HORZ_STRETCH_BLEND |
 						    HORZ_STRETCH_ENABLE);
 		}
 		newmode.fp_horz_stretch &= ~HORZ_AUTO_RATIO;
 
-		if (vRatio == 1) {
-			newmode.fp_vert_stretch =
-				rinfo->init_state.fp_vert_stretch;
-			newmode.fp_vert_stretch &= ~(VERT_STRETCH_BLEND |
-						     VERT_STRETCH_ENABLE);
-		} else {
-			newmode.fp_vert_stretch =
-				((((unsigned long)(vRatio * VERT_STRETCH_RATIO_MAX +
-				(int)0.5)) & VERT_STRETCH_RATIO_MASK)) |
-				(rinfo->init_state.fp_vert_stretch &
-				(VERT_PANEL_SIZE | VERT_STRETCH_RESERVED));
-			newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND |
-						    VERT_STRETCH_ENABLE);
+		if (mode->yres != rinfo->panel_yres) {
+				vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
+						   rinfo->panel_yres);
+				newmode.fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
+							   (newmode.fp_vert_stretch &
+							   (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
+				newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND |
+							    VERT_STRETCH_ENABLE);
 		}
 		newmode.fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;
 
@@ -2290,14 +2612,14 @@
 					 FP_USE_SHADOW_EN |
 					 FP_CRTC_USE_SHADOW_VEND |
 					 FP_CRT_SYNC_ALT));
+
 		newmode.fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
 					FP_CRTC_DONT_SHADOW_HEND);
 
 		newmode.lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
 		newmode.lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
 		newmode.tmds_crc = rinfo->init_state.tmds_crc;
-
-		newmode.crtc_ext_cntl &= ~CRTC_CRT_ON;
+		newmode.tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;
 
 		if (primary_mon == MT_LCD) {
 			newmode.lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
@@ -2305,21 +2627,25 @@
 		} else {
 			/* DFP */
 			newmode.fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
+			newmode.tmds_transmitter_cntl = (TMDS_RAN_PAT_RST |
+							 ICHCSEL) & ~(TMDS_PLLRST);
+			newmode.crtc_ext_cntl &= ~CRTC_CRT_ON;
 		}
 
-		newmode.fp_crtc_h_total_disp =
-				rinfo->init_state.fp_crtc_h_total_disp;
-		newmode.fp_crtc_v_total_disp =
-				rinfo->init_state.fp_crtc_v_total_disp;
-		newmode.fp_h_sync_strt_wid =
-				rinfo->init_state.fp_h_sync_strt_wid;
-		newmode.fp_v_sync_strt_wid =
-				rinfo->init_state.fp_v_sync_strt_wid;
+		newmode.fp_crtc_h_total_disp = newmode.crtc_h_total_disp;
+		newmode.fp_crtc_v_total_disp = newmode.crtc_v_total_disp;
+		newmode.fp_h_sync_strt_wid = newmode.crtc_h_sync_strt_wid;
+		newmode.fp_v_sync_strt_wid = newmode.crtc_v_sync_strt_wid;
 	}
 
 	/* do it! */
 	radeon_write_mode (rinfo, &newmode);
 
+#if defined(CONFIG_BOOTX_TEXT)
+	btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
+			     rinfo->depth, rinfo->pitch*64);
+#endif
+
 	return;
 }
 
@@ -2331,7 +2657,8 @@
 	int primary_mon = PRIMARY_MONITOR(rinfo);
 
 	/* blank screen */
-	OUTREG8(CRTC_EXT_CNTL + 1, 4);
+	OUTREGP(CRTC_EXT_CNTL, CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS,
+		~(CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS | CRTC_HSYNC_DIS));
 
 	for (i=0; i<9; i++)
 		OUTREG(common_regs[i].reg, common_regs[i].val);
@@ -2347,16 +2674,6 @@
 	OUTREG(CRTC_OFFSET, 0);
 	OUTREG(CRTC_OFFSET_CNTL, 0);
 	OUTREG(CRTC_PITCH, mode->crtc_pitch);
-#if 1
-	printk("CRTC_H_TOTAL_DISP = 0x%x, H_SYNC = 0x%x\n",
-		mode->crtc_h_total_disp, mode->crtc_h_sync_strt_wid);
-	printk("CRTC_V_TOTAL_DISP = 0x%x, V_SYNC = 0x%x\n",
-		mode->crtc_v_total_disp, mode->crtc_v_sync_strt_wid);
-	printk("PPLL_DIV_3 = 0x%x, PPLL_REF_DIV = 0x%x\n",
-		mode->ppll_div_3, mode->ppll_ref_div);
-	printk("DDA_CONFIG = 0x%x, DDA_ON_OFF = 0x%x\n",
-		mode->dda_config, mode->dda_on_off);
-#endif
 
 #if defined(__BIG_ENDIAN)
 	OUTREG(SURFACE_CNTL, mode->surface_cntl);
@@ -2396,27 +2713,31 @@
 		OUTREG(FP_CRTC_V_TOTAL_DISP, mode->fp_crtc_v_total_disp);
 		OUTREG(FP_H_SYNC_STRT_WID, mode->fp_h_sync_strt_wid);
 		OUTREG(FP_V_SYNC_STRT_WID, mode->fp_v_sync_strt_wid);
-		OUTREG(TMDS_CRC, mode->tmds_crc);
 		OUTREG(FP_HORZ_STRETCH, mode->fp_horz_stretch);
 		OUTREG(FP_VERT_STRETCH, mode->fp_vert_stretch);
 		OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
+		OUTREG(TMDS_CRC, mode->tmds_crc);
+		OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
 
 		if (primary_mon == MT_LCD) {
 			unsigned int tmp = INREG(LVDS_GEN_CNTL);
 
+			mode->lvds_gen_cntl &= ~LVDS_STATE_MASK;
+			mode->lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_STATE_MASK);
+
 			if ((tmp & (LVDS_ON | LVDS_BLON)) ==
-			    (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)))
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
-		} else {
-			/* DVI */
-			if (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)) {
-				udelay(1000);
+			    (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON))) {
 				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
 			} else {
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl |
-				       LVDS_BLON);
-				udelay(1000);
-				OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				if (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)) {
+					udelay(1000);
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				} else {
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl |
+					       LVDS_BLON);
+					udelay(1000);
+					OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
+				}
 			}
 		}
 	}
@@ -2428,6 +2749,204 @@
 }
 
 
+#ifdef CONFIG_PMAC_BACKLIGHT
+
+static int backlight_conv[] = {
+	0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
+	0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
+};
+
+#define BACKLIGHT_LVDS_OFF
+#undef BACKLIGHT_DAC_OFF
+
+/* We turn off the LCD completely instead of just dimming the backlight.
+ * This provides some greater power saving and the display is useless
+ * without backlight anyway.
+ */
+
+static int radeon_set_backlight_enable(int on, int level, void *data)
+{
+	struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
+	unsigned int lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
+
+	lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
+	if (on && (level > BACKLIGHT_OFF)) {
+		lvds_gen_cntl |= LVDS_DIGON;
+		if (!lvds_gen_cntl & LVDS_ON) {
+			lvds_gen_cntl &= ~LVDS_BLON;
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+			(void)INREG(LVDS_GEN_CNTL);
+			mdelay(10);
+			lvds_gen_cntl |= LVDS_BLON;
+			OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		}
+		lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+		lvds_gen_cntl |= (backlight_conv[level] <<
+				  LVDS_BL_MOD_LEVEL_SHIFT);
+		lvds_gen_cntl |= (LVDS_ON | LVDS_EN);
+		lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
+	} else {
+		lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
+		lvds_gen_cntl |= (backlight_conv[0] <<
+				  LVDS_BL_MOD_LEVEL_SHIFT);
+		lvds_gen_cntl |= LVDS_DISPLAY_DIS;
+		OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+		udelay(10);
+		lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGON);
+	}
+
+	OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
+	rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
+	rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
+
+	return 0;
+}
+
+static int radeon_set_backlight_level(int level, void *data)
+{
+	return radeon_set_backlight_enable(1, level, data);
+}
+#endif /* CONFIG_PMAC_BACKLIGHT */
+
+
+#ifdef CONFIG_PMAC_PBOOK
+static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
+{
+	u16 pwr_cmd;
+
+	if (!rinfo->pm_reg)
+		return;
+
+	/* Set the chip into appropriate suspend mode (we use D2,
+	 * D3 would require a compete re-initialization of the chip,
+	 * including PCI config registers, clocks, AGP conf, ...)
+	 */
+	if (suspend) {
+		/* Make sure CRTC2 is reset.  Remove that the day
+		 * we decide to actually use CRTC2 and replace it with
+		 * real code for disabling the CRTC2 output during sleep.
+		 */
+
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+
+		/* Switch PCI power managment to D2 */
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				      (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK)
+				      | 2);
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+	} else {
+		/* Switch back PCI powermanagment to D0 */
+		mdelay(100);
+		pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
+		mdelay(100);
+		pci_read_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
+				     &pwr_cmd);
+		mdelay(100);
+	}
+}
+
+/*
+ * Save the contents of the framebuffer when we go to sleep,
+ * and restore it when we wake up again.
+ */
+
+int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when)
+{
+	struct radeonfb_info *rinfo;
+
+	for (rinfo = board_list; rinfo != NULL; rinfo = rinfo->next) {
+		struct fb_fix_screeninfo fix;
+		int nb;
+
+		switch (rinfo->chipset) {
+			case PCI_DEVICE_ID_RADEON_LW:
+			case PCI_DEVICE_ID_RADEON_LY:
+			case PCI_DEVICE_ID_RADEON_LZ:
+				break;
+			default:
+				return PBOOK_SLEEP_REFUSE;
+		}
+
+		radeonfb_get_fix(&fix, fg_console, (struct fb_info *)rinfo);
+		nb = fb_display[fg_console].var.yres * fix.line_length;
+
+		switch (when) {
+			case PBOOK_SLEEP_REQUEST:
+#if 0
+				rinfo->save_framebuffer = vmalloc(nb);
+				if (rinfo->save_framebuffer == NULL)
+					return PBOOK_SLEEP_REFUSE;
+#endif
+				break;
+			case PBOOK_SLEEP_REJECT:
+#if 0
+				if (rinfo->save_framebuffer) {
+					vfree(rinfo->save_framebuffer);
+					rinfo->save_framebuffer = 0;
+				}
+#endif
+				break;
+			case PBOOK_SLEEP_NOW:
+				radeon_engine_idle();
+				radeon_engine_reset();
+				radeon_engine_idle();
+
+#if 0
+				/* Backup framebuffer content */
+				if (rinfo->save_framebuffer)
+					memcpy_fromio(rinfo->save_framebuffer,
+						      (void *)rinfo->fb_base,
+						      nb);
+#endif
+
+				/* Blank display and LCD */
+				radeonfb_blank(VESA_POWERDOWN+1,
+					       (struct fb_info *)rinfo);
+
+				/* Sleep */
+				radeon_set_suspend(rinfo, 1);
+
+				break;
+			case PBOOK_WAKE:
+				/* Wakeup */
+				radeon_set_suspend(rinfo, 0);
+
+				radeon_engine_reset();
+				if (!noaccel) {
+					radeon_engine_init(rinfo);
+					radeon_engine_reset();
+				}
+
+#if 0
+				/* Restore framebuffer content */
+				if (rinfo->save_framebuffer) {
+					memcpy_toio((void *)rinfo->fb_base,
+						    rinfo->save_framebuffer,
+						    nb);
+					vfree(rinfo->save_framebuffer);
+					rinfo->save_framebuffer = 0;
+				}
+#endif
+
+				if (rinfo->currcon_display) {
+					radeonfb_set_var(&rinfo->currcon_display->var, rinfo->currcon,
+							 (struct fb_info *) rinfo);
+					radeon_set_dispsw(rinfo, rinfo->currcon_display);
+					do_install_cmap(rinfo->currcon,
+							(struct fb_info *)rinfo);
+				}
+
+				radeonfb_blank(0, (struct fb_info *)rinfo);
+				break;
+		}
+	}
+
+	return PBOOK_SLEEP_OK;
+}
+
+#endif /* CONFIG_PMAC_PBOOK */
 
 /*
  * text console acceleration
@@ -2500,7 +3019,6 @@
 	OUTREG(DST_Y_X, (srcy << 16) | srcx);
 	OUTREG(DST_WIDTH_HEIGHT, (width << 16) | height);
 }
-
 
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/fbdev.c linux/drivers/video/riva/fbdev.c
--- linux.orig/drivers/video/riva/fbdev.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/riva/fbdev.c	Wed Dec 26 18:20:25 2001
@@ -136,6 +136,11 @@
 	CH_GEFORCE2_GTS,
 	CH_GEFORCE2_ULTRA,
 	CH_QUADRO2_PRO,
+	CH_GEFORCE2_GO,
+        CH_GEFORCE3,
+        CH_GEFORCE3_1,
+        CH_GEFORCE3_2,
+        CH_QUADRO_DDC
 };
 
 /* directly indexed by riva_chips enum, above */
@@ -158,6 +163,11 @@
 	{ "GeForce2-GTS", NV_ARCH_10},
 	{ "GeForce2-ULTRA", NV_ARCH_10},
 	{ "Quadro2-PRO", NV_ARCH_10},
+        { "GeForce2-Go", NV_ARCH_10},
+        { "GeForce3", NV_ARCH_20}, 
+        { "GeForce3 Ti 200", NV_ARCH_20},
+        { "GeForce3 Ti 500", NV_ARCH_20},
+        { "Quadro DDC", NV_ARCH_20}
 };
 
 static struct pci_device_id rivafb_pci_tbl[] __devinitdata = {
@@ -195,6 +205,16 @@
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_ULTRA },
 	{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO,
 	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO2_PRO },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE2_GO },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_1 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_GEFORCE3_2 },
+        { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC,
+          PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_QUADRO_DDC },
 	{ 0, } /* terminate list */
 };
 MODULE_DEVICE_TABLE(pci, rivafb_pci_tbl);
@@ -1315,6 +1335,7 @@
 		fix->accel = FB_ACCEL_NV4;
 		break;
 	case NV_ARCH_10:	/* FIXME: ID for GeForce */
+	case NV_ARCH_20:
 		fix->accel = FB_ACCEL_NV4;
 		break;
 
@@ -1930,6 +1951,7 @@
 		break;
 	case NV_ARCH_04:
 	case NV_ARCH_10:
+	case NV_ARCH_20:
 		rinfo->riva.PCRTC = (unsigned *)(rinfo->ctrl_base + 0x00600000);
 		rinfo->riva.PRAMIN = (unsigned *)(rinfo->ctrl_base + 0x00710000);
 		break;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/riva_hw.c linux/drivers/video/riva/riva_hw.c
--- linux.orig/drivers/video/riva/riva_hw.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/riva/riva_hw.c	Wed Dec 26 18:20:25 2001
@@ -1220,6 +1220,7 @@
             state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             nv10UpdateArbitrationSettings(VClk, 
                                           pixelDepth * 8, 
                                          &(state->arbitration0),
@@ -1285,6 +1286,7 @@
             chip->Tri05 = (RivaTexturedTriangle05 *)&(chip->FIFO[0x0000E000/4]);
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             /*
              * Initialize state for the RivaTriangle3D05 routines.
              */
@@ -1393,6 +1395,7 @@
             chip->PGRAPH[0x0000067C/4] = state->pitch3;
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             LOAD_FIXED_STATE(nv10,PFIFO);
             LOAD_FIXED_STATE(nv10,PRAMIN);
             LOAD_FIXED_STATE(nv10,PGRAPH);
@@ -1421,15 +1424,31 @@
                     chip->Tri03 = 0L;
                     break;
             }
-            chip->PGRAPH[0x00000640/4] = state->offset0;
-            chip->PGRAPH[0x00000644/4] = state->offset1;
-            chip->PGRAPH[0x00000648/4] = state->offset2;
-            chip->PGRAPH[0x0000064C/4] = state->offset3;
-            chip->PGRAPH[0x00000670/4] = state->pitch0;
-            chip->PGRAPH[0x00000674/4] = state->pitch1;
-            chip->PGRAPH[0x00000678/4] = state->pitch2;
-            chip->PGRAPH[0x0000067C/4] = state->pitch3;
-            chip->PGRAPH[0x00000680/4] = state->pitch3;
+
+	    if (chip->Architecture == NV_ARCH_10) {
+            	chip->PGRAPH[0x00000640/4] = state->offset0;
+            	chip->PGRAPH[0x00000644/4] = state->offset1;
+            	chip->PGRAPH[0x00000648/4] = state->offset2;
+            	chip->PGRAPH[0x0000064C/4] = state->offset3;
+            	chip->PGRAPH[0x00000670/4] = state->pitch0;
+            	chip->PGRAPH[0x00000674/4] = state->pitch1;
+            	chip->PGRAPH[0x00000678/4] = state->pitch2;
+            	chip->PGRAPH[0x0000067C/4] = state->pitch3;
+            	chip->PGRAPH[0x00000680/4] = state->pitch3;
+	    } else {
+		chip->PGRAPH[0x00000820/4] = state->offset0;
+		chip->PGRAPH[0x00000824/4] = state->offset1;
+		chip->PGRAPH[0x00000828/4] = state->offset2;
+		chip->PGRAPH[0x0000082C/4] = state->offset3;
+		chip->PGRAPH[0x00000850/4] = state->pitch0;
+		chip->PGRAPH[0x00000854/4] = state->pitch1;
+		chip->PGRAPH[0x00000858/4] = state->pitch2;
+		chip->PGRAPH[0x0000085C/4] = state->pitch3;
+		chip->PGRAPH[0x00000860/4] = state->pitch3;
+		chip->PGRAPH[0x00000864/4] = state->pitch3;
+		chip->PGRAPH[0x000009A4/4] = chip->PFB[0x00000200/4];
+		chip->PGRAPH[0x000009A8/4] = chip->PFB[0x00000204/4];
+	    }
             chip->PGRAPH[0x00000B00/4] = chip->PFB[0x00000240/4];
             chip->PGRAPH[0x00000B04/4] = chip->PFB[0x00000244/4];
             chip->PGRAPH[0x00000B08/4] = chip->PFB[0x00000248/4];
@@ -1607,6 +1626,7 @@
             state->pitch3   = chip->PGRAPH[0x0000067C/4];
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             state->offset0  = chip->PGRAPH[0x00000640/4];
             state->offset1  = chip->PGRAPH[0x00000644/4];
             state->offset2  = chip->PGRAPH[0x00000648/4];
@@ -1970,6 +1990,7 @@
             nv4GetConfig(chip);
             break;
         case NV_ARCH_10:
+	case NV_ARCH_20:
             nv10GetConfig(chip);
             break;
         default:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/riva/riva_hw.h linux/drivers/video/riva/riva_hw.h
--- linux.orig/drivers/video/riva/riva_hw.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/riva/riva_hw.h	Wed Dec 26 18:20:25 2001
@@ -74,6 +74,8 @@
 #define NV_ARCH_03  0x03
 #define NV_ARCH_04  0x04
 #define NV_ARCH_10  0x10
+#define NV_ARCH_20  0x20
+
 /***************************************************************************\
 *                                                                           *
 *                             FIFO registers.                               *
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/tdfxfb.c linux/drivers/video/tdfxfb.c
--- linux.orig/drivers/video/tdfxfb.c	Fri Dec 21 17:41:55 2001
+++ linux/drivers/video/tdfxfb.c	Tue Jan 22 23:58:14 2002
@@ -401,7 +401,7 @@
 /*
  *  Internal routines
  */
-static void tdfxfb_set_par(const struct tdfxfb_par* par,
+static void tdfxfb_set_par(struct tdfxfb_par* par,
 			   struct fb_info_tdfx* 
 			   info);
 static int  tdfxfb_decode_var(const struct fb_var_screeninfo *var,
@@ -1275,7 +1275,7 @@
 
 /* ------------------------------------------------------------------------- */
 
-static void tdfxfb_set_par(const struct tdfxfb_par* par,
+static void tdfxfb_set_par(struct tdfxfb_par* par,
 			   struct fb_info_tdfx*     info) {
   struct fb_info_tdfx* i = (struct fb_info_tdfx*)info;
   struct banshee_reg reg;
@@ -1290,6 +1290,28 @@
 
   cpp = (par->bpp + 7)/8;
   
+  reg.vidcfg = 
+    VIDCFG_VIDPROC_ENABLE |
+    VIDCFG_DESK_ENABLE    |
+    VIDCFG_CURS_X11 |
+    ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) |
+    (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0);
+
+  /* PLL settings */
+  freq = par->pixclock;
+
+  reg.dacmode = 0;
+  reg.vidcfg  &= ~VIDCFG_2X;
+
+  if(freq > i->max_pixclock/2) {
+    freq = freq > i->max_pixclock ? i->max_pixclock : freq;
+    reg.dacmode |= DACMODE_2X;
+    reg.vidcfg  |= VIDCFG_2X;
+    par->hdispend >>= 1;
+    par->hsyncsta >>= 1;
+    par->hsyncend >>= 1;
+    par->htotal   >>= 1;
+  }
   wd = (par->hdispend >> 3) - 1;
 
   hd  = (par->hdispend >> 3) - 1;
@@ -1356,9 +1378,7 @@
   reg.crt[0x02] = hbs;
   reg.crt[0x03] = 0x80 | (hbe & 0x1f);
   reg.crt[0x04] = hs;
-  reg.crt[0x05] = 
-    ((hbe & 0x20) << 2) | 
-    (he & 0x1f);
+  reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f);
   reg.crt[0x06] = vt;
   reg.crt[0x07] = 
     ((vs & 0x200) >> 2) |
@@ -1380,9 +1400,7 @@
   reg.crt[0x0e] = 0x00;
   reg.crt[0x0f] = 0x00;
   reg.crt[0x10] = vs;
-  reg.crt[0x11] = 
-    (ve & 0x0f) |
-    0x20;
+  reg.crt[0x11] = (ve & 0x0f) | 0x20;
   reg.crt[0x12] = vd;
   reg.crt[0x13] = wd;
   reg.crt[0x14] = 0x00;
@@ -1411,13 +1429,6 @@
     VGAINIT0_EXTSHIFTOUT;
   reg.vgainit1 = tdfx_inl(VGAINIT1) & 0x1fffff;
 
-  reg.vidcfg = 
-    VIDCFG_VIDPROC_ENABLE |
-    VIDCFG_DESK_ENABLE    |
-    VIDCFG_CURS_X11 |
-    ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) |
-    (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0);
-  
   fb_info.cursor.enable=reg.vidcfg | VIDCFG_HWCURSOR_ENABLE;
   fb_info.cursor.disable=reg.vidcfg;
    
@@ -1433,16 +1444,6 @@
   reg.srcbase   = reg.startaddr;
   reg.dstbase   = reg.startaddr;
 
-  /* PLL settings */
-  freq = par->pixclock;
-
-  reg.dacmode &= ~DACMODE_2X;
-  reg.vidcfg  &= ~VIDCFG_2X;
-  if(freq > i->max_pixclock/2) {
-    freq = freq > i->max_pixclock ? i->max_pixclock : freq;
-    reg.dacmode |= DACMODE_2X;
-    reg.vidcfg  |= VIDCFG_2X;
-  }
   reg.vidpll = do_calc_pll(freq, &fout);
 #if 0
   reg.mempll = do_calc_pll(..., &fout);
@@ -1473,9 +1474,13 @@
 #endif
 
   do_write_regs(&reg);
-
+  if (reg.vidcfg & VIDCFG_2X) {
+    par->hdispend <<= 1;
+    par->hsyncsta <<= 1;
+    par->hsyncend <<= 1;
+    par->htotal   <<= 1;
+  }
   i->current_par = *par;
-
 }
 
 static int tdfxfb_decode_var(const struct fb_var_screeninfo* var,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/tridentfb.c linux/drivers/video/tridentfb.c
--- linux.orig/drivers/video/tridentfb.c	Thu Jan  1 00:00:00 1970
+++ linux/drivers/video/tridentfb.c	Wed Feb 13 17:44:14 2002
@@ -0,0 +1,1307 @@
+/*
+ * Frame buffer driver for Trident Blade and Image series
+ *
+ * Copyright 2001,2002 - Jani Monoses   <jani@astechnix.ro>
+ *
+ * $Id: tridentfb.c,v 1.2 2002/02/13 17:44:14 marcelo Exp $
+ *
+ * CREDITS:(in order of appearance)
+ * 	skeletonfb.c by Geert Uytterhoeven and other fb code in drivers/video
+ * 	Special thanks ;) to Mattia Crivellini <tia@mclink.it>
+ * 	much inspired by the XFree86 4.1.0 Trident driver sources by Alan Hourihane
+ * 	the FreeVGA project
+ *	Francesco Salvestrini <salvestrini@users.sf.net> XP support,code,suggestions
+ * NOTES:
+ * 	Tested on Compaq Presario 12XL300 with CyberBladei1
+ * 	Tested on Toshiba 1800-514 with CyberBladeXPAi1
+ * 	No monitors were harmed during the writing of this driver
+ * TODO:
+ * 	timing value tweaking so it looks good on every monitor in every mode
+ * 	test text acceleration for the Image series
+ *	test DPMS stuff
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+#include <video/fbcon-cfb32.h>
+
+#include "tridentfb.h"
+
+#define VERSION		"0.6.8"
+
+struct tridentfb_par {
+	struct fb_var_screeninfo var;
+	int bpp;
+	int hres;
+	int vres;
+	int linelength;
+	int vclk;		//in MHz
+
+	int vtotal;
+	int vdispend;
+	int vsyncstart;
+	int vsyncend;
+	int vblankstart;
+	int vblankend;
+
+	int htotal;
+	int hdispend;
+	int hsyncstart;
+	int hsyncend;
+	int hblankstart;
+	int hblankend;
+};
+
+struct tridentfb_info {
+	struct fb_info_gen gen;
+	unsigned int fbmem_virt;	//framebuffer virtual memory address
+	unsigned int fbmem;		//framebuffer physical memory address
+	unsigned int memsize;		//size of fbmem
+	unsigned int io;		//io space address
+	unsigned int io_virt;		//iospace virtual memory address
+	unsigned int nativex;		//flat panel xres
+	struct tridentfb_par currentmode;
+};
+
+static struct fb_ops tridentfb_ops;
+
+static struct tridentfb_info fb_info;
+static struct display disp;
+
+static struct { unsigned char red,green,blue,transp; } palette[256];
+
+static struct fb_var_screeninfo default_var;
+
+static char * tridentfb_name = "Trident";
+
+static int family;
+static int pci_id;
+
+static int defaultaccel;
+static int displaytype;
+
+static int pseudo_pal[16];
+
+
+/* defaults which are normally overriden by user values */
+
+/* video mode */
+static char * mode = "640x480";
+static int bpp = 8;
+
+static int noaccel;
+static int accel;
+
+static int center;
+static int stretch;
+
+static int fp;
+static int crt;
+
+static int memsize;
+static int memdiff;
+static int nativex;
+
+
+MODULE_PARM(mode,"s");
+MODULE_PARM(bpp,"i");
+MODULE_PARM(center,"i");
+MODULE_PARM(stretch,"i");
+MODULE_PARM(noaccel,"i");
+MODULE_PARM(accel,"i");
+MODULE_PARM(memsize,"i");
+MODULE_PARM(memdiff,"i");
+MODULE_PARM(nativex,"i");
+MODULE_PARM(fp,"i");
+MODULE_PARM(crt,"i");
+
+
+
+#define CRT 0x3D0		//CRTC registers offset for color display
+
+#ifndef TRIDENT_MMIO
+	#define TRIDENT_MMIO 1
+#endif
+
+#if TRIDENT_MMIO
+	#define t_outb(val,reg)	writeb(val,fb_info.io_virt + reg)
+	#define t_inb(reg)	readb(fb_info.io_virt + reg)
+#else
+	#define t_outb(val,reg) outb(val,reg)
+	#define t_inb(reg) inb(reg)
+#endif
+
+
+static struct accel_switch {
+	void (*init_accel)(int,int);
+	void (*wait_engine)(void);
+	void (*fill_rect)(int,int,int,int,int);
+	void (*copy_rect)(int,int,int,int,int,int);
+} *acc;
+
+#define writemmr(r,v)	writel(v, fb_info.io_virt + r)
+#define readmmr(r)	readl(fb_info.io_virt + r)
+
+/*
+ * Blade specific acceleration.Not XP's though those are
+ * unaccelerated.
+ */
+
+#define point(x,y) ((y)<<16|(x))
+#define STA	0x2120
+#define CMD	0x2144
+#define ROP	0x2148
+#define CLR	0x2160
+#define SR1	0x2100
+#define SR2	0x2104
+#define DR1	0x2108
+#define DR2	0x210C
+
+#define REPL(x)	x = x | x<<16
+#define ROP_S	0xCC
+
+static void blade_init_accel(int pitch,int bpp)
+{
+	int v1 = (pitch>>3)<<20;
+	int tmp = 0,v2;
+	switch (bpp) {
+		case 8:tmp = 0;break;
+		case 15:tmp = 5;break;
+		case 16:tmp = 1;break;
+		case 24:
+		case 32:tmp = 2;break;
+	}
+	v2 = v1 | (tmp<<29);
+	writemmr(0x21C0,v2);
+	writemmr(0x21C4,v2);
+	writemmr(0x21B8,v2);
+	writemmr(0x21BC,v2);
+	writemmr(0x21D0,v1);
+	writemmr(0x21D4,v1);
+	writemmr(0x21C8,v1);
+	writemmr(0x21CC,v1);
+	writemmr(0x216C,0);
+}
+
+static void blade_wait_engine(void)
+{
+	while(readmmr(STA) & 0xFA800000);
+}
+
+static void blade_fill_rect(int x,int y,int w,int h,int c)
+{
+	writemmr(CLR,c);
+	writemmr(ROP,ROP_S);
+	writemmr(CMD,0x20000000|1<<19|1<<4|2<<2);
+
+	writemmr(DR1,point(x,y));
+	writemmr(DR2,point(x+w-1,y+h-1));
+}
+
+static void blade_copy_rect(int x1,int y1,int x2,int y2,int w,int h)
+{
+	int s1,s2,d1,d2;
+	int direction = 2;
+	s1 = point(x1,y1);
+	s2 = point(x1+w-1,y1+h-1);
+	d1 = point(x2,y2);
+	d2 = point(x2+w-1,y2+h-1);
+
+	if ((y1 > y2) || ((y1 == y2) && (x1 >x2)))
+			direction = 0;
+
+
+	writemmr(ROP,ROP_S);
+	writemmr(CMD,0xE0000000|1<<19|1<<4|1<<2|direction);
+
+	writemmr(SR1,direction?s2:s1);
+	writemmr(SR2,direction?s1:s2);
+	writemmr(DR1,direction?d2:d1);
+	writemmr(DR2,direction?d1:d2);
+}
+
+static struct accel_switch accel_blade = {
+	blade_init_accel,
+	blade_wait_engine,
+	blade_fill_rect,
+	blade_copy_rect,
+};
+
+/*
+ * Image specific acceleration functions
+ */
+static void image_init_accel(int pitch,int bpp)
+{
+	int tmp = 0;
+   	switch (bpp) {
+		case 8:tmp = 0;break;
+		case 15:tmp = 5;break;
+		case 16:tmp = 1;break;
+		case 24:
+		case 32:tmp = 2;break;
+	}
+	writemmr(0x2120, 0xF0000000);
+	writemmr(0x2120, 0x40000000|tmp);
+	writemmr(0x2120, 0x80000000);
+	writemmr(0x2144, 0x00000000);
+	writemmr(0x2148, 0x00000000);
+	writemmr(0x2150, 0x00000000);
+	writemmr(0x2154, 0x00000000);
+	writemmr(0x2120, 0x60000000 |(pitch<<16) |pitch);
+	writemmr(0x216C, 0x00000000);
+	writemmr(0x2170, 0x00000000);
+	writemmr(0x217C, 0x00000000);
+	writemmr(0x2120, 0x10000000);
+	writemmr(0x2130, (2047 << 16) | 2047);
+}
+
+static void image_wait_engine(void)
+{
+	while(readmmr(0x2164) & 0xF0000000);
+}
+
+static void image_fill_rect(int x,int y,int w,int h,int c)
+{
+	writemmr(0x2120,0x80000000);
+	writemmr(0x2120,0x90000000|ROP_S);
+
+	writemmr(0x2144,c);
+
+	writemmr(DR1,point(x,y));
+	writemmr(DR2,point(x+w-1,y+h-1));
+
+	writemmr(0x2124,0x80000000|3<<22|1<<10|1<<9);
+}
+
+static void image_copy_rect(int x1,int y1,int x2,int y2,int w,int h)
+{
+	int s1,s2,d1,d2;
+	int direction = 2;
+	s1 = point(x1,y1);
+	s2 = point(x1+w-1,y1+h-1);
+	d1 = point(x2,y2);
+	d2 = point(x2+w-1,y2+h-1);
+
+	if ((y1 > y2) || ((y1 == y2) && (x1 >x2)))
+			direction = 0;
+
+	writemmr(0x2120,0x80000000);
+	writemmr(0x2120,0x90000000|ROP_S);
+
+	writemmr(SR1,direction?s2:s1);
+	writemmr(SR2,direction?s1:s2);
+	writemmr(DR1,direction?d2:d1);
+	writemmr(DR2,direction?d1:d2);
+	writemmr(0x2124,0x80000000|1<<22|1<<10|1<<7|direction);
+}
+
+
+static struct accel_switch accel_image = {
+	image_init_accel,
+	image_wait_engine,
+	image_fill_rect,
+	image_copy_rect,
+};
+
+/*
+ * Accel functions called by the upper layers
+ */
+
+static void trident_bmove (struct display *p, int sy, int sx,
+				int dy, int dx, int height, int width)
+{
+	sx *= fontwidth(p);
+	dx *= fontwidth(p);
+	width *= fontwidth(p);
+	sy *= fontheight(p);
+	dy *= fontheight(p);
+	height *= fontheight(p);
+	acc->copy_rect(sx,sy,dx,dy,width,height);
+	acc->wait_engine();
+}
+static void trident_clear_helper (int c, struct display *p,
+				int sy, int sx, int height, int width)
+{
+	sx *= fontwidth(p);
+	sy *= fontheight(p);
+	width *= fontwidth(p);
+	height *= fontheight(p);
+	acc->fill_rect(sx,sy,width,height,c);
+	acc->wait_engine();
+}
+
+
+#ifdef FBCON_HAS_CFB8
+static void trident_8bpp_clear (struct vc_data *conp, struct display *p,
+				int sy, int sx, int height, int width)
+{
+	int c;
+	c = attr_bgcol_ec(p,conp) & 0xFF;
+	c |= c<<8;
+	c |= c<<16;
+	trident_clear_helper(c,p,sy,sx,height,width);
+}
+
+static struct display_switch trident_8bpp = {
+	setup:		fbcon_cfb8_setup,
+	bmove:		trident_bmove,
+	clear:		trident_8bpp_clear,
+	putc:		fbcon_cfb8_putc,
+	putcs:		fbcon_cfb8_putcs,
+	revc:		fbcon_cfb8_revc,
+	clear_margins:	fbcon_cfb8_clear_margins,
+	fontwidthmask:	FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
+};
+#endif
+#ifdef FBCON_HAS_CFB16
+static void trident_16bpp_clear (struct vc_data *conp, struct display *p,
+				int sy, int sx, int height, int width)
+{
+	int c;
+	c = ((u16*)p->dispsw_data)[attr_bgcol_ec(p,conp)];
+	c = c | c<<16;
+	trident_clear_helper(c,p,sy,sx,height,width);
+}
+
+static struct display_switch trident_16bpp = {
+	setup:		fbcon_cfb16_setup,
+	bmove:		trident_bmove,
+	clear:		trident_16bpp_clear,
+	putc:		fbcon_cfb16_putc,
+	putcs:		fbcon_cfb16_putcs,
+	revc:		fbcon_cfb16_revc,
+	clear_margins:	fbcon_cfb16_clear_margins,
+	fontwidthmask:	FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
+};
+#endif
+#ifdef FBCON_HAS_CFB32
+static void trident_32bpp_clear (struct vc_data *conp, struct display *p,
+				int sy, int sx, int height, int width)
+{
+	int c;
+	c = ((u32*)p->dispsw_data)[attr_bgcol_ec(p,conp)];
+	trident_clear_helper(c,p,sy,sx,height,width);
+}
+
+static struct display_switch trident_32bpp = {
+	setup:		fbcon_cfb32_setup,
+	bmove:		trident_bmove,
+	clear:		trident_32bpp_clear,
+	putc:		fbcon_cfb32_putc,
+	putcs:		fbcon_cfb32_putcs,
+	revc:		fbcon_cfb32_revc,
+	clear_margins:	fbcon_cfb32_clear_margins,
+	fontwidthmask:	FONTWIDTH (4) | FONTWIDTH (8) | FONTWIDTH (12) | FONTWIDTH (16)
+};
+#endif
+
+/*
+ * Hardware access functions
+ */
+
+static inline unsigned char read3X4(int reg)
+{
+	writeb(reg, fb_info.io_virt + CRT + 4);
+	return readb(fb_info.io_virt + CRT + 5);
+}
+
+static inline void write3X4(int reg, unsigned char val)
+{
+	writeb(reg, fb_info.io_virt + CRT + 4);
+	writeb(val, fb_info.io_virt + CRT + 5);
+}
+
+static inline unsigned char read3C4(int reg)
+{
+	t_outb(reg, 0x3C4);
+	return t_inb(0x3C5);
+}
+
+static inline void write3C4(int reg, unsigned char val)
+{
+	t_outb(reg, 0x3C4);
+	t_outb(val, 0x3C5);
+}
+
+static inline unsigned char read3CE(int reg)
+{
+	t_outb(reg, 0x3CE);
+	return t_inb(0x3CF);
+}
+
+static inline void writeAttr(int reg, unsigned char val)
+{
+	readb(fb_info.io_virt + CRT + 0x0A);	//flip-flop to index
+	t_outb(reg, 0x3C0);
+	t_outb(val, 0x3C0);
+}
+
+static inline unsigned char readAttr(int reg)
+{
+	readb(fb_info.io_virt + CRT + 0x0A);	//flip-flop to index
+	t_outb(reg, 0x3C0);
+	return t_inb(0x3C1);
+}
+
+static inline void write3CE(int reg, unsigned char val)
+{
+	t_outb(reg, 0x3CE);
+	t_outb(val, 0x3CF);
+}
+
+#define unprotect_all()	write3C4(Protection, 0x92);unprotect()
+#define unprotect()	write3C4(NewMode1,0xC2)
+#define bios_reg(reg) 	write3CE(BiosReg, reg)
+#define enable_mmio()	outb(PCIReg, 0x3D4); \
+			outb(inb(0x3D5) | 0x01, 0x3D5)
+#define crtc_unlock()	write3X4(CRTVSyncEnd, read3X4(CRTVSyncEnd) & 0x7F)
+
+/*  Return flat panel's maximum x resolution */
+static int __init get_nativex(void)
+{
+	int x,y,tmp;
+
+	if (nativex)
+		return nativex;
+
+       	tmp = (read3CE(VertStretch) >> 4) & 3;
+
+	/* detection broken on XPAi ??? misdetects 1024 for 800 */
+	if (pci_id == CYBERBLADEXPAi1 && tmp == 3)
+		tmp = 2;
+
+	switch (tmp) {
+		case 0:x = 1280;y = 1024;break;
+		case 1:x = 640;y = 480;break;
+		case 2:x = 1024;y = 768;break;
+		default:x = 800;y = 600;break;
+	}
+
+	output("%dx%d flat panel found\n", x, y);
+	return x;
+}
+
+/* Set pitch */
+static void set_lwidth(int width)
+{
+	write3X4(Offset, width & 0xFF);
+	write3X4(AddColReg, (read3X4(AddColReg) & 0xCF) | ((width & 0x300) >>4));
+}
+
+/* For resolutions smaller than FP resolution stretch */
+static void screen_stretch(void)
+{
+	write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 1);
+	write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 1);
+}
+
+/* For resolutions smaller than FP resolution center */
+static void screen_center(void)
+{
+	bios_reg(0);		// no stretch
+	write3CE(VertStretch,(read3CE(VertStretch) & 0x7C) | 0x80);
+	write3CE(HorStretch,(read3CE(HorStretch) & 0x7C) | 0x80);
+}
+
+/* Address of first shown pixel in display memory */
+static void set_screen_start(int base)
+{
+	write3X4(StartAddrLow, base & 0xFF);
+	write3X4(StartAddrHigh, (base & 0xFF00) >>8);
+	write3X4(CRTCModuleTest, (read3X4(CRTCModuleTest) & 0xDF) | ((base & 0x10000) >>11));
+	write3X4(CRTHiOrd, (read3X4(CRTHiOrd) & 0xF8) | (base & 0xE0000) >> 17);
+}
+
+
+#error "Floating point maths. This needs fixing before the driver is safe"
+#define calc_freq(n,m,k) ((NTSC * (n+8))/((m+2)*(1<<k)))
+
+/* Set dotclock frequency */
+static void set_vclk(int freq)
+{
+	int m,n,k;
+	int f,fi,d,di;
+	unsigned char lo=0,hi=0;
+
+	d = 20;
+	for(k = 2;k>=0;k--)
+	for(m = 0;m<63;m++)
+	for(n = 0;n<128;n++) {
+		fi = calc_freq(n,m,k);
+		if ((di = abs(fi - freq)) < d) {
+			d = di;
+			f = fi;
+			lo = n;
+			hi = (k<<6) | m;
+		}
+	}
+	write3C4(ClockHigh,hi);
+	write3C4(ClockLow,lo);
+	debug("VCLK = %X %X\n",hi,lo);
+}
+
+/* Set number of lines for flat panels*/
+static void set_number_of_lines(int lines)
+{
+	int tmp = read3CE(CyberEnhance) & 0x8F;
+	if (lines > 768)
+		tmp |= 0x30;
+	else if (lines > 600)
+		tmp |= 0x20;
+	else if (lines > 480)
+		tmp |= 0x10;
+	write3CE(CyberEnhance, tmp);
+}
+
+/*
+ * If we see that FP is active we assume we have one.
+ * Otherwise we have a CRT display.User can override.
+ */
+static unsigned int __init get_displaytype(void)
+{
+	if (fp)
+		return DISPLAY_FP;
+	if (crt)
+		return DISPLAY_CRT;
+	return (read3CE(FPConfig) & 0x10)?DISPLAY_FP:DISPLAY_CRT;
+}
+
+/* Try detecting the video memory size */
+static unsigned int __init get_memsize(void)
+{
+	unsigned char tmp;
+	unsigned int k;
+
+	/* If memory size provided by user */
+	if (memsize)
+		k = memsize * Kb;
+	else
+	switch (pci_id) {
+		case CYBER9525DVD:k = 2560 * Kb;break;
+		case CYBERBLADEXPAi1:k = 16 * Mb;break;
+		case CYBERBLADEXPm16:k = 16 * Mb;break;
+		case CYBERBLADEXPm8:k = 8 * Mb;break;
+		default:
+			tmp = read3X4(SPR) & 0x0F;
+			switch (tmp) {
+				case 3:k = 1 * Mb;break;
+				case 7:k = 2 * Mb;break;
+				case 15:k = 4 * Mb;break;
+				case 4:k = 8 * Mb;break;
+				default:k = 1 * Mb;
+			}
+	}
+
+	k -= memdiff * Kb;
+	output("framebuffer size = %d Kb\n", k/Kb);
+	return k;
+}
+
+
+/* Fill in fix */
+static int trident_encode_fix(struct fb_fix_screeninfo *fix,
+				  const void *par,
+				  struct fb_info_gen *info)
+{
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+
+	debug("enter\n");
+
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	strcpy(fix->id,tridentfb_name);
+
+	fix->smem_start = i->fbmem;
+	fix->smem_len = i->memsize;
+
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+
+	fix->visual = p->bpp==8 ? FB_VISUAL_PSEUDOCOLOR:FB_VISUAL_TRUECOLOR;
+
+	fix->xpanstep = fix->ywrapstep = 0;
+	fix->ypanstep = 1;
+	fix->line_length = p->linelength;
+	fix->mmio_start = 0;
+	fix->mmio_len = 0;
+
+	fix->accel = FB_ACCEL_NONE;
+
+	debug("exit\n");
+	return 0;
+}
+
+/* Fill in par from var */
+static int trident_decode_var(const struct fb_var_screeninfo *var,
+				  void *par,
+				  struct fb_info_gen *info)
+{
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	int vres,vfront,vback,vsync;
+	debug("enter\n");
+	p->var = *var;
+	p->bpp = var->bits_per_pixel;
+
+	if (p->bpp == 24 )
+		p->bpp = 32;
+
+	p->linelength = var->xres_virtual * p->bpp/8;
+
+	switch (p->bpp) {
+		case 8:
+			p->var.red.offset = 0;
+			p->var.green.offset = 0;
+			p->var.blue.offset = 0;
+			p->var.red.length = 6;
+			p->var.green.length = 6;
+			p->var.blue.length = 6;
+			break;
+		case 16:
+			p->var.red.offset = 11;
+			p->var.green.offset = 5;
+			p->var.blue.offset = 0;
+			p->var.red.length = 5;
+			p->var.green.length = 6;
+			p->var.blue.length = 5;
+			break;
+		case 32:
+			p->var.red.offset = 16;
+			p->var.green.offset = 8;
+			p->var.blue.offset = 0;
+			p->var.red.length = 8;
+			p->var.green.length = 8;
+			p->var.blue.length = 8;
+			break;
+		default:
+			return -EINVAL;
+	}
+
+	/* convert from picoseconds to MHz */
+	p->vclk = 1000000/var->pixclock;
+
+	if (p->bpp == 32)
+		p->vclk *=2;
+
+	p->hres = var->xres;
+	vres = p->vres = var->yres;
+
+	/* See if requested resolution is larger than flat panel */
+	if (p->hres > i->nativex && flatpanel) {
+		return -EINVAL;
+	}
+
+	/* See if requested resolution fits in available memory */
+	if (p->hres * p->vres * p->bpp/8 > i->memsize) {
+		return -EINVAL;
+	}
+
+	vfront = var->upper_margin;
+	vback =	var->lower_margin;
+	vsync =	var->vsync_len;
+
+	/* Compute horizontal and vertical VGA CRTC timing values */
+	if (var->vmode & FB_VMODE_INTERLACED) {
+		vres /= 2;
+		vfront /=2;
+		vback /=2;
+		vsync /=2;
+	}
+
+	if (var->vmode & FB_VMODE_DOUBLE) {
+		vres *= 2;
+		vfront *=2;
+		vback *=2;
+		vsync *=2;
+	}
+
+	p->htotal = (p->hres + var->left_margin + var->right_margin + var->hsync_len)/8 - 10;
+	p->hdispend = p->hres/8 - 1;
+	p->hsyncstart = (p->hres + var->right_margin)/8;
+	p->hsyncend = var->hsync_len/8;
+	p->hblankstart = p->hdispend + 1;
+	p->hblankend = p->htotal + 5;
+
+	p->vtotal = vres + vfront + vback + vsync - 2;
+	p->vdispend = vres - 1;
+	p->vsyncstart = vres + vback;
+	p->vsyncend = vsync;
+	p->vblankstart = vres;
+	p->vblankend = p->vtotal + 2;
+
+	debug("exit\n");
+
+	return 0;
+}
+
+
+/* Fill in var from info */
+static int trident_encode_var(struct fb_var_screeninfo *var,
+				  const void *par,
+				  struct fb_info_gen *info)
+{
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+	debug("enter\n");
+	*var = p->var;
+	var->bits_per_pixel = p->bpp;
+	debug("exit\n");
+	return 0;
+}
+
+/* Fill in par from hardware */
+static void trident_get_par(void *par, struct fb_info_gen *info)
+{
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+
+	debug("enter\n");
+	*p = i->currentmode;
+	debug("exit\n");
+}
+
+/* Pan the display */
+static int trident_pan_display(const struct fb_var_screeninfo *var,
+				   struct fb_info_gen *info)
+{
+	unsigned int offset;
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+
+	debug("enter\n");
+
+	offset = (var->xoffset + (var->yoffset * var->xres))
+			* var->bits_per_pixel/32;
+	i->currentmode.var.xoffset = var->xoffset;
+	i->currentmode.var.yoffset = var->yoffset;
+	set_screen_start(offset);
+	debug("exit\n");
+	return 0;
+}
+
+/* Set the hardware from par */
+static void trident_set_par(const void *par, struct fb_info_gen *info)
+{
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	unsigned char tmp;
+	debug("enter\n");
+
+	i->currentmode = *p;
+	unprotect_all();
+	crtc_unlock();
+	enable_mmio();
+
+	write3CE(CyberControl,8);
+	if (flatpanel && p->hres < i->nativex) {
+		/*
+		 * on flat panels with native size larger
+		 * than requested resolution decide whether
+		 * we stretch or center
+		 */
+		t_outb(0xEB,0x3C2);
+		write3CE(CyberControl,0x81);
+		if (center) //|| (p->bpp==32 && pci_id == CYBERBLADEi1D))
+			screen_center();
+		else if (stretch)
+			screen_stretch();
+
+	} else {
+		t_outb(0x2B,0x3C2);
+		write3CE(CyberControl,8);
+	}
+
+	/* vertical timing values */
+	write3X4(CRTVTotal, p->vtotal & 0xFF);
+	write3X4(CRTVDispEnd, p->vdispend & 0xFF);
+	write3X4(CRTVSyncStart, p->vsyncstart & 0xFF);
+	write3X4(CRTVSyncEnd, (p->vsyncend & 0x0F));
+	write3X4(CRTVBlankStart, p->vblankstart & 0xFF);
+	write3X4(CRTVBlankEnd, 0/*p->vblankend & 0xFF*/);
+
+	/* horizontal timing values */
+	write3X4(CRTHTotal, p->htotal & 0xFF);
+	write3X4(CRTHDispEnd, p->hdispend & 0xFF);
+	write3X4(CRTHSyncStart, p->hsyncstart & 0xFF);
+	write3X4(CRTHSyncEnd, (p->hsyncend & 0x1F) | ((p->hblankend & 0x20)<<2));
+	write3X4(CRTHBlankStart, p->hblankstart & 0xFF);
+	write3X4(CRTHBlankEnd, 0/*(p->hblankend & 0x1F)*/);
+
+	/* higher bits of vertical timing values */
+	tmp = 0x10;
+	if (p->vtotal & 0x100) tmp |= 0x01;
+	if (p->vdispend & 0x100) tmp |= 0x02;
+	if (p->vsyncstart & 0x100) tmp |= 0x04;
+	if (p->vblankstart & 0x100) tmp |= 0x08;
+
+	if (p->vtotal & 0x200) tmp |= 0x20;
+	if (p->vdispend & 0x200) tmp |= 0x40;
+	if (p->vsyncstart & 0x200) tmp |= 0x80;
+
+	write3X4(CRTOverflow, tmp);
+
+
+	tmp = read3X4(CRTHiOrd) | 0x08;	//line compare bit 10
+
+	if (p->vtotal & 0x400) tmp |= 0x80;
+	if (p->vblankstart & 0x400) tmp |= 0x40;
+	if (p->vsyncstart & 0x400) tmp |= 0x20;
+	if (p->vdispend & 0x400) tmp |= 0x10;
+	write3X4(CRTHiOrd, tmp);
+
+	write3X4(HorizOverflow, 0);
+
+	tmp = 0x40;
+	if (p->vblankstart & 0x200) tmp |= 0x20;
+	if (p->var.vmode & FB_VMODE_DOUBLE) tmp |= 0x80;  //double scan for 200 line modes
+	write3X4(CRTMaxScanLine, tmp);
+	write3X4(CRTLineCompare,0xFF);
+	write3X4(CRTPRowScan,0);
+	write3X4(CRTModeControl,0xC3);
+	write3X4(LinearAddReg,0x20);	//enable linear addressing
+	tmp = (p->var.vmode & FB_VMODE_INTERLACED) ? 0x84:0x80;
+	write3X4(CRTCModuleTest,tmp);	//enable access extended memory
+	write3X4(GraphEngReg, 0x80);	//enable GE for text acceleration
+
+	if (p->var.accel_flags & FB_ACCELF_TEXT)
+		acc->init_accel(p->hres,p->bpp);
+
+	switch (p->bpp) {
+		case 8:tmp=0;break;
+		case 16:tmp=5;break;
+		case 24:
+			/* tmp=0x29;break; */
+			/* seems like 24bpp is same as 32bpp when using vesafb */
+		case 32:tmp=9;break;
+	}
+	write3X4(PixelBusReg, tmp);
+
+	write3X4(InterfaceSel, 0x5B);	//32bit internal data path
+	write3X4(DRAMControl, 0x30);	//both IO,linear enable
+	write3X4(Performance, 0xBF);
+	write3X4(PCIReg,0x07);		//MMIO & PCI read and write burst enable
+
+	set_vclk(p->vclk);
+
+	write3C4(0,3);
+	write3C4(1,1);		//set char clock 8 dots wide
+	write3C4(2,0x0F);	//enable 4 maps because needed in chain4 mode
+	write3C4(3,0);
+	write3C4(4,0x0E);	//memory mode enable bitmaps ??
+
+	write3CE(MiscExtFunc,(p->bpp==32)?0x1A:0x12);	//divide clock by 2 if 32bpp
+							//chain4 mode display and CPU path
+	write3CE(0x5,0x40);	//no CGA compat,allow 256 col
+	write3CE(0x6,0x05);	//graphics mode
+	write3CE(0x7,0x0F);	//planes?
+
+	writeAttr(0x10,0x41);	//graphics mode and support 256 color modes
+	writeAttr(0x12,0x0F);	//planes
+	writeAttr(0x13,0);	//horizontal pel panning
+
+	//colors
+	for(tmp = 0;tmp < 0x10;tmp++)
+		writeAttr(tmp,tmp);
+	readb(fb_info.io_virt + CRT + 0x0A);	//flip-flop to index
+	t_outb(0x20, 0x3C0);			//enable attr
+
+	switch (p->bpp) {
+		case 8:	tmp = 0;break;		//256 colors
+		case 15: tmp = 0x10;break;
+		case 16: tmp = 0x30;break;	//hicolor
+		case 24: 			//truecolor
+		case 32: tmp = 0xD0;break;
+	}
+
+	t_inb(0x3C8);
+	t_inb(0x3C6);
+	t_inb(0x3C6);
+	t_inb(0x3C6);
+	t_inb(0x3C6);
+	t_outb(tmp,0x3C6);
+	t_inb(0x3C8);
+
+	if (flatpanel)
+		set_number_of_lines(p->vres);
+	set_lwidth(p->hres*p->bpp/(4*16));
+
+	trident_pan_display(&p->var,info);
+	debug("exit\n");
+}
+
+/* Get value of one color register */
+static int trident_getcolreg(unsigned regno, unsigned *red,
+				 unsigned *green, unsigned *blue,
+				 unsigned *transp, struct fb_info *info)
+{
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	int m = i->currentmode.bpp==8?256:16;
+
+	debug("enter %d\n",regno);
+
+	if (regno >= m)
+		return 1;
+
+	*red = palette[regno].red;
+	*green = palette[regno].green;
+	*blue = palette[regno].blue;
+	*transp = palette[regno].transp;
+
+	debug("exit\n");
+	return 0;
+}
+
+/* Set one color register */
+static int trident_setcolreg(unsigned regno, unsigned red, unsigned green,
+				 unsigned blue, unsigned transp,
+				 struct fb_info *info)
+{
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	int bpp = i->currentmode.bpp;
+	int m = bpp==8?256:16;
+
+	debug("enter %d\n",regno);
+
+	if (regno >= m)
+		return 1;
+
+	palette[regno].red = red;
+	palette[regno].green = green;
+	palette[regno].blue = blue;
+	palette[regno].transp = transp;
+
+	if (bpp==8) {
+		t_outb(0xFF,0x3C6);
+		t_outb(regno,0x3C8);
+
+		t_outb(red>>10,0x3C9);
+		t_outb(green>>10,0x3C9);
+		t_outb(blue>>10,0x3C9);
+
+	} else
+	if (bpp == 16) 			/* RGB 565 */
+			((u16*)info->pseudo_palette)[regno] = (red & 0xF800) |
+			((green & 0xFC00) >> 5) | ((blue & 0xF800) >> 11);
+	else
+	if (bpp == 32)		/* ARGB 8888 */
+		((u32*)info->pseudo_palette)[regno] =
+			((transp & 0xFF00) <<16) 	|
+			((red & 0xFF00) << 8) 		|
+			((green & 0xFF00))		|
+			((blue & 0xFF00)>>8);
+
+	debug("exit\n");
+	return 0;
+}
+
+/* Try blanking the screen.For flat panels it does nothing */
+static int trident_blank(int blank_mode, struct fb_info_gen *info)
+{
+	unsigned char PMCont,DPMSCont;
+
+	debug("enter\n");
+	if (flatpanel)
+		return 0;
+	t_outb(0x04,0x83C8); /* Read DPMS Control */
+	PMCont = t_inb(0x83C6) & 0xFC;
+	DPMSCont = read3CE(PowerStatus) & 0xFC;
+	switch (blank_mode)
+	{
+	case VESA_NO_BLANKING:
+		/* Screen: On, HSync: On, VSync: On */
+		PMCont |= 0x03;
+		DPMSCont |= 0x00;
+		break;
+	case VESA_HSYNC_SUSPEND:
+		/* Screen: Off, HSync: Off, VSync: On */
+		PMCont |= 0x02;
+		DPMSCont |= 0x01;
+		break;
+	case VESA_VSYNC_SUSPEND:
+		/* Screen: Off, HSync: On, VSync: Off */
+		PMCont |= 0x02;
+		DPMSCont |= 0x02;
+		break;
+	case VESA_POWERDOWN:
+		/* Screen: Off, HSync: Off, VSync: Off */
+		PMCont |= 0x00;
+		DPMSCont |= 0x03;
+		break;
+    	}
+
+	write3CE(PowerStatus,DPMSCont);
+    	t_outb(4,0x83C8);
+    	t_outb(PMCont,0x83C6);
+
+	debug("exit\n");
+	return 0;
+}
+
+/* Set display switch used by console */
+static void trident_set_disp(const void *par, struct display *disp,
+				 struct fb_info_gen *info)
+{
+	struct tridentfb_info * i = (struct tridentfb_info *)info;
+	struct fb_info * ii = (struct fb_info *)info;
+	struct tridentfb_par * p = (struct tridentfb_par *)par;
+	int isaccel = p->var.accel_flags & FB_ACCELF_TEXT;
+
+	disp->screen_base = (char *)i->fbmem_virt;
+	debug("enter\n");
+#ifdef FBCON_HAS_CFB8
+	if (p->bpp == 8 ) {
+		if (isaccel)
+			disp->dispsw = &trident_8bpp;
+		else
+			disp->dispsw = &fbcon_cfb8;
+	} else
+#endif
+#ifdef FBCON_HAS_CFB16
+	if (p->bpp == 16) {
+		if (isaccel)
+			disp->dispsw = &trident_16bpp;
+		else
+			disp->dispsw = &fbcon_cfb16;
+		disp->dispsw_data =ii->pseudo_palette;	/* console palette */
+	} else
+#endif
+#ifdef FBCON_HAS_CFB32
+	if (p->bpp == 32) {
+		if (isaccel)
+			disp->dispsw = &trident_32bpp;
+		else
+			disp->dispsw = &fbcon_cfb32;
+		disp->dispsw_data =ii->pseudo_palette;	/* console palette */
+	} else
+#endif
+	disp->dispsw = &fbcon_dummy;
+	debug("exit\n");
+}
+
+static struct fbgen_hwswitch trident_hwswitch = {
+	NULL, /* detect not needed */
+	trident_encode_fix,
+	trident_decode_var,
+	trident_encode_var,
+	trident_get_par,
+	trident_set_par,
+	trident_getcolreg,
+	trident_setcolreg,
+	trident_pan_display,
+	trident_blank,
+	trident_set_disp
+};
+
+/* List of boards that we are trying to support */
+static struct almost_supported_board {
+	int pci_id;
+	int family;
+	struct accel_switch * acc;
+	char* board_name;
+	int accel;
+} asb[] __initdata = {
+	{ BLADE3D,		BLADE,	&accel_blade, "Blade3D",	ACCEL	},
+	{ CYBERBLADEi7,	BLADE,	&accel_blade, "CyberBladei7",	ACCEL	},
+	{ CYBERBLADEi7D,	BLADE,	&accel_blade, "CyberBladei7D",	ACCEL	},
+	{ CYBERBLADEi1,	BLADE,	&accel_blade, "CyberBladei1",	ACCEL	},
+	{ CYBERBLADEi1D,	BLADE,	&accel_blade, "CyberBladei1D",	ACCEL	},
+	{ CYBERBLADEAi1,	BLADE,	&accel_blade, "CyberBladeAi1",	ACCEL	},
+	{ CYBERBLADEAi1D,	BLADE,	&accel_blade, "CyberBladeAi1D",	ACCEL	},
+	{ CYBERBLADEE4,	BLADE,	&accel_blade, "CyberBladeE4",	ACCEL	},
+
+	{ IMAGE975,	IMAGE,	&accel_image,	"IMAGE975",	NOACCEL	},
+	{ IMAGE985,	IMAGE,	&accel_image,	"IMAGE985",	NOACCEL	},
+	{ CYBER9320,	IMAGE,	&accel_image,	"Cyber9320",	NOACCEL	},
+	{ CYBER9388,	IMAGE,	&accel_image,	"Cyber9388",	NOACCEL	},
+	{ CYBER9520,	IMAGE,	&accel_image,	"Cyber9520",	NOACCEL	},
+	{ CYBER9525DVD,	IMAGE,	&accel_image,	"Cyber9525DVD",	NOACCEL	},
+	{ CYBER9397,	IMAGE,	&accel_image,	"Cyber9397",	NOACCEL	},
+	{ CYBER9397DVD,	IMAGE,	&accel_image,	"Cyber9397DVD",	NOACCEL	},
+
+	{ CYBERBLADEXPAi1,	XP,	&accel_blade,	"CyberBladeXPAi1",	NOACCEL },
+	{ CYBERBLADEXPm8,	XP,	&accel_blade,	"CyberBladeXPm8",	NOACCEL },
+	{ CYBERBLADEXPm16,	XP,	&accel_blade,	"CyberBladeXPm16",	NOACCEL },
+};
+
+static __init int trident_find_board(void)
+{
+	int i;
+	struct pci_dev * board;
+
+	for (i = 0;i < ARRAY_SIZE(asb);i++) {
+      		if ((board = pci_find_device(PCI_VENDOR_ID_TRIDENT,
+				   asb[i].pci_id,
+				   NULL))) {
+	 		family = asb[i].family;
+	 		acc = asb[i].acc;
+	 		pci_id = asb[i].pci_id;
+	 		defaultaccel = asb[i].accel;
+
+			fb_info.io = pci_resource_start(board,1);
+			fb_info.fbmem = pci_resource_start(board,0);
+	 		output("%s board found\n", asb[i].board_name);
+	 		return 1;
+      		}
+   	}
+	output("No Trident board found\n");
+	return 0;
+}
+
+int __init tridentfb_init(void)
+{
+
+	output("Trident framebuffer  %s initializing\n", VERSION);
+
+	if (!trident_find_board())
+     		return -1;
+
+	if (!request_mem_region(fb_info.io, TRIDENT_IOSIZE, "tridentfb")) {
+		debug("request_region failed!\n");
+		return -1;
+	};
+
+	fb_info.io_virt = (unsigned int)ioremap_nocache(fb_info.io, TRIDENT_IOSIZE);
+
+	if (!fb_info.io_virt) {
+		release_region(fb_info.io, TRIDENT_IOSIZE);
+		debug("ioremap failed\n");
+		return -1;
+	}
+
+	fb_info.memsize = get_memsize();
+	if (!request_mem_region(fb_info.fbmem, fb_info.memsize, "tridentfb")) {
+		debug("request_mem_region failed!\n");
+		return -1;
+	}
+
+	fb_info.fbmem_virt = (unsigned int)ioremap_nocache(fb_info.fbmem, fb_info.memsize);
+
+	if (!fb_info.fbmem_virt) {
+		release_mem_region(fb_info.fbmem, fb_info.memsize);
+		debug("ioremap failed\n");
+		return -1;
+	}
+
+	debug("Trident board found : mem = %X,io = %X, mem_v = %X, io_v = %X\n",
+		fb_info.fbmem, fb_info.io, fb_info.fbmem_virt, fb_info.io_virt);
+
+	fb_info.gen.parsize = sizeof (struct tridentfb_par);
+	fb_info.gen.fbhw = &trident_hwswitch;
+
+	strcpy(fb_info.gen.info.modename, tridentfb_name);
+	displaytype = get_displaytype();
+	if(flatpanel)
+		fb_info.nativex = get_nativex();
+	fb_info.gen.info.changevar = NULL;
+	fb_info.gen.info.node = NODEV;
+	fb_info.gen.info.fbops = &tridentfb_ops;
+	fb_info.gen.info.disp = &disp;
+
+	fb_info.gen.info.switch_con = &fbgen_switch;
+	fb_info.gen.info.updatevar = &fbgen_update_var;
+	fb_info.gen.info.blank = &fbgen_blank;
+
+	fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
+	fb_info.gen.info.fontname[0] = '\0';
+	fb_info.gen.info.pseudo_palette = pseudo_pal;
+
+	/* This should give a reasonable default video mode */
+	fb_find_mode(&default_var,&fb_info.gen.info,mode,NULL,0,NULL,bpp);
+	/*
+	 * Unless user explicitly requires accel/noaccel use
+	 * per chip defaults.Accel has priority over noaccel.
+	 */
+	if (accel)
+		defaultaccel = ACCEL;
+	else if (noaccel)
+		defaultaccel = NOACCEL;
+
+	if (defaultaccel == ACCEL)
+		default_var.accel_flags |= FB_ACCELF_TEXT;
+	else
+		default_var.accel_flags &= ~FB_ACCELF_TEXT;
+
+	trident_decode_var(&default_var, &fb_info.currentmode, &fb_info.gen);
+	fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
+	default_var.activate |= FB_ACTIVATE_NOW;
+	fbgen_set_disp(-1, &fb_info.gen);
+
+	if (register_framebuffer(&fb_info.gen.info) < 0) {
+		printk("Could not register Trident framebuffer\n");
+		return -EINVAL;
+	}
+
+	output("fb%d: %s frame buffer device %dx%d-%dbpp\n",
+	   GET_FB_IDX(fb_info.gen.info.node), fb_info.gen.info.modename,default_var.xres,
+	   default_var.yres,default_var.bits_per_pixel);
+	return 0;
+}
+
+void __exit tridentfb_exit(void)
+{
+	unregister_framebuffer(&fb_info.gen.info);
+	iounmap((void *)fb_info.io_virt);
+	iounmap((void *)fb_info.fbmem_virt);
+}
+
+/*
+ * Parse user specified options (`video=trident:')
+ * example:
+ * 	video=trident:800x600,bpp=16,noaccel
+ */
+int tridentfb_setup(char *options)
+{
+	char * opt;
+	if (!options || !*options)
+		return 0;
+	for(opt = strtok(options,",");opt;opt = strtok(NULL,",")){
+		if (!opt) continue;
+		if (!strncmp(opt,"noaccel",7))
+			noaccel = 1;
+		else if (!strncmp(opt,"accel",5))
+			accel = 1;
+		else if (!strncmp(opt,"fp",2))
+			displaytype = DISPLAY_FP;
+		else if (!strncmp(opt,"crt",3))
+			displaytype = DISPLAY_CRT;
+		else if (!strncmp(opt,"bpp=",4))
+			bpp = simple_strtoul(opt+4,NULL,0);
+		else if (!strncmp(opt,"center",6))
+			center = 1;
+		else if (!strncmp(opt,"stretch",7))
+			stretch = 1;
+		else if (!strncmp(opt,"memsize=",8))
+			memsize = simple_strtoul(opt+8,NULL,0);
+		else if (!strncmp(opt,"memdiff=",8))
+			memdiff = simple_strtoul(opt+8,NULL,0);
+		else if (!strncmp(opt,"nativex=",8))
+			nativex = simple_strtoul(opt+8,NULL,0);
+		else
+			mode = opt;
+	}
+	return 0;
+}
+
+static struct fb_ops tridentfb_ops = {
+	fb_get_fix:fbgen_get_fix,
+	fb_get_var:fbgen_get_var,
+	fb_set_var:fbgen_set_var,
+	fb_get_cmap:fbgen_get_cmap,
+	fb_set_cmap:fbgen_set_cmap,
+	fb_pan_display:fbgen_pan_display,
+};
+
+#ifdef MODULE
+module_init(tridentfb_init);
+#endif
+module_exit(tridentfb_exit);
+
+MODULE_AUTHOR("Jani Monoses <jani@astechnix.ro>");
+MODULE_DESCRIPTION("Framebuffer driver for Trident cards");
+MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/tridentfb.h linux/drivers/video/tridentfb.h
--- linux.orig/drivers/video/tridentfb.h	Thu Jan  1 00:00:00 1970
+++ linux/drivers/video/tridentfb.h	Thu Feb  7 18:19:01 2002
@@ -0,0 +1,169 @@
+
+#ifndef TRIDENTFB_DEBUG
+#define TRIDENTFB_DEBUG 0
+#endif
+
+#if TRIDENTFB_DEBUG
+#define debug(f,a...)	printk("%s:" f,  __FUNCTION__ , ## a)
+#else
+#define debug(f,a...)
+#endif
+
+#define output(f, a...) printk("tridentfb: " f, ## a)
+
+#define Kb	(1024)
+#define Mb	(Kb*Kb)
+
+/* PCI IDS of supported cards temporarily here */
+
+#define CYBER9320	0x9320
+#define CYBER9388	0x9388
+#define CYBER9397	0x9397
+#define CYBER9397DVD	0x939A
+#define CYBER9520	0x9520
+#define CYBER9525DVD	0x9525
+#define IMAGE975	0x9750
+#define IMAGE985	0x9850
+#define BLADE3D		0x9880
+#define CYBERBLADEE4	0x9540
+#define CYBERBLADEi7	0x8400
+#define CYBERBLADEi7D	0x8420
+#define CYBERBLADEi1	0x8500
+#define CYBERBLADEi1D	0x8520
+#define CYBERBLADEAi1	0x8600
+#define CYBERBLADEAi1D	0x8620
+#define CYBERBLADEXPAi1 0x8820
+#define CYBERBLADEXPm8  0x9910
+#define CYBERBLADEXPm16 0x9930
+
+/* acceleration families */
+#define IMAGE	0
+#define BLADE	1
+#define XP	2
+
+#define is_image()	(family == IMAGE)
+#define is_blade()	(family == BLADE)
+#define is_xp()		(family == XP)
+
+/* these defines are for 'lcd' variable */
+#define LCD_STRETCH	0
+#define LCD_CENTER	1
+#define LCD_BIOS	2
+
+/* display types */
+#define DISPLAY_CRT	0
+#define DISPLAY_FP	1
+
+#define flatpanel (displaytype == DISPLAY_FP)
+/* these are for defaultaccel variable */
+#define ACCEL	1
+#define NOACCEL	0
+
+#define TRIDENT_IOSIZE	0x20000
+#define NTSC 14.31818
+#define PAL  17.73448
+
+/* General Registers */
+#define SPR	0x1F		/* Software Programming Register (videoram) */
+
+/* 3C4 */
+#define RevisionID 0x09
+#define OldOrNew 0x0B
+#define ConfPort1 0x0C
+#define ConfPort2 0x0C
+#define NewMode2 0x0D
+#define NewMode1 0x0E
+#define Protection 0x11
+#define MCLKLow 0x16
+#define MCLKHigh 0x17
+#define ClockLow 0x18
+#define ClockHigh 0x19
+#define SSetup 0x20
+#define SKey 0x37
+#define SPKey 0x57
+
+/* 0x3x4 */
+#define CRTHTotal	0x00
+#define CRTHDispEnd	0x01
+#define CRTHBlankStart	0x02
+#define CRTHBlankEnd	0x03
+#define CRTHSyncStart	0x04
+#define CRTHSyncEnd	0x05
+
+#define CRTVTotal	0x06
+#define CRTVDispEnd	0x12
+#define CRTVBlankStart	0x15
+#define CRTVBlankEnd	0x16
+#define CRTVSyncStart	0x10
+#define CRTVSyncEnd	0x11
+
+#define CRTOverflow	0x07
+#define CRTPRowScan	0x08
+#define CRTMaxScanLine	0x09
+#define CRTModeControl	0x17
+#define CRTLineCompare	0x18
+
+/* 3x4 */
+#define StartAddrHigh 0x0C
+#define StartAddrLow 0x0D
+#define Offset 0x13
+#define Underline 0x14
+#define CRTCMode 0x17
+#define CRTCModuleTest 0x1E
+#define FIFOControl 0x20
+#define LinearAddReg 0x21
+#define DRAMTiming 0x23
+#define New32 0x23
+#define RAMDACTiming 0x25
+#define CRTHiOrd 0x27
+#define AddColReg 0x29
+#define InterfaceSel 0x2A
+#define HorizOverflow 0x2B
+#define GETest 0x2D
+#define Performance 0x2F
+#define GraphEngReg 0x36
+#define I2C 0x37
+#define PixelBusReg 0x38
+#define PCIReg 0x39
+#define DRAMControl 0x3A
+#define MiscContReg 0x3C
+#define CursorXLow 0x40
+#define CursorXHigh 0x41
+#define CursorYLow 0x42
+#define CursorYHigh 0x43
+#define CursorLocLow 0x44
+#define CursorLocHigh 0x45
+#define CursorXOffset 0x46
+#define CursorYOffset 0x47
+#define CursorFG1 0x48
+#define CursorFG2 0x49
+#define CursorFG3 0x4A
+#define CursorFG4 0x4B
+#define CursorBG1 0x4C
+#define CursorBG2 0x4D
+#define CursorBG3 0x4E
+#define CursorBG4 0x4F
+#define CursorControl 0x50
+#define PCIRetry 0x55
+#define PreEndControl 0x56
+#define PreEndFetch 0x57
+#define PCIMaster 0x60
+#define Enhancement0 0x62
+#define NewEDO 0x64
+#define TVinterface 0xC0
+#define TVMode 0xC1
+#define ClockControl 0xCF
+
+
+/* 3CE */
+#define MiscExtFunc 0x0F
+#define PowerStatus 0x23
+#define MiscIntContReg 0x2F
+#define CyberControl 0x30
+#define CyberEnhance 0x31
+#define FPConfig     0x33
+#define VertStretch  0x52
+#define HorStretch   0x53
+#define BiosMode     0x5c
+#define BiosReg      0x5d
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/valkyriefb.c linux/drivers/video/valkyriefb.c
--- linux.orig/drivers/video/valkyriefb.c	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/valkyriefb.c	Wed Dec 26 16:50:52 2001
@@ -7,6 +7,8 @@
  *  Vmode-switching changes and vmode 15/17 modifications created 29 August
  *  1998 by Barry K. Nathan <barryn@pobox.com>.
  *
+ *  Ported to m68k Macintosh by David Huggins-Daines <dhd@debian.org>
+ *
  *  Derived directly from:
  *
  *   controlfb.c -- frame buffer device for the PowerMac 'control' display
@@ -59,7 +61,12 @@
 #include <linux/adb.h>
 #include <linux/cuda.h>
 #include <asm/io.h>
+#ifdef CONFIG_MAC
+#include <asm/bootinfo.h>
+#include <asm/macintosh.h>
+#else
 #include <asm/prom.h>
+#endif
 #include <asm/pgtable.h>
 
 #include <video/fbcon.h>
@@ -71,8 +78,15 @@
 
 static int can_soft_blank = 1;
 
+#ifdef CONFIG_MAC
+/* We don't yet have functions to read the PRAM... perhaps we can
+   adapt them from the PPC code? */
+static int default_vmode = VMODE_640_480_67;
+static int default_cmode = CMODE_8;
+#else
 static int default_vmode = VMODE_NVRAM;
 static int default_cmode = CMODE_NVRAM;
+#endif
 static char fontname[40] __initdata = { 0 };
 
 static int currcon = 0;
@@ -117,7 +131,6 @@
 int valkyriefb_init(void);
 int valkyriefb_setup(char*);
 
-static void valkyrie_of_init(struct device_node *dp);
 static int valkyrie_get_fix(struct fb_fix_screeninfo *fix, int con,
 			 struct fb_info *info);
 static int valkyrie_get_var(struct fb_var_screeninfo *var, int con,
@@ -444,6 +457,7 @@
 	p->sense = read_valkyrie_sense(p);
 	printk(KERN_INFO "Monitor sense value = 0x%x, ", p->sense);
 
+#ifdef CONFIG_NVRAM
 	/* Try to pick a video mode out of NVRAM if we have one. */
 	if (default_vmode == VMODE_NVRAM) {
 		default_vmode = nvram_read_byte(NV_VMODE);
@@ -452,12 +466,13 @@
 		 || !valkyrie_reg_init[default_vmode - 1])
 			default_vmode = VMODE_CHOOSE;
 	}
+	if (default_cmode == CMODE_NVRAM)
+		default_cmode = nvram_read_byte(NV_CMODE);
+#endif
 	if (default_vmode == VMODE_CHOOSE)
 		default_vmode = mac_map_monitor_sense(p->sense);
 	if (!valkyrie_reg_init[default_vmode - 1])
 		default_vmode = VMODE_640_480_67;
-	if (default_cmode == CMODE_NVRAM)
-		default_cmode = nvram_read_byte(NV_CMODE);
 
 	/*
 	 * Reduce the pixel size if we don't have enough VRAM or bandwitdh.
@@ -550,48 +565,57 @@
 
 int __init valkyriefb_init(void)
 {
+	struct fb_info_valkyrie	*p;
+	unsigned long frame_buffer_phys, cmap_regs_phys, flags;
+
+#ifdef CONFIG_MAC
+	if (!MACH_IS_MAC)
+		return 0;
+	if (!(mac_bi_data.id == MAC_MODEL_Q630
+	      /* I'm not sure about this one */
+	    || mac_bi_data.id == MAC_MODEL_P588))
+		return 0;
+
+	/* Hardcoded addresses... welcome to 68k Macintosh country :-) */
+	frame_buffer_phys = 0xf9000000;
+	cmap_regs_phys = 0x50f24000;
+	flags = IOMAP_NOCACHE_SER; /* IOMAP_WRITETHROUGH?? */
+#else /* ppc (!CONFIG_MAC) */
 	struct device_node *dp;
 
 	dp = find_devices("valkyrie");
-	if (dp != 0)
-		valkyrie_of_init(dp);
-	return 0;
-}
+	if (dp == 0)
+		return 0;
 
-static void __init valkyrie_of_init(struct device_node *dp)
-{
-	struct fb_info_valkyrie	*p;
-	unsigned long addr;
-	
 	if(dp->n_addrs != 1) {
 		printk(KERN_ERR "expecting 1 address for valkyrie (got %d)", dp->n_addrs);
-		return;
+		return 0;
 	}	
 
+	frame_buffer_phys = dp->addrs[0].address;
+	cmap_regs_phys = dp->addrs[0].address+0x304000;
+	flags = _PAGE_WRITETHRU;
+#endif /* ppc (!CONFIG_MAC) */
+
 	p = kmalloc(sizeof(*p), GFP_ATOMIC);
 	if (p == 0)
-		return;
+		return 0;
 	memset(p, 0, sizeof(*p));
 
 	/* Map in frame buffer and registers */
-	addr = dp->addrs[0].address;
-	if (!request_mem_region(addr, dp->addrs[0].size, "valkyriefb")) {
+	if (!request_mem_region(frame_buffer_phys, 0x100000, "valkyriefb")) {
 		kfree(p);
-		return;
+		return 0;
 	}
-	p->frame_buffer_phys  = addr;
-	p->frame_buffer  = __ioremap(addr, 0x100000, _PAGE_WRITETHRU);
-	p->cmap_regs_phys     = addr + 0x304000;
-	p->cmap_regs     = ioremap(p->cmap_regs_phys,4096);
-	p->valkyrie_regs_phys = addr + 0x30a000;
-	p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 4096);
-
-	/*
-	 * kps: As far as I know, all Valkyries have fixed usable VRAM.
-	 */
 	p->total_vram = 0x100000;
-
+	p->frame_buffer_phys  = frame_buffer_phys;
+	p->frame_buffer = __ioremap(frame_buffer_phys, p->total_vram, flags);
+	p->cmap_regs_phys = cmap_regs_phys;
+	p->cmap_regs = ioremap(p->cmap_regs_phys, 0x1000);
+	p->valkyrie_regs_phys = cmap_regs_phys+0x6000;
+	p->valkyrie_regs = ioremap(p->valkyrie_regs_phys, 0x1000);
 	init_valkyrie(p);
+	return 0;
 }
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/drivers/video/valkyriefb.h linux/drivers/video/valkyriefb.h
--- linux.orig/drivers/video/valkyriefb.h	Tue Jan 22 17:51:23 2002
+++ linux/drivers/video/valkyriefb.h	Wed Dec 26 16:50:52 2001
@@ -9,6 +9,8 @@
  * 
  * vmode 10 changed by Steven Borley <sjb@salix.demon.co.uk>, 14 mai 2000
  *
+ * Ported to 68k Macintosh by David Huggins-Daines <dhd@debian.org>
+ *
  * 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
@@ -37,12 +39,19 @@
  *  Copyright (C) 1998 Jon Howell
  */
 
+#ifdef CONFIG_MAC
+/* Valkyrie registers are word-aligned on m68k */
+#define VALKYRIE_REG_PADSIZE	3
+#else
+#define VALKYRIE_REG_PADSIZE	7
+#endif
+
 /*
  * Structure of the registers for the Valkyrie colormap registers.
  */
 struct cmap_regs {
 	unsigned char addr;
-	char pad1[7];
+	char pad1[VALKYRIE_REG_PADSIZE];
 	unsigned char lut;
 };
 
@@ -52,7 +61,7 @@
 
 struct vpreg {			/* padded register */
 	unsigned char r;
-	char pad[7];
+	char pad[VALKYRIE_REG_PADSIZE];
 };
 
 
@@ -81,6 +90,7 @@
 	int	vres;
 };
 
+#ifndef CONFIG_MAC
 /* Register values for 1024x768, 75Hz mode (17) */
 /* I'm not sure which mode this is (16 or 17), so I'm defining it as 17,
  * since the equivalent mode in controlfb (which I adapted this from) is
@@ -125,14 +135,6 @@
 	1024, 768
 };
 
-/* Register values for 832x624, 75Hz mode (13) */
-static struct valkyrie_regvals valkyrie_reg_init_13 = {
-    9,
-    { 23, 42, 3 },  /* pixel clock = 57.07MHz for V=74.27Hz */
-    { 832, 0 },
-	832, 624
-};
-
 /* Register values for 800x600, 72Hz mode (11) */
 static struct valkyrie_regvals valkyrie_reg_init_11 = {
     13,
@@ -140,6 +142,15 @@
     { 800, 0 },
 	800, 600
 };
+#endif /* CONFIG_MAC */
+
+/* Register values for 832x624, 75Hz mode (13) */
+static struct valkyrie_regvals valkyrie_reg_init_13 = {
+    9,
+    { 23, 42, 3 },  /* pixel clock = 57.07MHz for V=74.27Hz */
+    { 832, 0 },
+	832, 624
+};
 
 /* Register values for 800x600, 60Hz mode (10) */
 static struct valkyrie_regvals valkyrie_reg_init_10 = {
@@ -177,6 +188,15 @@
 	NULL,
 	NULL,
 	&valkyrie_reg_init_10,
+#ifdef CONFIG_MAC
+	NULL,
+	NULL,
+	&valkyrie_reg_init_13,
+	NULL,
+	NULL,
+	NULL,
+	NULL,
+#else
 	&valkyrie_reg_init_11,
 	NULL,
 	&valkyrie_reg_init_13,
@@ -184,6 +204,7 @@
 	&valkyrie_reg_init_15,
 	NULL,
 	&valkyrie_reg_init_17,
+#endif
 	NULL,
 	NULL,
 	NULL
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/Config.in linux/fs/Config.in
--- linux.orig/fs/Config.in	Mon Nov 12 17:34:16 2001
+++ linux/fs/Config.in	Thu Jan 17 21:21:15 2002
@@ -8,9 +8,9 @@
 tristate 'Kernel automounter support' CONFIG_AUTOFS_FS
 tristate 'Kernel automounter version 4 support (also supports v3)' CONFIG_AUTOFS4_FS
 
-dep_tristate 'Reiserfs support' CONFIG_REISERFS_FS $CONFIG_EXPERIMENTAL
-dep_mbool '  Have reiserfs do extra internal checking' CONFIG_REISERFS_CHECK $CONFIG_REISERFS_FS $CONFIG_EXPERIMENTAL
-dep_mbool '  Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS $CONFIG_EXPERIMENTAL
+tristate 'Reiserfs support' CONFIG_REISERFS_FS
+dep_mbool '  Have reiserfs do extra internal checking' CONFIG_REISERFS_CHECK $CONFIG_REISERFS_FS
+dep_mbool '  Stats in /proc/fs/reiserfs' CONFIG_REISERFS_PROC_INFO $CONFIG_REISERFS_FS
 
 dep_tristate 'ADFS file system support' CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
 dep_mbool '  ADFS write support (DANGEROUS)' CONFIG_ADFS_FS_RW $CONFIG_ADFS_FS $CONFIG_EXPERIMENTAL
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/Makefile linux/fs/Makefile
--- linux.orig/fs/Makefile	Mon Nov 12 17:34:16 2001
+++ linux/fs/Makefile	Mon Feb  4 17:38:23 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the Linux filesystems.
 #
-# 14 Sep 2000, Christoph Hellwig <hch@caldera.de>
+# 14 Sep 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 # 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/adfs/dir_f.c linux/fs/adfs/dir_f.c
--- linux.orig/fs/adfs/dir_f.c	Mon Sep 18 22:14:06 2000
+++ linux/fs/adfs/dir_f.c	Wed Jan 23 20:15:01 2002
@@ -193,7 +193,7 @@
 			goto release_buffers;
 		}
 
-		dir->bh[blk] = bread(sb->s_dev, phys, sb->s_blocksize);
+		dir->bh[blk] = sb_bread(sb, phys);
 		if (!dir->bh[blk])
 			goto release_buffers;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/adfs/dir_fplus.c linux/fs/adfs/dir_fplus.c
--- linux.orig/fs/adfs/dir_fplus.c	Mon Sep 18 22:14:06 2000
+++ linux/fs/adfs/dir_fplus.c	Wed Jan 23 20:15:01 2002
@@ -35,7 +35,7 @@
 		goto out;
 	}
 
-	dir->bh[0] = bread(sb->s_dev, block, sb->s_blocksize);
+	dir->bh[0] = sb_bread(sb, block);
 	if (!dir->bh[0])
 		goto out;
 	dir->nr_buffers += 1;
@@ -60,7 +60,7 @@
 			goto out;
 		}
 
-		dir->bh[blk] = bread(sb->s_dev, block, sb->s_blocksize);
+		dir->bh[blk] = sb_bread(sb, block);
 		if (!dir->bh[blk])
 			goto out;
 		dir->nr_buffers = blk;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/adfs/super.c linux/fs/adfs/super.c
--- linux.orig/fs/adfs/super.c	Thu Oct 25 20:53:53 2001
+++ linux/fs/adfs/super.c	Wed Jan 23 20:15:01 2002
@@ -263,7 +263,7 @@
 		dm[zone].dm_startbit = 0;
 		dm[zone].dm_endbit   = zone_size;
 		dm[zone].dm_startblk = zone * zone_size - ADFS_DR_SIZE_BITS;
-		dm[zone].dm_bh       = bread(sb->s_dev, map_addr, sb->s_blocksize);
+		dm[zone].dm_bh       = sb_bread(sb, map_addr);
 
 		if (!dm[zone].dm_bh) {
 			adfs_error(sb, "unable to read map");
@@ -319,8 +319,9 @@
 	if (parse_options(sb, data))
 		goto error;
 
+	sb->s_blocksize = BLOCK_SIZE;
 	set_blocksize(dev, BLOCK_SIZE);
-	if (!(bh = bread(dev, ADFS_DISCRECORD / BLOCK_SIZE, BLOCK_SIZE))) {
+	if (!(bh = sb_bread(sb, ADFS_DISCRECORD / BLOCK_SIZE))) {
 		adfs_error(sb, "unable to read superblock");
 		goto error;
 	}
@@ -354,7 +355,7 @@
 
 		brelse(bh);
 		set_blocksize(dev, sb->s_blocksize);
-		bh = bread(dev, ADFS_DISCRECORD / sb->s_blocksize, sb->s_blocksize);
+		bh = sb_bread(sb, ADFS_DISCRECORD / sb->s_blocksize);
 		if (!bh) {
 			adfs_error(sb, "couldn't read superblock on "
 				"2nd try.");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/affs/Changes linux/fs/affs/Changes
--- linux.orig/fs/affs/Changes	Tue Sep 11 15:19:35 2001
+++ linux/fs/affs/Changes	Tue Jan 22 23:28:55 2002
@@ -28,6 +28,12 @@
 
 Please direct bug reports to: zippel@linux-m68k.org
 
+Version 3.19
+------------
+
+- sizeof changes from Kernel Janitor Project
+- several bug fixes found with fsx
+
 Version 3.18
 ------------
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/affs/bitmap.c linux/fs/affs/bitmap.c
--- linux.orig/fs/affs/bitmap.c	Wed Apr 25 21:57:09 2001
+++ linux/fs/affs/bitmap.c	Tue Jan 22 23:28:55 2002
@@ -292,7 +292,7 @@
 	AFFS_SB->s_bmap_bits = sb->s_blocksize * 8 - 32;
 	AFFS_SB->s_bmap_count = (AFFS_SB->s_partition_size - AFFS_SB->s_reserved +
 				 AFFS_SB->s_bmap_bits - 1) / AFFS_SB->s_bmap_bits;
-	size = AFFS_SB->s_bmap_count * sizeof(struct affs_bm_info);
+	size = AFFS_SB->s_bmap_count * sizeof(*bm);
 	bm = AFFS_SB->s_bitmap = kmalloc(size, GFP_KERNEL);
 	if (!AFFS_SB->s_bitmap) {
 		printk(KERN_ERR "AFFS: Bitmap allocation failed\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/affs/file.c linux/fs/affs/file.c
--- linux.orig/fs/affs/file.c	Tue Sep 11 15:19:35 2001
+++ linux/fs/affs/file.c	Tue Jan 22 23:28:55 2002
@@ -510,7 +510,7 @@
 static int
 affs_do_readpage_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
 	struct buffer_head *bh;
 	char *data;
@@ -518,6 +518,8 @@
 	u32 tmp;
 
 	pr_debug("AFFS: read_page(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
+	if (from > to || to > PAGE_CACHE_SIZE)
+		BUG();
 	data = page_address(page);
 	bsize = AFFS_SB->s_data_blksize;
 	tmp = (page->index << PAGE_CACHE_SHIFT) + from;
@@ -528,7 +530,9 @@
 		bh = affs_bread_ino(inode, bidx, 0);
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
-		tmp = min(bsize - boff, from - to);
+		tmp = min(bsize - boff, to - from);
+		if (from + tmp > to || tmp > bsize)
+			BUG();
 		memcpy(data + from, AFFS_DATA(bh) + boff, tmp);
 		affs_brelse(bh);
 		bidx++;
@@ -539,9 +543,8 @@
 }
 
 static int
-affs_extent_file_ofs(struct file *file, u32 newsize)
+affs_extent_file_ofs(struct inode *inode, u32 newsize)
 {
-	struct inode *inode = file->f_dentry->d_inode;
 	struct super_block *sb = inode->i_sb;
 	struct buffer_head *bh, *prev_bh;
 	u32 bidx, boff;
@@ -551,7 +554,7 @@
 	pr_debug("AFFS: extent_file(%u, %d)\n", (u32)inode->i_ino, newsize);
 	bsize = AFFS_SB->s_data_blksize;
 	bh = NULL;
-	size = inode->i_size;
+	size = AFFS_INODE->mmu_private;
 	bidx = size / bsize;
 	boff = size % bsize;
 	if (boff) {
@@ -559,6 +562,8 @@
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		tmp = min(bsize - boff, newsize - size);
+		if (boff + tmp > bsize || tmp > bsize)
+			BUG();
 		memset(AFFS_DATA(bh) + boff, 0, tmp);
 		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
 		affs_fix_checksum(sb, bh);
@@ -577,18 +582,21 @@
 		if (IS_ERR(bh))
 			goto out;
 		tmp = min(bsize, newsize - size);
+		if (tmp > bsize)
+			BUG();
 		AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
 		AFFS_DATA_HEAD(bh)->key = cpu_to_be32(inode->i_ino);
 		AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
 		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
 		affs_fix_checksum(sb, bh);
+		bh->b_state &= ~(1UL << BH_New);
 		mark_buffer_dirty_inode(bh, inode);
 		if (prev_bh) {
 			u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
 			if (tmp)
-				affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+				affs_warning(sb, "extent_file_ofs", "next block already set for %d (%d)", bidx, tmp);
 			AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
-			affs_adjust_checksum(prev_bh, bidx - tmp);
+			affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
 			mark_buffer_dirty_inode(prev_bh, inode);
 			affs_brelse(prev_bh);
 		}
@@ -596,7 +604,7 @@
 		bidx++;
 	}
 	affs_brelse(bh);
-	inode->i_size = AFFS_INODE->mmu_private = size;
+	inode->i_size = AFFS_INODE->mmu_private = newsize;
 	return 0;
 
 out:
@@ -607,7 +615,7 @@
 static int
 affs_readpage_ofs(struct file *file, struct page *page)
 {
-	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *inode = page->mapping->host;
 	u32 to;
 	int err;
 
@@ -627,22 +635,22 @@
 
 static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *inode = page->mapping->host;
 	u32 size, offset;
 	u32 tmp;
 	int err = 0;
 
 	pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to);
-	if (Page_Uptodate(page))
-		return 0;
-
-	size = inode->i_size;
 	offset = page->index << PAGE_CACHE_SHIFT;
-	if (offset + from > size) {
-		err = affs_extent_file_ofs(file, offset + from);
+	if (offset + from > AFFS_INODE->mmu_private) {
+		err = affs_extent_file_ofs(inode, offset + from);
 		if (err)
 			return err;
 	}
+	size = inode->i_size;
+
+	if (Page_Uptodate(page))
+		return 0;
 
 	if (from) {
 		err = affs_do_readpage_ofs(file, page, 0, from);
@@ -653,7 +661,7 @@
 		memset(page_address(page) + to, 0, PAGE_CACHE_SIZE - to);
 		if (size > offset + to) {
 			if (size < offset + PAGE_CACHE_SIZE)
-				tmp = size & PAGE_CACHE_MASK;
+				tmp = size & ~PAGE_CACHE_MASK;
 			else
 				tmp = PAGE_CACHE_SIZE;
 			err = affs_do_readpage_ofs(file, page, to, tmp);
@@ -664,7 +672,7 @@
 
 static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to)
 {
-	struct inode *inode = file->f_dentry->d_inode;
+	struct inode *inode = page->mapping->host;
 	struct super_block *sb = inode->i_sb;
 	struct buffer_head *bh, *prev_bh;
 	char *data;
@@ -686,6 +694,8 @@
 		if (IS_ERR(bh))
 			return PTR_ERR(bh);
 		tmp = min(bsize - boff, to - from);
+		if (boff + tmp > bsize || tmp > bsize)
+			BUG();
 		memcpy(AFFS_DATA(bh) + boff, data + from, tmp);
 		AFFS_DATA_HEAD(bh)->size = cpu_to_be32(be32_to_cpu(AFFS_DATA_HEAD(bh)->size) + tmp);
 		affs_fix_checksum(sb, bh);
@@ -710,12 +720,13 @@
 			AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
 			AFFS_DATA_HEAD(bh)->size = cpu_to_be32(bsize);
 			AFFS_DATA_HEAD(bh)->next = 0;
+			bh->b_state &= ~(1UL << BH_New);
 			if (prev_bh) {
 				u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
 				if (tmp)
-					affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+					affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
 				AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
-				affs_adjust_checksum(prev_bh, bidx - tmp);
+				affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
 				mark_buffer_dirty_inode(prev_bh, inode);
 			}
 		}
@@ -732,6 +743,8 @@
 		if (IS_ERR(bh))
 			goto out;
 		tmp = min(bsize, to - from);
+		if (tmp > bsize)
+			BUG();
 		memcpy(AFFS_DATA(bh), data + from, tmp);
 		if (bh->b_state & (1UL << BH_New)) {
 			AFFS_DATA_HEAD(bh)->ptype = cpu_to_be32(T_DATA);
@@ -739,12 +752,13 @@
 			AFFS_DATA_HEAD(bh)->sequence = cpu_to_be32(bidx);
 			AFFS_DATA_HEAD(bh)->size = cpu_to_be32(tmp);
 			AFFS_DATA_HEAD(bh)->next = 0;
+			bh->b_state &= ~(1UL << BH_New);
 			if (prev_bh) {
 				u32 tmp = be32_to_cpu(AFFS_DATA_HEAD(prev_bh)->next);
 				if (tmp)
-					affs_warning(sb, "prepare_write_ofs", "next block already set for %d (%d)", bidx, tmp);
+					affs_warning(sb, "commit_write_ofs", "next block already set for %d (%d)", bidx, tmp);
 				AFFS_DATA_HEAD(prev_bh)->next = cpu_to_be32(bh->b_blocknr);
-				affs_adjust_checksum(prev_bh, bidx - tmp);
+				affs_adjust_checksum(prev_bh, bh->b_blocknr - tmp);
 				mark_buffer_dirty_inode(prev_bh, inode);
 			}
 		} else if (be32_to_cpu(AFFS_DATA_HEAD(bh)->size) < tmp)
@@ -808,7 +822,8 @@
 	struct buffer_head *ext_bh;
 	int i;
 
-	pr_debug("AFFS: truncate(inode=%d, size=%u)\n", (u32)inode->i_ino, (u32)inode->i_size);
+	pr_debug("AFFS: truncate(inode=%d, oldsize=%u, newsize=%u)\n",
+		 (u32)inode->i_ino, (u32)AFFS_INODE->mmu_private, (u32)inode->i_size);
 
 	last_blk = 0;
 	ext = 0;
@@ -839,10 +854,19 @@
 
 	// lock cache
 	ext_bh = affs_get_extblock(inode, ext);
+	if (IS_ERR(ext_bh)) {
+		affs_warning(sb, "truncate", "unexpected read error for ext block %u (%d)",
+			     ext, PTR_ERR(ext_bh));
+		return;
+	}
 	if (AFFS_INODE->i_lc) {
 		/* clear linear cache */
-		for (i = (ext + 1) >> AFFS_INODE->i_lc_shift; i < AFFS_LC_SIZE; i++)
-			AFFS_INODE->i_lc[i] = 0;
+		i = (ext + 1) >> AFFS_INODE->i_lc_shift;
+		if (AFFS_INODE->i_lc_size > i) {
+			AFFS_INODE->i_lc_size = i;
+			for (; i < AFFS_LC_SIZE; i++)
+				AFFS_INODE->i_lc[i] = 0;
+		}
 		/* clear associative cache */
 		for (i = 0; i < AFFS_AC_SIZE; i++)
 			if (AFFS_INODE->i_ac[i].ext >= ext)
@@ -873,6 +897,19 @@
 	if (inode->i_size) {
 		AFFS_INODE->i_blkcnt = last_blk + 1;
 		AFFS_INODE->i_extcnt = ext + 1;
+		if (AFFS_SB->s_flags & SF_OFS) {
+			struct buffer_head *bh = affs_bread_ino(inode, last_blk, 0);
+			u32 tmp;
+			if (IS_ERR(bh)) {
+				affs_warning(sb, "truncate", "unexpected read error for last block %u (%d)",
+					     last_blk, PTR_ERR(bh));
+				return;
+			}
+			tmp = be32_to_cpu(AFFS_DATA_HEAD(bh)->next);
+			AFFS_DATA_HEAD(bh)->next = 0;
+			affs_adjust_checksum(bh, -tmp);
+			affs_brelse(bh);
+		}
 	} else {
 		AFFS_INODE->i_blkcnt = 0;
 		AFFS_INODE->i_extcnt = 1;
@@ -891,4 +928,5 @@
 		ext_key = be32_to_cpu(AFFS_TAIL(sb, ext_bh)->extension);
 		affs_brelse(ext_bh);
 	}
+	affs_free_prealloc(inode);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/affs/inode.c linux/fs/affs/inode.c
--- linux.orig/fs/affs/inode.c	Sun Sep 30 19:26:08 2001
+++ linux/fs/affs/inode.c	Tue Jan 22 23:28:55 2002
@@ -68,7 +68,7 @@
 	inode->i_size = 0;
 	inode->i_nlink = 1;
 	inode->i_mode = 0;
-	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+	memset(AFFS_INODE, 0, sizeof(*AFFS_INODE));
 	init_MUTEX(&AFFS_INODE->i_link_lock);
 	init_MUTEX(&AFFS_INODE->i_ext_lock);
 	AFFS_INODE->i_extcnt = 1;
@@ -318,7 +318,7 @@
 	inode->i_ino     = block;
 	inode->i_nlink   = 1;
 	inode->i_mtime   = inode->i_atime = inode->i_ctime = CURRENT_TIME;
-	memset(AFFS_INODE, 0, sizeof(struct affs_inode_info));
+	memset(AFFS_INODE, 0, sizeof(*AFFS_INODE));
 	AFFS_INODE->i_extcnt = 1;
 	AFFS_INODE->i_ext_last = ~1;
 	init_MUTEX(&AFFS_INODE->i_link_lock);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/affs/super.c linux/fs/affs/super.c
--- linux.orig/fs/affs/super.c	Tue Nov 13 17:19:41 2001
+++ linux/fs/affs/super.c	Wed Jan 23 20:15:01 2002
@@ -240,7 +240,7 @@
 
 	sb->s_magic             = AFFS_SUPER_MAGIC;
 	sb->s_op                = &affs_sops;
-	memset(AFFS_SB, 0, sizeof(struct affs_sb_info));
+	memset(AFFS_SB, 0, sizeof(*AFFS_SB));
 	init_MUTEX(&AFFS_SB->s_bmlock);
 
 	if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block,
@@ -332,7 +332,7 @@
 			       blocksize == 2048 ? 11 : 12;
 
 	/* Find out which kind of FS we have */
-	boot_bh = bread(sb->s_dev, 0, sb->s_blocksize);
+	boot_bh = sb_bread(sb, 0);
 	if (!boot_bh) {
 		printk(KERN_ERR "AFFS: Cannot read boot block\n");
 		goto out_error;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/bfs/dir.c linux/fs/bfs/dir.c
--- linux.orig/fs/bfs/dir.c	Thu Nov 16 21:18:26 2000
+++ linux/fs/bfs/dir.c	Wed Jan 23 20:15:01 2002
@@ -41,7 +41,7 @@
 	while (f->f_pos < dir->i_size) {
 		offset = f->f_pos & (BFS_BSIZE-1);
 		block = dir->iu_sblock + (f->f_pos >> BFS_BSIZE_BITS);
-		bh = bread(dev, block, BFS_BSIZE);
+		bh = sb_bread(dir->i_sb, block);
 		if (!bh) {
 			f->f_pos += BFS_BSIZE - offset;
 			continue;
@@ -270,7 +270,7 @@
 	sblock = dir->iu_sblock;
 	eblock = dir->iu_eblock;
 	for (block=sblock; block<=eblock; block++) {
-		bh = bread(dev, block, BFS_BSIZE);
+		bh = sb_bread(dir->i_sb, block);
 		if(!bh) 
 			return -ENOSPC;
 		for (off=0; off<BFS_BSIZE; off+=BFS_DIRENT_SIZE) {
@@ -319,7 +319,7 @@
 	block = offset = 0;
 	while (block * BFS_BSIZE + offset < dir->i_size) {
 		if (!bh) {
-			bh = bread(dir->i_dev, dir->iu_sblock + block, BFS_BSIZE);
+			bh = sb_bread(dir->i_sb, dir->iu_sblock + block);
 			if (!bh) {
 				block++;
 				continue;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/bfs/inode.c linux/fs/bfs/inode.c
--- linux.orig/fs/bfs/inode.c	Sun Sep 30 19:26:08 2001
+++ linux/fs/bfs/inode.c	Wed Jan 23 20:15:01 2002
@@ -47,7 +47,7 @@
 	}
 
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
-	bh = bread(dev, block, BFS_BSIZE);
+	bh = sb_bread(inode->i_sb, block);
 	if (!bh) {
 		printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
 		make_bad_inode(inode);
@@ -100,7 +100,7 @@
 
 	lock_kernel();
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
-	bh = bread(dev, block, BFS_BSIZE);
+	bh = sb_bread(inode->i_sb, block);
 	if (!bh) {
 		printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
 		unlock_kernel();
@@ -153,7 +153,7 @@
 	lock_kernel();
 	mark_inode_dirty(inode);
 	block = (ino - BFS_ROOT_INO)/BFS_INODES_PER_BLOCK + 1;
-	bh = bread(dev, block, BFS_BSIZE);
+	bh = sb_bread(s, block);
 	if (!bh) {
 		printf("Unable to read inode %s:%08lx\n", bdevname(dev), ino);
 		unlock_kernel();
@@ -252,7 +252,7 @@
 	s->s_blocksize = BFS_BSIZE;
 	s->s_blocksize_bits = BFS_BSIZE_BITS;
 
-	bh = bread(dev, 0, BFS_BSIZE);
+	bh = sb_bread(s, 0);
 	if(!bh)
 		goto out;
 	bfs_sb = (struct bfs_super_block *)bh->b_data;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/binfmt_elf.c linux/fs/binfmt_elf.c
--- linux.orig/fs/binfmt_elf.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/binfmt_elf.c	Mon Feb  4 19:11:16 2002
@@ -138,6 +138,22 @@
 	} else
 		u_platform = p;
 
+#if defined(__i386__) && defined(CONFIG_SMP)
+	/*
+	 * In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
+	 * by the processes running on the same package. One thing we can do
+	 * is to shuffle the initial stack for them.
+	 *
+	 * The conditionals here are unneeded, but kept in to make the
+	 * code behaviour the same as pre change unless we have hyperthreaded
+	 * processors. This keeps Mr Marcelo Person happier but should be
+	 * removed for 2.5
+	 */
+	 
+	if(smp_num_siblings > 1)
+		u_platform = u_platform - ((current->pid % 64) << 7);
+#endif	
+
 	/*
 	 * Force 16 byte _final_ alignment here for generality.
 	 */
@@ -505,30 +521,10 @@
 #if 0
 			printk("Using ELF interpreter %s\n", elf_interpreter);
 #endif
-#ifdef __sparc__
-			if (ibcs2_interpreter) {
-				unsigned long old_pers = current->personality;
-				struct exec_domain *old_domain = current->exec_domain;
-				struct exec_domain *new_domain;
-				struct fs_struct *old_fs = current->fs, *new_fs;
-				get_exec_domain(old_domain);
-				atomic_inc(&old_fs->count);
-
-				set_personality(PER_SVR4);
-				interpreter = open_exec(elf_interpreter);
-
-				new_domain = current->exec_domain;
-				new_fs = current->fs;
-				current->personality = old_pers;
-				current->exec_domain = old_domain;
-				current->fs = old_fs;
-				put_exec_domain(new_domain);
-				put_fs_struct(new_fs);
-			} else
-#endif
-			{
-				interpreter = open_exec(elf_interpreter);
-			}
+
+			SET_PERSONALITY(elf_ex, ibcs2_interpreter);
+
+			interpreter = open_exec(elf_interpreter);
 			retval = PTR_ERR(interpreter);
 			if (IS_ERR(interpreter))
 				goto out_free_interp;
@@ -602,10 +598,6 @@
 	current->flags &= ~PF_FORKNOEXEC;
 	elf_entry = (unsigned long) elf_ex.e_entry;
 
-	/* Do this immediately, since STACK_TOP as used in setup_arg_pages
-	   may depend on the personality.  */
-	SET_PERSONALITY(elf_ex, ibcs2_interpreter);
-
 	/* Do this so that we can load the interpreter, if need be.  We will
 	   change some of these later */
 	current->mm->rss = 0;
@@ -1049,6 +1041,23 @@
 
 	}
 
+	memset(&prstatus, 0, sizeof(prstatus));
+	/*
+	 * This transfers the registers from regs into the standard
+	 * coredump arrangement, whatever that is.
+	 */
+#ifdef ELF_CORE_COPY_REGS
+	ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
+#else
+	if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
+	{
+		printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n",
+			(long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));
+	}
+	else
+		*(struct pt_regs *)&prstatus.pr_reg = *regs;
+#endif
+
 	/* now stop all vm operations */
 	down_write(&current->mm->mmap_sem);
 	segs = current->mm->map_count;
@@ -1092,7 +1101,6 @@
 	 * Set up the notes in similar form to SVR4 core dumps made
 	 * with info from their /proc.
 	 */
-	memset(&prstatus, 0, sizeof(prstatus));
 
 	notes[0].name = "CORE";
 	notes[0].type = NT_PRSTATUS;
@@ -1114,22 +1122,6 @@
 	prstatus.pr_cstime.tv_sec = CT_TO_SECS(current->times.tms_cstime);
 	prstatus.pr_cstime.tv_usec = CT_TO_USECS(current->times.tms_cstime);
 
-	/*
-	 * This transfers the registers from regs into the standard
-	 * coredump arrangement, whatever that is.
-	 */
-#ifdef ELF_CORE_COPY_REGS
-	ELF_CORE_COPY_REGS(prstatus.pr_reg, regs)
-#else
-	if (sizeof(elf_gregset_t) != sizeof(struct pt_regs))
-	{
-		printk("sizeof(elf_gregset_t) (%ld) != sizeof(struct pt_regs) (%ld)\n",
-			(long)sizeof(elf_gregset_t), (long)sizeof(struct pt_regs));
-	}
-	else
-		*(struct pt_regs *)&prstatus.pr_reg = *regs;
-#endif
-
 #ifdef DEBUG
 	dump_regs("Passed in regs", (elf_greg_t *)regs);
 	dump_regs("prstatus regs", (elf_greg_t *)&prstatus.pr_reg);
@@ -1225,9 +1217,11 @@
 
 		if (!maydump(vma))
 			continue;
+
 #ifdef DEBUG
-		printk("elf_core_dump: writing %08lx %lx\n", addr, len);
+		printk("elf_core_dump: writing %08lx-%08lx\n", vma->vm_start, vma->vm_end);
 #endif
+
 		for (addr = vma->vm_start;
 		     addr < vma->vm_end;
 		     addr += PAGE_SIZE) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/block_dev.c linux/fs/block_dev.c
--- linux.orig/fs/block_dev.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/block_dev.c	Mon Jan 14 18:01:23 2002
@@ -171,11 +171,15 @@
 
 static int __block_fsync(struct inode * inode)
 {
-	int ret;
+	int ret, err;
 
-	filemap_fdatasync(inode->i_mapping);
-	ret = sync_buffers(inode->i_rdev, 1);
-	filemap_fdatawait(inode->i_mapping);
+	ret = filemap_fdatasync(inode->i_mapping);
+	err = sync_buffers(inode->i_rdev, 1);
+	if (err && !ret)
+		ret = err;
+	err = filemap_fdatawait(inode->i_mapping);
+	if (err && !ret)
+		ret = err;
 
 	return ret;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/buffer.c linux/fs/buffer.c
--- linux.orig/fs/buffer.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/buffer.c	Wed Feb  6 21:23:32 2002
@@ -401,9 +401,9 @@
 	struct file * file;
 	struct dentry * dentry;
 	struct inode * inode;
-	int err;
+	int ret, err;
 
-	err = -EBADF;
+	ret = -EBADF;
 	file = fget(fd);
 	if (!file)
 		goto out;
@@ -411,21 +411,27 @@
 	dentry = file->f_dentry;
 	inode = dentry->d_inode;
 
-	err = -EINVAL;
-	if (!file->f_op || !file->f_op->fsync)
+	ret = -EINVAL;
+	if (!file->f_op || !file->f_op->fsync) {
+		/* Why?  We can still call filemap_fdatasync */
 		goto out_putf;
+	}
 
 	/* We need to protect against concurrent writers.. */
 	down(&inode->i_sem);
-	filemap_fdatasync(inode->i_mapping);
+	ret = filemap_fdatasync(inode->i_mapping);
 	err = file->f_op->fsync(file, dentry, 0);
-	filemap_fdatawait(inode->i_mapping);
+	if (err && !ret)
+		ret = err;
+	err = filemap_fdatawait(inode->i_mapping);
+	if (err && !ret)
+		ret = err;
 	up(&inode->i_sem);
 
 out_putf:
 	fput(file);
 out:
-	return err;
+	return ret;
 }
 
 asmlinkage long sys_fdatasync(unsigned int fd)
@@ -433,9 +439,9 @@
 	struct file * file;
 	struct dentry * dentry;
 	struct inode * inode;
-	int err;
+	int ret, err;
 
-	err = -EBADF;
+	ret = -EBADF;
 	file = fget(fd);
 	if (!file)
 		goto out;
@@ -443,20 +449,24 @@
 	dentry = file->f_dentry;
 	inode = dentry->d_inode;
 
-	err = -EINVAL;
+	ret = -EINVAL;
 	if (!file->f_op || !file->f_op->fsync)
 		goto out_putf;
 
 	down(&inode->i_sem);
-	filemap_fdatasync(inode->i_mapping);
+	ret = filemap_fdatasync(inode->i_mapping);
 	err = file->f_op->fsync(file, dentry, 1);
-	filemap_fdatawait(inode->i_mapping);
+	if (err && !ret)
+		ret = err;
+	err = filemap_fdatawait(inode->i_mapping);
+	if (err && !ret)
+		ret = err;
 	up(&inode->i_sem);
 
 out_putf:
 	fput(file);
 out:
-	return err;
+	return ret;
 }
 
 /* After several hours of tedious analysis, the following hash
@@ -1512,6 +1522,7 @@
 	int err, i;
 	unsigned long block;
 	struct buffer_head *bh, *head;
+	int need_unlock;
 
 	if (!PageLocked(page))
 		BUG();
@@ -1567,8 +1578,34 @@
 	return 0;
 
 out:
+	/*
+	 * ENOSPC, or some other error.  We may already have added some
+	 * blocks to the file, so we need to write these out to avoid
+	 * exposing stale data.
+	 */
 	ClearPageUptodate(page);
-	UnlockPage(page);
+	bh = head;
+	need_unlock = 1;
+	/* Recovery: lock and submit the mapped buffers */
+	do {
+		if (buffer_mapped(bh)) {
+			lock_buffer(bh);
+			set_buffer_async_io(bh);
+			need_unlock = 0;
+		}
+		bh = bh->b_this_page;
+	} while (bh != head);
+	do {
+		struct buffer_head *next = bh->b_this_page;
+		if (buffer_mapped(bh)) {
+			set_bit(BH_Uptodate, &bh->b_state);
+			clear_bit(BH_Dirty, &bh->b_state);
+			submit_bh(WRITE, bh);
+		}
+		bh = next;
+	} while (bh != head);
+	if (need_unlock)
+		UnlockPage(page);
 	return err;
 }
 
@@ -1599,6 +1636,7 @@
 			continue;
 		if (block_start >= to)
 			break;
+		clear_bit(BH_New, &bh->b_state);
 		if (!buffer_mapped(bh)) {
 			err = get_block(inode, block, bh, 1);
 			if (err)
@@ -1633,12 +1671,35 @@
 	 */
 	while(wait_bh > wait) {
 		wait_on_buffer(*--wait_bh);
-		err = -EIO;
 		if (!buffer_uptodate(*wait_bh))
-			goto out;
+			return -EIO;
 	}
 	return 0;
 out:
+	/*
+	 * Zero out any newly allocated blocks to avoid exposing stale
+	 * data.  If BH_New is set, we know that the block was newly
+	 * allocated in the above loop.
+	 */
+	bh = head;
+	block_start = 0;
+	do {
+		block_end = block_start+blocksize;
+		if (block_end <= from)
+			goto next_bh;
+		if (block_start >= to)
+			break;
+		if (buffer_new(bh)) {
+			if (buffer_uptodate(bh))
+				printk(KERN_ERR "%s: zeroing uptodate buffer!\n", __FUNCTION__);
+			memset(kaddr+block_start, 0, bh->b_size);
+			set_bit(BH_Uptodate, &bh->b_state);
+			mark_buffer_dirty(bh);
+		}
+next_bh:
+		block_start = block_end;
+		bh = bh->b_this_page;
+	} while (bh != head);
 	return err;
 }
 
@@ -1760,6 +1821,52 @@
 	return 0;
 }
 
+/* utility function for filesystems that need to do work on expanding
+ * truncates.  Uses prepare/commit_write to allow the filesystem to
+ * deal with the hole.  
+ */
+int generic_cont_expand(struct inode *inode, loff_t size)
+{
+	struct address_space *mapping = inode->i_mapping;
+	struct page *page;
+	unsigned long index, offset, limit;
+	int err;
+
+	err = -EFBIG;
+        limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
+	if (limit != RLIM_INFINITY && size > (loff_t)limit) {
+		send_sig(SIGXFSZ, current, 0);
+		goto out;
+	}
+	if (size > inode->i_sb->s_maxbytes)
+		goto out;
+
+	offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
+
+	/* ugh.  in prepare/commit_write, if from==to==start of block, we 
+	** skip the prepare.  make sure we never send an offset for the start
+	** of a block
+	*/
+	if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
+		offset++;
+	}
+	index = size >> PAGE_CACHE_SHIFT;
+	err = -ENOMEM;
+	page = grab_cache_page(mapping, index);
+	if (!page)
+		goto out;
+	err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
+	if (!err) {
+		err = mapping->a_ops->commit_write(NULL, page, offset, offset);
+	}
+	UnlockPage(page);
+	page_cache_release(page);
+	if (err > 0)
+		err = 0;
+out:
+	return err;
+}
+
 /*
  * For moronic filesystems that do not allow holes in file.
  * We may have to extend the file.
@@ -1989,6 +2096,48 @@
 	goto done;
 }
 
+/*
+ * Commence writeout of all the buffers against a page.  The
+ * page must be locked.   Returns zero on success or a negative
+ * errno.
+ */
+int writeout_one_page(struct page *page)
+{
+	struct buffer_head *bh, *head = page->buffers;
+
+	if (!PageLocked(page))
+		BUG();
+	bh = head;
+	do {
+		if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
+			continue;
+
+		bh->b_flushtime = jiffies;
+		ll_rw_block(WRITE, 1, &bh);	
+	} while ((bh = bh->b_this_page) != head);
+	return 0;
+}
+EXPORT_SYMBOL(writeout_one_page);
+
+/*
+ * Wait for completion of I/O of all buffers against a page.  The page
+ * must be locked.  Returns zero on success or a negative errno.
+ */
+int waitfor_one_page(struct page *page)
+{
+	int error = 0;
+	struct buffer_head *bh, *head = page->buffers;
+
+	bh = head;
+	do {
+		wait_on_buffer(bh);
+		if (buffer_req(bh) && !buffer_uptodate(bh))
+			error = -EIO;
+	} while ((bh = bh->b_this_page) != head);
+	return error;
+}
+EXPORT_SYMBOL(waitfor_one_page);
+
 int generic_block_bmap(struct address_space *mapping, long block, get_block_t *get_block)
 {
 	struct buffer_head tmp;
@@ -2003,8 +2152,10 @@
 {
 	int i, nr_blocks, retval;
 	unsigned long * blocks = iobuf->blocks;
+	int length;
 
-	nr_blocks = iobuf->length / blocksize;
+	length = iobuf->length;
+	nr_blocks = length / blocksize;
 	/* build the blocklist */
 	for (i = 0; i < nr_blocks; i++, blocknr++) {
 		struct buffer_head bh;
@@ -2014,8 +2165,14 @@
 		bh.b_size = blocksize;
 
 		retval = get_block(inode, blocknr, &bh, rw == READ ? 0 : 1);
-		if (retval)
-			goto out;
+		if (retval) {
+			if (!i)
+				/* report error to userspace */
+				goto out;
+			else
+				/* do short I/O utill 'i' */
+				break;
+		}
 
 		if (rw == READ) {
 			if (buffer_new(&bh))
@@ -2034,9 +2191,13 @@
 		blocks[i] = bh.b_blocknr;
 	}
 
+	/* patch length to handle short I/O */
+	iobuf->length = i * blocksize;
 	retval = brw_kiovec(rw, 1, &iobuf, inode->i_dev, iobuf->blocks, blocksize);
-
+	/* restore orig length */
+	iobuf->length = length;
  out:
+
 	return retval;
 }
 
@@ -2562,6 +2723,9 @@
 
 	printk("Buffer memory:   %6dkB\n",
 			atomic_read(&buffermem_pages) << (PAGE_SHIFT-10));
+
+	printk("Cache memory:   %6dkB\n",
+			(atomic_read(&page_cache_size)- atomic_read(&buffermem_pages)) << (PAGE_SHIFT-10));
 
 #ifdef CONFIG_SMP /* trylock does nothing on UP and so we could deadlock */
 	if (!spin_trylock(&lru_list_lock))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/coda/coda_linux.c linux/fs/coda/coda_linux.c
--- linux.orig/fs/coda/coda_linux.c	Wed Apr 25 23:18:54 2001
+++ linux/fs/coda/coda_linux.c	Mon Feb  4 17:44:52 2002
@@ -172,6 +172,8 @@
 	        inode->i_mtime = attr->va_mtime.tv_sec;
         if (attr->va_ctime.tv_sec != -1)
 	        inode->i_ctime = attr->va_ctime.tv_sec;
+	if (!inode->i_ctime)
+		inode->i_ctime = inode->i_mtime;
 }
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/coda/dir.c linux/fs/coda/dir.c
--- linux.orig/fs/coda/dir.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/coda/dir.c	Mon Feb  4 17:44:52 2002
@@ -197,7 +197,7 @@
 	/* optimistically we can also act as if our nose bleeds. The
          * granularity of the mtime is coarse anyways so we might actually be
          * right most of the time. Note: we only do this for directories. */
-	dir->i_mtime = CURRENT_TIME;
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 #endif
 	if (link)
 		dir->i_nlink += link;
@@ -209,7 +209,7 @@
         int error=0;
 	const char *name=de->d_name.name;
 	int length=de->d_name.len;
-	struct inode *result = NULL;
+	struct inode *inode = NULL;
 	struct ViceFid newfid;
 	struct coda_vattr attrs;
 
@@ -230,16 +230,15 @@
 		return error;
 	}
 
-	error = coda_cnode_make(&result, &newfid, dir->i_sb);
-	if ( error ) {
+	inode = coda_iget(dir->i_sb, &newfid, &attrs);
+	if ( IS_ERR(inode) ) {
 		d_drop(de);
-		result = NULL;
-		return error;
+		return PTR_ERR(inode);
 	}
 
 	/* invalidate the directory cnode's attributes */
 	coda_dir_changed(dir, 0);
-	d_instantiate(de, result);
+	d_instantiate(de, inode);
         return 0;
 }
 
@@ -519,6 +518,7 @@
 		result = vfs_readdir(file, filldir, dirent);
         }
 
+	UPDATE_ATIME(inode);
 	return result;
 }
 
@@ -657,7 +657,7 @@
 		goto bad;
 
 	cii = ITOC(de->d_inode);
-	if (cii->c_flags & (C_PURGE | C_FLUSH))
+	if (!(cii->c_flags & (C_PURGE | C_FLUSH)))
 		goto out;
 
 	shrink_dcache_parent(de);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/coda/file.c linux/fs/coda/file.c
--- linux.orig/fs/coda/file.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/coda/file.c	Mon Feb  4 17:44:52 2002
@@ -36,6 +36,7 @@
 	struct inode *inode = file->f_dentry->d_inode;
 	struct coda_inode_info *cii = ITOC(inode);
 	struct file *cfile;
+	ssize_t ret;
 
 	cfile = cii->c_container;
 	if (!cfile) BUG();
@@ -43,7 +44,12 @@
 	if (!cfile->f_op || !cfile->f_op->read)
 		return -EINVAL;
 
-	return cfile->f_op->read(cfile, buf, count, ppos);
+	down(&inode->i_sem);
+	ret = cfile->f_op->read(cfile, buf, count, ppos);
+	UPDATE_ATIME(inode);
+	up(&inode->i_sem);
+
+	return ret;
 }
 
 static ssize_t
@@ -70,6 +76,7 @@
 
 	cfile->f_flags = flags;
 	inode->i_size = cinode->i_size;
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 	up(&inode->i_sem);
 
 	return ret;
@@ -81,6 +88,7 @@
 	struct inode *inode = file->f_dentry->d_inode;
 	struct coda_inode_info *cii = ITOC(inode);
 	struct file *cfile;
+	int ret;
 
 	cfile = cii->c_container;
 
@@ -89,7 +97,12 @@
 	if (!cfile->f_op || !cfile->f_op->mmap)
 		return -ENODEV;
 
-	return cfile->f_op->mmap(cfile, vma);
+	down(&inode->i_sem);
+	ret = cfile->f_op->mmap(cfile, vma);
+	UPDATE_ATIME(inode);
+	up(&inode->i_sem);
+
+	return ret;
 }
 
 int coda_open(struct inode *i, struct file *f)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/coda/inode.c linux/fs/coda/inode.c
--- linux.orig/fs/coda/inode.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/coda/inode.c	Mon Feb  4 17:44:52 2002
@@ -240,9 +240,11 @@
         struct coda_vattr vattr;
         int error;
 	
-        memset(&vattr, 0, sizeof(vattr)); 
+	inode->i_ctime = CURRENT_TIME;
 
+        memset(&vattr, 0, sizeof(vattr)); 
         coda_iattr_to_vattr(iattr, &vattr);
+
         vattr.va_type = C_VNON; /* cannot set type */
 	CDEBUG(D_SUPER, "vattr.va_mode %o\n", vattr.va_mode);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/coda/psdev.c linux/fs/coda/psdev.c
--- linux.orig/fs/coda/psdev.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/coda/psdev.c	Mon Feb  4 17:44:52 2002
@@ -411,7 +411,7 @@
 static int __init init_coda(void)
 {
 	int status;
-	printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.15, coda@cs.cmu.edu\n");
+	printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.18, coda@cs.cmu.edu\n");
 
 	status = init_coda_psdev();
 	if ( status ) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/cramfs/inode.c linux/fs/cramfs/inode.c
--- linux.orig/fs/cramfs/inode.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/cramfs/inode.c	Wed Jan 23 20:15:01 2002
@@ -153,7 +153,7 @@
 
 		bh = NULL;
 		if (blocknr + i < devsize) {
-			bh = getblk(sb->s_dev, blocknr + i, PAGE_CACHE_SIZE);
+			bh = sb_getblk(sb, blocknr + i);
 			if (!buffer_uptodate(bh))
 				read_array[unread++] = bh;
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/dcache.c linux/fs/dcache.c
--- linux.orig/fs/dcache.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/dcache.c	Wed Jan 23 02:53:39 2002
@@ -1262,7 +1262,7 @@
 		panic("Cannot create buffer head SLAB cache");
 
 	names_cachep = kmem_cache_create("names_cache", 
-			PATH_MAX + 1, 0, 
+			PATH_MAX, 0, 
 			SLAB_HWCACHE_ALIGN, NULL, NULL);
 	if (!names_cachep)
 		panic("Cannot create names SLAB cache");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/devfs/base.c linux/fs/devfs/base.c
--- linux.orig/fs/devfs/base.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/devfs/base.c	Mon Jan 21 20:27:04 2002
@@ -1,6 +1,6 @@
 /*  devfs (Device FileSystem) driver.
 
-    Copyright (C) 1998-2001  Richard Gooch
+    Copyright (C) 1998-2002  Richard Gooch
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -592,6 +592,22 @@
 	       Added poisoning to <devfs_put>.
 	       Improved debugging messages.
   v1.7
+    20011221   Richard Gooch <rgooch@atnf.csiro.au>
+	       Corrected (made useful) debugging message in <unregister>.
+	       Moved <kmem_cache_create> in <mount_devfs_fs> to <init_devfs_fs>
+    20011224   Richard Gooch <rgooch@atnf.csiro.au>
+	       Added magic number to guard against scribbling drivers.
+    20011226   Richard Gooch <rgooch@atnf.csiro.au>
+	       Only return old entry in <devfs_mk_dir> if a directory.
+	       Defined macros for error and debug messages.
+  v1.8
+    20020113   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed (rare, old) race in <devfs_lookup>.
+  v1.9
+    20020120   Richard Gooch <rgooch@atnf.csiro.au>
+	       Fixed deadlock bug in <devfs_d_revalidate_wait>.
+	       Tag VFS deletable in <devfs_mk_symlink> if handle ignored.
+  v1.10
 */
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -624,7 +640,7 @@
 #include <asm/bitops.h>
 #include <asm/atomic.h>
 
-#define DEVFS_VERSION            "1.7 (20011216)"
+#define DEVFS_VERSION            "1.10 (20020120)"
 
 #define DEVFS_NAME "devfs"
 
@@ -633,6 +649,7 @@
 #define STRING_LENGTH 256
 #define FAKE_BLOCK_SIZE 1024
 #define POISON_PTR ( *(void **) poison_array )
+#define MAGIC_VALUE 0x327db823
 
 #ifndef TRUE
 #  define TRUE 1
@@ -669,9 +686,28 @@
 #define OPTION_MOUNT            0x01
 #define OPTION_ONLY             0x02
 
-#define OOPS(format, args...) {printk (format, ## args); \
-                               printk ("Forcing Oops\n"); \
-                               BUG();}
+#define PRINTK(format, args...) \
+   {printk (KERN_ERR "%s" format, __FUNCTION__ , ## args);}
+
+#define OOPS(format, args...) \
+   {printk (KERN_CRIT "%s" format, __FUNCTION__ , ## args); \
+    printk ("Forcing Oops\n"); \
+    BUG();}
+
+#ifdef CONFIG_DEVFS_DEBUG
+#  define VERIFY_ENTRY(de) \
+   {if ((de) && (de)->magic_number != MAGIC_VALUE) \
+        OOPS ("(%p): bad magic value: %x\n", (de), (de)->magic_number);}
+#  define WRITE_ENTRY_MAGIC(de,magic) (de)->magic_number = (magic)
+#  define DPRINTK(flag, format, args...) \
+   {if (devfs_debug & flag) \
+	printk (KERN_INFO "%s" format, __FUNCTION__ , ## args);}
+#else
+#  define VERIFY_ENTRY(de)
+#  define WRITE_ENTRY_MAGIC(de,magic)
+#  define DPRINTK(flag, format, args...)
+#endif
+
 
 struct directory_type
 {
@@ -728,6 +764,9 @@
 
 struct devfs_entry
 {
+#ifdef CONFIG_DEVFS_DEBUG
+    unsigned int magic_number;
+#endif
     void *info;
     atomic_t refcount;           /*  When this drops to zero, it's unused    */
     union 
@@ -746,7 +785,7 @@
     umode_t mode;
     unsigned short namelen;      /*  I think 64k+ filenames are a way off... */
     unsigned char hide:1;
-    unsigned char vfs_created:1; /*  Whether created by driver or VFS        */
+    unsigned char vfs_deletable:1;/*  Whether the VFS may delete the entry   */
     char name[1];                /*  This is just a dummy: the allocated array
 				     is bigger. This is NULL-terminated      */
 };
@@ -837,6 +876,7 @@
 
 static struct devfs_entry *devfs_get (struct devfs_entry *de)
 {
+    VERIFY_ENTRY (de);
     if (de) atomic_inc (&de->refcount);
     return de;
 }   /*  End Function devfs_get  */
@@ -849,25 +889,22 @@
 void devfs_put (devfs_handle_t de)
 {
     if (!de) return;
-    if (de->info == POISON_PTR)
-	OOPS ("%s: devfs_put(%p): poisoned pointer\n", DEVFS_NAME, de);
+    VERIFY_ENTRY (de);
+    if (de->info == POISON_PTR) OOPS ("(%p): poisoned pointer\n", de);
     if ( !atomic_dec_and_test (&de->refcount) ) return;
-    if (de == root_entry)
-	OOPS ("%s: devfs_put(): root entry being freed\n", DEVFS_NAME);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_FREE)
-	printk ("%s: devfs_put(%s): de: %p, parent: %p \"%s\"\n",
-		DEVFS_NAME, de->name, de, de->parent,
-		de->parent ? de->parent->name : "no parent");
-#endif
+    if (de == root_entry) OOPS ("(%p): root entry being freed\n", de);
+    DPRINTK (DEBUG_FREE, "(%s): de: %p, parent: %p \"%s\"\n",
+	     de->name, de, de->parent,
+	     de->parent ? de->parent->name : "no parent");
     if ( S_ISLNK (de->mode) ) kfree (de->u.symlink.linkname);
     if ( ( S_ISCHR (de->mode) || S_ISBLK (de->mode) ) && de->u.fcb.autogen )
     {
 	devfs_dealloc_devnum ( S_ISCHR (de->mode) ? DEVFS_SPECIAL_CHR :
 			       DEVFS_SPECIAL_BLK,
-			       MKDEV (de->u.fcb.u.device.major,
-				      de->u.fcb.u.device.minor) );
+			       mk_kdev (de->u.fcb.u.device.major,
+					de->u.fcb.u.device.minor) );
     }
+    WRITE_ENTRY_MAGIC (de, 0);
 #ifdef CONFIG_DEVFS_DEBUG
     spin_lock (&stat_lock);
     --stat_num_entries;
@@ -898,7 +935,7 @@
 
     if ( !S_ISDIR (dir->mode) )
     {
-	printk ("%s: search_dir(%s): not a directory\n", DEVFS_NAME,dir->name);
+	PRINTK ("(%s): not a directory\n", dir->name);
 	return NULL;
     }
     for (curr = dir->u.dir.first; curr != NULL; curr = curr->next)
@@ -940,6 +977,7 @@
     spin_unlock (&counter_lock);
     if (name) memcpy (new->name, name, namelen);
     new->namelen = namelen;
+    WRITE_ENTRY_MAGIC (new, MAGIC_VALUE);
 #ifdef CONFIG_DEVFS_DEBUG
     spin_lock (&stat_lock);
     ++stat_num_entries;
@@ -972,8 +1010,7 @@
     if (old_de) *old_de = NULL;
     if ( !S_ISDIR (dir->mode) )
     {
-	printk ("%s: append_entry(%s): dir: \"%s\" is not a directory\n",
-		DEVFS_NAME, de->name, dir->name);
+	PRINTK ("(%s): dir: \"%s\" is not a directory\n", de->name, dir->name);
 	devfs_put (de);
 	return -ENOTDIR;
     }
@@ -1033,16 +1070,16 @@
     if ( ( new = _devfs_alloc_entry (".devfsd", 0, S_IFCHR |S_IRUSR |S_IWUSR) )
 	 == NULL ) return NULL;
     devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
-    new->u.fcb.u.device.major = MAJOR (devnum);
-    new->u.fcb.u.device.minor = MINOR (devnum);
+    new->u.fcb.u.device.major = major (devnum);
+    new->u.fcb.u.device.minor = minor (devnum);
     new->u.fcb.ops = &devfsd_fops;
     _devfs_append_entry (root_entry, new, FALSE, NULL);
 #ifdef CONFIG_DEVFS_DEBUG
     if ( ( new = _devfs_alloc_entry (".stat", 0, S_IFCHR | S_IRUGO | S_IWUGO) )
 	 == NULL ) return NULL;
     devnum = devfs_alloc_devnum (DEVFS_SPECIAL_CHR);
-    new->u.fcb.u.device.major = MAJOR (devnum);
-    new->u.fcb.u.device.minor = MINOR (devnum);
+    new->u.fcb.u.device.major = major (devnum);
+    new->u.fcb.u.device.minor = minor (devnum);
     new->u.fcb.ops = &stat_fops;
     _devfs_append_entry (root_entry, new, FALSE, NULL);
 #endif
@@ -1143,15 +1180,13 @@
     if ( ( *dir = _devfs_make_parent_for_leaf (*dir, name, namelen,
 					       &leaf_pos) ) == NULL )
     {
-	printk ("%s: prepare_leaf(%s): could not create parent path\n",
-		DEVFS_NAME, name);
+	PRINTK ("(%s): could not create parent path\n", name);
 	return NULL;
     }
     if ( ( de = _devfs_alloc_entry (name + leaf_pos, namelen - leaf_pos,mode) )
 	 == NULL )
     {
-	printk ("%s: prepare_leaf(%s): could not allocate entry\n",
-		DEVFS_NAME, name);
+	PRINTK ("(%s): could not allocate entry\n", name);
 	devfs_put (*dir);
 	return NULL;
     }
@@ -1198,7 +1233,7 @@
 
 
 /**
- *	find_by_dev - Find a devfs entry in a directory.
+ *	_devfs_find_by_dev - Find a devfs entry in a directory.
  *	@dir: The directory where to search
  *	@major: The major number to search for.
  *	@minor: The minor number to search for.
@@ -1209,9 +1244,9 @@
  *	devfs_get() is performed.
  */
 
-static struct devfs_entry *find_by_dev (struct devfs_entry *dir,
-					unsigned int major, unsigned int minor,
-					char type)
+static struct devfs_entry *_devfs_find_by_dev (struct devfs_entry *dir,
+					       unsigned int major,
+					       unsigned int minor, char type)
 {
     struct devfs_entry *entry, *de;
 
@@ -1219,7 +1254,7 @@
     if (dir == NULL) return NULL;
     if ( !S_ISDIR (dir->mode) )
     {
-	printk ("%s: find_by_dev(): not a directory\n", DEVFS_NAME);
+	PRINTK ("(%p): not a directory\n", dir);
 	devfs_put (dir);
 	return NULL;
     }
@@ -1244,7 +1279,7 @@
     for (entry = dir->u.dir.first; entry != NULL; entry = entry->next)
     {
 	if ( !S_ISDIR (entry->mode) ) continue;
-	de = find_by_dev (entry, major, minor, type);
+	de = _devfs_find_by_dev (entry, major, minor, type);
 	if (de)
 	{
 	    read_unlock (&dir->u.dir.lock);
@@ -1255,11 +1290,11 @@
     read_unlock (&dir->u.dir.lock);
     devfs_put (dir);
     return NULL;
-}   /*  End Function find_by_dev  */
+}   /*  End Function _devfs_find_by_dev  */
 
 
 /**
- *	find_entry - Find a devfs entry.
+ *	_devfs_find_entry - Find a devfs entry.
  *	@dir: The handle to the parent devfs directory entry. If this is %NULL the
  *		name is relative to the root of the devfs.
  *	@name: The name of the entry. This may be %NULL.
@@ -1275,9 +1310,11 @@
  *	devfs_get() is performed.
  */
 
-static struct devfs_entry *find_entry (devfs_handle_t dir, const char *name,
-				       unsigned int major, unsigned int minor,
-				       char type, int traverse_symlink)
+static struct devfs_entry *_devfs_find_entry (devfs_handle_t dir,
+					      const char *name,
+					      unsigned int major,
+					      unsigned int minor,
+					      char type, int traverse_symlink)
 {
     struct devfs_entry *entry;
 
@@ -1290,14 +1327,14 @@
 	    /*  Skip leading pathname component  */
 	    if (namelen < 2)
 	    {
-		printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name);
+		PRINTK ("(%s): too short\n", name);
 		return NULL;
 	    }
 	    for (++name, --namelen; (*name != '/') && (namelen > 0);
 		 ++name, --namelen);
 	    if (namelen < 2)
 	    {
-		printk ("%s: find_entry(%s): too short\n", DEVFS_NAME, name);
+		PRINTK ("(%s): too short\n", name);
 		return NULL;
 	    }
 	    ++name;
@@ -1308,12 +1345,13 @@
     }
     /*  Have to search by major and minor: slow  */
     if ( (major == 0) && (minor == 0) ) return NULL;
-    return find_by_dev (root_entry, major, minor, type);
-}   /*  End Function find_entry  */
+    return _devfs_find_by_dev (root_entry, major, minor, type);
+}   /*  End Function _devfs_find_entry  */
 
 static struct devfs_entry *get_devfs_entry_from_vfs_inode (struct inode *inode)
 {
     if (inode == NULL) return NULL;
+    VERIFY_ENTRY ( (struct devfs_entry *) inode->u.generic_ip );
     return inode->u.generic_ip;
 }   /*  End Function get_devfs_entry_from_vfs_inode  */
 
@@ -1495,7 +1533,7 @@
 
     if (name == NULL)
     {
-	printk ("%s: devfs_register(): NULL name pointer\n", DEVFS_NAME);
+	PRINTK ("(): NULL name pointer\n");
 	return NULL;
     }
     if (ops == NULL)
@@ -1503,54 +1541,48 @@
 	if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major);
 	if (ops == NULL)
 	{
-	    printk ("%s: devfs_register(%s): NULL ops pointer\n",
-		    DEVFS_NAME, name);
+	    PRINTK ("(%s): NULL ops pointer\n", name);
 	    return NULL;
 	}
-	printk ("%s: devfs_register(%s): NULL ops, got %p from major table\n",
-		DEVFS_NAME, name, ops);
+	PRINTK ("(%s): NULL ops, got %p from major table\n", name, ops);
     }
     if ( S_ISDIR (mode) )
     {
-	printk("%s: devfs_register(%s): creating directories is not allowed\n",
-	       DEVFS_NAME, name);
+	PRINTK ("(%s): creating directories is not allowed\n", name);
 	return NULL;
     }
     if ( S_ISLNK (mode) )
     {
-	printk ("%s: devfs_register(%s): creating symlinks is not allowed\n",
-		DEVFS_NAME, name);
+	PRINTK ("(%s): creating symlinks is not allowed\n", name);
 	return NULL;
     }
     if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) &&
 	 (flags & DEVFS_FL_AUTO_DEVNUM) )
     {
-	if ( ( devnum = devfs_alloc_devnum (devtype) ) == NODEV )
+	if ( kdev_none ( devnum = devfs_alloc_devnum (devtype) ) )
 	{
-	    printk ("%s: devfs_register(%s): exhausted %s device numbers\n",
-		    DEVFS_NAME, name, S_ISCHR (mode) ? "char" : "block");
+	    PRINTK ("(%s): exhausted %s device numbers\n",
+		    name, S_ISCHR (mode) ? "char" : "block");
 	    return NULL;
 	}
-	major = MAJOR (devnum);
-	minor = MINOR (devnum);
+	major = major (devnum);
+	minor = minor (devnum);
     }
     if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
     {
-	printk ("%s: devfs_register(%s): could not prepare leaf\n",
-		DEVFS_NAME, name);
-	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
+	PRINTK ("(%s): could not prepare leaf\n", name);
+	if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
 	return NULL;
     }
     if ( S_ISCHR (mode) || S_ISBLK (mode) )
     {
 	de->u.fcb.u.device.major = major;
 	de->u.fcb.u.device.minor = minor;
-	de->u.fcb.autogen = (devnum == NODEV) ? FALSE : TRUE;
+	de->u.fcb.autogen = kdev_none (devnum) ? FALSE : TRUE;
     }
     else if ( !S_ISREG (mode) )
     {
-	printk ("%s: devfs_register(%s): illegal mode: %x\n",
-		DEVFS_NAME, name, mode);
+	PRINTK ("(%s): illegal mode: %x\n", name, mode);
 	devfs_put (de);
 	devfs_put (dir);
 	return (NULL);
@@ -1574,17 +1606,13 @@
     if ( ( err = _devfs_append_entry (dir, de, de->u.fcb.removable, NULL) )
 	 != 0 )
     {
-	printk("%s: devfs_register(%s): could not append to parent, err: %d\n",
-	       DEVFS_NAME, name, err);
+	PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
 	devfs_put (dir);
-	if (devnum != NODEV) devfs_dealloc_devnum (devtype, devnum);
+	if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
 	return NULL;
     }
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_register(%s): de: %p dir: %p \"%s\"  pp: %p\n",
-		DEVFS_NAME, name, de, dir, dir->name, dir->parent);
-#endif
+    DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\"  pp: %p\n",
+	     name, de, dir, dir->name, dir->parent);
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
     devfs_put (dir);
     return de;
@@ -1620,7 +1648,7 @@
 
 
 /**
- *	unregister - Unregister a device entry from it's parent.
+ *	_devfs_unregister - Unregister a device entry from it's parent.
  *	@dir: The parent directory.
  *	@de: The entry to unregister.
  *
@@ -1628,7 +1656,7 @@
  *	unlocked by this function.
  */
 
-static void unregister (struct devfs_entry *dir, struct devfs_entry *de)
+static void _devfs_unregister (struct devfs_entry *dir, struct devfs_entry *de)
 {
     int unhooked = _devfs_unhook (de);
 
@@ -1647,17 +1675,14 @@
 	write_lock (&de->u.dir.lock);
 	de->u.dir.no_more_additions = TRUE;
 	child = de->u.dir.first;
-	unregister (de, child);
+	VERIFY_ENTRY (child);
+	_devfs_unregister (de, child);
 	if (!child) break;
-#ifdef CONFIG_DEVFS_DEBUG
-	if (devfs_debug & DEBUG_UNREGISTER)
-	    printk ( "%s: unregister(%s): child: %p  refcount: %d\n",
-		     DEVFS_NAME, child->name, child,
-		     atomic_read (&de->refcount) );
-#endif
+	DPRINTK (DEBUG_UNREGISTER, "(%s): child: %p  refcount: %d\n",
+		 child->name, child, atomic_read (&child->refcount) );
 	devfs_put (child);
     }
-}   /*  End Function unregister  */
+}   /*  End Function _devfs_unregister  */
 
 
 /**
@@ -1668,14 +1693,12 @@
 
 void devfs_unregister (devfs_handle_t de)
 {
+    VERIFY_ENTRY (de);
     if ( (de == NULL) || (de->parent == NULL) ) return;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_UNREGISTER)
-	printk ( "%s: devfs_unregister(%s): de: %p  refcount: %d\n",
-		 DEVFS_NAME, de->name, de, atomic_read (&de->refcount) );
-#endif
+    DPRINTK (DEBUG_UNREGISTER, "(%s): de: %p  refcount: %d\n",
+	     de->name, de, atomic_read (&de->refcount) );
     write_lock (&de->parent->u.dir.lock);
-    unregister (de->parent, de);
+    _devfs_unregister (de->parent, de);
     devfs_put (de);
 }   /*  End Function devfs_unregister  */
 
@@ -1691,16 +1714,12 @@
     if (handle != NULL) *handle = NULL;
     if (name == NULL)
     {
-	printk ("%s: devfs_do_symlink(): NULL name pointer\n", DEVFS_NAME);
+	PRINTK ("(): NULL name pointer\n");
 	return -EINVAL;
     }
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_do_symlink(%s)\n", DEVFS_NAME, name);
-#endif
     if (link == NULL)
     {
-	printk ("%s: devfs_do_symlink(): NULL link pointer\n", DEVFS_NAME);
+	PRINTK ("(%s): NULL link pointer\n", name);
 	return -EINVAL;
     }
     linklength = strlen (link);
@@ -1711,8 +1730,7 @@
     if ( ( de = _devfs_prepare_leaf (&dir, name, S_IFLNK | S_IRUGO | S_IXUGO) )
 	 == NULL )
     {
-	printk ("%s: devfs_do_symlink(%s): could not prepare leaf\n",
-		DEVFS_NAME, name);
+	PRINTK ("(%s): could not prepare leaf\n", name);
 	kfree (newlink);
 	return -ENOTDIR;
     }
@@ -1722,8 +1740,7 @@
     de->u.symlink.length = linklength;
     if ( ( err = _devfs_append_entry (dir, de, FALSE, NULL) ) != 0 )
     {
-	printk ("%s: devfs_do_symlink(%s): could not append to parent, err: %d\n",
-		DEVFS_NAME, name, err);
+	PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
 	devfs_put (dir);
 	return err;
     }
@@ -1758,9 +1775,11 @@
     devfs_handle_t de;
 
     if (handle != NULL) *handle = NULL;
+    DPRINTK (DEBUG_REGISTER, "(%s)\n", name);
     err = devfs_do_symlink (dir, name, flags, link, &de, info);
     if (err) return err;
-    if (handle != NULL) *handle = de;
+    if (handle == NULL) de->vfs_deletable = TRUE;
+    else *handle = de;
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
     return 0;
 }   /*  End Function devfs_mk_symlink  */
@@ -1787,39 +1806,35 @@
 
     if (name == NULL)
     {
-	printk ("%s: devfs_mk_dir(): NULL name pointer\n", DEVFS_NAME);
+	PRINTK ("(): NULL name pointer\n");
 	return NULL;
     }
     if ( ( de = _devfs_prepare_leaf (&dir, name, MODE_DIR) ) == NULL )
     {
-	printk ("%s: devfs_mk_dir(%s): could not prepare leaf\n",
-		DEVFS_NAME, name);
+	PRINTK ("(%s): could not prepare leaf\n", name);
 	return NULL;
     }
     de->info = info;
     if ( ( err = _devfs_append_entry (dir, de, FALSE, &old) ) != 0 )
     {
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,1)
-	if (old)
+	if ( old && S_ISDIR (old->mode) )
 	{
-	    printk("%s: devfs_mk_dir(%s): using old entry in dir: %p \"%s\"\n",
-		   DEVFS_NAME, name, dir, dir->name);
-	    old->vfs_created = FALSE;
+	    PRINTK ("(%s): using old entry in dir: %p \"%s\"\n",
+		    name, dir, dir->name);
+	    old->vfs_deletable = FALSE;
 	    devfs_put (dir);
 	    return old;
 	}
 #endif
-	printk ("%s: devfs_mk_dir(%s): could not append to dir: %p \"%s\", err: %d\n",
-		DEVFS_NAME, name, dir, dir->name, err);
+	PRINTK ("(%s): could not append to dir: %p \"%s\", err: %d\n",
+		name, dir, dir->name, err);
 	devfs_put (old);
 	devfs_put (dir);
 	return NULL;
     }
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_REGISTER)
-	printk ("%s: devfs_mk_dir(%s): de: %p dir: %p \"%s\"\n",
-		DEVFS_NAME, name, de, dir, dir->name);
-#endif
+    DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\"\n",
+	     name, de, dir, dir->name);
     devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, 0);
     devfs_put (dir);
     return de;
@@ -1850,7 +1865,7 @@
 				 char type, int traverse_symlinks)
 {
     if ( (name != NULL) && (name[0] == '\0') ) name = NULL;
-    return find_entry (dir, name, major, minor, type, traverse_symlinks);
+    return _devfs_find_entry (dir, name, major, minor, type,traverse_symlinks);
 }   /*  End Function devfs_get_handle  */
 
 
@@ -1881,6 +1896,7 @@
     unsigned int fl = 0;
 
     if (de == NULL) return -EINVAL;
+    VERIFY_ENTRY (de);
     if (de->hide) fl |= DEVFS_FL_HIDE;
     if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
     {
@@ -1904,11 +1920,8 @@
 int devfs_set_flags (devfs_handle_t de, unsigned int flags)
 {
     if (de == NULL) return -EINVAL;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_SET_FLAGS)
-	printk ("%s: devfs_set_flags(): de->name: \"%s\"\n",
-		DEVFS_NAME, de->name);
-#endif
+    VERIFY_ENTRY (de);
+    DPRINTK (DEBUG_SET_FLAGS, "(%s): flags: %x\n", de->name, flags);
     de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
     if ( S_ISCHR (de->mode) || S_ISBLK (de->mode) || S_ISREG (de->mode) )
     {
@@ -1932,6 +1945,7 @@
 		       unsigned int *minor)
 {
     if (de == NULL) return -EINVAL;
+    VERIFY_ENTRY (de);
     if ( S_ISDIR (de->mode) ) return -EISDIR;
     if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) ) return -EINVAL;
     if (major != NULL) *major = de->u.fcb.u.device.major;
@@ -1972,6 +1986,7 @@
 #define NAMEOF(de) ( (de)->mode ? (de)->name : (de)->u.name )
 
     if (de == NULL) return -EINVAL;
+    VERIFY_ENTRY (de);
     if (de->namelen >= buflen) return -ENAMETOOLONG; /*  Must be first       */
     path[buflen - 1] = '\0';
     if (de->parent == NULL) return buflen - 1;       /*  Don't prepend root  */
@@ -2001,6 +2016,7 @@
     struct module *owner;
 
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
 	return NULL;
     if (de->u.fcb.ops == NULL) return NULL;
@@ -2031,6 +2047,7 @@
     struct module *owner;
 
     if (de == NULL) return;
+    VERIFY_ENTRY (de);
     if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
 	return;
     if (de->u.fcb.ops == NULL) return;
@@ -2052,6 +2069,7 @@
 int devfs_set_file_size (devfs_handle_t de, unsigned long size)
 {
     if (de == NULL) return -EINVAL;
+    VERIFY_ENTRY (de);
     if ( !S_ISREG (de->mode) ) return -EINVAL;
     if (de->u.fcb.u.file.size == size) return 0;
     de->u.fcb.u.file.size = size;
@@ -2071,6 +2089,7 @@
 void *devfs_get_info (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     return de->info;
 }   /*  End Function devfs_get_info  */
 
@@ -2085,6 +2104,7 @@
 int devfs_set_info (devfs_handle_t de, void *info)
 {
     if (de == NULL) return -EINVAL;
+    VERIFY_ENTRY (de);
     de->info = info;
     return 0;
 }   /*  End Function devfs_set_info  */
@@ -2099,6 +2119,7 @@
 devfs_handle_t devfs_get_parent (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     return de->parent;
 }   /*  End Function devfs_get_parent  */
 
@@ -2113,6 +2134,7 @@
 devfs_handle_t devfs_get_first_child (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     if ( !S_ISDIR (de->mode) ) return NULL;
     return de->u.dir.first;
 }   /*  End Function devfs_get_first_child  */
@@ -2128,6 +2150,7 @@
 devfs_handle_t devfs_get_next_sibling (devfs_handle_t de)
 {
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     return de->next;
 }   /*  End Function devfs_get_next_sibling  */
 
@@ -2143,14 +2166,15 @@
 void devfs_auto_unregister (devfs_handle_t master, devfs_handle_t slave)
 {
     if (master == NULL) return;
+    VERIFY_ENTRY (master);
+    VERIFY_ENTRY (slave);
     if (master->slave != NULL)
     {
 	/*  Because of the dumbness of the layers above, ignore duplicates  */
 	if (master->slave == slave) return;
-	printk ("%s: devfs_auto_unregister(): only one slave allowed\n",
-		DEVFS_NAME);
-	OOPS ("  master: \"%s\"  old slave: \"%s\"  new slave: \"%s\"\n",
-	      master->name, master->slave->name, slave->name);
+	PRINTK ("(%s): only one slave allowed\n", master->name);
+	OOPS ("():  old slave: \"%s\"  new slave: \"%s\"\n",
+	      master->slave->name, slave->name);
     }
     master->slave = slave;
 }   /*  End Function devfs_auto_unregister  */
@@ -2166,6 +2190,7 @@
 devfs_handle_t devfs_get_unregister_slave (devfs_handle_t master)
 {
     if (master == NULL) return NULL;
+    VERIFY_ENTRY (master);
     return master->slave;
 }   /*  End Function devfs_get_unregister_slave  */
 
@@ -2181,6 +2206,7 @@
 const char *devfs_get_name (devfs_handle_t de, unsigned int *namelen)
 {
     if (de == NULL) return NULL;
+    VERIFY_ENTRY (de);
     if (namelen != NULL) *namelen = de->namelen;
     return de->name;
 }   /*  End Function devfs_get_name  */
@@ -2360,7 +2386,7 @@
  *	@buf: A working area that will be used. This must not go out of scope
  *            until devfsd is idle again.
  *
- *	Returns 0 on success, else a negative error code.
+ *	Returns 0 on success (event was queued), else a negative error code.
  */
 
 static int try_modload (struct devfs_entry *parent, struct fs_info *fs_info,
@@ -2375,10 +2401,11 @@
     buf->parent = parent;
     buf->namelen = namelen;
     buf->u.name = name;
+    WRITE_ENTRY_MAGIC (buf, MAGIC_VALUE);
     if ( !devfsd_notify_de (buf, DEVFSD_NOTIFY_LOOKUP, 0,
 			    current->euid, current->egid, fs_info, 0) )
 	return -ENOENT;
-    /*  Possible success  */
+    /*  Possible success: event has been queued  */
     return 0;
 }   /*  End Function try_modload  */
 
@@ -2394,7 +2421,7 @@
 {
     int tmp;
     int retval = 0;
-    kdev_t dev = MKDEV (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
+    kdev_t dev = mk_kdev (de->u.fcb.u.device.major, de->u.fcb.u.device.minor);
     struct block_device_operations *bdops;
     extern int warn_no_part;
 
@@ -2487,16 +2514,10 @@
     if (retval != 0) return retval;
     retval = inode_setattr (inode, iattr);
     if (retval != 0) return retval;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_CHANGE)
-    {
-	printk ("%s: notify_change(%d): VFS inode: %p  devfs_entry: %p\n",
-		DEVFS_NAME, (int) inode->i_ino, inode, de);
-	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
-		DEVFS_NAME, (int) inode->i_mode,
-		(int) inode->i_uid, (int) inode->i_gid);
-    }
-#endif
+    DPRINTK (DEBUG_I_CHANGE, "(%d): VFS inode: %p  devfs_entry: %p\n",
+	     (int) inode->i_ino, inode, de);
+    DPRINTK (DEBUG_I_CHANGE, "():   mode: 0%o  uid: %d  gid: %d\n",
+	     (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
     /*  Inode is not on hash chains, thus must save permissions here rather
 	than in a write_inode() method  */
     if ( ( !S_ISREG (inode->i_mode) && !S_ISCHR (inode->i_mode) &&
@@ -2541,7 +2562,7 @@
 
 
 /**
- *	get_vfs_inode - Get a VFS inode.
+ *	_devfs_get_vfs_inode - Get a VFS inode.
  *	@sb: The super block.
  *	@de: The devfs inode.
  *	@dentry: The dentry to register with the devfs inode.
@@ -2550,9 +2571,9 @@
  *       performed if the inode is created.
  */
 
-static struct inode *get_vfs_inode (struct super_block *sb,
-				    struct devfs_entry *de,
-				    struct dentry *dentry)
+static struct inode *_devfs_get_vfs_inode (struct super_block *sb,
+					   struct devfs_entry *de,
+					   struct dentry *dentry)
 {
     int is_fcb = FALSE;
     struct inode *inode;
@@ -2560,8 +2581,7 @@
     if (de->prev == de) return NULL;  /*  Quick check to see if unhooked  */
     if ( ( inode = new_inode (sb) ) == NULL )
     {
-	printk ("%s: get_vfs_inode(%s): new_inode() failed, de: %p\n",
-		DEVFS_NAME, de->name, de);
+	PRINTK ("(%s): new_inode() failed, de: %p\n", de->name, de);
 	return NULL;
     }
     if (de->parent)
@@ -2578,11 +2598,8 @@
     }
     inode->u.generic_ip = devfs_get (de);
     inode->i_ino = de->inode.ino;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_GET)
-	printk ("%s: get_vfs_inode(%d): VFS inode: %p  devfs_entry: %p\n",
-		DEVFS_NAME, (int) inode->i_ino, inode, de);
-#endif
+    DPRINTK (DEBUG_I_GET, "(%d): VFS inode: %p  devfs_entry: %p\n",
+	     (int) inode->i_ino, inode, de);
     inode->i_blocks = 0;
     inode->i_blksize = FAKE_BLOCK_SIZE;
     inode->i_op = &devfs_iops;
@@ -2590,22 +2607,21 @@
     inode->i_rdev = NODEV;
     if ( S_ISCHR (de->mode) )
     {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
-			       de->u.fcb.u.device.minor);
+	inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
+				 de->u.fcb.u.device.minor);
 	inode->i_cdev = cdget ( kdev_t_to_nr (inode->i_rdev) );
 	is_fcb = TRUE;
     }
     else if ( S_ISBLK (de->mode) )
     {
-	inode->i_rdev = MKDEV (de->u.fcb.u.device.major,
-			       de->u.fcb.u.device.minor);
+	inode->i_rdev = mk_kdev (de->u.fcb.u.device.major,
+				 de->u.fcb.u.device.minor);
 	if (bd_acquire (inode) == 0)
 	{
 	    if (!inode->i_bdev->bd_op && de->u.fcb.ops)
 		inode->i_bdev->bd_op = de->u.fcb.ops;
 	}
-	else printk ("%s: get_vfs_inode(%d): no block device from bdget()\n",
-		     DEVFS_NAME, (int) inode->i_ino);
+	else PRINTK ("(%d): no block device from bdget()\n",(int)inode->i_ino);
 	is_fcb = TRUE;
     }
     else if ( S_ISFIFO (de->mode) ) inode->i_fop = &def_fifo_fops;
@@ -2632,14 +2648,10 @@
     inode->i_atime = de->inode.atime;
     inode->i_mtime = de->inode.mtime;
     inode->i_ctime = de->inode.ctime;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_GET)
-	printk ("%s:   mode: 0%o  uid: %d  gid: %d\n",
-		DEVFS_NAME, (int) inode->i_mode,
-		(int) inode->i_uid, (int) inode->i_gid);
-#endif
+    DPRINTK (DEBUG_I_GET, "():   mode: 0%o  uid: %d  gid: %d\n",
+	     (int) inode->i_mode, (int) inode->i_uid, (int) inode->i_gid);
     return inode;
-}   /*  End Function get_vfs_inode  */
+}   /*  End Function _devfs_get_vfs_inode  */
 
 
 /*  File operations for device entries follow  */
@@ -2655,11 +2667,8 @@
     fs_info = inode->i_sb->u.generic_sbp;
     parent = get_devfs_entry_from_vfs_inode (file->f_dentry->d_inode);
     if ( (long) file->f_pos < 0 ) return -EINVAL;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_F_READDIR)
-	printk ("%s: readdir(): fs_info: %p  pos: %ld\n", DEVFS_NAME,
-		fs_info, (long) file->f_pos);
-#endif
+    DPRINTK (DEBUG_F_READDIR, "(%s): fs_info: %p  pos: %ld\n",
+	     parent->name, fs_info, (long) file->f_pos);
     switch ( (long) file->f_pos )
     {
       case 0:
@@ -2779,13 +2788,7 @@
 
 static void devfs_d_release (struct dentry *dentry)
 {
-#ifdef CONFIG_DEVFS_DEBUG
-    struct inode *inode = dentry->d_inode;
-
-    if (devfs_debug & DEBUG_D_RELEASE)
-	printk ("%s: d_release(): dentry: %p inode: %p\n",
-		DEVFS_NAME, dentry, inode);
-#endif
+    DPRINTK (DEBUG_D_RELEASE, "(%p): inode: %p\n", dentry, dentry->d_inode);
 }   /*  End Function devfs_d_release  */
 
 /**
@@ -2799,14 +2802,11 @@
     struct devfs_entry *de;
 
     de = get_devfs_entry_from_vfs_inode (inode);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_D_IPUT)
-	printk ("%s: d_iput(): dentry: %p inode: %p de: %p  de->dentry: %p\n",
-		DEVFS_NAME, dentry, inode, de, de->inode.dentry);
-#endif
+    DPRINTK (DEBUG_D_IPUT,"(%s): dentry: %p inode: %p de: %p de->dentry: %p\n",
+	     de->name, dentry, inode, de, de->inode.dentry);
     if ( de->inode.dentry && (de->inode.dentry != dentry) )
-	OOPS ("%s: d_iput(%s): de: %p dentry: %p de->dentry: %p\n",
-	      DEVFS_NAME, de->name, de, dentry, de->inode.dentry);
+	OOPS ("(%s): de: %p dentry: %p de->dentry: %p\n",
+	      de->name, de, dentry, de->inode.dentry);
     de->inode.dentry = NULL;
     iput (inode);
     devfs_put (de);
@@ -2846,20 +2846,13 @@
     /*  Unhash dentry if negative (has no inode)  */
     if (inode == NULL)
     {
-#ifdef CONFIG_DEVFS_DEBUG
-	if (devfs_debug & DEBUG_D_DELETE)
-	    printk ("%s: d_delete(): dropping negative dentry: %p\n",
-		    DEVFS_NAME, dentry);
-#endif
+	DPRINTK (DEBUG_D_DELETE, "(%p): dropping negative dentry\n", dentry);
 	return 1;
     }
     fs_info = inode->i_sb->u.generic_sbp;
     de = get_devfs_entry_from_vfs_inode (inode);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_D_DELETE)
-	printk ("%s: d_delete(): dentry: %p  inode: %p  devfs_entry: %p\n",
-		DEVFS_NAME, dentry, inode, de);
-#endif
+    DPRINTK (DEBUG_D_DELETE, "(%p): inode: %p  devfs_entry: %p\n",
+	     dentry, inode, de);
     if (de == NULL) return 0;
     if ( !S_ISCHR (de->mode) && !S_ISBLK (de->mode) && !S_ISREG (de->mode) )
 	return 0;
@@ -2876,40 +2869,58 @@
     return 0;
 }   /*  End Function devfs_d_delete  */
 
+struct devfs_lookup_struct
+{
+    devfs_handle_t de;
+    wait_queue_head_t wait_queue;
+};
+
 static int devfs_d_revalidate_wait (struct dentry *dentry, int flags)
 {
     struct inode *dir = dentry->d_parent->d_inode;
     struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
+    devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
+    struct devfs_lookup_struct *lookup_info = dentry->d_fsdata;
+    DECLARE_WAITQUEUE (wait, current);
 
-    if ( !dentry->d_inode && is_devfsd_or_child (fs_info) )
+    if ( is_devfsd_or_child (fs_info) )
     {
-	devfs_handle_t de;
-	devfs_handle_t parent = get_devfs_entry_from_vfs_inode (dir);
+	devfs_handle_t de = lookup_info->de;
 	struct inode *inode;
 
-#ifdef CONFIG_DEVFS_DEBUG
-	if (devfs_debug & DEBUG_I_LOOKUP)
-	    printk ("%s: d_revalidate(%s): dentry: %p by: \"%s\"\n",
-		    DEVFS_NAME, dentry->d_name.name, dentry, current->comm);
-#endif
-	read_lock (&parent->u.dir.lock);
-	de = _devfs_search_dir (parent, dentry->d_name.name,
-				dentry->d_name.len);
-	read_unlock (&parent->u.dir.lock);
-	if (de == NULL) return 1;
+	DPRINTK (DEBUG_I_LOOKUP,
+		 "(%s): dentry: %p inode: %p de: %p by: \"%s\"\n",
+		 dentry->d_name.name, dentry, dentry->d_inode, de,
+		 current->comm);
+	if (dentry->d_inode) return 1;
+	if (de == NULL)
+	{
+	    read_lock (&parent->u.dir.lock);
+	    de = _devfs_search_dir (parent, dentry->d_name.name,
+				    dentry->d_name.len);
+	    read_unlock (&parent->u.dir.lock);
+	    if (de == NULL) return 1;
+	    lookup_info->de = de;
+	}
 	/*  Create an inode, now that the driver information is available  */
-	inode = get_vfs_inode (dir->i_sb, de, dentry);
-	devfs_put (de);
+	inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
 	if (!inode) return 1;
-#ifdef CONFIG_DEVFS_DEBUG
-	if (devfs_debug & DEBUG_I_LOOKUP)
-	    printk ("%s: d_revalidate(): new VFS inode(%u): %p  devfs_entry: %p\n",
-		    DEVFS_NAME, de->inode.ino, inode, de);
-#endif
+	DPRINTK (DEBUG_I_LOOKUP,
+		 "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
+		 de->name, de->inode.ino, inode, de, current->comm);
 	d_instantiate (dentry, inode);
 	return 1;
     }
-    if ( wait_for_devfsd_finished (fs_info) ) dentry->d_op = &devfs_dops;
+    if (lookup_info == NULL) return 1;  /*  Early termination  */
+    read_lock (&parent->u.dir.lock);
+    if (dentry->d_fsdata)
+    {
+	add_wait_queue (&lookup_info->wait_queue, &wait);
+	current->state = TASK_UNINTERRUPTIBLE;
+	read_unlock (&parent->u.dir.lock);
+	schedule ();
+    }
+    else read_unlock (&parent->u.dir.lock);
     return 1;
 }   /*  End Function devfs_d_revalidate_wait  */
 
@@ -2918,20 +2929,20 @@
 
 static struct dentry *devfs_lookup (struct inode *dir, struct dentry *dentry)
 {
+    struct devfs_entry tmp;  /*  Must stay in scope until devfsd idle again  */
+    struct devfs_lookup_struct lookup_info;
     struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
     struct devfs_entry *parent, *de;
     struct inode *inode;
+    struct dentry *retval = NULL;
 
     /*  Set up the dentry operations before anything else, to ensure cleaning
 	up on any error  */
     dentry->d_op = &devfs_dops;
     /*  First try to get the devfs entry for this directory  */
     parent = get_devfs_entry_from_vfs_inode (dir);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_LOOKUP)
-	printk ("%s: lookup(%s): dentry: %p parent: %p by: \"%s\"\n",
-		DEVFS_NAME, dentry->d_name.name, dentry, parent,current->comm);
-#endif
+    DPRINTK (DEBUG_I_LOOKUP, "(%s): dentry: %p parent: %p by: \"%s\"\n",
+	     dentry->d_name.name, dentry, parent, current->comm);
     if (parent == NULL) return ERR_PTR (-ENOENT);
     read_lock (&parent->u.dir.lock);
     de = _devfs_search_dir (parent, dentry->d_name.name, dentry->d_name.len);
@@ -2945,63 +2956,61 @@
 				dentry->d_name.len);
 	read_unlock (&parent->u.dir.lock);
     }
+    lookup_info.de = de;
+    init_waitqueue_head (&lookup_info.wait_queue);
+    dentry->d_fsdata = &lookup_info;
     if (de == NULL)
     {   /*  Try with devfsd. For any kind of failure, leave a negative dentry
 	    so someone else can deal with it (in the case where the sysadmin
 	    does a mknod()). It's important to do this before hashing the
 	    dentry, so that the devfsd queue is filled before revalidates
 	    can start  */
-	struct devfs_entry tmp;
-
 	if (try_modload (parent, fs_info,
 			 dentry->d_name.name, dentry->d_name.len, &tmp) < 0)
-	{
+	{   /*  Lookup event was not queued to devfsd  */
 	    d_add (dentry, NULL);
 	    return NULL;
 	}
-	/*  devfsd claimed success  */
-	dentry->d_op = &devfs_wait_dops;
-	d_add (dentry, NULL);  /*  Open the floodgates  */
-	/*  Unlock directory semaphore, which will release any waiters. They
-	    will get the hashed dentry, and may be forced to wait for
-	    revalidation  */
-	up (&dir->i_sem);
-	devfs_d_revalidate_wait (dentry, 0);  /*  I might have to wait too   */
-	down (&dir->i_sem);      /*  Grab it again because them's the rules  */
-	/*  If someone else has been so kind as to make the inode, we go home
-	    early  */
-	if (dentry->d_inode) return NULL;
+    }
+    dentry->d_op = &devfs_wait_dops;
+    d_add (dentry, NULL);  /*  Open the floodgates  */
+    /*  Unlock directory semaphore, which will release any waiters. They
+	will get the hashed dentry, and may be forced to wait for
+	revalidation  */
+    up (&dir->i_sem);
+    wait_for_devfsd_finished (fs_info);  /*  If I'm not devfsd, must wait  */
+    down (&dir->i_sem);      /*  Grab it again because them's the rules  */
+    de = lookup_info.de;
+    /*  If someone else has been so kind as to make the inode, we go home
+	early  */
+    if (dentry->d_inode) goto out;
+    if (de == NULL)
+    {
 	read_lock (&parent->u.dir.lock);
 	de = _devfs_search_dir (parent, dentry->d_name.name,
 				dentry->d_name.len);
 	read_unlock (&parent->u.dir.lock);
-	if (de == NULL) return NULL;
+	if (de == NULL) goto out;
 	/*  OK, there's an entry now, but no VFS inode yet  */
     }
-    else
+    /*  Create an inode, now that the driver information is available  */
+    inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry);
+    if (!inode)
     {
-	dentry->d_op = &devfs_wait_dops;
-	d_add (dentry, NULL);  /*  Open the floodgates  */
+	retval = ERR_PTR (-ENOMEM);
+	goto out;
     }
-    /*  Create an inode, now that the driver information is available  */
-    inode = get_vfs_inode (dir->i_sb, de, dentry);
-    devfs_put (de);
-    if (!inode) return ERR_PTR (-ENOMEM);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_LOOKUP)
-	printk ("%s: lookup(): new VFS inode(%u): %p  devfs_entry: %p\n",
-		DEVFS_NAME, de->inode.ino, inode, de);
-#endif
+    DPRINTK (DEBUG_I_LOOKUP, "(%s): new VFS inode(%u): %p de: %p by: \"%s\"\n",
+	     de->name, de->inode.ino, inode, de, current->comm);
     d_instantiate (dentry, inode);
-    if (dentry->d_op == &devfs_wait_dops)
-    {   /*  Unlock directory semaphore, which will release any waiters. They
-	    will get the hashed dentry, and may be forced to wait for
-	    revalidation  */
-	up (&dir->i_sem);
-	devfs_d_revalidate_wait (dentry, 0);  /*  I might have to wait too   */
-	down (&dir->i_sem);      /*  Grab it again because them's the rules  */
-    }
-    return NULL;
+out:
+    dentry->d_op = &devfs_dops;
+    dentry->d_fsdata = NULL;
+    write_lock (&parent->u.dir.lock);
+    wake_up (&lookup_info.wait_queue);
+    write_unlock (&parent->u.dir.lock);
+    devfs_put (de);
+    return retval;
 }   /*  End Function devfs_lookup  */
 
 static int devfs_unlink (struct inode *dir, struct dentry *dentry)
@@ -3011,13 +3020,10 @@
     struct inode *inode = dentry->d_inode;
     struct fs_info *fs_info = dir->i_sb->u.generic_sbp;
 
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_UNLINK)
-	printk ("%s: unlink(%s)\n", DEVFS_NAME, dentry->d_name.name);
-#endif
     de = get_devfs_entry_from_vfs_inode (inode);
+    DPRINTK (DEBUG_I_UNLINK, "(%s): de: %p\n", dentry->d_name.name, de);
     if (de == NULL) return -ENOENT;
-    if (!de->vfs_created) return -EPERM;
+    if (!de->vfs_deletable) return -EPERM;
     write_lock (&de->parent->u.dir.lock);
     unhooked = _devfs_unhook (de);
     write_unlock (&de->parent->u.dir.lock);
@@ -3043,25 +3049,19 @@
     if (parent == NULL) return -ENOENT;
     err = devfs_do_symlink (parent, dentry->d_name.name, DEVFS_FL_NONE,
 			    symname, &de, NULL);
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_DISABLED)
-	printk ("%s: symlink(): errcode from <devfs_do_symlink>: %d\n",
-		DEVFS_NAME, err);
-#endif
+    DPRINTK (DEBUG_DISABLED, "(%s): errcode from <devfs_do_symlink>: %d\n",
+	     dentry->d_name.name, err);
     if (err < 0) return err;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     de->inode.uid = current->euid;
     de->inode.gid = current->egid;
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
-    if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+    if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_DISABLED)
-	printk ("%s: symlink(): new VFS inode(%u): %p  dentry: %p\n",
-		DEVFS_NAME, de->inode.ino, inode, dentry);
-#endif
+    DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p  dentry: %p\n",
+	     dentry->d_name.name, de->inode.ino, inode, dentry);
     d_instantiate (dentry, inode);
     if ( !is_devfsd_or_child (fs_info) )
 	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
@@ -3081,7 +3081,7 @@
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     if ( ( err = _devfs_append_entry (parent, de, FALSE, NULL) ) != 0 )
 	return err;
     de->inode.uid = current->euid;
@@ -3089,13 +3089,10 @@
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
-    if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+    if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_DISABLED)
-	printk ("%s: mkdir(): new VFS inode(%u): %p  dentry: %p\n",
-		DEVFS_NAME, de->inode.ino, inode, dentry);
-#endif
+    DPRINTK (DEBUG_DISABLED, "(%s): new VFS inode(%u): %p  dentry: %p\n",
+	     dentry->d_name.name, de->inode.ino, inode, dentry);
     d_instantiate (dentry, inode);
     if ( !is_devfsd_or_child (fs_info) )
 	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
@@ -3114,7 +3111,7 @@
     de = get_devfs_entry_from_vfs_inode (inode);
     if (de == NULL) return -ENOENT;
     if ( !S_ISDIR (de->mode) ) return -ENOTDIR;
-    if (!de->vfs_created) return -EPERM;
+    if (!de->vfs_deletable) return -EPERM;
     /*  First ensure the directory is empty and will stay thay way  */
     write_lock (&de->u.dir.lock);
     de->u.dir.no_more_additions = TRUE;
@@ -3142,16 +3139,13 @@
     struct devfs_entry *parent, *de;
     struct inode *inode;
 
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_MKNOD)
-	printk ("%s: mknod(%s): mode: 0%o  dev: %d\n",
-		DEVFS_NAME, dentry->d_name.name, mode, rdev);
-#endif
+    DPRINTK (DEBUG_I_MKNOD, "(%s): mode: 0%o  dev: %d\n",
+	     dentry->d_name.name, mode, rdev);
     parent = get_devfs_entry_from_vfs_inode (dir);
     if (parent == NULL) return -ENOENT;
     de = _devfs_alloc_entry (dentry->d_name.name, dentry->d_name.len, mode);
     if (!de) return -ENOMEM;
-    de->vfs_created = TRUE;
+    de->vfs_deletable = TRUE;
     if ( S_ISBLK (mode) || S_ISCHR (mode) )
     {
 	de->u.fcb.u.device.major = MAJOR (rdev);
@@ -3164,13 +3158,10 @@
     de->inode.atime = CURRENT_TIME;
     de->inode.mtime = CURRENT_TIME;
     de->inode.ctime = CURRENT_TIME;
-    if ( ( inode = get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
+    if ( ( inode = _devfs_get_vfs_inode (dir->i_sb, de, dentry) ) == NULL )
 	return -ENOMEM;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_I_MKNOD)
-	printk ("%s:   new VFS inode(%u): %p  dentry: %p\n",
-		DEVFS_NAME, de->inode.ino, inode, dentry);
-#endif
+    DPRINTK (DEBUG_I_MKNOD, ":   new VFS inode(%u): %p  dentry: %p\n",
+	     de->inode.ino, inode, dentry);
     d_instantiate (dentry, inode);
     if ( !is_devfsd_or_child (fs_info) )
 	devfsd_notify_de (de, DEVFSD_NOTIFY_CREATE, inode->i_mode,
@@ -3238,15 +3229,11 @@
     sb->s_blocksize_bits = 10;
     sb->s_magic = DEVFS_SUPER_MAGIC;
     sb->s_op = &devfs_sops;
-    if ( ( root_inode = get_vfs_inode (sb, root_entry, NULL) ) == NULL )
+    if ( ( root_inode = _devfs_get_vfs_inode (sb, root_entry, NULL) ) == NULL )
 	goto out_no_root;
     sb->s_root = d_alloc_root (root_inode);
     if (!sb->s_root) goto out_no_root;
-#ifdef CONFIG_DEVFS_DEBUG
-    if (devfs_debug & DEBUG_S_READ)
-	printk ("%s: read super, made devfs ptr: %p\n",
-		DEVFS_NAME, sb->u.generic_sbp);
-#endif
+    DPRINTK (DEBUG_S_READ, "(): made devfs ptr: %p\n", sb->u.generic_sbp);
     return sb;
 
 out_no_root:
@@ -3479,6 +3466,10 @@
 
     printk ("%s: v%s Richard Gooch (rgooch@atnf.csiro.au)\n",
 	    DEVFS_NAME, DEVFS_VERSION);
+    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
+					  sizeof (struct devfsd_buf_entry),
+					  0, 0, NULL, NULL);
+    if (!devfsd_buf_cache) OOPS ("(): unable to allocate event slab\n");
 #ifdef CONFIG_DEVFS_DEBUG
     devfs_debug = devfs_debug_init;
     printk ("%s: devfs_debug: 0x%0x\n", DEVFS_NAME, devfs_debug);
@@ -3498,9 +3489,6 @@
 {
     int err;
 
-    devfsd_buf_cache = kmem_cache_create ("devfsd_event",
-					  sizeof (struct devfsd_buf_entry),
-					  0, 0, NULL, NULL);
     if ( !(boot_options & OPTION_MOUNT) ) return;
     err = do_mount ("none", "/dev", "devfs", 0, "");
     if (err == 0) printk ("Mounted devfs on /dev\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/efs/dir.c linux/fs/efs/dir.c
--- linux.orig/fs/efs/dir.c	Fri Nov 17 19:36:27 2000
+++ linux/fs/efs/dir.c	Wed Jan 23 20:15:01 2002
@@ -40,7 +40,7 @@
 	/* look at all blocks */
 	while (block < inode->i_blocks) {
 		/* read the dir block */
-		bh = bread(inode->i_dev, efs_bmap(inode, block), EFS_DIRBSIZE);
+		bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
 
 		if (!bh) {
 			printk(KERN_ERR "EFS: readdir(): failed to read dir block %d\n", block);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/efs/inode.c linux/fs/efs/inode.c
--- linux.orig/fs/efs/inode.c	Sun Sep 30 19:26:08 2001
+++ linux/fs/efs/inode.c	Wed Jan 23 20:15:01 2002
@@ -77,7 +77,7 @@
 			(EFS_BLOCKSIZE / sizeof(struct efs_dinode))) *
 		sizeof(struct efs_dinode);
 
-	bh = bread(inode->i_dev, block, EFS_BLOCKSIZE);
+	bh = sb_bread(inode->i_sb, block);
 	if (!bh) {
 		printk(KERN_WARNING "EFS: bread() failed at block %d\n", block);
 		goto read_inode_error;
@@ -271,7 +271,7 @@
 		if (first || lastblock != iblock) {
 			if (bh) brelse(bh);
 
-			bh = bread(inode->i_dev, iblock, EFS_BLOCKSIZE);
+			bh = sb_bread(inode->i_sb, iblock);
 			if (!bh) {
 				printk(KERN_ERR "EFS: bread() failed at block %d\n", iblock);
 				return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/efs/namei.c linux/fs/efs/namei.c
--- linux.orig/fs/efs/namei.c	Wed Jun  2 06:25:48 1999
+++ linux/fs/efs/namei.c	Wed Jan 23 20:15:01 2002
@@ -24,7 +24,7 @@
 
 	for(block = 0; block < inode->i_blocks; block++) {
 
-		bh = bread(inode->i_dev, efs_bmap(inode, block), EFS_DIRBSIZE);
+		bh = sb_bread(inode->i_sb, efs_bmap(inode, block));
 		if (!bh) {
 			printk(KERN_ERR "EFS: find_entry(): failed to read dir block %d\n", block);
 			return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/efs/super.c linux/fs/efs/super.c
--- linux.orig/fs/efs/super.c	Wed Apr 18 06:16:38 2001
+++ linux/fs/efs/super.c	Wed Jan 23 20:15:01 2002
@@ -137,11 +137,14 @@
 	struct buffer_head *bh;
 
  	sb = SUPER_INFO(s);
-
+ 
+	s->s_magic		= EFS_SUPER_MAGIC;
+	s->s_blocksize		= EFS_BLOCKSIZE;
+	s->s_blocksize_bits	= EFS_BLOCKSIZE_BITS;
 	set_blocksize(dev, EFS_BLOCKSIZE);
   
 	/* read the vh (volume header) block */
-	bh = bread(dev, 0, EFS_BLOCKSIZE);
+	bh = sb_bread(s, 0);
 
 	if (!bh) {
 		printk(KERN_ERR "EFS: cannot read volume header\n");
@@ -160,7 +163,7 @@
 		goto out_no_fs_ul;
 	}
 
-	bh = bread(dev, sb->fs_start + EFS_SUPER, EFS_BLOCKSIZE);
+	bh = sb_bread(s, sb->fs_start + EFS_SUPER);
 	if (!bh) {
 		printk(KERN_ERR "EFS: cannot read superblock\n");
 		goto out_no_fs_ul;
@@ -174,10 +177,6 @@
 		goto out_no_fs_ul;
 	}
 	brelse(bh);
- 
-	s->s_magic		= EFS_SUPER_MAGIC;
-	s->s_blocksize		= EFS_BLOCKSIZE;
-	s->s_blocksize_bits	= EFS_BLOCKSIZE_BITS;
 
 	if (!(s->s_flags & MS_RDONLY)) {
 #ifdef DEBUG
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/efs/symlink.c linux/fs/efs/symlink.c
--- linux.orig/fs/efs/symlink.c	Fri Dec 29 22:07:57 2000
+++ linux/fs/efs/symlink.c	Wed Jan 23 20:15:01 2002
@@ -26,13 +26,13 @@
 	lock_kernel();
 	/* read first 512 bytes of link target */
 	err = -EIO;
-	bh = bread(inode->i_dev, efs_bmap(inode, 0), EFS_BLOCKSIZE);
+	bh = sb_bread(inode->i_sb, efs_bmap(inode, 0));
 	if (!bh)
 		goto fail;
 	memcpy(link, bh->b_data, (size > EFS_BLOCKSIZE) ? EFS_BLOCKSIZE : size);
 	brelse(bh);
 	if (size > EFS_BLOCKSIZE) {
-		bh = bread(inode->i_dev, efs_bmap(inode, 1), EFS_BLOCKSIZE);
+		bh = sb_bread(inode->i_sb, efs_bmap(inode, 1));
 		if (!bh)
 			goto fail;
 		memcpy(link + EFS_BLOCKSIZE, bh->b_data, size - EFS_BLOCKSIZE);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext2/balloc.c linux/fs/ext2/balloc.c
--- linux.orig/fs/ext2/balloc.c	Fri Oct  5 19:23:53 2001
+++ linux/fs/ext2/balloc.c	Wed Jan 23 20:15:01 2002
@@ -88,7 +88,7 @@
 	if (!gdp)
 		goto error_out;
 	retval = 0;
-	bh = bread (sb->s_dev, le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
+	bh = sb_bread(sb, le32_to_cpu(gdp->bg_block_bitmap));
 	if (!bh) {
 		ext2_error (sb, "read_block_bitmap",
 			    "Cannot read block bitmap - "
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext2/dir.c linux/fs/ext2/dir.c
--- linux.orig/fs/ext2/dir.c	Mon Sep 17 20:16:30 2001
+++ linux/fs/ext2/dir.c	Tue Jan 22 23:04:46 2002
@@ -53,8 +53,13 @@
 	int err = 0;
 	dir->i_version = ++event;
 	page->mapping->a_ops->commit_write(NULL, page, from, to);
-	if (IS_SYNC(dir))
-		err = waitfor_one_page(page);
+	if (IS_SYNC(dir)) {
+		int err2;
+		err = writeout_one_page(page);
+		err2 = waitfor_one_page(page);
+		if (err == 0)
+			err = err2;
+	}
 	return err;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext2/ialloc.c linux/fs/ext2/ialloc.c
--- linux.orig/fs/ext2/ialloc.c	Sun Nov 11 17:59:56 2001
+++ linux/fs/ext2/ialloc.c	Wed Jan 23 20:15:01 2002
@@ -51,8 +51,7 @@
 	if (!desc)
 		goto error_out;
 
-	bh = bread(sb->s_dev, le32_to_cpu(desc->bg_inode_bitmap),
-			sb->s_blocksize);
+	bh = sb_bread(sb, le32_to_cpu(desc->bg_inode_bitmap));
 	if (!bh)
 		ext2_error (sb, "read_inode_bitmap",
 			    "Cannot read inode bitmap - "
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext2/inode.c linux/fs/ext2/inode.c
--- linux.orig/fs/ext2/inode.c	Wed Nov 21 22:07:25 2001
+++ linux/fs/ext2/inode.c	Wed Jan 23 20:15:01 2002
@@ -239,8 +239,7 @@
 				 Indirect chain[4],
 				 int *err)
 {
-	kdev_t dev = inode->i_dev;
-	int size = inode->i_sb->s_blocksize;
+	struct super_block *sb = inode->i_sb;
 	Indirect *p = chain;
 	struct buffer_head *bh;
 
@@ -250,7 +249,7 @@
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
-		bh = bread(dev, le32_to_cpu(p->key), size);
+		bh = sb_bread(sb, le32_to_cpu(p->key));
 		if (!bh)
 			goto failure;
 		/* Reader: pointers */
@@ -399,7 +398,7 @@
 		 * Get buffer_head for parent block, zero it out and set 
 		 * the pointer to new one, then send parent to disk.
 		 */
-		bh = getblk(inode->i_dev, parent, blocksize);
+		bh = sb_getblk(inode->i_sb, parent);
 		lock_buffer(bh);
 		memset(bh->b_data, 0, blocksize);
 		branch[n].bh = bh;
@@ -763,7 +762,7 @@
 			if (!nr)
 				continue;
 			*p = 0;
-			bh = bread (inode->i_dev, nr, inode->i_sb->s_blocksize);
+			bh = sb_bread(inode->i_sb, nr);
 			/*
 			 * A read failure? Report error and clear slot
 			 * (should be rare).
@@ -921,7 +920,7 @@
 		EXT2_INODE_SIZE(inode->i_sb);
 	block = le32_to_cpu(gdp[desc].bg_inode_table) +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+	if (!(bh = sb_bread(inode->i_sb, block))) {
 		ext2_error (inode->i_sb, "ext2_read_inode",
 			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
@@ -1063,7 +1062,7 @@
 		EXT2_INODE_SIZE(inode->i_sb);
 	block = le32_to_cpu(gdp[desc].bg_inode_table) +
 		(offset >> EXT2_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+	if (!(bh = sb_bread(inode->i_sb, block))) {
 		ext2_error (inode->i_sb, "ext2_write_inode",
 			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext2/super.c linux/fs/ext2/super.c
--- linux.orig/fs/ext2/super.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/ext2/super.c	Wed Jan 23 20:15:01 2002
@@ -433,6 +433,7 @@
 		printk ("EXT2-fs: unable to set blocksize %d\n", blocksize);
 		return NULL;
 	}
+	sb->s_blocksize = blocksize;
 
 	/*
 	 * If the superblock doesn't start on a sector boundary,
@@ -444,7 +445,7 @@
 		offset = (sb_block*BLOCK_SIZE) % blocksize;
 	}
 
-	if (!(bh = bread (dev, logic_sb_block, blocksize))) {
+	if (!(bh = sb_bread(sb, logic_sb_block))) {
 		printk ("EXT2-fs: unable to read superblock\n");
 		return NULL;
 	}
@@ -503,7 +504,7 @@
 
 		logic_sb_block = (sb_block*BLOCK_SIZE) / blocksize;
 		offset = (sb_block*BLOCK_SIZE) % blocksize;
-		bh = bread (dev, logic_sb_block, blocksize);
+		bh = sb_bread(sb, logic_sb_block);
 		if(!bh) {
 			printk("EXT2-fs: Couldn't read superblock on "
 			       "2nd try.\n");
@@ -607,8 +608,7 @@
 		goto failed_mount;
 	}
 	for (i = 0; i < db_count; i++) {
-		sb->u.ext2_sb.s_group_desc[i] = bread (dev, logic_sb_block + i + 1,
-						       sb->s_blocksize);
+		sb->u.ext2_sb.s_group_desc[i] = sb_bread(sb, logic_sb_block + i + 1);
 		if (!sb->u.ext2_sb.s_group_desc[i]) {
 			for (j = 0; j < i; j++)
 				brelse (sb->u.ext2_sb.s_group_desc[j]);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext3/balloc.c linux/fs/ext3/balloc.c
--- linux.orig/fs/ext3/balloc.c	Fri Nov  9 22:25:04 2001
+++ linux/fs/ext3/balloc.c	Wed Jan 23 20:15:01 2002
@@ -91,8 +91,7 @@
 	if (!gdp)
 		goto error_out;
 	retval = 0;
-	bh = bread (sb->s_dev,
-			le32_to_cpu(gdp->bg_block_bitmap), sb->s_blocksize);
+	bh = sb_bread(sb, le32_to_cpu(gdp->bg_block_bitmap));
 	if (!bh) {
 		ext3_error (sb, "read_block_bitmap",
 			    "Cannot read block bitmap - "
@@ -353,8 +352,7 @@
 #ifdef CONFIG_JBD_DEBUG
 		{
 			struct buffer_head *debug_bh;
-			debug_bh = get_hash_table(sb->s_dev, block + i,
-							sb->s_blocksize);
+			debug_bh = sb_get_hash_table(sb, block + i);
 			if (debug_bh) {
 				BUFFER_TRACE(debug_bh, "Deleted!");
 				if (!bh2jh(bitmap_bh)->b_committed_data)
@@ -702,7 +700,7 @@
 		struct buffer_head *debug_bh;
 
 		/* Record bitmap buffer state in the newly allocated block */
-		debug_bh = get_hash_table(sb->s_dev, tmp, sb->s_blocksize);
+		debug_bh = sb_get_hash_table(sb, tmp);
 		if (debug_bh) {
 			BUFFER_TRACE(debug_bh, "state when allocated");
 			BUFFER_TRACE2(debug_bh, bh, "bitmap state");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext3/ialloc.c linux/fs/ext3/ialloc.c
--- linux.orig/fs/ext3/ialloc.c	Fri Nov  9 22:25:04 2001
+++ linux/fs/ext3/ialloc.c	Wed Jan 23 20:15:01 2002
@@ -60,8 +60,7 @@
 		retval = -EIO;
 		goto error_out;
 	}
-	bh = bread (sb->s_dev,
-			le32_to_cpu(gdp->bg_inode_bitmap), sb->s_blocksize);
+	bh = sb_bread(sb, le32_to_cpu(gdp->bg_inode_bitmap));
 	if (!bh) {
 		ext3_error (sb, "read_inode_bitmap",
 			    "Cannot read inode bitmap - "
@@ -506,7 +505,7 @@
 	if (IS_SYNC(inode))
 		handle->h_sync = 1;
 	insert_inode_hash(inode);
-	inode->i_generation = event++;
+	inode->i_generation = sb->u.ext3_sb.s_next_generation++;
 
 	inode->u.ext3_i.i_state = EXT3_STATE_NEW;
 	err = ext3_mark_inode_dirty(handle, inode);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext3/inode.c linux/fs/ext3/inode.c
--- linux.orig/fs/ext3/inode.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/ext3/inode.c	Wed Jan 23 20:15:01 2002
@@ -388,8 +388,7 @@
 static Indirect *ext3_get_branch(struct inode *inode, int depth, int *offsets,
 				 Indirect chain[4], int *err)
 {
-	kdev_t dev = inode->i_dev;
-	int blocksize = inode->i_sb->s_blocksize;
+	struct super_block *sb = inode->i_sb;
 	Indirect *p = chain;
 	struct buffer_head *bh;
 
@@ -399,7 +398,7 @@
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
-		bh = bread(dev, le32_to_cpu(p->key), blocksize);
+		bh = sb_bread(sb, le32_to_cpu(p->key));
 		if (!bh)
 			goto failure;
 		/* Reader: pointers */
@@ -557,7 +556,7 @@
 			 * and set the pointer to new one, then send
 			 * parent to disk.  
 			 */
-			bh = getblk(inode->i_dev, parent, blocksize);
+			bh = sb_getblk(inode->i_sb, parent);
 			branch[n].bh = bh;
 			lock_buffer(bh);
 			BUFFER_TRACE(bh, "call get_create_access");
@@ -858,8 +857,7 @@
 	*errp = ext3_get_block_handle(handle, inode, block, &dummy, create);
 	if (!*errp && buffer_mapped(&dummy)) {
 		struct buffer_head *bh;
-		bh = getblk(dummy.b_dev, dummy.b_blocknr,
-					inode->i_sb->s_blocksize);
+		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
 		if (buffer_new(&dummy)) {
 			J_ASSERT(create != 0);
 			J_ASSERT(handle != 0);
@@ -1576,9 +1574,6 @@
 		u32 *first, u32 *last)
 {
 	u32 *p;
-	kdev_t dev = inode->i_sb->s_dev;
-	unsigned long blocksize = inode->i_sb->s_blocksize;
-
 	if (try_to_extend_transaction(handle, inode)) {
 		if (bh) {
 			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
@@ -1604,7 +1599,7 @@
 			struct buffer_head *bh;
 
 			*p = 0;
-			bh = get_hash_table(dev, nr, blocksize);
+			bh = sb_get_hash_table(inode->i_sb, nr);
 			ext3_forget(handle, 0, inode, bh, nr);
 		}
 	}
@@ -1717,7 +1712,7 @@
 				continue;		/* A hole */
 
 			/* Go read the buffer for the next level down */
-			bh = bread(inode->i_dev, nr, inode->i_sb->s_blocksize);
+			bh = sb_bread(inode->i_sb, nr);
 
 			/*
 			 * A read failure? Report error and clear slot
@@ -2030,7 +2025,7 @@
 		EXT3_INODE_SIZE(inode->i_sb);
 	block = le32_to_cpu(gdp[desc].bg_inode_table) +
 		(offset >> EXT3_BLOCK_SIZE_BITS(inode->i_sb));
-	if (!(bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize))) {
+	if (!(bh = sb_bread(inode->i_sb, block))) {
 		ext3_error (inode->i_sb, "ext3_get_inode_loc",
 			    "unable to read inode block - "
 			    "inode=%lu, block=%lu", inode->i_ino, block);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ext3/super.c linux/fs/ext3/super.c
--- linux.orig/fs/ext3/super.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/ext3/super.c	Wed Jan 23 20:15:01 2002
@@ -29,6 +29,7 @@
 #include <linux/locks.h>
 #include <linux/blkdev.h>
 #include <linux/smp_lock.h>
+#include <linux/random.h>
 #include <asm/uaccess.h>
 
 #ifdef CONFIG_JBD_DEBUG
@@ -925,6 +926,7 @@
 		goto out_fail;
 	}
 
+	sb->s_blocksize = blocksize;
 	set_blocksize (dev, blocksize);
 
 	/*
@@ -936,7 +938,7 @@
 		offset = (sb_block * EXT3_MIN_BLOCK_SIZE) % blocksize;
 	}
 
-	if (!(bh = bread (dev, logic_sb_block, blocksize))) {
+	if (!(bh = sb_bread(sb, logic_sb_block))) {
 		printk (KERN_ERR "EXT3-fs: unable to read superblock\n");
 		goto out_fail;
 	}
@@ -1009,7 +1011,7 @@
 		set_blocksize (dev, sb->s_blocksize);
 		logic_sb_block = (sb_block * EXT3_MIN_BLOCK_SIZE) / blocksize;
 		offset = (sb_block * EXT3_MIN_BLOCK_SIZE) % blocksize;
-		bh = bread (dev, logic_sb_block, blocksize);
+		bh = sb_bread(sb, logic_sb_block);
 		if (!bh) {
 			printk(KERN_ERR 
 			       "EXT3-fs: Can't read superblock on 2nd try.\n");
@@ -1093,8 +1095,7 @@
 		goto failed_mount;
 	}
 	for (i = 0; i < db_count; i++) {
-		sbi->s_group_desc[i] = bread(dev, logic_sb_block + i + 1,
-					     blocksize);
+		sbi->s_group_desc[i] = sb_bread(sb, logic_sb_block + i + 1);
 		if (!sbi->s_group_desc[i]) {
 			printk (KERN_ERR "EXT3-fs: "
 				"can't read group descriptor %d\n", i);
@@ -1115,6 +1116,7 @@
 	sbi->s_loaded_inode_bitmaps = 0;
 	sbi->s_loaded_block_bitmaps = 0;
 	sbi->s_gdb_count = db_count;
+	get_random_bytes(&sbi->s_next_generation, sizeof(u32));
 	/*
 	 * set up enough so that it can read an inode
 	 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/fat/buffer.c linux/fs/fat/buffer.c
--- linux.orig/fs/fat/buffer.c	Tue Jul 24 21:23:53 2001
+++ linux/fs/fat/buffer.c	Wed Jan 23 20:15:01 2002
@@ -59,12 +59,12 @@
 
 struct buffer_head *default_fat_bread(struct super_block *sb, int block)
 {
-	return bread (sb->s_dev, block, sb->s_blocksize);
+	return sb_bread(sb, block);
 }
 
 struct buffer_head *default_fat_getblk(struct super_block *sb, int block)
 {
-	return getblk (sb->s_dev, block, sb->s_blocksize);
+	return sb_getblk(sb, block);
 }
 
 void default_fat_brelse(struct super_block *sb, struct buffer_head *bh)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/fat/inode.c linux/fs/fat/inode.c
--- linux.orig/fs/fat/inode.c	Tue Nov 20 22:15:26 2001
+++ linux/fs/fat/inode.c	Wed Jan 23 20:15:01 2002
@@ -406,7 +406,7 @@
 	}
 	inode->i_blksize = 1 << sbi->cluster_bits;
 	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
-			   & ~(inode->i_blksize - 1)) / 512;
+			   & ~(inode->i_blksize - 1)) >> 9;
 	MSDOS_I(inode)->i_logstart = 0;
 	MSDOS_I(inode)->mmu_private = inode->i_size;
 
@@ -584,7 +584,7 @@
 
 	sb->s_blocksize = hard_blksize;
 	set_blocksize(sb->s_dev, hard_blksize);
-	bh = bread(sb->s_dev, 0, sb->s_blocksize);
+	bh = sb_bread(sb, 0);
 	if (bh == NULL) {
 		printk("FAT: unable to read boot sector\n");
 		goto out_fail;
@@ -656,7 +656,7 @@
 			(sbi->fsinfo_sector * logical_sector_size) % hard_blksize;
 		fsinfo_bh = bh;
 		if (fsinfo_block != 0) {
-			fsinfo_bh = bread(sb->s_dev, fsinfo_block, hard_blksize);
+			fsinfo_bh = sb_bread(sb, fsinfo_block);
 			if (fsinfo_bh == NULL) {
 				printk("FAT: bread failed, FSINFO block"
 				       " (blocknr = %d)\n", fsinfo_block);
@@ -952,7 +952,7 @@
 	/* this is as close to the truth as we can get ... */
 	inode->i_blksize = 1 << sbi->cluster_bits;
 	inode->i_blocks = ((inode->i_size + inode->i_blksize - 1)
-			   & ~(inode->i_blksize - 1)) / 512;
+			   & ~(inode->i_blksize - 1)) >> 9;
 	inode->i_mtime = inode->i_atime =
 		date_dos2unix(CF_LE_W(de->time),CF_LE_W(de->date));
 	inode->i_ctime =
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs.h linux/fs/freevxfs/vxfs.h
--- linux.orig/fs/freevxfs/vxfs.h	Mon May 21 19:31:06 2001
+++ linux/fs/freevxfs/vxfs.h	Mon Feb  4 19:16:43 2002
@@ -30,7 +30,7 @@
 #ifndef _VXFS_SUPER_H_
 #define _VXFS_SUPER_H_
 
-#ident "$Id: vxfs.h 1.11 2001/05/21 15:40:28 hch Exp hch $"
+#ident "$Id: vxfs.h 1.12 2001/12/28 19:48:03 hch Exp $"
 
 /*
  * Veritas filesystem driver - superblock structure.
@@ -39,6 +39,7 @@
  * superblocks of the Veritas Filesystem.
  */
 #include <linux/types.h>
+#include "vxfs_kcompat.h"
 
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_bmap.c linux/fs/freevxfs/vxfs_bmap.c
--- linux.orig/fs/freevxfs/vxfs_bmap.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/freevxfs/vxfs_bmap.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_bmap.c,v 1.23 2001/07/05 19:48:03 hch Exp hch $"
+#ident "$Id: vxfs_bmap.c,v 1.25 2002/01/02 23:36:55 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - filesystem to disk block mapping.
@@ -64,49 +64,47 @@
  *   The physical block number on success, else Zero.
  */
 static daddr_t
-vxfs_bmap_ext4(struct inode *ip, long iblock)
+vxfs_bmap_ext4(struct inode *ip, long bn)
 {
-	struct vxfs_inode_info		*vip = VXFS_INO(ip);
-	struct super_block		*sbp = ip->i_sb;
-	kdev_t				dev = ip->i_dev;
-	u_long				bsize = sbp->s_blocksize;
-	long				size = 0;
-	int				i;
-
-	for (i = 0; i < VXFS_NDADDR; i++) {
-		struct direct		*dp = vip->vii_ext4.ve4_direct + i;
+	struct super_block *sb = ip->i_sb;
+	struct vxfs_inode_info *vip = VXFS_INO(ip);
+	unsigned long bsize = sb->s_blocksize;
+	u32 indsize = vip->vii_ext4.ve4_indsize;
+	int i;
 
-#ifdef DIAGNOSTIC
-		printk(KERN_DEBUG "iblock: %ld, %d (size: %lu)\n", iblock, i, size);
-		printk(KERN_DEBUG "dp->extent: %d, dp->size: %d\n", dp->extent, dp->size);
-#endif
+	if (indsize > sb->s_blocksize)
+		goto fail_size;
 
-		if (iblock >= size && iblock < (size + dp->size))
-			return ((iblock - size) + dp->extent);
-		size += dp->size;
+	for (i = 0; i < VXFS_NDADDR; i++) {
+		struct direct *d = vip->vii_ext4.ve4_direct + i;
+		if (bn >= 0 && bn < d->size)
+			return (bn + d->extent);
+		bn -= d->size;
 	}
 
-	iblock -= size;
+	if ((bn / (indsize * indsize * bsize / 4)) == 0) {
+		struct buffer_head *buf;
+		daddr_t	bno;
+		u32 *indir;
+
+		buf = sb_bread(sb, vip->vii_ext4.ve4_indir[0]);
+		if (!buf || !buffer_mapped(buf))
+			goto fail_buf;
 
-	if (!(iblock / (vip->vii_ext4.ve4_indsize * vip->vii_ext4.ve4_indsize * bsize >> 2))) {
-		struct buffer_head	*bp;
-		daddr_t			pblock;
-
-		/*
-		 * XXX: is the second indir only used for
-		 *	double indirect extents?
-		 */
-		bp = bread(dev, vip->vii_ext4.ve4_indir[0],
-				bsize * ((vip->vii_ext4.ve4_indsize) / bsize) + 1);
-		pblock = *(bp->b_data + ((iblock / vip->vii_ext4.ve4_indsize) %
-					(vip->vii_ext4.ve4_indsize * bsize)));
+		indir = (u32 *)buf->b_data;
+		bno = indir[(bn/indsize) % (indsize*bn)] + (bn%indsize);
 
-		brelse(bp);
-		return (pblock + (iblock % vip->vii_ext4.ve4_indsize));
+		brelse(buf);
+		return bno;
 	} else
 		printk(KERN_WARNING "no matching indir?");
 
 	return 0;
+
+fail_size:
+	printk("vxfs: indirect extent to big!\n");
+fail_buf:
+	return 0;
 }
 
 /**
@@ -137,9 +135,8 @@
 		struct vxfs_typed	*typ;
 		int64_t			off;
 
-		bp = bread(ip->i_dev,
-				indir + (i / VXFS_TYPED_PER_BLOCK(ip->i_sb)),
-				ip->i_sb->s_blocksize);
+		bp = sb_bread(ip->i_sb,
+				indir + (i / VXFS_TYPED_PER_BLOCK(ip->i_sb)));
 		if (!buffer_mapped(bp))
 			return 0;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_extern.h linux/fs/freevxfs/vxfs_extern.h
--- linux.orig/fs/freevxfs/vxfs_extern.h	Sun Sep  2 17:34:36 2001
+++ linux/fs/freevxfs/vxfs_extern.h	Mon Feb  4 19:16:43 2002
@@ -30,7 +30,7 @@
 #ifndef _VXFS_EXTERN_H_
 #define _VXFS_EXTERN_H_
 
-#ident "$Id: vxfs_extern.h,v 1.21 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_extern.h,v 1.22 2001/12/28 20:50:47 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - external prototypes.
@@ -71,7 +71,7 @@
 extern int			vxfs_read_olt(struct super_block *, u_long);
 
 /* vxfs_subr.c */
-extern struct page *		vxfs_get_page(struct inode *, u_long);
+extern struct page *		vxfs_get_page(struct address_space *, u_long);
 extern __inline__ void		vxfs_put_page(struct page *);
 extern struct buffer_head *	vxfs_bread(struct inode *, int);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_fshead.c linux/fs/freevxfs/vxfs_fshead.c
--- linux.orig/fs/freevxfs/vxfs_fshead.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/freevxfs/vxfs_fshead.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_fshead.c,v 1.19 2001/08/07 16:14:10 hch Exp hch $"
+#ident "$Id: vxfs_fshead.c,v 1.20 2002/01/02 22:02:12 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - fileset header routines.
@@ -81,9 +81,9 @@
 	if (buffer_mapped(bp)) {
 		struct vxfs_fsh		*fhp;
 
-		if (!(fhp = kmalloc(sizeof(struct vxfs_fsh), SLAB_KERNEL)))
+		if (!(fhp = kmalloc(sizeof(*fhp), SLAB_KERNEL)))
 			return NULL;
-		memcpy(fhp, bp->b_data, sizeof(struct vxfs_fsh));
+		memcpy(fhp, bp->b_data, sizeof(*fhp));
 
 		brelse(bp);
 		return (fhp);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_inode.c linux/fs/freevxfs/vxfs_inode.c
--- linux.orig/fs/freevxfs/vxfs_inode.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/freevxfs/vxfs_inode.c	Mon Feb  4 19:16:43 2002
@@ -27,12 +27,13 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_inode.c,v 1.37 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_inode.c,v 1.42 2002/01/02 23:51:36 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - inode routines.
  */
 #include <linux/fs.h>
+#include <linux/pagemap.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 
@@ -47,6 +48,7 @@
 extern struct inode_operations vxfs_immed_symlink_iops;
 
 static struct file_operations vxfs_file_operations = {
+	.open =			generic_file_open,
 	.llseek =		generic_file_llseek,
 	.read =			generic_file_read,
 	.mmap =			generic_file_mmap,
@@ -104,7 +106,7 @@
 
 	block = extent + ((ino * VXFS_ISIZE) / sbp->s_blocksize);
 	offset = ((ino % (sbp->s_blocksize / VXFS_ISIZE)) * VXFS_ISIZE);
-	bp = bread(sbp->s_dev, block, sbp->s_blocksize);
+	bp = sb_bread(sbp, block);
 
 	if (buffer_mapped(bp)) {
 		struct vxfs_inode_info	*vip;
@@ -113,7 +115,7 @@
 		if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
 			goto fail;
 		dip = (struct vxfs_dinode *)(bp->b_data + offset);
-		memcpy(vip, dip, sizeof(struct vxfs_inode_info));
+		memcpy(vip, dip, sizeof(*vip));
 #ifdef DIAGNOSTIC
 		vxfs_dumpi(vip, ino);
 #endif
@@ -145,7 +147,7 @@
 	u_long				offset;
 
 	offset = (ino % (PAGE_SIZE / VXFS_ISIZE)) * VXFS_ISIZE;
-	pp = vxfs_get_page(ilistp, ino * VXFS_ISIZE / PAGE_SIZE);
+	pp = vxfs_get_page(ilistp->i_mapping, ino * VXFS_ISIZE / PAGE_SIZE);
 
 	if (!IS_ERR(pp)) {
 		struct vxfs_inode_info	*vip;
@@ -155,7 +157,7 @@
 		if (!(vip = kmem_cache_alloc(vxfs_inode_cachep, SLAB_KERNEL)))
 			goto fail;
 		dip = (struct vxfs_dinode *)(kaddr + offset);
-		memcpy(vip, dip, sizeof(struct vxfs_inode_info));
+		memcpy(vip, dip, sizeof(*vip));
 #ifdef DIAGNOSTIC
 		vxfs_dumpi(vip, ino);
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_kcompat.h linux/fs/freevxfs/vxfs_kcompat.h
--- linux.orig/fs/freevxfs/vxfs_kcompat.h	Thu Jan  1 00:00:00 1970
+++ linux/fs/freevxfs/vxfs_kcompat.h	Tue Feb  5 16:46:03 2002
@@ -0,0 +1,43 @@
+#ifndef _VXFS_KCOMPAT_H
+#define _VXFS_KCOMPAT_H
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
+
+#include <linux/blkdev.h>
+
+typedef long sector_t;
+
+/* Dito.  */
+static inline void map_bh(struct buffer_head *bh, struct super_block *sb, int block)
+{
+	bh->b_state |= 1 << BH_Mapped;
+	bh->b_dev = sb->s_dev;
+	bh->b_blocknr = block;
+}
+
+/* From fs/block_dev.c (Linux 2.5.2-pre2)  */
+static inline int sb_set_blocksize(struct super_block *sb, int size)
+{
+	int bits;
+	if (set_blocksize(sb->s_dev, size) < 0)
+		return 0;
+	sb->s_blocksize = size;
+	for (bits = 9, size >>= 9; size >>= 1; bits++)
+		;
+	sb->s_blocksize_bits = bits;
+	return sb->s_blocksize;
+}
+
+/* Dito.  */
+static inline int sb_min_blocksize(struct super_block *sb, int size)
+{
+	int minsize = get_hardsect_size(sb->s_dev);
+	if (size < minsize)
+		size = minsize;
+	return sb_set_blocksize(sb, size);
+}
+
+#endif /* Kernel 2.4 */
+#endif /* _VXFS_KCOMPAT_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_lookup.c linux/fs/freevxfs/vxfs_lookup.c
--- linux.orig/fs/freevxfs/vxfs_lookup.c	Thu Jun 28 00:10:55 2001
+++ linux/fs/freevxfs/vxfs_lookup.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_lookup.c,v 1.19 2001/05/30 19:50:20 hch Exp hch $"
+#ident "$Id: vxfs_lookup.c,v 1.21 2002/01/02 22:00:13 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - lookup and other directory related code.
@@ -126,7 +126,7 @@
 		caddr_t			kaddr;
 		struct page		*pp;
 
-		pp = vxfs_get_page(ip, page);
+		pp = vxfs_get_page(ip->i_mapping, page);
 		if (IS_ERR(pp))
 			continue;
 		kaddr = (caddr_t)page_address(pp);
@@ -273,7 +273,7 @@
 		caddr_t			kaddr;
 		struct page		*pp;
 
-		pp = vxfs_get_page(ip, page);
+		pp = vxfs_get_page(ip->i_mapping, page);
 		if (IS_ERR(pp))
 			continue;
 		kaddr = (caddr_t)page_address(pp);
@@ -318,6 +318,5 @@
 done:
 	fp->f_pos = ((page << PAGE_CACHE_SHIFT) | offset) + 2;
 out:
-	fp->f_version = ip->i_version;
 	return 0;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_olt.c linux/fs/freevxfs/vxfs_olt.c
--- linux.orig/fs/freevxfs/vxfs_olt.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/freevxfs/vxfs_olt.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_olt.c,v 1.9 2001/08/07 16:14:45 hch Exp hch $"
+#ident "$Id: vxfs_olt.c,v 1.10 2002/01/02 23:03:58 hch Exp hch $"
 
 /* 
  * Veritas filesystem driver - object location table support.
@@ -85,14 +85,23 @@
 	char			*oaddr, *eaddr;
 
 
-	bp = bread(sbp->s_dev,
-			vxfs_oblock(sbp, infp->vsi_oltext, bsize), bsize);
+	bp = sb_bread(sbp, vxfs_oblock(sbp, infp->vsi_oltext, bsize));
 	if (!bp || !bp->b_data)
 		goto fail;
 
 	op = (struct vxfs_olt *)bp->b_data;
 	if (op->olt_magic != VXFS_OLT_MAGIC) {
 		printk(KERN_NOTICE "vxfs: ivalid olt magic number\n");
+		goto fail;
+	}
+
+	/*
+	 * It is in theory possible that vsi_oltsize is > 1.
+	 * I've not seen any such filesystem yet and I'm lazy..  --hch
+	 */
+	if (infp->vsi_oltsize > 1) {
+		printk(KERN_NOTICE "vxfs: oltsize > 1 detected.\n");
+		printk(KERN_NOTICE "vxfs: please notify hch@caldera.de\n");
 		goto fail;
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_subr.c linux/fs/freevxfs/vxfs_subr.c
--- linux.orig/fs/freevxfs/vxfs_subr.c	Fri Nov  9 22:25:35 2001
+++ linux/fs/freevxfs/vxfs_subr.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_subr.c,v 1.5 2001/04/26 22:49:51 hch Exp hch $"
+#ident "$Id: vxfs_subr.c,v 1.8 2001/12/28 20:50:47 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - shared subroutines.
@@ -37,6 +37,7 @@
 #include <linux/slab.h>
 #include <linux/pagemap.h>
 
+#include "vxfs_kcompat.h"
 #include "vxfs_extern.h"
 
 
@@ -62,9 +63,8 @@
  *   The wanted page on success, else a NULL pointer.
  */
 struct page *
-vxfs_get_page(struct inode *ip, u_long n)
+vxfs_get_page(struct address_space *mapping, u_long n)
 {
-	struct address_space *		mapping = ip->i_mapping;
 	struct page *			pp;
 
 	pp = read_cache_page(mapping, n,
@@ -114,7 +114,7 @@
 	daddr_t			pblock;
 
 	pblock = vxfs_bmap1(ip, block);
-	bp = bread(ip->i_dev, pblock, ip->i_sb->s_blocksize);
+	bp = sb_bread(ip->i_sb, pblock);
 
 	return (bp);
 }
@@ -135,17 +135,14 @@
  *   Zero on success, else a negativ error code (-EIO).
  */
 static int
-vxfs_getblk(struct inode *ip, long iblock,
+vxfs_getblk(struct inode *ip, sector_t iblock,
 	    struct buffer_head *bp, int create)
 {
 	daddr_t			pblock;
 
 	pblock = vxfs_bmap1(ip, iblock);
 	if (pblock != 0) {
-		bp->b_dev = ip->i_dev;
-		bp->b_blocknr = pblock;
-		bp->b_state |= (1UL << BH_Mapped);
-
+		map_bh(bp, ip->i_sb, pblock);
 		return 0;
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/freevxfs/vxfs_super.c linux/fs/freevxfs/vxfs_super.c
--- linux.orig/fs/freevxfs/vxfs_super.c	Thu Oct 11 16:43:38 2001
+++ linux/fs/freevxfs/vxfs_super.c	Mon Feb  4 19:16:43 2002
@@ -27,7 +27,7 @@
  * SUCH DAMAGE.
  */
 
-#ident "$Id: vxfs_super.c,v 1.26 2001/08/07 16:13:30 hch Exp hch $"
+#ident "$Id: vxfs_super.c,v 1.29 2002/01/02 22:02:12 hch Exp hch $"
 
 /*
  * Veritas filesystem driver - superblock related routines.
@@ -62,19 +62,6 @@
 	.statfs =		vxfs_statfs,
 };
 
-static __inline__ u_long
-vxfs_validate_bsize(kdev_t dev)
-{
-	u_long			bsize;
-	
-	bsize = get_hardsect_size(dev);
-	if (bsize < BLOCK_SIZE)
-		bsize = BLOCK_SIZE;
-
-	set_blocksize(dev, bsize);
-	return (bsize);
-}
-
 /**
  * vxfs_put_super - free superblock resources
  * @sbp:	VFS superblock.
@@ -153,21 +140,24 @@
 {
 	struct vxfs_sb_info	*infp;
 	struct vxfs_sb		*rsbp;
-	struct buffer_head	*bp;
+	struct buffer_head	*bp = NULL;
 	u_long			bsize;
-	kdev_t			dev = sbp->s_dev;
 
-	infp = kmalloc(sizeof(struct vxfs_sb_info), GFP_KERNEL);
+	infp = kmalloc(sizeof(*infp), GFP_KERNEL);
 	if (!infp) {
 		printk(KERN_WARNING "vxfs: unable to allocate incore superblock\n");
 		return NULL;
 	}
-	memset(infp, 0, sizeof(struct vxfs_sb_info));
+	memset(infp, 0, sizeof(*infp));
 
-	bsize = vxfs_validate_bsize(dev);
+	bsize = sb_min_blocksize(sbp, BLOCK_SIZE);
+	if (!bsize) {
+		printk(KERN_WARNING "vxfs: unable to set blocksize\n");
+		goto out;
+	}
 
-	bp = bread(dev, 1, bsize);
-	if (!bp) {
+	bp = sb_bread(sbp, 1);
+	if (!bp || !buffer_mapped(bp)) {
 		if (!silent) {
 			printk(KERN_WARNING
 				"vxfs: unable to read disk superblock\n");
@@ -194,31 +184,15 @@
 #endif
 
 	sbp->s_magic = rsbp->vs_magic;
-	sbp->s_blocksize = rsbp->vs_bsize;
 	sbp->u.generic_sbp = (void *)infp;
 
 	infp->vsi_raw = rsbp;
 	infp->vsi_bp = bp;
 	infp->vsi_oltext = rsbp->vs_oltext[0];
 	infp->vsi_oltsize = rsbp->vs_oltsize;
-	
 
-	switch (rsbp->vs_bsize) {
-	case 1024:
-		sbp->s_blocksize_bits = 10;
-		break;
-	case 2048:
-		sbp->s_blocksize_bits = 11;
-		break;
-	case 4096:
-		sbp->s_blocksize_bits = 12;
-		break;
-	default:
-		if (!silent) {
-			printk(KERN_WARNING
-				"vxfs: unsupported blocksise: %d\n",
-				rsbp->vs_bsize);
-		}
+	if (!sb_set_blocksize(sbp, rsbp->vs_bsize)) {
+		printk(KERN_WARNING "vxfs: unable to set final block size\n");
 		goto out;
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/hfs/file.c linux/fs/hfs/file.c
--- linux.orig/fs/hfs/file.c	Sun Aug 12 17:56:56 2001
+++ linux/fs/hfs/file.c	Wed Jan 23 20:15:01 2002
@@ -61,7 +61,7 @@
 struct buffer_head *hfs_getblk(struct hfs_fork *fork, int block, int create)
 {
 	int tmp;
-	kdev_t dev = fork->entry->mdb->sys_mdb->s_dev;
+	struct super_block *sb = fork->entry->mdb->sys_mdb;
 
 	tmp = hfs_extent_map(fork, block, create);
 
@@ -71,7 +71,7 @@
 		*/
 		if (tmp) {
 			hfs_cat_mark_dirty(fork->entry);
-			return getblk(dev, tmp, HFS_SECTOR_SIZE);
+			return sb_getblk(sb, tmp);
 		}
 		return NULL;
 	} else {
@@ -80,8 +80,7 @@
 		   we waited on the I/O in getblk to complete.
 		*/
 		do {
-			struct buffer_head *bh =
-					getblk(dev, tmp, HFS_SECTOR_SIZE);
+			struct buffer_head *bh = sb_getblk(sb, tmp);
 			int tmp2 = hfs_extent_map(fork, block, 0);
 
 			if (tmp2 == tmp) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/hfs/super.c linux/fs/hfs/super.c
--- linux.orig/fs/hfs/super.c	Thu Oct 25 07:02:26 2001
+++ linux/fs/hfs/super.c	Wed Jan 23 20:15:01 2002
@@ -402,6 +402,8 @@
 
 	/* set the device driver to 512-byte blocks */
 	set_blocksize(dev, HFS_SECTOR_SIZE);
+	s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
+	s->s_blocksize = HFS_SECTOR_SIZE;
 
 #ifdef CONFIG_MAC_PARTITION
 	/* check to see if we're in a partition */
@@ -437,8 +439,6 @@
 	}
 
 	s->s_magic = HFS_SUPER_MAGIC;
-	s->s_blocksize_bits = HFS_SECTOR_SIZE_BITS;
-	s->s_blocksize = HFS_SECTOR_SIZE;
 	s->s_op = &hfs_super_operations;
 
 	/* try to get the root inode */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/hfs/sysdep.c linux/fs/hfs/sysdep.c
--- linux.orig/fs/hfs/sysdep.c	Tue Feb 13 22:13:45 2001
+++ linux/fs/hfs/sysdep.c	Wed Jan 23 20:15:01 2002
@@ -41,9 +41,9 @@
 	hfs_buffer tmp = HFS_BAD_BUFFER;
 
 	if (read) {
-		tmp = bread(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+		tmp = sb_bread(sys_mdb, block);
 	} else {
-		tmp = getblk(sys_mdb->s_dev, block, HFS_SECTOR_SIZE);
+		tmp = sb_getblk(sys_mdb, block);
 		if (tmp) {
 			mark_buffer_uptodate(tmp, 1);
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/hpfs/buffer.c linux/fs/hpfs/buffer.c
--- linux.orig/fs/hpfs/buffer.c	Sat Dec  9 19:43:03 2000
+++ linux/fs/hpfs/buffer.c	Wed Jan 23 20:15:01 2002
@@ -122,12 +122,9 @@
 void *hpfs_map_sector(struct super_block *s, unsigned secno, struct buffer_head **bhp,
 		 int ahead)
 {
-	kdev_t dev = s->s_dev;
 	struct buffer_head *bh;
 
-	if (!ahead || secno + ahead >= s->s_hpfs_fs_size)
-		*bhp = bh = bread(dev, secno, 512);
-	else *bhp = bh = bread(dev, secno, 512);
+	*bhp = bh = sb_bread(s, secno);
 	if (bh != NULL)
 		return bh->b_data;
 	else {
@@ -143,7 +140,7 @@
 	struct buffer_head *bh;
 	/*return hpfs_map_sector(s, secno, bhp, 0);*/
 
-	if ((*bhp = bh = getblk(s->s_dev, secno, 512)) != NULL) {
+	if ((*bhp = bh = sb_getblk(s, secno)) != NULL) {
 		if (!buffer_uptodate(bh)) wait_on_buffer(bh);
 		mark_buffer_uptodate(bh, 1);
 		return bh->b_data;
@@ -158,7 +155,6 @@
 void *hpfs_map_4sectors(struct super_block *s, unsigned secno, struct quad_buffer_head *qbh,
 		   int ahead)
 {
-	kdev_t dev = s->s_dev;
 	struct buffer_head *bh;
 	char *data;
 
@@ -173,24 +169,22 @@
 		goto bail;
 	}
 
-	if (!ahead || secno + 4 + ahead > s->s_hpfs_fs_size)
-		qbh->bh[0] = bh = bread(dev, secno, 512);
-	else qbh->bh[0] = bh = bread(dev, secno, 512);
+	qbh->bh[0] = bh = sb_bread(s, secno);
 	if (!bh)
 		goto bail0;
 	memcpy(data, bh->b_data, 512);
 
-	qbh->bh[1] = bh = bread(dev, secno + 1, 512);
+	qbh->bh[1] = bh = sb_bread(s, secno + 1);
 	if (!bh)
 		goto bail1;
 	memcpy(data + 512, bh->b_data, 512);
 
-	qbh->bh[2] = bh = bread(dev, secno + 2, 512);
+	qbh->bh[2] = bh = sb_bread(s, secno + 2);
 	if (!bh)
 		goto bail2;
 	memcpy(data + 2 * 512, bh->b_data, 512);
 
-	qbh->bh[3] = bh = bread(dev, secno + 3, 512);
+	qbh->bh[3] = bh = sb_bread(s, secno + 3);
 	if (!bh)
 		goto bail3;
 	memcpy(data + 3 * 512, bh->b_data, 512);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/hpfs/super.c linux/fs/hpfs/super.c
--- linux.orig/fs/hpfs/super.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/hpfs/super.c	Mon Jan 14 18:53:53 2002
@@ -3,7 +3,7 @@
  *
  *  Mikulas Patocka (mikulas@artax.karlin.mff.cuni.cz), 1998-1999
  *
- *  mouning, unmounting, error handling
+ *  mounting, unmounting, error handling
  */
 
 #include <linux/string.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/dcache.c linux/fs/intermezzo/dcache.c
--- linux.orig/fs/intermezzo/dcache.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/dcache.c	Mon Jan  7 14:09:06 2002
@@ -15,7 +15,6 @@
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/locks.h>
-#include <linux/slab.h>
 #include <asm/segment.h>
 #include <asm/uaccess.h>
 #include <linux/string.h>
@@ -23,7 +22,7 @@
 #include <linux/intermezzo_fs.h>
 
 static int presto_dentry_revalidate(struct dentry *de, int );
-static kmem_cache_t * presto_dentry_slab;
+
 
 /* called when a cache lookup succeeds */
 static int presto_dentry_revalidate(struct dentry *de, int flag)
@@ -49,91 +48,15 @@
 	}
 }
 
-static void presto_d_release(struct dentry *dentry)
+static void presto_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
-        if (!presto_d2d(dentry)) {
-                /* This should really only happen in the case of a dentry
-                 * with no inode. */
-                return;
-        }
-
-        presto_d2d(dentry)->dd_count--;
-
-        if (! presto_d2d(dentry)->dd_count) {
-                kmem_cache_free(presto_dentry_slab, presto_d2d(dentry));
-		dentry->d_fsdata = NULL;
-        }
+	dentry->d_time = 0;
+	iput(inode);
 }
 
 struct dentry_operations presto_dentry_ops = 
 {
 	d_revalidate: presto_dentry_revalidate,
-        d_release: presto_d_release
+        d_iput: presto_dentry_iput
 };
 
-
-// XXX THIS DEPENDS ON THE KERNEL LOCK!
-
-void presto_set_dd(struct dentry * dentry)
-{
-        ENTRY;
-        if (dentry == NULL)
-                BUG();
-
-        if (dentry->d_fsdata) {
-                printk("VERY BAD: dentry: %p\n", dentry);
-                if (dentry->d_inode)
-                        printk("    inode: %ld\n", dentry->d_inode->i_ino);
-                EXIT;
-                return;
-        }
-
-        if (dentry->d_inode == NULL) {
-                dentry->d_fsdata = kmem_cache_alloc(presto_dentry_slab,
-                                                    SLAB_KERNEL);
-                memset(dentry->d_fsdata, 0, sizeof(struct presto_dentry_data));
-                presto_d2d(dentry)->dd_count = 1;
-                EXIT;
-                return;
-        }
-
-        /* If there's already a dentry for this inode, share the data */
-        if (dentry->d_alias.next != &dentry->d_inode->i_dentry ||
-            dentry->d_alias.prev != &dentry->d_inode->i_dentry) {
-                struct dentry *de;
-
-                if (dentry->d_alias.next != &dentry->d_inode->i_dentry)
-                        de = list_entry(dentry->d_alias.next, struct dentry,
-                                        d_alias);
-                else
-                        de = list_entry(dentry->d_alias.prev, struct dentry,
-                                        d_alias);
-
-                dentry->d_fsdata = de->d_fsdata;
-                presto_d2d(dentry)->dd_count++;
-                EXIT;
-                return;
-        }
-
-        dentry->d_fsdata = kmem_cache_alloc(presto_dentry_slab, SLAB_KERNEL);
-        memset(dentry->d_fsdata, 0, sizeof(struct presto_dentry_data));
-        presto_d2d(dentry)->dd_count = 1;
-        EXIT;
-        return; 
-}
-
-void presto_init_ddata_cache(void)
-{
-        ENTRY;
-        presto_dentry_slab =
-                kmem_cache_create("presto_cache",
-                                  sizeof(struct presto_dentry_data), 0,
-                                  SLAB_HWCACHE_ALIGN, NULL,
-                                  NULL);
-        EXIT;
-}
-
-void presto_cleanup_ddata_cache(void)
-{
-        kmem_cache_destroy(presto_dentry_slab);
-}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/dir.c linux/fs/intermezzo/dir.c
--- linux.orig/fs/intermezzo/dir.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/dir.c	Mon Jan  7 14:09:06 2002
@@ -163,20 +163,13 @@
                 return ERR_PTR(-EPERM);
         }
         inode = iget(dir->i_sb, ino);
-        if (!inode || is_bad_inode(inode)) {
+        if (!inode || !inode->i_nlink || is_bad_inode(inode)) {
                 CDEBUG(D_PIOCTL, "fatal: invalid inode %ld (%s).\n",
                        ino, inode ? inode->i_nlink ? "bad inode" :
                        "no links" : "NULL");
                 error = -ENOENT;
                 EXIT;
                 goto cleanup_iput;
-        } else if (inode->i_nlink == 0) {
-                /* This is quite evil, but we have little choice.  If we were
-                 * to iput() again with i_nlink == 0, delete_inode would get
-                 * called again, which ext3 really Does Not Like. */
-                atomic_dec(&inode->i_count);
-                EXIT;
-                return ERR_PTR(-ENOENT);
         }
 
         /* We need to make sure we have the right inode (by checking the
@@ -216,7 +209,6 @@
         unsigned int generation;
 
         ENTRY;
-        CDEBUG(D_CACHE, "calling presto_prep on dentry %p\n", dentry);
         error = presto_prep(dentry->d_parent, &cache, &fset);
         if ( error  ) {
                 EXIT;
@@ -245,7 +237,7 @@
                 if (iops && iops->lookup) 
                         de = iops->lookup(dir, dentry);
                 else {
-		        printk("filesystem has no lookup\n");
+                        printk("filesystem has no lookup\n");
                         EXIT;
                         goto exit;
                 }
@@ -261,8 +253,6 @@
                 goto exit;
         }
 
-        presto_set_dd(dentry);
-
         /* some file systems set the methods in lookup, not in
            read_inode, as a result we should set the methods here 
            as well as in read_inode 
@@ -704,7 +694,6 @@
 		double_down(&old_dir->i_zombie, &new_dir->i_zombie);
 }
 
-
 // XXX this can be optimized: renamtes across filesets only require 
 //     multiple KML records, but can locally be executed normally. 
 int presto_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -724,6 +713,7 @@
                 EXIT;
                 return error;
         }
+
         error = presto_prep(new_parent, &new_cache, &new_fset);
         if ( error ) {
                 EXIT;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/inode.c linux/fs/intermezzo/inode.c
--- linux.orig/fs/intermezzo/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/inode.c	Mon Jan  7 14:09:06 2002
@@ -47,6 +47,8 @@
 void presto_set_ops(struct inode *inode, struct  filter_fs *filter)
 {
 	ENTRY; 
+        if (!inode || is_bad_inode(inode))
+                return;
 	if (inode->i_gid == presto_excluded_gid ) { 
 		EXIT;
                 CDEBUG(D_INODE, "excluded methods for %ld at %p, %p\n",
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/journal.c linux/fs/intermezzo/journal.c
--- linux.orig/fs/intermezzo/journal.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/journal.c	Mon Jan  7 14:09:06 2002
@@ -301,9 +301,8 @@
 	/* XXX needs to be done after reservation, 
 	   disable ths until version 1.2 */
         if ( dentry ) { 
-                s.prevrec = cpu_to_le32(rec->offset - 
-                                        presto_d2d(dentry)->dd_kml_offset);
-                presto_d2d(dentry)->dd_kml_offset = rec->offset;
+                s.prevrec = cpu_to_le32(rec->offset - dentry->d_time);
+                dentry->d_time = (unsigned long) rec->offset;
         } else { 
                 s.prevrec = -1;
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/presto.c linux/fs/intermezzo/presto.c
--- linux.orig/fs/intermezzo/presto.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/presto.c	Thu Jan 17 17:28:09 2002
@@ -50,18 +50,16 @@
         return err;
 }
 
-inline struct presto_dentry_data *presto_d2d(struct dentry *dentry)
+
+static inline int presto_dentry_is_fsetroot(struct dentry *dentry)
 {
-        return (struct presto_dentry_data *)dentry->d_fsdata;
+        return ((long) dentry->d_fsdata) & PRESTO_FSETROOT;
 }
 
 static inline struct presto_file_set *presto_dentry2fset(struct dentry *dentry)
 {
-        if (dentry->d_fsdata == NULL) {
-                printk("fucked dentry: %p\n", dentry);
-                BUG();
-        }
-        return presto_d2d(dentry)->dd_fset;
+        return (struct presto_file_set *)
+                (((long) dentry->d_fsdata) - PRESTO_FSETROOT);
 }
 
 /* find the presto minor device for this inode */
@@ -114,7 +112,7 @@
         ENTRY;
         fsde = de;
         for ( ; ; ) {
-                if ( presto_dentry2fset(fsde) ) {
+                if ( presto_dentry_is_fsetroot(fsde) ) {
                         EXIT;
                         return presto_dentry2fset(fsde);
                 }
@@ -185,21 +183,36 @@
                 return 1;
         }
 
+        /* if it is a fsetroot, it's stored in the fset_flags */
+        if ( fset && presto_dentry_is_fsetroot(dentry) ) {
+                EXIT;
+                return fset->fset_data & flag;
+        }
+
         EXIT;
-        return (presto_d2d(dentry)->dd_flags & flag);
+        return ((int)(long)dentry->d_fsdata & flag);
 }
 
 /* set a bit in the dentry flags */
 void presto_set(struct dentry *dentry, int flag)
 {
 
-        ENTRY;
         if ( dentry->d_inode ) {
                 CDEBUG(D_INODE, "SET ino %ld, flag %x\n",
                        dentry->d_inode->i_ino, flag);
         }
-        presto_d2d(dentry)->dd_flags |= flag;
-        EXIT;
+
+        if ( presto_dentry_is_fsetroot(dentry)) {
+                struct presto_file_set *fset = presto_dentry2fset(dentry);
+                if (fset) {
+                        fset->fset_data |= flag;
+                        CDEBUG(D_INODE, "Setting fset->fset_data: now %x\n",
+                               fset->fset_data);
+                }
+        } else {
+                CDEBUG(D_INODE, "Setting dentry->d_fsdata\n");
+                ((int)(long)dentry->d_fsdata) |= flag;
+        }
 }
 
 /* given a path: complete the closes on the fset */
@@ -451,27 +464,39 @@
         struct dentry *dentry;
         int error;
 
+        CDEBUG(D_INODE, "name: %s, and flag %x, or flag %x\n",
+               name, and_flag, or_flag);
+
         error = presto_walk(name, &nd);
         if (error)
                 return error;
         dentry = nd.dentry;
-
-        CDEBUG(D_INODE, "name: %s, and flag %x, or flag %x, dd_flags %x\n",
-               name, and_flag, or_flag, presto_d2d(dentry)->dd_flags);
-
+        CDEBUG(D_INODE, "dentry at %p, d_fsdata %p\n", dentry, dentry->d_fsdata);
 
         error = -ENXIO;
         if ( !presto_ispresto(dentry->d_inode) )
                 goto out;
 
         error = 0;
+        if ( presto_dentry_is_fsetroot(dentry) ) {
+                struct presto_file_set *fset = presto_dentry2fset(dentry);
+                CDEBUG(D_INODE, "Setting fset fset_data: fset %p\n", fset);
+                if ( fset ) {
+                        fset->fset_data &= and_flag;
+                        fset->fset_data |= or_flag;
+                        if (res) {
+                                *res = fset->fset_data;
+                        }
+                }
+                CDEBUG(D_INODE, "fset %p, flags %x data %x\n", 
+                       fset, fset->fset_flags, fset->fset_data);
+        } else {
+                ((int)(long)dentry->d_fsdata) &= and_flag;
+                ((int)(long)dentry->d_fsdata) |= or_flag;
+                if (res) 
+                        *res = (int)(long)dentry->d_fsdata;
+        }
 
-        presto_d2d(dentry)->dd_flags  &= and_flag;
-        presto_d2d(dentry)->dd_flags  |= or_flag;
-        if (res) 
-                *res = presto_d2d(dentry)->dd_flags;
-
-        // XXX this check makes no sense as d_count can change anytime.
         /* indicate if we were the only users while changing the flag */
         if ( atomic_read(&dentry->d_count) > 1 )
                 error = -EBUSY;
@@ -810,7 +835,6 @@
 
         error = -EEXIST;
         CDEBUG(D_INODE, "\n");
-
         fset2 = presto_fset(dentry);
         if (fset2 && (fset2->fset_mtpt == dentry) ) { 
                 printk(KERN_ERR "Fsetroot already set (path %s)\n", path);
@@ -825,7 +849,7 @@
         fset->fset_flags = flags;
 	fset->fset_file_maxio = FSET_DEFAULT_MAX_FILEIO; 
 
-        presto_d2d(dentry)->dd_fset = fset;
+        dentry->d_fsdata = (void *) ( ((long)fset) + PRESTO_FSETROOT );
         list_add(&fset->fset_list, &cache->cache_fset_list);
 
         error = presto_init_kml_file(fset);
@@ -869,15 +893,15 @@
                 cache->cache_flags |= CACHE_FSETROOT_SET;
         }
 
-        CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p, fset %s, cache %p, presto_d2d(dentry)->dd_fset %p\n",
-               fset, dentry, fset->fset_mtpt, fset->fset_name, cache, presto_d2d(dentry)->dd_fset);
+        CDEBUG(D_PIOCTL, "-------> fset at %p, dentry at %p, mtpt %p, fset %s, cache %p, d_fsdata %p\n",
+               fset, dentry, fset->fset_mtpt, fset->fset_name, cache, dentry->d_fsdata);
 
         EXIT;
         return 0;
 
  out_list_del:
         list_del(&fset->fset_list);
-        presto_d2d(dentry)->dd_fset = NULL;
+        dentry->d_fsdata = 0;
  out_dput:
         path_release(&fset->fset_nd); 
  out_free:
@@ -907,7 +931,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto kml_out;
         }
@@ -925,44 +949,12 @@
         return error;
 }
 
-static void presto_cleanup_fset(struct presto_file_set *fset)
-{
-	int error;
-	struct presto_cache *cache;
-
-	ENTRY;
-#ifdef  CONFIG_KREINT
-        error = kml_cleanup (fset);
-        if ( error ) {
-                printk("InterMezzo: Closing kml for fset %s: %d\n",
-                       fset->fset_name, error);
-        }
-#endif
-
-        error = presto_close_journal_file(fset);
-        if ( error ) {
-                printk("InterMezzo: Closing journal for fset %s: %d\n",
-                       fset->fset_name, error);
-        }
-        cache = fset->fset_cache;
-        cache->cache_flags &= ~CACHE_FSETROOT_SET;
-
-        list_del(&fset->fset_list);
-
-	presto_d2d(fset->fset_mtpt)->dd_fset = NULL;
-        path_release(&fset->fset_nd);
-
-        fset->fset_mtpt = NULL;
-        PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
-        PRESTO_FREE(fset, sizeof(*fset));
-        EXIT;
-}
-
 int presto_clear_fsetroot(char *path)
 {
         struct nameidata nd;
         struct presto_file_set *fset;
         struct dentry *dentry;
+        struct presto_cache *cache;
         int error;
 
         ENTRY;
@@ -980,7 +972,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto put_out;
         }
@@ -991,7 +983,28 @@
                 goto put_out;
         }
 
-	presto_cleanup_fset(fset);
+#ifdef  CONFIG_KREINT
+        error = kml_cleanup (fset);
+        if ( error ) {
+                printk("InterMezzo: Closing kml for fset %s: %d\n",
+                       fset->fset_name, error);
+        }
+#endif
+
+        error = presto_close_journal_file(fset);
+        if ( error ) {
+                printk("InterMezzo: Closing journal for fset %s: %d\n",
+                       fset->fset_name, error);
+        }
+        cache = fset->fset_cache;
+        cache->cache_flags &= ~CACHE_FSETROOT_SET;
+
+        list_del(&fset->fset_list);
+        dentry->d_fsdata = 0;
+        path_release(&fset->fset_nd);
+        fset->fset_mtpt = NULL;
+        PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) + 1);
+        PRESTO_FREE(fset, sizeof(*fset));
         EXIT;
 
 put_out:
@@ -1024,7 +1037,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto put_out;
         }
@@ -1035,18 +1048,29 @@
                 goto put_out;
         }
 
-	error = 0;
+        cache = fset->fset_cache;
         cache = fset->fset_cache;
         cache->cache_flags &= ~CACHE_FSETROOT_SET;
 
         tmp = &cache->cache_fset_list;
         tmpnext = tmp->next;
         while ( tmpnext != &cache->cache_fset_list) {
-		tmp = tmpnext;
-                tmpnext = tmp->next;
+		tmp=tmpnext;
+                tmpnext=tmp->next;
                 fset = list_entry(tmp, struct presto_file_set, fset_list);
 
-		presto_cleanup_fset(fset);
+                
+                error = presto_close_journal_file(fset);
+                if ( error ) {
+                        printk("InterMezzo: Closing journal for fset %s: %d\n",
+                               fset->fset_name, error);
+                }
+                list_del(&fset->fset_list);
+                fset->fset_mtpt->d_fsdata = 0;
+                path_release(&fset->fset_nd);
+                fset->fset_mtpt = NULL;
+                PRESTO_FREE(fset->fset_name, strlen(fset->fset_name) +1);
+                PRESTO_FREE(fset, sizeof(*fset));
         }
 
         EXIT;
@@ -1079,7 +1103,7 @@
         }
 
         error = -EINVAL;
-        if ( ! presto_dentry2fset(dentry)) {
+        if ( ! presto_dentry_is_fsetroot(dentry)) {
                 EXIT;
                 goto kml_out;
         }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/psdev.c linux/fs/intermezzo/psdev.c
--- linux.orig/fs/intermezzo/psdev.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/psdev.c	Mon Jan  7 14:09:06 2002
@@ -1465,8 +1465,8 @@
                                 break;
                         /* signal is present: after timeout always return
                            really smart idea, probably useless ... */
-                        if ( jiffies > req->rq_posttime +
-                             upc_comms[minor].uc_timeout * HZ )
+                        if ( time_after(jiffies, req->rq_posttime +
+                             upc_comms[minor].uc_timeout * HZ) )
                                 break;
                 }
                 schedule();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/super.c linux/fs/intermezzo/super.c
--- linux.orig/fs/intermezzo/super.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/super.c	Mon Jan  7 14:09:06 2002
@@ -319,6 +319,7 @@
                 goto out_err;
         }
 
+	
 	cache->cache_sb = mysb;
         ops = filter_get_filter_fs(cache_type);
 
@@ -347,7 +348,6 @@
                                         &presto_dentry_ops);
                 presto_sb->s_root->d_op = filter_c2udops(cache->cache_filter);
                 cache->cache_mtde = mysb->s_root;
-                presto_set_dd(mysb->s_root);
         }
 
         CDEBUG(D_MALLOC, "after mounting: kmem %ld, vmem %ld\n",
@@ -481,7 +481,6 @@
         }
 
         presto_init_cache_hash();
-        presto_init_ddata_cache();
 
         status = register_filesystem(&presto_fs_type);
         if (status) {
@@ -513,7 +512,7 @@
 
         presto_psdev_cleanup();
         cleanup_intermezzo_sysctl();
-        presto_cleanup_ddata_cache();
+
 
 #ifdef PRESTO_DEVEL
         unregister_chrdev(PRESTO_PSDEV_MAJOR, "intermezzo_psdev_devel");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/intermezzo/vfs.c linux/fs/intermezzo/vfs.c
--- linux.orig/fs/intermezzo/vfs.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/intermezzo/vfs.c	Mon Jan  7 14:09:06 2002
@@ -197,7 +197,7 @@
 
         error = -EPERM;
         iops = filter_c2cdiops(fset->fset_cache->cache_filter); 
-        if (!iops) { 
+        if (!iops) {
                 EXIT;
                 return error;
         }
@@ -449,8 +449,17 @@
             dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
                 /* was this already done? */
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                if ( !filter_c2cfiops(cache->cache_filter) )
+                        filter_setup_file_ops(cache->cache_filter, 
+                                              dentry->d_inode,
+                                              &presto_file_iops,
+                                              &presto_file_fops);
+
+                /* make the new inode ours */
+                dentry->d_inode->i_op = 
+                        filter_c2ufiops(cache->cache_filter);
+                dentry->d_inode->i_fop = 
+                        filter_c2uffops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, 
                                         dentry->d_op, 
                                         &presto_dentry_ops);
@@ -480,8 +489,8 @@
         presto_getversion(&new_file_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_create(&rec, fset, dentry, &tgt_dir_ver,
-                                              &new_file_ver, 
-					      dentry->d_inode->i_mode);
+                                              &new_file_ver,
+                                              dentry->d_inode->i_mode);
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_CREATE | 0x20);
         if ( presto_do_expect(info, dentry->d_inode) )
@@ -936,8 +945,15 @@
             dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
                 
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
+                /* was this already done? */
+                if ( !filter_c2csiops(cache->cache_filter) )
+                        filter_setup_symlink_ops(cache->cache_filter, 
+                                                 dentry->d_inode,
+                                                 &presto_sym_iops,
+                                                 NULL);
 
+                /* make the new inode ours */
+                dentry->d_inode->i_op = filter_c2usiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
                                         &presto_dentry_ops);
                 dentry->d_op = filter_c2udops(cache->cache_filter);
@@ -1098,9 +1114,8 @@
         if ( dentry->d_inode && !error && 
              dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
-
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                /* make it ours */
+                dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, 
                                         dentry->d_op, 
                                         &presto_dentry_ops);
@@ -1128,8 +1143,8 @@
         presto_getversion(&new_dir_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_mkdir(&rec, fset, dentry, &tgt_dir_ver,
-                                             &new_dir_ver, 
-					     dentry->d_inode->i_mode);
+                                             &new_dir_ver,
+                                             dentry->d_inode->i_mode);
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKDIR | 0x20);
         if ( presto_do_expect(info, dentry->d_inode) )
@@ -1398,12 +1413,15 @@
         }
 
         error = iops->mknod(dir->d_inode, dentry, mode, dev);
+        if (error) {
+                EXIT;
+                goto exit_commit;
+        }
         if ( dentry->d_inode &&
              dentry->d_inode->i_gid != presto_excluded_gid) {
                 struct presto_cache *cache = fset->fset_cache;
-
-                presto_set_ops(dentry->d_inode, cache->cache_filter);
-
+                /* make it ours */
+                dentry->d_inode->i_op = filter_c2udiops(cache->cache_filter);
                 filter_setup_dentry_ops(cache->cache_filter, dentry->d_op, 
                                         &presto_dentry_ops);
                 dentry->d_op = filter_c2udops(cache->cache_filter);
@@ -1429,8 +1447,8 @@
         presto_getversion(&new_node_ver, dentry->d_inode);
         if ( presto_do_kml(info, dentry->d_inode) )
                 error = presto_journal_mknod(&rec, fset, dentry, &tgt_dir_ver,
-                                             &new_node_ver, 
-					     dentry->d_inode->i_mode,
+                                             &new_node_ver,
+                                             dentry->d_inode->i_mode,
                                              MAJOR(dev), MINOR(dev) );
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x20);
@@ -1439,6 +1457,7 @@
 
         presto_debug_fail_blkdev(fset, PRESTO_OP_MKNOD | 0x30);
         EXIT;
+ exit_commit:
         presto_trans_commit(fset, handle);
  exit_lock2:
         unlock_kernel();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/isofs/dir.c linux/fs/isofs/dir.c
--- linux.orig/fs/isofs/dir.c	Fri Feb  9 19:29:44 2001
+++ linux/fs/isofs/dir.c	Wed Jan 23 20:15:01 2002
@@ -123,7 +123,7 @@
 		int de_len;
 
 		if (!bh) {
-			bh = isofs_bread(inode, bufsize, block);
+			bh = isofs_bread(inode, block);
 			if (!bh)
 				return 0;
 		}
@@ -158,7 +158,7 @@
 			brelse(bh);
 			bh = NULL;
 			if (offset) {
-				bh = isofs_bread(inode, bufsize, block);
+				bh = isofs_bread(inode, block);
 				if (!bh)
 					return 0;
 				memcpy((void *) tmpde + slop, bh->b_data, offset);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/isofs/inode.c linux/fs/isofs/inode.c
--- linux.orig/fs/isofs/inode.c	Thu Oct 25 20:53:53 2001
+++ linux/fs/isofs/inode.c	Wed Jan 23 20:15:01 2002
@@ -527,6 +527,7 @@
 	}
 
 	set_blocksize(dev, opt.blocksize);
+	s->s_blocksize = opt.blocksize;
 
 	s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
 
@@ -540,8 +541,8 @@
 	    struct iso_volume_descriptor  * vdp;
 
 	    block = iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits);
-	    if (!(bh = bread(dev, block, opt.blocksize)))
-		goto out_no_read;		
+	    if (!(bh = sb_bread(s, block)))
+		goto out_no_read;
 
 	    vdp = (struct iso_volume_descriptor *)bh->b_data;
 	    hdp = (struct hs_volume_descriptor *)bh->b_data;
@@ -896,7 +897,6 @@
 	unsigned int firstext;
 	unsigned long nextino;
 	int section, rv;
-	unsigned int blocksize = inode->i_sb->s_blocksize;
 
 	lock_kernel();
 
@@ -957,7 +957,7 @@
 			(*bh_result)->b_blocknr  = firstext + b_off - offset;
 			(*bh_result)->b_state   |= (1UL << BH_Mapped);
 		} else {
-			*bh_result = getblk(inode->i_dev, firstext+b_off-offset, blocksize);
+			*bh_result = sb_getblk(inode->i_sb, firstext+b_off-offset);
 			if ( !*bh_result )
 				goto abort;
 		}
@@ -1000,12 +1000,12 @@
 	return 0;
 }
 
-struct buffer_head *isofs_bread(struct inode *inode, unsigned int bufsize, unsigned int block)
+struct buffer_head *isofs_bread(struct inode *inode, unsigned int block)
 {
 	unsigned int blknr = isofs_bmap(inode, block);
 	if (!blknr)
 		return NULL;
-	return bread(inode->i_dev, blknr, bufsize);
+	return sb_bread(inode->i_sb, blknr);
 }
 
 static int isofs_readpage(struct file *file, struct page *page)
@@ -1060,7 +1060,7 @@
 		unsigned int de_len;
 
 		if (!bh) {
-			bh = bread(inode->i_dev, block, bufsize);
+			bh = sb_bread(inode->i_sb, block);
 			if (!bh)
 				goto out_noread;
 		}
@@ -1092,7 +1092,7 @@
 			brelse(bh);
 			bh = NULL;
 			if (offset) {
-				bh = bread(inode->i_dev, block, bufsize);
+				bh = sb_bread(inode->i_sb, block);
 				if (!bh)
 					goto out_noread;
 				memcpy((void *) tmpde + slop, bh->b_data, offset);
@@ -1150,7 +1150,7 @@
 	unsigned long offset;
 	int volume_seq_no, i;
 
-	bh = bread(inode->i_dev, block, bufsize);
+	bh = sb_bread(inode->i_sb, block);
 	if (!bh)
 		goto out_badread;
 
@@ -1168,7 +1168,7 @@
 		}
 		memcpy(tmpde, bh->b_data + offset, frag1);
 		brelse(bh);
-		bh = bread(inode->i_dev, ++block, bufsize);
+		bh = sb_bread(inode->i_sb, ++block);
 		if (!bh)
 			goto out_badread;
 		memcpy((char *)tmpde+frag1, bh->b_data, de_len - frag1);
@@ -1345,7 +1345,7 @@
 #ifdef LEAK_CHECK
 #undef malloc
 #undef free_s
-#undef bread
+#undef sb_bread
 #undef brelse
 
 void * leak_check_malloc(unsigned int size){
@@ -1360,9 +1360,9 @@
   return kfree(obj);
 }
 
-struct buffer_head * leak_check_bread(int dev, int block, int size){
+struct buffer_head * leak_check_bread(struct super_block *sb, int block){
   check_bread++;
-  return bread(dev, block, size);
+  return sb_bread(sb, block);
 }
 
 void leak_check_brelse(struct buffer_head * bh){
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/isofs/namei.c linux/fs/isofs/namei.c
--- linux.orig/fs/isofs/namei.c	Fri Feb  9 19:29:44 2001
+++ linux/fs/isofs/namei.c	Wed Jan 23 20:15:01 2002
@@ -78,7 +78,7 @@
 		char *dpnt;
 
 		if (!bh) {
-			bh = isofs_bread(dir, bufsize, block);
+			bh = isofs_bread(dir, block);
 			if (!bh)
 				return 0;
 		}
@@ -108,7 +108,7 @@
 			brelse(bh);
 			bh = NULL;
 			if (offset) {
-				bh = isofs_bread(dir, bufsize, block);
+				bh = isofs_bread(dir, block);
 				if (!bh)
 					return 0;
 				memcpy((void *) tmpde + slop, bh->b_data, offset);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/isofs/rock.c linux/fs/isofs/rock.c
--- linux.orig/fs/isofs/rock.c	Thu Oct 25 20:53:53 2001
+++ linux/fs/isofs/rock.c	Wed Jan 23 20:15:01 2002
@@ -69,7 +69,7 @@
     block = cont_extent; \
     offset = cont_offset; \
     offset1 = 0; \
-    pbh = bread(DEV->i_dev, block, ISOFS_BUFFER_SIZE(DEV)); \
+    pbh = sb_bread(DEV->i_sb, block); \
     if(pbh){       \
       memcpy(buffer + offset1, pbh->b_data + offset, cont_size - offset1); \
       brelse(pbh); \
@@ -511,7 +511,7 @@
 
 	block = inode->i_ino >> bufbits;
 	lock_kernel();
-	bh = bread(inode->i_dev, block, bufsize);
+	bh = sb_bread(inode->i_sb, block);
 	if (!bh)
 		goto out_noread;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/checkpoint.c linux/fs/jbd/checkpoint.c
--- linux.orig/fs/jbd/checkpoint.c	Fri Nov  9 22:25:04 2001
+++ linux/fs/jbd/checkpoint.c	Thu Jan 17 21:23:50 2002
@@ -448,11 +448,8 @@
 			struct journal_head *last_jh = jh->b_cpprev;
 			struct journal_head *next_jh = jh;
 			do {
-				struct buffer_head *bh;
-
 				jh = next_jh;
 				next_jh = jh->b_cpnext;
-				bh = jh2bh(jh);
 				ret += __try_to_free_cp_buf(jh);
 			} while (jh != last_jh);
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/commit.c linux/fs/jbd/commit.c
--- linux.orig/fs/jbd/commit.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/jbd/commit.c	Thu Jan 17 21:23:50 2002
@@ -26,7 +26,7 @@
 /*
  * Default IO end handler for temporary BJ_IO buffer_heads.
  */
-static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
 {
 	BUFFER_TRACE(bh, "");
 	mark_buffer_uptodate(bh, uptodate);
@@ -410,6 +410,7 @@
 		flags = journal_write_metadata_buffer(commit_transaction,
 						      jh, &new_jh, blocknr);
 		set_bit(BH_JWrite, &jh2bh(new_jh)->b_state);
+		set_bit(BH_Lock, &jh2bh(new_jh)->b_state);
 		wbuf[bufs++] = jh2bh(new_jh);
 
 		/* Record the new block's tag in the current descriptor
@@ -453,7 +454,6 @@
 			unlock_journal(journal);
 			for (i=0; i<bufs; i++) {
 				struct buffer_head *bh = wbuf[i];
-				set_bit(BH_Lock, &bh->b_state);
 				clear_bit(BH_Dirty, &bh->b_state);
 				bh->b_end_io = journal_end_buffer_io_sync;
 				submit_bh(WRITE, bh);
@@ -561,8 +561,7 @@
 		journal_unfile_buffer(jh);
 		jh->b_transaction = NULL;
 		journal_unlock_journal_head(jh);
-		__brelse(bh);		/* One for getblk */
-		/* AKPM: bforget here */
+		put_bh(bh);			/* One for getblk */
 	}
 
 	jbd_debug(3, "JBD: commit phase 6\n");
@@ -594,9 +593,11 @@
 	JBUFFER_TRACE(descriptor, "write commit block");
 	{
 		struct buffer_head *bh = jh2bh(descriptor);
-		ll_rw_block(WRITE, 1, &bh);
+		clear_bit(BH_Dirty, &bh->b_state);
+		bh->b_end_io = journal_end_buffer_io_sync;
+		submit_bh(WRITE, bh);
 		wait_on_buffer(bh);
-		__brelse(bh);		/* One for getblk() */
+		put_bh(bh);		/* One for getblk() */
 		journal_unlock_journal_head(descriptor);
 	}
 	lock_journal(journal);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/journal.c linux/fs/jbd/journal.c
--- linux.orig/fs/jbd/journal.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/jbd/journal.c	Thu Jan 17 21:23:50 2002
@@ -654,6 +654,8 @@
  * We play buffer_head aliasing tricks to write data/metadata blocks to
  * the journal without copying their contents, but for journal
  * descriptor blocks we do need to generate bona fide buffers.
+ *
+ * We return a jh whose bh is locked and ready to be populated.
  */
 
 struct journal_head * journal_get_descriptor_buffer(journal_t *journal)
@@ -668,7 +670,7 @@
 		return NULL;
 
 	bh = getblk(journal->j_dev, blocknr, journal->j_blocksize);
-	bh->b_state |= (1 << BH_Dirty);
+	lock_buffer(bh);
 	BUFFER_TRACE(bh, "return this buffer");
 	return journal_add_journal_head(bh);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/recovery.c linux/fs/jbd/recovery.c
--- linux.orig/fs/jbd/recovery.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/jbd/recovery.c	Thu Jan 17 21:23:50 2002
@@ -472,6 +472,7 @@
 						goto failed;
 					}
 
+					lock_buffer(nbh);
 					memcpy(nbh->b_data, obh->b_data,
 							journal->j_blocksize);
 					if (flags & JFS_FLAG_ESCAPE) {
@@ -483,6 +484,7 @@
 					mark_buffer_dirty(nbh);
 					BUFFER_TRACE(nbh, "marking uptodate");
 					mark_buffer_uptodate(nbh, 1);
+					unlock_buffer(nbh);
 					++info->nr_replays;
 					/* ll_rw_block(WRITE, 1, &nbh); */
 					brelse(obh);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/revoke.c linux/fs/jbd/revoke.c
--- linux.orig/fs/jbd/revoke.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/jbd/revoke.c	Thu Jan 17 21:23:50 2002
@@ -531,6 +531,7 @@
 
 	if (is_journal_aborted(journal)) {
 		JBUFFER_TRACE(descriptor, "brelse");
+		unlock_buffer(jh2bh(descriptor));
 		__brelse(jh2bh(descriptor));
 		return;
 	}
@@ -541,7 +542,9 @@
 	{
 		struct buffer_head *bh = jh2bh(descriptor);
 		BUFFER_TRACE(bh, "write");
-		ll_rw_block (WRITE, 1, &bh);
+		clear_bit(BH_Dirty, &bh->b_state);
+		bh->b_end_io = journal_end_buffer_io_sync;
+		submit_bh(WRITE, bh);
 	}
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jbd/transaction.c linux/fs/jbd/transaction.c
--- linux.orig/fs/jbd/transaction.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/jbd/transaction.c	Thu Jan 17 21:23:50 2002
@@ -97,6 +97,8 @@
 
 	lock_journal(journal);
 
+repeat_locked:
+
 	if (is_journal_aborted(journal) ||
 	    (journal->j_errno != 0 && !(journal->j_flags & JFS_ACK_ERR))) {
 		unlock_journal(journal);
@@ -110,7 +112,6 @@
 		goto repeat;
 	}
 	
-repeat_locked:
 	if (!journal->j_running_transaction)
 		get_transaction(journal, 0);
 	/* @@@ Error? */
@@ -604,6 +605,7 @@
 		spin_unlock(&journal_datalist_lock);
 		unlock_buffer(jh2bh(jh));
 		lock_journal(journal);
+		goto repeat;
 	}
 
 	J_ASSERT_JH(jh, !buffer_locked(jh2bh(jh)));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/dir.c linux/fs/jffs2/dir.c
--- linux.orig/fs/jffs2/dir.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/jffs2/dir.c	Mon Jan  7 15:26:44 2002
@@ -31,13 +31,14 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: dir.c,v 1.42 2001/05/24 22:24:39 dwmw2 Exp $
+ * $Id: dir.c,v 1.45 2001/12/27 22:43:20 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/mtd/compatmac.h> /* For completion */
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_i.h>
 #include <linux/jffs2_fs_sb.h>
@@ -541,7 +542,7 @@
 
 	f = JFFS2_INODE_INFO(inode);
 
-	ri->dsize = ri->csize = strlen(target);
+	inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
 	ri->totlen = sizeof(*ri) + ri->dsize;
 	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
 
@@ -931,20 +932,16 @@
 	ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
 	
 	if (ret) {
-		/* Try to delete the _new_ link to return a clean failure */
-		int ret2 = jffs2_do_unlink(new_dir_i, new_dentry, 1);
-		if (ret2) {
-			struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
-			down(&f->sem);
-			old_dentry->d_inode->i_nlink = f->inocache->nlink++;
-			up(&f->sem);
+		/* Oh shit. We really ought to make a single node which can do both atomically */
+		struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+		down(&f->sem);
+		old_dentry->d_inode->i_nlink = f->inocache->nlink++;
+		up(&f->sem);
 		       
-			printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (old err %d, new err %d). You now have a hard link\n", ret, ret2);
-			/* Might as well let the VFS know */
-			d_instantiate(new_dentry, old_dentry->d_inode);
-			atomic_inc(&old_dentry->d_inode->i_count);
-		}
-		
+		printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
+		/* Might as well let the VFS know */
+		d_instantiate(new_dentry, old_dentry->d_inode);
+		atomic_inc(&old_dentry->d_inode->i_count);
 	}
 	return ret;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c
--- linux.orig/fs/jffs2/readinode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/jffs2/readinode.c	Mon Jan  7 15:26:44 2002
@@ -31,7 +31,7 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: readinode.c,v 1.56 2001/07/26 20:32:39 dwmw2 Exp $
+ * $Id: readinode.c,v 1.57 2001/12/27 22:49:46 dwmw2 Exp $
  *
  */
 
@@ -408,6 +408,12 @@
 
 	case S_IFLNK:
 		inode->i_op = &jffs2_symlink_inode_operations;
+		/* Hack to work around broken isize in old symlink code.
+		   Remove this when dwmw2 comes to his senses and stops
+		   symlinks from being an entirely gratuitous special
+		   case. */
+		if (!inode->i_size)
+			inode->i_size = latest_node.dsize;
 		break;
 		
 	case S_IFDIR:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/jffs2/write.c linux/fs/jffs2/write.c
--- linux.orig/fs/jffs2/write.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/jffs2/write.c	Mon Jan  7 14:08:22 2002
@@ -31,7 +31,7 @@
  * provisions above, a recipient may use your version of this file
  * under either the RHEPL or the GPL.
  *
- * $Id: write.c,v 1.28 2001/05/01 16:25:25 dwmw2 Exp $
+ * $Id: write.c,v 1.30 2001/12/30 16:01:11 dwmw2 Exp $
  *
  */
 
@@ -207,8 +207,6 @@
 	}
 	raw->flash_offset = flash_ofs;
 	raw->totlen = PAD(ri->totlen);
-	raw->next_in_ino = f->inocache->nodes;
-	f->inocache->nodes = raw;
 	raw->next_phys = NULL;
 
 	fn->ofs = ri->offset;
@@ -222,6 +220,14 @@
 		       sizeof(*ri)+datalen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
 		if (retlen) {
+			/* Doesn't belong to any inode */
+			raw->next_in_ino = NULL;
+
+			/* Don't change raw->size to match retlen. We may have 
+			   written the node header already, and only the data will
+			   seem corrupted, in which case the scan would skip over
+			   any node we write before the original intended end of 
+			   this node */
 			jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
 			jffs2_mark_node_obsolete(c, raw);
 		} else {
@@ -237,6 +243,11 @@
 	}
 	/* Mark the space used */
 	jffs2_add_physical_node_ref(c, raw, retlen, 0);
+
+	/* Link into per-inode list */
+	raw->next_in_ino = f->inocache->nodes;
+	f->inocache->nodes = raw;
+
 	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
 	if (writelen)
 		*writelen = retlen;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/minix/bitmap.c linux/fs/minix/bitmap.c
--- linux.orig/fs/minix/bitmap.c	Fri Sep  7 16:45:51 2001
+++ linux/fs/minix/bitmap.c	Wed Jan 23 20:15:01 2002
@@ -133,7 +133,7 @@
 	ino--;
 	block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
 		 ino / MINIX_INODES_PER_BLOCK;
-	*bh = bread(sb->s_dev, block, BLOCK_SIZE);
+	*bh = sb_bread(sb, block);
 	if (!*bh) {
 		printk("unable to read i-node block\n");
 		return NULL;
@@ -158,7 +158,7 @@
 	ino--;
 	block = 2 + sbi->s_imap_blocks + sbi->s_zmap_blocks +
 		 ino / MINIX2_INODES_PER_BLOCK;
-	*bh = bread(sb->s_dev, block, BLOCK_SIZE);
+	*bh = sb_bread(sb, block);
 	if (!*bh) {
 		printk("unable to read i-node block\n");
 		return NULL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/minix/dir.c linux/fs/minix/dir.c
--- linux.orig/fs/minix/dir.c	Fri Sep  7 16:45:51 2001
+++ linux/fs/minix/dir.c	Tue Jan 22 23:04:46 2002
@@ -36,8 +36,13 @@
 	struct inode *dir = (struct inode *)page->mapping->host;
 	int err = 0;
 	page->mapping->a_ops->commit_write(NULL, page, from, to);
-	if (IS_SYNC(dir))
-		err = waitfor_one_page(page);
+	if (IS_SYNC(dir)) {
+		int err2;
+		err = writeout_one_page(page);
+		err2 = waitfor_one_page(page);
+		if (err == 0)
+			err = err2;
+	}
 	return err;
 }
 
@@ -236,10 +241,10 @@
 
 	lock_page(page);
 	err = mapping->a_ops->prepare_write(NULL, page, from, to);
-	if (err)
-		BUG();
-	de->inode = 0;
-	err = dir_commit_chunk(page, from, to);
+	if (err == 0) {
+		de->inode = 0;
+		err = dir_commit_chunk(page, from, to);
+	}
 	UnlockPage(page);
 	dir_put_page(page);
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
@@ -336,10 +341,10 @@
 
 	lock_page(page);
 	err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
-	if (err)
-		BUG();
-	de->inode = inode->i_ino;
-	err = dir_commit_chunk(page, from, to);
+	if (err == 0) {
+		de->inode = inode->i_ino;
+		err = dir_commit_chunk(page, from, to);
+	}
 	UnlockPage(page);
 	dir_put_page(page);
 	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/minix/inode.c linux/fs/minix/inode.c
--- linux.orig/fs/minix/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/minix/inode.c	Wed Jan 23 20:15:01 2002
@@ -143,15 +143,15 @@
 		goto out_bad_hblock;
 
 	set_blocksize(dev, BLOCK_SIZE);
-	if (!(bh = bread(dev,1,BLOCK_SIZE)))
+	s->s_blocksize = BLOCK_SIZE;
+	s->s_blocksize_bits = BLOCK_SIZE_BITS;
+	if (!(bh = sb_bread(s, 1)))
 		goto out_bad_sb;
 
 	ms = (struct minix_super_block *) bh->b_data;
 	sbi->s_ms = ms;
 	sbi->s_sbh = bh;
 	sbi->s_mount_state = ms->s_state;
-	s->s_blocksize = BLOCK_SIZE;
-	s->s_blocksize_bits = BLOCK_SIZE_BITS;
 	sbi->s_ninodes = ms->s_ninodes;
 	sbi->s_nzones = ms->s_nzones;
 	sbi->s_imap_blocks = ms->s_imap_blocks;
@@ -198,12 +198,12 @@
 
 	block=2;
 	for (i=0 ; i < sbi->s_imap_blocks ; i++) {
-		if (!(sbi->s_imap[i]=bread(dev,block,BLOCK_SIZE)))
+		if (!(sbi->s_imap[i]=sb_bread(s, block)))
 			goto out_no_bitmap;
 		block++;
 	}
 	for (i=0 ; i < sbi->s_zmap_blocks ; i++) {
-		if (!(sbi->s_zmap[i]=bread(dev,block,BLOCK_SIZE)))
+		if (!(sbi->s_zmap[i]=sb_bread(s, block)))
 			goto out_no_bitmap;
 		block++;
 	}
@@ -214,7 +214,7 @@
 	/* set up enough so that it can read an inode */
 	s->s_op = &minix_sops;
 	root_inode = iget(s, MINIX_ROOT_INO);
-	if (!root_inode)
+	if (!root_inode || is_bad_inode(root_inode))
 		goto out_no_root;
 
 	s->s_root = d_alloc_root(root_inode);
@@ -353,8 +353,10 @@
 	int i;
 
 	raw_inode = minix_V1_raw_inode(inode->i_sb, inode->i_ino, &bh);
-	if (!raw_inode)
+	if (!raw_inode) {
+		make_bad_inode(inode);
 		return;
+	}
 	inode->i_mode = raw_inode->i_mode;
 	inode->i_uid = (uid_t)raw_inode->i_uid;
 	inode->i_gid = (gid_t)raw_inode->i_gid;
@@ -378,8 +380,10 @@
 	int i;
 
 	raw_inode = minix_V2_raw_inode(inode->i_sb, inode->i_ino, &bh);
-	if (!raw_inode)
+	if (!raw_inode) {
+		make_bad_inode(inode);
 		return;
+	}
 	inode->i_mode = raw_inode->i_mode;
 	inode->i_uid = (uid_t)raw_inode->i_uid;
 	inode->i_gid = (gid_t)raw_inode->i_gid;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/minix/itree_common.c linux/fs/minix/itree_common.c
--- linux.orig/fs/minix/itree_common.c	Fri Sep  7 16:45:51 2001
+++ linux/fs/minix/itree_common.c	Wed Jan 23 20:15:01 2002
@@ -30,7 +30,7 @@
 					Indirect chain[DEPTH],
 					int *err)
 {
-	kdev_t dev = inode->i_dev;
+	struct super_block *sb = inode->i_sb;
 	Indirect *p = chain;
 	struct buffer_head *bh;
 
@@ -40,7 +40,7 @@
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
-		bh = bread(dev, block_to_cpu(p->key), BLOCK_SIZE);
+		bh = sb_bread(sb, block_to_cpu(p->key));
 		if (!bh)
 			goto failure;
 		/* Reader: pointers */
@@ -79,7 +79,7 @@
 		if (!nr)
 			break;
 		branch[n].key = cpu_to_block(nr);
-		bh = getblk(inode->i_dev, parent, BLOCK_SIZE);
+		bh = sb_getblk(inode->i_sb, parent);
 		lock_buffer(bh);
 		memset(bh->b_data, 0, BLOCK_SIZE);
 		branch[n].bh = bh;
@@ -277,7 +277,7 @@
 			if (!nr)
 				continue;
 			*p = 0;
-			bh = bread (inode->i_dev, nr, BLOCK_SIZE);
+			bh = sb_bread(inode->i_sb, nr);
 			if (!bh)
 				continue;
 			free_branches(inode, (block_t*)bh->b_data,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/namei.c linux/fs/namei.c
--- linux.orig/fs/namei.c	Wed Oct 17 21:46:29 2001
+++ linux/fs/namei.c	Wed Jan 23 02:53:39 2002
@@ -99,16 +99,17 @@
  * kernel data space before using them..
  *
  * POSIX.1 2.4: an empty pathname is invalid (ENOENT).
+ * PATH_MAX includes the nul terminator --RR.
  */
 static inline int do_getname(const char *filename, char *page)
 {
 	int retval;
-	unsigned long len = PATH_MAX + 1;
+	unsigned long len = PATH_MAX;
 
 	if ((unsigned long) filename >= TASK_SIZE) {
 		if (!segment_eq(get_fs(), KERNEL_DS))
 			return -EFAULT;
-	} else if (TASK_SIZE - (unsigned long) filename < PATH_MAX + 1)
+	} else if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
 		len = TASK_SIZE - (unsigned long) filename;
 
 	retval = strncpy_from_user((char *)page, filename, len);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/namespace.c linux/fs/namespace.c
--- linux.orig/fs/namespace.c	Fri Dec 21 17:41:55 2001
+++ linux/fs/namespace.c	Wed Jan 23 21:21:17 2002
@@ -563,6 +563,67 @@
 	return err;
 }
 
+static int do_move_mount(struct nameidata *nd, char *old_name)
+{
+	struct nameidata old_nd, parent_nd;
+	struct vfsmount *p;
+	int err = 0;
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+	if (!old_name || !*old_name)
+		return -EINVAL;
+	if (path_init(old_name, LOOKUP_POSITIVE|LOOKUP_FOLLOW, &old_nd))
+		err = path_walk(old_name, &old_nd);
+	if (err)
+		return err;
+
+	down(&mount_sem);
+	while(d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry))
+		;
+	err = -EINVAL;
+	if (!check_mnt(nd->mnt) || !check_mnt(old_nd.mnt))
+		goto out;
+
+	err = -ENOENT;
+	down(&nd->dentry->d_inode->i_zombie);
+	if (IS_DEADDIR(nd->dentry->d_inode))
+		goto out1;
+
+	spin_lock(&dcache_lock);
+	if (!IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
+		goto out2;
+
+	err = -EINVAL;
+	if (old_nd.dentry != old_nd.mnt->mnt_root)
+		goto out2;
+
+	if (old_nd.mnt == old_nd.mnt->mnt_parent)
+		goto out2;
+
+	if (S_ISDIR(nd->dentry->d_inode->i_mode) !=
+	      S_ISDIR(old_nd.dentry->d_inode->i_mode))
+		goto out2;
+
+	err = -ELOOP;
+	for (p = nd->mnt; p->mnt_parent!=p; p = p->mnt_parent)
+		if (p == old_nd.mnt)
+			goto out2;
+	err = 0;
+
+	detach_mnt(old_nd.mnt, &parent_nd);
+	attach_mnt(old_nd.mnt, nd);
+out2:
+	spin_unlock(&dcache_lock);
+out1:
+	up(&nd->dentry->d_inode->i_zombie);
+out:
+	up(&mount_sem);
+	if (!err)
+		path_release(&parent_nd);
+	path_release(&old_nd);
+	return err;
+}
+
 static int do_add_mount(struct nameidata *nd, char *type, int flags,
 			int mnt_flags, char *name, void *data)
 {
@@ -679,6 +740,8 @@
 				    data_page);
 	else if (flags & MS_BIND)
 		retval = do_loopback(&nd, dev_name, flags & MS_REC);
+	else if (flags & MS_MOVE)
+		retval = do_move_mount(&nd, dev_name);
 	else
 		retval = do_add_mount(&nd, type_page, flags, mnt_flags,
 				      dev_name, data_page);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/file.c linux/fs/nfs/file.c
--- linux.orig/fs/nfs/file.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/nfs/file.c	Mon Jan 14 18:01:23 2002
@@ -161,15 +161,10 @@
 static int nfs_commit_write(struct file *file, struct page *page, unsigned offset, unsigned to)
 {
 	long status;
-	loff_t pos = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + to;
-	struct inode *inode = page->mapping->host;
 
 	lock_kernel();
 	status = nfs_updatepage(file, page, offset, to-offset);
 	unlock_kernel();
-	/* most likely it's already done. CHECKME */
-	if (pos > inode->i_size)
-		inode->i_size = pos;
 	return status;
 }
 
@@ -249,6 +244,7 @@
 {
 	struct inode * inode = filp->f_dentry->d_inode;
 	int	status = 0;
+	int	status2;
 
 	dprintk("NFS: nfs_lock(f=%4x/%ld, t=%x, fl=%x, r=%Ld:%Ld)\n",
 			inode->i_dev, inode->i_ino,
@@ -283,11 +279,15 @@
 	 * Flush all pending writes before doing anything
 	 * with locks..
 	 */
-	filemap_fdatasync(inode->i_mapping);
+	status = filemap_fdatasync(inode->i_mapping);
 	down(&inode->i_sem);
-	status = nfs_wb_all(inode);
+	status2 = nfs_wb_all(inode);
+	if (status2 && !status)
+		status = status2;
 	up(&inode->i_sem);
-	filemap_fdatawait(inode->i_mapping);
+	status2 = filemap_fdatawait(inode->i_mapping);
+	if (status2 && !status)
+		status = status2;
 	if (status < 0)
 		return status;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/inode.c linux/fs/nfs/inode.c
--- linux.orig/fs/nfs/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/nfs/inode.c	Wed Dec 26 17:15:45 2001
@@ -107,17 +107,10 @@
 	inode->i_rdev = 0;
 	/* We can't support UPDATE_ATIME(), since the server will reset it */
 	inode->i_flags |= S_NOATIME;
-	NFS_FILEID(inode) = 0;
-	NFS_FSID(inode) = 0;
-	NFS_FLAGS(inode) = 0;
 	INIT_LIST_HEAD(&inode->u.nfs_i.read);
 	INIT_LIST_HEAD(&inode->u.nfs_i.dirty);
 	INIT_LIST_HEAD(&inode->u.nfs_i.commit);
 	INIT_LIST_HEAD(&inode->u.nfs_i.writeback);
-	inode->u.nfs_i.nread = 0;
-	inode->u.nfs_i.ndirty = 0;
-	inode->u.nfs_i.ncommit = 0;
-	inode->u.nfs_i.npages = 0;
 	NFS_CACHEINV(inode);
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
@@ -655,19 +648,6 @@
 			inode->i_op = &nfs_symlink_inode_operations;
 		else
 			init_special_inode(inode, inode->i_mode, fattr->rdev);
-		/*
-		 * Preset the size and mtime, as there's no need
-		 * to invalidate the caches.
-		 */ 
-		inode->i_size  = nfs_size_to_loff_t(fattr->size);
-		inode->i_mtime = nfs_time_to_secs(fattr->mtime);
-		inode->i_atime = nfs_time_to_secs(fattr->atime);
-		inode->i_ctime = nfs_time_to_secs(fattr->ctime);
-		NFS_CACHE_CTIME(inode) = fattr->ctime;
-		NFS_CACHE_MTIME(inode) = fattr->mtime;
-		NFS_CACHE_ISIZE(inode) = fattr->size;
-		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
-		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 		memcpy(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh));
 	}
 	nfs_refresh_inode(inode, fattr);
@@ -697,6 +677,9 @@
 		return 0;
 	if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0)
 		return 0;
+	/* Force an attribute cache update if inode->i_count == 0 */
+	if (!atomic_read(&inode->i_count))
+		NFS_CACHEINV(inode);
 	return 1;
 }
 
@@ -797,7 +780,9 @@
 	if (!S_ISREG(inode->i_mode))
 		attr->ia_valid &= ~ATTR_SIZE;
 
+	filemap_fdatasync(inode->i_mapping);
 	error = nfs_wb_all(inode);
+	filemap_fdatawait(inode->i_mapping);
 	if (error)
 		goto out;
 
@@ -825,6 +810,8 @@
 		fattr.pre_ctime = NFS_CACHE_CTIME(inode);
 		fattr.valid |= NFS_ATTR_WCC;
 	}
+	/* Force an attribute cache update */
+	NFS_CACHEINV(inode);
 	error = nfs_refresh_inode(inode, &fattr);
 out:
 	return error;
@@ -966,6 +953,34 @@
 }
 
 /*
+ * nfs_fattr_obsolete - Test if attribute data is newer than cached data
+ * @inode: inode
+ * @fattr: attributes to test
+ *
+ * Avoid stuffing the attribute cache with obsolete information.
+ * We always accept updates if the attribute cache timed out, or if
+ * fattr->ctime is newer than our cached value.
+ * If fattr->ctime matches the cached value, we still accept the update
+ * if it increases the file size.
+ */
+static inline
+int nfs_fattr_obsolete(struct inode *inode, struct nfs_fattr *fattr)
+{
+	s64 cdif;
+
+	if (time_after(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
+		goto out_valid;
+	if ((cdif = (s64)fattr->ctime - (s64)NFS_CACHE_CTIME(inode)) > 0)
+		goto out_valid;
+	/* Ugh... */
+	if (cdif == 0 && fattr->size > NFS_CACHE_ISIZE(inode))
+		goto out_valid;
+	return -1;
+ out_valid:
+	return 0;
+}
+
+/*
  * Many nfs protocol calls return the new file attributes after
  * an operation.  Here we update the inode to reflect the state
  * of the server's inode.
@@ -982,6 +997,7 @@
 {
 	__u64		new_size, new_mtime;
 	loff_t		new_isize;
+	time_t		new_atime;
 	int		invalid = 0;
 
 	dfprintk(VFS, "NFS: refresh_inode(%x/%ld ct=%d info=0x%x)\n",
@@ -1007,6 +1023,11 @@
 	new_size = fattr->size;
  	new_isize = nfs_size_to_loff_t(fattr->size);
 
+	new_atime = nfs_time_to_secs(fattr->atime);
+	/* Avoid races */
+	if (nfs_fattr_obsolete(inode, fattr))
+		goto out_nochange;
+
 	/*
 	 * Update the read time so we don't revalidate too often.
 	 */
@@ -1056,7 +1077,7 @@
 	NFS_CACHE_CTIME(inode) = fattr->ctime;
 	inode->i_ctime = nfs_time_to_secs(fattr->ctime);
 
-	inode->i_atime = nfs_time_to_secs(fattr->atime);
+	inode->i_atime = new_atime;
 
 	NFS_CACHE_MTIME(inode) = new_mtime;
 	inode->i_mtime = nfs_time_to_secs(new_mtime);
@@ -1093,7 +1114,10 @@
 	if (invalid)
 		nfs_zap_caches(inode);
 	return 0;
-
+ out_nochange:
+	if (new_atime - inode->i_atime > 0)
+		inode->i_atime = new_atime;
+	return 0;
  out_changed:
 	/*
 	 * Big trouble! The inode has become a different object.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/nfs2xdr.c linux/fs/nfs/nfs2xdr.c
--- linux.orig/fs/nfs/nfs2xdr.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/nfs/nfs2xdr.c	Wed Dec 26 17:15:45 2001
@@ -270,14 +270,12 @@
 
 	count = ntohl(*p++);
 	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-	recvd = req->rq_rlen - hdrlen;
-	if (p != iov[req->rq_rnr-1].iov_base) {
-		/* Unexpected reply header size. Punt.
-		 * XXX: Move iovec contents to align data on page
-		 * boundary and adjust RPC header size guess */
-		printk(KERN_WARNING "NFS: Odd RPC header size in read reply: %d\n", hdrlen);
-		return -errno_NFSERR_IO;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READ header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
+
+	recvd = req->rq_rlen - hdrlen;
 	if (count > recvd) {
 		printk(KERN_WARNING "NFS: server cheating in read reply: "
 			"count %d > recvd %d\n", count, recvd);
@@ -448,27 +446,23 @@
 nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs_readdirres *res)
 {
 	struct iovec		*iov = req->rq_rvec;
+	int			 hdrlen;
 	int			 status, nr;
 	u32			*end, *entry, len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
-	if ((void *) p != ((u8 *) iov->iov_base+iov->iov_len)) {
-		/* Unexpected reply header size. Punt. */
-		printk(KERN_WARNING "NFS: Odd RPC header size in readdirres reply\n");
-		return -errno_NFSERR_IO;
+
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
 	}
 
+
 	/* Get start and end address of XDR data */
 	p   = (u32 *) iov[1].iov_base;
 	end = (u32 *) ((u8 *) p + iov[1].iov_len);
-
-	/* Get start and end of dirent buffer */
-	if (res->buffer != p) {
-		printk(KERN_ERR "NFS: Bad result buffer in readdir\n");
-		return -errno_NFSERR_IO;
-	}
-
 	for (nr = 0; *p++; nr++) {
 		entry = p - 1;
 		if (p + 2 > end)
@@ -598,13 +592,21 @@
 static int
 nfs_xdr_readlinkres(struct rpc_rqst *req, u32 *p, struct nfs_readlinkres *res)
 {
+	struct iovec *iov = req->rq_rvec;
 	u32	*strlen;
 	char	*string;
+	int	hdrlen;
 	int	status;
 	unsigned int len;
 
 	if ((status = ntohl(*p++)))
 		return -nfs_stat_to_errno(status);
+	hdrlen = (u8 *) p - (u8 *) iov->iov_base;
+	if (iov->iov_len > hdrlen) {
+		dprintk("NFS: READLINK header is short. iovec will be shifted.\n");
+		xdr_shift_iovec(iov, req->rq_rnr, iov->iov_len - hdrlen);
+	}
+
 	strlen = (u32*)res->buffer;
 	/* Convert length of symlink */
 	len = ntohl(*strlen);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/read.c linux/fs/nfs/read.c
--- linux.orig/fs/nfs/read.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/nfs/read.c	Wed Dec 26 17:15:45 2001
@@ -397,7 +397,7 @@
 {
 	struct nfs_read_data	*data = (struct nfs_read_data *) task->tk_calldata;
 	struct inode		*inode = data->inode;
-	int			count = data->res.count;
+	unsigned int		count = data->res.count;
 
 	dprintk("NFS: %4d nfs_readpage_result, (status %d)\n",
 		task->tk_pid, task->tk_status);
@@ -411,9 +411,15 @@
 		struct page *page = req->wb_page;
 		nfs_list_remove_request(req);
 
-		if (task->tk_status >= 0 && count >= 0) {
+		if (task->tk_status >= 0) {
+			if (count < PAGE_CACHE_SIZE) {
+				char *p = kmap(page);
+				memset(p + count, 0, PAGE_CACHE_SIZE - count);
+				kunmap(page);
+				count = 0;
+			} else
+				count -= PAGE_CACHE_SIZE;
 			SetPageUptodate(page);
-			count -= PAGE_CACHE_SIZE;
 		} else
 			SetPageError(page);
 		flush_dcache_page(page);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfs/write.c linux/fs/nfs/write.c
--- linux.orig/fs/nfs/write.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/nfs/write.c	Wed Dec 26 17:15:45 2001
@@ -213,6 +213,7 @@
 		    unsigned int offset, unsigned int count)
 {
 	struct nfs_page	*req;
+	loff_t		end;
 	int		status;
 
 	req = nfs_update_request(file, inode, page, offset, count);
@@ -223,6 +224,10 @@
 		req->wb_cred = get_rpccred(NFS_I(inode)->mm_cred);
 	nfs_unlock_request(req);
 	nfs_strategy(inode);
+	end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+	if (inode->i_size < end)
+		inode->i_size = end;
+
  out:
 	return status;
 }
@@ -795,6 +800,7 @@
 	struct dentry	*dentry = file->f_dentry;
 	struct inode	*inode = dentry->d_inode;
 	struct nfs_page	*req;
+	loff_t		end;
 	int		status = 0;
 
 	dprintk("NFS:      nfs_updatepage(%s/%s %d@%Ld)\n",
@@ -826,6 +832,10 @@
 		goto done;
 
 	status = 0;
+	end = ((loff_t)page->index<<PAGE_CACHE_SHIFT) + (loff_t)(offset + count);
+	if (inode->i_size < end)
+		inode->i_size = end;
+
 	/* If we wrote past the end of the page.
 	 * Call the strategy routine so it can send out a bunch
 	 * of requests.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nfsd/nfssvc.c linux/fs/nfsd/nfssvc.c
--- linux.orig/fs/nfsd/nfssvc.c	Wed Oct 17 21:16:34 2001
+++ linux/fs/nfsd/nfssvc.c	Mon Feb  4 16:55:14 2002
@@ -99,7 +99,7 @@
 		if (error < 0)
 			goto failure;
 #endif
-		get_fast_time(&nfssvc_boot);		/* record boot time */
+		do_gettimeofday(&nfssvc_boot);		/* record boot time */
 	} else
 		nfsd_serv->sv_nrthreads++;
 	nrservs -= (nfsd_serv->sv_nrthreads-1);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nls/Config.in linux/fs/nls/Config.in
--- linux.orig/fs/nls/Config.in	Tue Jan 22 17:51:23 2002
+++ linux/fs/nls/Config.in	Mon Jan 14 18:53:53 2002
@@ -43,6 +43,7 @@
   tristate 'Korean charset (CP949, EUC-KR)'        CONFIG_NLS_CODEPAGE_949
   tristate 'Thai charset (CP874, TIS-620)'         CONFIG_NLS_CODEPAGE_874
   tristate 'Hebrew charsets (ISO-8859-8, CP1255)'  CONFIG_NLS_ISO8859_8
+  tristate 'Windows CP1250 (Slavic/Central European Languages)' CONFIG_NLS_CODEPAGE_1250
   tristate 'Windows CP1251 (Bulgarian, Belarusian)' CONFIG_NLS_CODEPAGE_1251
   tristate 'NLS ISO 8859-1  (Latin 1; Western European Languages)' CONFIG_NLS_ISO8859_1
   tristate 'NLS ISO 8859-2  (Latin 2; Slavic/Central European Languages)' CONFIG_NLS_ISO8859_2
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/nls/nls_cp1250.c linux/fs/nls/nls_cp1250.c
--- linux.orig/fs/nls/nls_cp1250.c	Thu Jan  1 00:00:00 1970
+++ linux/fs/nls/nls_cp1250.c	Mon Jan 14 18:54:47 2002
@@ -0,0 +1,365 @@
+/*
+ * linux/fs/nls_cp1250.c
+ *
+ * Charset cp1250 translation tables.
+ * Generated automatically from the Unicode and charset
+ * tables from the Unicode Organization (www.unicode.org).
+ * The Unicode to charset table has only exact mappings.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/nls.h>
+#include <linux/errno.h>
+
+static wchar_t charset2uni[256] = {
+	/* 0x00*/
+	0x0000, 0x0001, 0x0002, 0x0003, 
+	0x0004, 0x0005, 0x0006, 0x0007, 
+	0x0008, 0x0009, 0x000a, 0x000b, 
+	0x000c, 0x000d, 0x000e, 0x000f, 
+	/* 0x10*/
+	0x0010, 0x0011, 0x0012, 0x0013, 
+	0x0014, 0x0015, 0x0016, 0x0017, 
+	0x0018, 0x0019, 0x001a, 0x001b, 
+	0x001c, 0x001d, 0x001e, 0x001f, 
+	/* 0x20*/
+	0x0020, 0x0021, 0x0022, 0x0023, 
+	0x0024, 0x0025, 0x0026, 0x0027, 
+	0x0028, 0x0029, 0x002a, 0x002b, 
+	0x002c, 0x002d, 0x002e, 0x002f, 
+	/* 0x30*/
+	0x0030, 0x0031, 0x0032, 0x0033, 
+	0x0034, 0x0035, 0x0036, 0x0037, 
+	0x0038, 0x0039, 0x003a, 0x003b, 
+	0x003c, 0x003d, 0x003e, 0x003f, 
+	/* 0x40*/
+	0x0040, 0x0041, 0x0042, 0x0043, 
+	0x0044, 0x0045, 0x0046, 0x0047, 
+	0x0048, 0x0049, 0x004a, 0x004b, 
+	0x004c, 0x004d, 0x004e, 0x004f, 
+	/* 0x50*/
+	0x0050, 0x0051, 0x0052, 0x0053, 
+	0x0054, 0x0055, 0x0056, 0x0057, 
+	0x0058, 0x0059, 0x005a, 0x005b, 
+	0x005c, 0x005d, 0x005e, 0x005f, 
+	/* 0x60*/
+	0x0060, 0x0061, 0x0062, 0x0063, 
+	0x0064, 0x0065, 0x0066, 0x0067, 
+	0x0068, 0x0069, 0x006a, 0x006b, 
+	0x006c, 0x006d, 0x006e, 0x006f, 
+	/* 0x70*/
+	0x0070, 0x0071, 0x0072, 0x0073, 
+	0x0074, 0x0075, 0x0076, 0x0077, 
+	0x0078, 0x0079, 0x007a, 0x007b, 
+	0x007c, 0x007d, 0x007e, 0x007f, 
+	/* 0x80*/
+	0x20ac, 0x0000, 0x201a, 0x0000, 
+	0x201e, 0x2026, 0x2020, 0x2021, 
+	0x0000, 0x2030, 0x0160, 0x2039, 
+	0x015a, 0x0164, 0x017d, 0x0179, 
+	/* 0x90*/
+	0x0000, 0x2018, 0x2019, 0x201c, 
+	0x201d, 0x2022, 0x2013, 0x2014, 
+	0x0000, 0x2122, 0x0161, 0x203a, 
+	0x015b, 0x0165, 0x017e, 0x017a, 
+	/* 0xa0*/
+	0x00a0, 0x02c7, 0x02d8, 0x0141, 
+	0x00a4, 0x0104, 0x00a6, 0x00a7, 
+	0x00a8, 0x00a9, 0x015e, 0x00ab, 
+	0x00ac, 0x00ad, 0x00ae, 0x017b, 
+	/* 0xb0*/
+	0x00b0, 0x00b1, 0x02db, 0x0142, 
+	0x00b4, 0x00b5, 0x00b6, 0x00b7, 
+	0x00b8, 0x0105, 0x015f, 0x00bb, 
+	0x013d, 0x02dd, 0x013e, 0x017c, 
+	/* 0xc0*/
+	0x0154, 0x00c1, 0x00c2, 0x0102, 
+	0x00c4, 0x0139, 0x0106, 0x00c7, 
+	0x010c, 0x00c9, 0x0118, 0x00cb, 
+	0x011a, 0x00cd, 0x00ce, 0x010e, 
+	/* 0xd0*/
+	0x0110, 0x0143, 0x0147, 0x00d3, 
+	0x00d4, 0x0150, 0x00d6, 0x00d7, 
+	0x0158, 0x016e, 0x00da, 0x0170, 
+	0x00dc, 0x00dd, 0x0162, 0x00df, 
+	/* 0xe0*/
+	0x0155, 0x00e1, 0x00e2, 0x0103, 
+	0x00e4, 0x013a, 0x0107, 0x00e7, 
+	0x010d, 0x00e9, 0x0119, 0x00eb, 
+	0x011b, 0x00ed, 0x00ee, 0x010f, 
+	/* 0xf0*/
+	0x0111, 0x0144, 0x0148, 0x00f3, 
+	0x00f4, 0x0151, 0x00f6, 0x00f7, 
+	0x0159, 0x016f, 0x00fa, 0x0171, 
+	0x00fc, 0x00fd, 0x0163, 0x02d9, 
+	};
+
+static unsigned char page00[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0xa0, 0x00, 0x00, 0x00, 0xa4, 0x00, 0xa6, 0xa7, /* 0xa0-0xa7 */
+	0xa8, 0xa9, 0x00, 0xab, 0xac, 0xad, 0xae, 0x00, /* 0xa8-0xaf */
+	0xb0, 0xb1, 0x00, 0x00, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+	0xb8, 0x00, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0xc1, 0xc2, 0x00, 0xc4, 0x00, 0x00, 0xc7, /* 0xc0-0xc7 */
+	0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcd, 0xce, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0xd3, 0xd4, 0x00, 0xd6, 0xd7, /* 0xd0-0xd7 */
+	0x00, 0x00, 0xda, 0x00, 0xdc, 0xdd, 0x00, 0xdf, /* 0xd8-0xdf */
+	0x00, 0xe1, 0xe2, 0x00, 0xe4, 0x00, 0x00, 0xe7, /* 0xe0-0xe7 */
+	0x00, 0xe9, 0x00, 0xeb, 0x00, 0xed, 0xee, 0x00, /* 0xe8-0xef */
+	0x00, 0x00, 0x00, 0xf3, 0xf4, 0x00, 0xf6, 0xf7, /* 0xf0-0xf7 */
+	0x00, 0x00, 0xfa, 0x00, 0xfc, 0xfd, 0x00, 0x00, /* 0xf8-0xff */
+	};
+
+static unsigned char page01[256] = {
+	0x00, 0x00, 0xc3, 0xe3, 0xa5, 0xb9, 0xc6, 0xe6, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0xcf, 0xef, /* 0x08-0x0f */
+	0xd0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0xca, 0xea, 0xcc, 0xec, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0xc5, 0xe5, 0x00, 0x00, 0xbc, 0xbe, 0x00, /* 0x38-0x3f */
+	0x00, 0xa3, 0xb3, 0xd1, 0xf1, 0x00, 0x00, 0xd2, /* 0x40-0x47 */
+	0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0xd5, 0xf5, 0x00, 0x00, 0xc0, 0xe0, 0x00, 0x00, /* 0x50-0x57 */
+	0xd8, 0xf8, 0x8c, 0x9c, 0x00, 0x00, 0xaa, 0xba, /* 0x58-0x5f */
+	0x8a, 0x9a, 0xde, 0xfe, 0x8d, 0x9d, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0xf9, /* 0x68-0x6f */
+	0xdb, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x8f, 0x9f, 0xaf, 0xbf, 0x8e, 0x9e, 0x00, /* 0x78-0x7f */
+
+	};
+
+static unsigned char page02[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1, /* 0xc0-0xc7 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+	0xa2, 0xff, 0x00, 0xb2, 0x00, 0xbd, 0x00, 0x00, /* 0xd8-0xdf */
+	};
+
+static unsigned char page20[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x96, 0x97, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x91, 0x92, 0x82, 0x00, 0x93, 0x94, 0x84, 0x00, /* 0x18-0x1f */
+	0x86, 0x87, 0x95, 0x00, 0x00, 0x00, 0x85, 0x00, /* 0x20-0x27 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x28-0x2f */
+	0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x30-0x37 */
+	0x00, 0x8b, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x38-0x3f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40-0x47 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x48-0x4f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50-0x57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x58-0x5f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60-0x67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x68-0x6f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70-0x77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x78-0x7f */
+
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x80-0x87 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+	0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+	};
+
+static unsigned char page21[256] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00-0x07 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x08-0x0f */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10-0x17 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x18-0x1f */
+	0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20-0x27 */
+	};
+
+static unsigned char *page_uni2charset[256] = {
+	page00,	page01,	page02,	NULL,	NULL,	NULL,	NULL,	NULL,
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	page20,	page21,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,
+	};
+
+static unsigned char charset2lower[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x40-0x47 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x48-0x4f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x50-0x57 */
+	0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x60-0x67 */
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, /* 0x68-0x6f */
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 0x70-0x77 */
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+	0x80, 0x00, 0x82, 0x00, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+	0x00, 0x89, 0x9a, 0x8b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x88-0x8f */
+	0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+	0x00, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* 0x98-0x9f */
+	0xa0, 0xa1, 0xa2, 0xb3, 0xa4, 0xb9, 0xa6, 0xa7, /* 0xa0-0xa7 */
+	0xa8, 0xa9, 0xba, 0xab, 0xac, 0xad, 0xae, 0xbf, /* 0xa8-0xaf */
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+	0xb8, 0xb9, 0xba, 0xbb, 0xbe, 0xbd, 0xbe, 0xbf, /* 0xb8-0xbf */
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xc0-0xc7 */
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xc8-0xcf */
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xd7, /* 0xd0-0xd7 */
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, /* 0xd8-0xdf */
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* 0xe0-0xe7 */
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* 0xe8-0xef */
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* 0xf0-0xf7 */
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, /* 0xf8-0xff */
+	};
+
+static unsigned char charset2upper[256] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 0x00-0x07 */
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, /* 0x08-0x0f */
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* 0x10-0x17 */
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, /* 0x18-0x1f */
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, /* 0x20-0x27 */
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 0x28-0x2f */
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0x30-0x37 */
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, /* 0x38-0x3f */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x40-0x47 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x48-0x4f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x50-0x57 */
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, /* 0x58-0x5f */
+	0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0x60-0x67 */
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 0x68-0x6f */
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 0x70-0x77 */
+	0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, /* 0x78-0x7f */
+
+	0x80, 0x00, 0x82, 0x00, 0x84, 0x85, 0x86, 0x87, /* 0x80-0x87 */
+	0x00, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x88-0x8f */
+	0x00, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 0x90-0x97 */
+	0x00, 0x99, 0x8a, 0x9b, 0x8c, 0x8d, 0x8e, 0x8f, /* 0x98-0x9f */
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* 0xa0-0xa7 */
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* 0xa8-0xaf */
+	0xb0, 0xb1, 0xb2, 0xa3, 0xb4, 0xb5, 0xb6, 0xb7, /* 0xb0-0xb7 */
+	0xb8, 0xa5, 0xaa, 0xbb, 0xbc, 0xbd, 0xbc, 0xaf, /* 0xb8-0xbf */
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xc0-0xc7 */
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xc8-0xcf */
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* 0xd0-0xd7 */
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x00, /* 0xd8-0xdf */
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* 0xe0-0xe7 */
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* 0xe8-0xef */
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, /* 0xf0-0xf7 */
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff, /* 0xf8-0xff */
+	};
+
+static int uni2char(wchar_t uni, unsigned char *out, int boundlen)
+{
+        unsigned char *uni2charset;
+        unsigned char cl = uni & 0x00ff;
+        unsigned char ch = (uni & 0xff00) >> 8;
+
+        if (boundlen <= 0)
+                return -ENAMETOOLONG;
+
+        uni2charset = page_uni2charset[ch];
+        if (uni2charset && uni2charset[cl])
+                out[0] = uni2charset[cl];
+        else
+                return -EINVAL;
+        return 1;
+}
+
+static int char2uni(const unsigned char *rawstring, int boundlen, wchar_t *uni)
+{
+        *uni = charset2uni[*rawstring];
+        if (*uni == 0x0000)
+                return -EINVAL;
+        return 1;
+}
+
+static struct nls_table table = {
+        "cp1250",
+        uni2char,
+        char2uni,
+        charset2lower,
+        charset2upper,
+        THIS_MODULE,
+};
+
+static int __init init_nls_cp1250(void)
+{
+        return register_nls(&table);
+}
+static void __exit exit_nls_cp1250(void)
+{
+        unregister_nls(&table);
+}
+
+module_init(init_nls_cp1250)
+module_exit(exit_nls_cp1250)
+
+MODULE_LICENSE("BSD without advertising clause");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ntfs/Makefile linux/fs/ntfs/Makefile
--- linux.orig/fs/ntfs/Makefile	Fri Dec 21 17:42:03 2001
+++ linux/fs/ntfs/Makefile	Thu Jan 17 17:06:08 2002
@@ -5,7 +5,7 @@
 obj-y   := fs.o sysctl.o support.o util.o inode.o dir.o super.o attr.o unistr.o
 obj-m   := $(O_TARGET)
 # New version format started 3 February 2001.
-EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.21\" #-DDEBUG
+EXTRA_CFLAGS = -DNTFS_VERSION=\"1.1.22\" #-DDEBUG
 
 include $(TOPDIR)/Rules.make
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ntfs/fs.c linux/fs/ntfs/fs.c
--- linux.orig/fs/ntfs/fs.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/ntfs/fs.c	Wed Jan 23 20:15:01 2002
@@ -1023,8 +1023,9 @@
 		ntfs_error("Unable to set blocksize %d.\n", blocksize);
 		goto ntfs_read_super_vol;
 	}
+	sb->s_blocksize = blocksize;
 	/* Read the super block (boot block). */
-	if (!(bh = bread(sb->s_dev, 0, blocksize))) {
+	if (!(bh = sb_bread(sb, 0))) {
 		ntfs_error("Reading super block failed\n");
 		goto ntfs_read_super_unl;
 	}
@@ -1071,8 +1072,7 @@
 	if (to_read < 1)
 		to_read = 1;
 	for (i = 0; i < to_read; i++) {
-		if (!(bh = bread(sb->s_dev, vol->mft_lcn + i,
-							  vol->cluster_size))) {
+		if (!(bh = sb_bread(sb, vol->mft_lcn + i))) {
 			ntfs_error("Could not read $Mft record 0\n");
 			goto ntfs_read_super_mft;
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ntfs/support.c linux/fs/ntfs/support.c
--- linux.orig/fs/ntfs/support.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/ntfs/support.c	Wed Jan 23 20:15:01 2002
@@ -169,7 +169,7 @@
 		   buf->do_read ? "get" : "put", cluster, start_offs, length);
 	to_copy = vol->cluster_size - start_offs;
 	while (length) {
-		if (!(bh = bread(sb->s_dev, cluster, vol->cluster_size))) {
+		if (!(bh = sb_bread(sb, cluster))) {
 			ntfs_debug(DEBUG_OTHER, "%s failed\n",
 				   buf->do_read ? "Reading" : "Writing");
 			error = -EIO;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ntfs/support.h linux/fs/ntfs/support.h
--- linux.orig/fs/ntfs/support.h	Sun Sep  9 23:45:12 2001
+++ linux/fs/ntfs/support.h	Thu Jan 17 17:06:08 2002
@@ -32,9 +32,39 @@
 
 #define ntfs_free(ptr)     kfree(ptr)
 
-#define ntfs_vmalloc(size)	vmalloc_32(size)
+/**
+ * ntfs_vmalloc - allocate memory in multiples of pages
+ * @size	number of bytes to allocate
+ *
+ * Allocates @size bytes of memory, rounded up to multiples of PAGE_SIZE and
+ * returns a pointer to the allocated memory.
+ *
+ * If there was insufficient memory to complete the request, return NULL.
+ */
+static inline void *ntfs_vmalloc(unsigned long size)
+{
+	if (size <= PAGE_SIZE) {
+		if (size) {
+			/* kmalloc() has per-CPU caches so if faster for now. */
+			return kmalloc(PAGE_SIZE, GFP_NOFS);
+			/* return (void *)__get_free_page(GFP_NOFS |
+					__GFP_HIGHMEM); */
+		}
+		BUG();
+	}
+	if (size >> PAGE_SHIFT < num_physpages)
+		return __vmalloc(size, GFP_NOFS | __GFP_HIGHMEM, PAGE_KERNEL);
+	return NULL;
+}
 
-#define ntfs_vfree(ptr)		vfree(ptr)
+static inline void ntfs_vfree(void *addr)
+{
+	if ((unsigned long)addr < VMALLOC_START) {
+		return kfree(addr);
+		/* return free_page((unsigned long)addr); */
+	}
+	vfree(addr);
+}
 
 void ntfs_bzero(void *s, int n);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/partitions/check.c linux/fs/partitions/check.c
--- linux.orig/fs/partitions/check.c	Fri Oct 12 00:25:10 2001
+++ linux/fs/partitions/check.c	Tue Feb  5 16:44:35 2002
@@ -338,7 +338,8 @@
 	if (!unregister)
 		devfs_register_disc (dev, minor);
 	for (part = 1; part < dev->max_p; part++) {
-		if ( unregister || (dev->part[part + minor].nr_sects < 1) ) {
+		if ( unregister || (dev->part[minor].nr_sects < 1) ||
+		     (dev->part[part + minor].nr_sects < 1) ) {
 			devfs_unregister (dev->part[part + minor].de);
 			dev->part[part + minor].de = NULL;
 			continue;
@@ -383,6 +384,8 @@
 
 	dev->part[first_minor].nr_sects = size;
 	/* No such device or no minors to use for partitions */
+	if ( !size && dev->flags && (dev->flags[drive] & GENHD_FL_REMOVABLE) )
+		devfs_register_partitions (dev, first_minor, 0);
 	if (!size || minors == 1)
 		return;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/partitions/msdos.c linux/fs/partitions/msdos.c
--- linux.orig/fs/partitions/msdos.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/partitions/msdos.c	Mon Jan 14 18:27:27 2002
@@ -257,45 +257,43 @@
 #ifdef CONFIG_BSD_DISKLABEL
 static void
 check_and_add_bsd_partition(struct gendisk *hd, struct bsd_partition *bsd_p,
-	int minor, int *current_minor)
+	int baseminor, int *current_minor)
 {
-	struct hd_struct *lin_p;
-		/* check relative position of partitions.  */
-	for (lin_p = hd->part + 1 + minor;
-	     lin_p - hd->part - minor < *current_minor; lin_p++) {
-			/* no relationship -> try again */
-		if (lin_p->start_sect + lin_p->nr_sects <= le32_to_cpu(bsd_p->p_offset) ||
-		    lin_p->start_sect >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size))
-			continue;	
-			/* equal -> no need to add */
-		if (lin_p->start_sect == le32_to_cpu(bsd_p->p_offset) && 
-			lin_p->nr_sects == le32_to_cpu(bsd_p->p_size)) 
-			return;
+	int i, bsd_start, bsd_size;
+
+	bsd_start = le32_to_cpu(bsd_p->p_offset);
+	bsd_size = le32_to_cpu(bsd_p->p_size);
+
+	/* check relative position of already allocated partitions */
+	for (i = baseminor+1; i < *current_minor; i++) {
+		int start = hd->part[i].start_sect;
+		int size = hd->part[i].nr_sects;
+
+		if (start+size <= bsd_start || start >= bsd_start+bsd_size)
+			continue;	/* no overlap */
+
+		if (start == bsd_start && size == bsd_size)
+			return;		/* equal -> no need to add */
+
+		if (start <= bsd_start && start+size >= bsd_start+bsd_size) {
 			/* bsd living within dos partition */
-		if (lin_p->start_sect <= le32_to_cpu(bsd_p->p_offset) && lin_p->start_sect 
-			+ lin_p->nr_sects >= le32_to_cpu(bsd_p->p_offset) + le32_to_cpu(bsd_p->p_size)) {
 #ifdef DEBUG_BSD_DISKLABEL
 			printk("w: %d %ld+%ld,%d+%d", 
-				lin_p - hd->part, 
-				lin_p->start_sect, lin_p->nr_sects, 
-				le32_to_cpu(bsd_p->p_offset),
-				le32_to_cpu(bsd_p->p_size));
+			       i, start, size, bsd_start, bsd_size);
 #endif
-			break;
+			break;		/* ok */
 		}
-	 /* ouch: bsd and linux overlap. Don't even try for that partition */
+
+		/* ouch: bsd and linux overlap */
 #ifdef DEBUG_BSD_DISKLABEL
 		printk("???: %d %ld+%ld,%d+%d",
-			lin_p - hd->part, lin_p->start_sect, lin_p->nr_sects,
-			le32_to_cpu(bsd_p->p_offset), le32_to_cpu(bsd_p->p_size));
+		       i, start, size, bsd_start, bsd_size);
 #endif
 		printk("???");
 		return;
-	} /* if the bsd partition is not currently known to linux, we end
-	   * up here 
-	   */
-	add_gd_partition(hd, *current_minor, le32_to_cpu(bsd_p->p_offset),
-			 le32_to_cpu(bsd_p->p_size));
+	}
+
+	add_gd_partition(hd, *current_minor, bsd_start, bsd_size);
 	(*current_minor)++;
 }
 
@@ -311,6 +309,7 @@
 	struct bsd_disklabel *l;
 	struct bsd_partition *p;
 	int mask = (1 << hd->minor_shift) - 1;
+	int baseminor = (minor & ~mask);
 	char buf[40];
 
 	l = (struct bsd_disklabel *)read_dev_sector(bdev, offset+1, &sect);
@@ -320,7 +319,7 @@
 		put_dev_sector(sect);
 		return;
 	}
-	printk(" %s: <%s", partition_name(hd, minor, buf), name);
+	printk(" %s: <%s:", partition_name(hd, minor, buf), name);
 
 	if (le16_to_cpu(l->d_npartitions) < max_partitions)
 		max_partitions = le16_to_cpu(l->d_npartitions);
@@ -329,7 +328,7 @@
 			break;
 		if (p->p_fstype == BSD_FS_UNUSED) 
 			continue;
-		check_and_add_bsd_partition(hd, p, minor, current_minor);
+		check_and_add_bsd_partition(hd, p, baseminor, current_minor);
 	}
 	put_dev_sector(sect);
 	printk(" >\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/proc/base.c linux/fs/proc/base.c
--- linux.orig/fs/proc/base.c	Thu Oct 11 06:42:47 2001
+++ linux/fs/proc/base.c	Wed Feb 13 17:42:57 2002
@@ -553,6 +553,7 @@
 			task_unlock(p);
 			if (!files)
 				goto out;
+			read_lock(&files->file_lock);
 			for (fd = filp->f_pos-2;
 			     fd < files->max_fds;
 			     fd++, filp->f_pos++) {
@@ -560,6 +561,7 @@
 
 				if (!fcheck_files(files, fd))
 					continue;
+				read_unlock(&files->file_lock);
 
 				j = NUMBUF;
 				i = fd;
@@ -570,9 +572,13 @@
 				} while (i);
 
 				ino = fake_ino(pid, PROC_PID_FD_DIR + fd);
-				if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0)
+				if (filldir(dirent, buf+j, NUMBUF-j, fd+2, ino, DT_LNK) < 0) {
+					read_lock(&files->file_lock);
 					break;
+				}
+				read_lock(&files->file_lock);
 			}
+			read_unlock(&files->file_lock);
 			put_files_struct(files);
 	}
 out:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/qnx4/bitmap.c linux/fs/qnx4/bitmap.c
--- linux.orig/fs/qnx4/bitmap.c	Tue Sep  5 21:07:30 2000
+++ linux/fs/qnx4/bitmap.c	Wed Jan 23 20:15:01 2002
@@ -69,7 +69,7 @@
 	struct buffer_head *bh;
 
 	while (total < size) {
-		if ((bh = bread(sb->s_dev, start + offset, QNX4_BLOCK_SIZE)) == NULL) {
+		if ((bh = sb_bread(sb, start + offset)) == NULL) {
 			printk("qnx4: I/O error in counting free blocks\n");
 			break;
 		}
@@ -96,7 +96,7 @@
 	QNX4DEBUG(("qnx4: is_free requesting block [%lu], bitmap in block [%lu]\n",
 		   (unsigned long) block, (unsigned long) start));
 	(void) size;		/* CHECKME */
-	bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
+	bh = sb_bread(sb, start);
 	if (bh == NULL) {
 		return -EIO;
 	}
@@ -124,7 +124,7 @@
 	QNX4DEBUG(("qnx4: set_bitmap requesting block [%lu], bitmap in block [%lu]\n",
 		   (unsigned long) block, (unsigned long) start));
 	(void) size;		/* CHECKME */
-	bh = bread(sb->s_dev, start, QNX4_BLOCK_SIZE);
+	bh = sb_bread(sb, start);
 	if (bh == NULL) {
 		return -EIO;
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/qnx4/dir.c linux/fs/qnx4/dir.c
--- linux.orig/fs/qnx4/dir.c	Fri Aug 11 21:29:02 2000
+++ linux/fs/qnx4/dir.c	Wed Jan 23 20:15:01 2002
@@ -36,7 +36,7 @@
 
 	while (filp->f_pos < inode->i_size) {
 		blknum = qnx4_block_map( inode, filp->f_pos >> QNX4_BLOCK_SIZE_BITS );
-		bh = bread(inode->i_dev, blknum, QNX4_BLOCK_SIZE);
+		bh = sb_bread(inode->i_sb, blknum);
 		if(bh==NULL) {
 			printk(KERN_ERR "qnx4_readdir: bread failed (%ld)\n", blknum);
 			break;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/qnx4/fsync.c linux/fs/qnx4/fsync.c
--- linux.orig/fs/qnx4/fsync.c	Thu Jul  6 04:38:48 2000
+++ linux/fs/qnx4/fsync.c	Wed Jan 23 20:15:01 2002
@@ -24,8 +24,6 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 
-#define blocksize QNX4_BLOCK_SIZE
-
 /*
  * The functions for qnx4 fs file synchronization.
  */
@@ -40,7 +38,7 @@
 	if (!*block)
 		return 0;
 	tmp = *block;
-	bh = get_hash_table(inode->i_dev, *block, blocksize);
+	bh = sb_get_hash_table(inode->i_sb, *block);
 	if (!bh)
 		return 0;
 	if (*block != tmp) {
@@ -74,7 +72,7 @@
 	rc = sync_block(inode, iblock, wait);
 	if (rc)
 		return rc;
-	*bh = bread(inode->i_dev, tmp, blocksize);
+	*bh = sb_bread(inode->i_sb, tmp);
 	if (tmp != *iblock) {
 		brelse(*bh);
 		*bh = NULL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/qnx4/inode.c linux/fs/qnx4/inode.c
--- linux.orig/fs/qnx4/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/qnx4/inode.c	Wed Jan 23 20:15:01 2002
@@ -95,7 +95,7 @@
 	QNX4DEBUG(("qnx4: write inode 2.\n"));
 	block = ino / QNX4_INODES_PER_BLOCK;
 	lock_kernel();
-	if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
+	if (!(bh = sb_bread(inode->i_sb, block))) {
 		printk("qnx4: major problem: unable to read inode from dev "
 		       "%s\n", kdevname(inode->i_dev));
 		unlock_kernel();
@@ -162,7 +162,7 @@
 	if ( nr >= 0 )
 		nr = qnx4_block_map( inode, nr );
 	if (nr) {
-		result = getblk(inode->i_dev, nr, QNX4_BLOCK_SIZE);
+		result = sb_getblk(inode->i_sb, nr);
 		return result;
 	}
 	if (!create) {
@@ -173,7 +173,7 @@
 	if (!tmp) {
 		return NULL;
 	}
-	result = getblk(inode->i_dev, tmp, QNX4_BLOCK_SIZE);
+	result = sb_getblk(inode->i_sb, tmp);
 	if (tst) {
 		qnx4_free_block(inode->i_sb, tmp);
 		brelse(result);
@@ -243,7 +243,7 @@
 		while ( --nxtnt > 0 ) {
 			if ( ix == 0 ) {
 				// read next xtnt block.
-				bh = bread( inode->i_dev, i_xblk - 1, QNX4_BLOCK_SIZE );
+				bh = sb_bread(inode->i_sb, i_xblk - 1);
 				if ( !bh ) {
 					QNX4DEBUG(("qnx4: I/O error reading xtnt block [%ld])\n", i_xblk - 1));
 					return -EIO;
@@ -307,7 +307,7 @@
 		rd = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_blk) - 1;
 		rl = le32_to_cpu(sb->u.qnx4_sb.sb->RootDir.di_first_xtnt.xtnt_size);
 		for (j = 0; j < rl; j++) {
-			bh = bread(sb->s_dev, rd + j, QNX4_BLOCK_SIZE);	/* root dir, first block */
+			bh = sb_bread(sb, rd + j);	/* root dir, first block */
 			if (bh == NULL) {
 				return "unable to read root entry.";
 			}
@@ -318,6 +318,10 @@
 					if (!strncmp(rootdir->di_fname, QNX4_BMNAME, sizeof QNX4_BMNAME)) {
 						found = 1;
 						sb->u.qnx4_sb.BitMap = kmalloc( sizeof( struct qnx4_inode_entry ), GFP_KERNEL );
+						if (!sb->u.qnx4_sb.BitMap) {
+							brelse (bh);
+							return "not enough memory for bitmap inode";
+						}
 						memcpy( sb->u.qnx4_sb.BitMap, rootdir, sizeof( struct qnx4_inode_entry ) );	/* keep bitmap inode known */
 						break;
 					}
@@ -350,7 +354,7 @@
 	/* Check the boot signature. Since the qnx4 code is
 	   dangerous, we should leave as quickly as possible
 	   if we don't belong here... */
-	bh = bread(dev, 0, QNX4_BLOCK_SIZE);
+	bh = sb_bread(s, 0);
 	if (!bh) {
 		printk("qnx4: unable to read the boot sector\n");
 		goto outnobh;
@@ -362,7 +366,7 @@
 	}
 	brelse(bh);
 
-	bh = bread(dev, 1, QNX4_BLOCK_SIZE);
+	bh = sb_bread(s, 1);
 	if (!bh) {
 		printk("qnx4: unable to read the superblock\n");
 		goto outnobh;
@@ -457,7 +461,7 @@
 	}
 	block = ino / QNX4_INODES_PER_BLOCK;
 
-	if (!(bh = bread(inode->i_dev, block, QNX4_BLOCK_SIZE))) {
+	if (!(bh = sb_bread(inode->i_sb, block))) {
 		printk("qnx4: major problem: unable to read inode from dev "
 		       "%s\n", kdevname(inode->i_dev));
 		return;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/Makefile linux/fs/reiserfs/Makefile
--- linux.orig/fs/reiserfs/Makefile	Wed Nov 21 17:56:28 2001
+++ linux/fs/reiserfs/Makefile	Wed Jan 16 18:31:49 2002
@@ -9,7 +9,7 @@
 
 O_TARGET := reiserfs.o
 obj-y   := bitmap.o do_balan.o namei.o inode.o file.o dir.o fix_node.o super.o prints.o objectid.o \
-lbalance.o ibalance.o stree.o hashes.o buffer2.o tail_conversion.o journal.o resize.o tail_conversion.o version.o item_ops.o ioctl.o procfs.o
+lbalance.o ibalance.o stree.o hashes.o buffer2.o tail_conversion.o journal.o resize.o version.o item_ops.o ioctl.o procfs.o
 
 obj-m   := $(O_TARGET)
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/file.c linux/fs/reiserfs/file.c
--- linux.orig/fs/reiserfs/file.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/file.c	Thu Jan 17 21:16:14 2002
@@ -100,6 +100,20 @@
 	if (get_inode_item_key_version(inode) == KEY_FORMAT_3_5 &&
 	    attr->ia_size > MAX_NON_LFS)
             return -EFBIG ;
+
+	/* fill in hole pointers in the expanding truncate case. */
+        if (attr->ia_size > inode->i_size) {
+	    error = generic_cont_expand(inode, attr->ia_size) ;
+	    if (inode->u.reiserfs_i.i_prealloc_count > 0) {
+		struct reiserfs_transaction_handle th ;
+		/* we're changing at most 2 bitmaps, inode + super */
+		journal_begin(&th, inode->i_sb, 4) ;
+		reiserfs_discard_prealloc (&th, inode);
+		journal_end(&th, inode->i_sb, 4) ;
+	    }
+	    if (error)
+	        return error ;
+	}
     }
 
     if ((((attr->ia_valid & ATTR_UID) && (attr->ia_uid & ~0xffff)) ||
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/fix_node.c linux/fs/reiserfs/fix_node.c
--- linux.orig/fs/reiserfs/fix_node.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/fix_node.c	Wed Jan 23 20:15:01 2002
@@ -920,7 +920,7 @@
   /* Get left neighbor block number. */
   n_left_neighbor_blocknr = B_N_CHILD_NUM(p_s_tb->FL[n_h], n_left_neighbor_position);
   /* Look for the left neighbor in the cache. */
-  if ( (left = get_hash_table(p_s_sb->s_dev, n_left_neighbor_blocknr, p_s_sb->s_blocksize)) ) {
+  if ( (left = sb_get_hash_table(p_s_sb, n_left_neighbor_blocknr)) ) {
 
     RFALSE( buffer_uptodate (left) && ! B_IS_IN_TREE(left),
 	    "vs-8170: left neighbor (%b %z) is not in the tree", left, left);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/inode.c linux/fs/reiserfs/inode.c
--- linux.orig/fs/reiserfs/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/reiserfs/inode.c	Wed Feb 13 17:36:10 2002
@@ -273,7 +273,9 @@
 	pathrelse (&path);
         if (p)
             kunmap(bh_result->b_page) ;
-	if ((args & GET_BLOCK_NO_HOLE)) {
+	// We do not return -ENOENT if there is a hole but page is uptodate, because it means
+	// That there is some MMAPED data associated with it that is yet to be written to disk.
+	if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
 	    return -ENOENT ;
 	}
         return 0 ;
@@ -294,9 +296,13 @@
 	    bh_result->b_dev = inode->i_dev;
 	    bh_result->b_blocknr = blocknr;
 	    bh_result->b_state |= (1UL << BH_Mapped);
-	} else if ((args & GET_BLOCK_NO_HOLE)) {
-	    ret = -ENOENT ;
-	}
+	} else
+	    // We do not return -ENOENT if there is a hole but page is uptodate, because it means
+	    // That there is some MMAPED data associated with it that is yet to be written to disk.
+	    if ((args & GET_BLOCK_NO_HOLE) && !Page_Uptodate(bh_result->b_page) ) {
+		ret = -ENOENT ;
+	    }
+
 	pathrelse (&path);
         if (p)
             kunmap(bh_result->b_page) ;
@@ -319,6 +325,16 @@
     */
     if (buffer_uptodate(bh_result)) {
         goto finished ;
+    } else 
+	/*
+	** grab_tail_page can trigger calls to reiserfs_get_block on up to date
+	** pages without any buffers.  If the page is up to date, we don't want
+	** read old data off disk.  Set the up to date bit on the buffer instead
+	** and jump to the end
+	*/
+	    if (Page_Uptodate(bh_result->b_page)) {
+		mark_buffer_uptodate(bh_result, 1);
+		goto finished ;
     }
 
     // read file tail into part of page
@@ -827,7 +843,7 @@
 	}
 	if (retval == POSITION_FOUND) {
 	    reiserfs_warning ("vs-825: reiserfs_get_block: "
-			      "%k should not be found\n", &key);
+			      "%K should not be found\n", &key);
 	    retval = -EEXIST;
 	    if (allocated_block_nr)
 	        reiserfs_free_block (&th, allocated_block_nr);
@@ -915,9 +931,6 @@
 	// (directories and symlinks)
 	struct stat_data * sd = (struct stat_data *)B_I_PITEM (bh, ih);
 
-	/* both old and new directories have old keys */
-	//version = (S_ISDIR (sd->sd_mode) ? ITEM_VERSION_1 : ITEM_VERSION_2);
-
 	inode->i_mode   = sd_v2_mode(sd);
 	inode->i_nlink  = sd_v2_nlink(sd);
 	inode->i_uid    = sd_v2_uid(sd);
@@ -937,6 +950,8 @@
 	    set_inode_item_key_version (inode, KEY_FORMAT_3_5);
 	else
             set_inode_item_key_version (inode, KEY_FORMAT_3_6);
+
+        set_inode_sd_version (inode, STAT_DATA_V2);
     }
 
     /* nopack = 0, by default */
@@ -1139,6 +1154,7 @@
 	/* a stale NFS handle can trigger this without it being an error */
 	pathrelse (&path_to_sd);
 	make_bad_inode(inode) ;
+	inode->i_nlink = 0;
 	return;
     }
 
@@ -1171,6 +1187,27 @@
 
 }
 
+/**
+ * reiserfs_find_actor() - "find actor" reiserfs supplies to iget4().
+ *
+ * @inode:    inode from hash table to check
+ * @inode_no: inode number we are looking for
+ * @opaque:   "cookie" passed to iget4(). This is &reiserfs_iget4_args.
+ *
+ * This function is called by iget4() to distinguish reiserfs inodes
+ * having the same inode numbers. Such inodes can only exist due to some
+ * error condition. One of them should be bad. Inodes with identical
+ * inode numbers (objectids) are distinguished by parent directory ids.
+ *
+ */
+static int reiserfs_find_actor( struct inode *inode, 
+				unsigned long inode_no, void *opaque )
+{
+    struct reiserfs_iget4_args *args;
+
+    args = opaque;
+    return INODE_PKEY( inode ) -> k_dir_id == args -> objectid;
+}
 
 struct inode * reiserfs_iget (struct super_block * s, const struct cpu_key * key)
 {
@@ -1178,7 +1215,8 @@
     struct reiserfs_iget4_args args ;
 
     args.objectid = key->on_disk_key.k_dir_id ;
-    inode = iget4 (s, key->on_disk_key.k_objectid, 0, (void *)(&args));
+    inode = iget4 (s, key->on_disk_key.k_objectid, 
+		   reiserfs_find_actor, (void *)(&args));
     if (!inode) 
 	return ERR_PTR(-ENOMEM) ;
 
@@ -1766,6 +1804,7 @@
     int bytes_copied = 0 ;
     int copy_size ;
 
+    kmap(bh_result->b_page) ;
 start_over:
     lock_kernel() ;
     journal_begin(&th, inode->i_sb, jbegin_count) ;
@@ -1838,10 +1877,8 @@
 
     /* this is where we fill in holes in the file. */
     if (use_get_block) {
-        kmap(bh_result->b_page) ;
 	retval = reiserfs_get_block(inode, block, bh_result, 
 	                            GET_BLOCK_CREATE | GET_BLOCK_NO_ISEM) ;
-        kunmap(bh_result->b_page) ;
 	if (!retval) {
 	    if (!buffer_mapped(bh_result) || bh_result->b_blocknr == 0) {
 	        /* get_block failed to find a mapped unformatted node. */
@@ -1850,6 +1887,7 @@
 	    }
 	}
     }
+    kunmap(bh_result->b_page) ;
     return retval ;
 }
 
@@ -2026,7 +2064,7 @@
     /* we test for O_SYNC here so we can commit the transaction
     ** for any packed tails the file might have had
     */
-    if (f->f_flags & O_SYNC) {
+    if (f && (f->f_flags & O_SYNC)) {
 	lock_kernel() ;
  	reiserfs_commit_for_inode(inode) ;
 	unlock_kernel();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/journal.c linux/fs/reiserfs/journal.c
--- linux.orig/fs/reiserfs/journal.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/reiserfs/journal.c	Wed Feb 13 17:24:37 2002
@@ -685,7 +685,7 @@
   count = 0 ;
   for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && i < (jl->j_len + 1) ; i++) {  /* everything but commit_bh */
     bn = reiserfs_get_journal_block(s) + (jl->j_start+i) % JOURNAL_BLOCK_COUNT;
-    tbh = get_hash_table(s->s_dev, bn, s->s_blocksize) ;
+    tbh = sb_get_hash_table(s, bn) ;
 
 /* kill this sanity check */
 if (count > (orig_commit_left + 2)) {
@@ -714,7 +714,7 @@
     for (i = 0 ; atomic_read(&(jl->j_commit_left)) > 1 && 
                  i < (jl->j_len + 1) ; i++) {  /* everything but commit_bh */
       bn = reiserfs_get_journal_block(s) + (jl->j_start + i) % JOURNAL_BLOCK_COUNT  ;
-      tbh = get_hash_table(s->s_dev, bn, s->s_blocksize) ;
+      tbh = sb_get_hash_table(s, bn) ;
 
       wait_on_buffer(tbh) ;
       if (!buffer_uptodate(tbh)) {
@@ -793,7 +793,7 @@
   while(cn) {
     if (cn->blocknr != 0) {
       if (debug) {
-        printk("block %lu, bh is %d, state %d\n", cn->blocknr, cn->bh ? 1: 0, 
+        printk("block %lu, bh is %d, state %ld\n", cn->blocknr, cn->bh ? 1: 0, 
 	        cn->state) ;
       }
       fake_bh.b_blocknr = cn->blocknr ;
@@ -1405,8 +1405,7 @@
     offset = d_bh->b_blocknr - reiserfs_get_journal_block(p_s_sb) ;
 
     /* ok, we have a journal description block, lets see if the transaction was valid */
-    c_bh = bread(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + ((offset + le32_to_cpu(desc->j_len) + 1) % JOURNAL_BLOCK_COUNT), 
-    		p_s_sb->s_blocksize) ;
+    c_bh = sb_bread(p_s_sb, reiserfs_get_journal_block(p_s_sb) + ((offset + le32_to_cpu(desc->j_len) + 1) % JOURNAL_BLOCK_COUNT)) ;
     if (!c_bh)
       return 0 ;
     commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
@@ -1460,7 +1459,7 @@
   unsigned long trans_offset ;
   int i;
 
-  d_bh = bread(p_s_sb->s_dev, cur_dblock, p_s_sb->s_blocksize) ;
+  d_bh = sb_bread(p_s_sb, cur_dblock) ;
   if (!d_bh)
     return 1 ;
   desc = (struct reiserfs_journal_desc *)d_bh->b_data ;
@@ -1484,8 +1483,7 @@
     brelse(d_bh) ;
     return 1 ;
   }
-  c_bh = bread(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + ((trans_offset + le32_to_cpu(desc->j_len) + 1) % JOURNAL_BLOCK_COUNT), 
-    		p_s_sb->s_blocksize) ;
+  c_bh = sb_bread(p_s_sb, reiserfs_get_journal_block(p_s_sb) + ((trans_offset + le32_to_cpu(desc->j_len) + 1) % JOURNAL_BLOCK_COUNT)) ;
   if (!c_bh) {
     brelse(d_bh) ;
     return 1 ;
@@ -1514,11 +1512,11 @@
   }
   /* get all the buffer heads */
   for(i = 0 ; i < le32_to_cpu(desc->j_len) ; i++) {
-    log_blocks[i] = getblk(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + (trans_offset + 1 + i) % JOURNAL_BLOCK_COUNT, p_s_sb->s_blocksize);
+    log_blocks[i] = sb_getblk(p_s_sb, reiserfs_get_journal_block(p_s_sb) + (trans_offset + 1 + i) % JOURNAL_BLOCK_COUNT);
     if (i < JOURNAL_TRANS_HALF) {
-      real_blocks[i] = getblk(p_s_sb->s_dev, le32_to_cpu(desc->j_realblock[i]), p_s_sb->s_blocksize) ;
+      real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(desc->j_realblock[i])) ;
     } else {
-      real_blocks[i] = getblk(p_s_sb->s_dev, le32_to_cpu(commit->j_realblock[i - JOURNAL_TRANS_HALF]), p_s_sb->s_blocksize) ;
+      real_blocks[i] = sb_getblk(p_s_sb, le32_to_cpu(commit->j_realblock[i - JOURNAL_TRANS_HALF])) ;
     }
     if (real_blocks[i]->b_blocknr >= reiserfs_get_journal_block(p_s_sb) &&
         real_blocks[i]->b_blocknr < (reiserfs_get_journal_block(p_s_sb)+JOURNAL_BLOCK_COUNT)) {
@@ -1619,10 +1617,9 @@
   ** is the first unflushed, and if that transaction is not valid, 
   ** replay is done
   */
-  SB_JOURNAL(p_s_sb)->j_header_bh = bread(p_s_sb->s_dev, 
+  SB_JOURNAL(p_s_sb)->j_header_bh = sb_bread(p_s_sb, 
                                           reiserfs_get_journal_block(p_s_sb) + 
-					  JOURNAL_BLOCK_COUNT, 
-					  p_s_sb->s_blocksize) ;
+					  JOURNAL_BLOCK_COUNT) ;
   if (!SB_JOURNAL(p_s_sb)->j_header_bh) {
     return 1 ;
   }
@@ -1643,7 +1640,7 @@
     ** there is nothing more we can do, and it makes no sense to read 
     ** through the whole log.
     */
-    d_bh = bread(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + le32_to_cpu(jh->j_first_unflushed_offset), p_s_sb->s_blocksize) ;
+    d_bh = sb_bread(p_s_sb, reiserfs_get_journal_block(p_s_sb) + le32_to_cpu(jh->j_first_unflushed_offset)) ;
     ret = journal_transaction_is_valid(p_s_sb, d_bh, NULL, NULL) ;
     if (!ret) {
       continue_replay = 0 ;
@@ -1663,7 +1660,7 @@
   ** all the valid transactions, and pick out the oldest.
   */
   while(continue_replay && cur_dblock < (reiserfs_get_journal_block(p_s_sb) + JOURNAL_BLOCK_COUNT)) {
-    d_bh = bread(p_s_sb->s_dev, cur_dblock, p_s_sb->s_blocksize) ;
+    d_bh = sb_bread(p_s_sb, cur_dblock) ;
     ret = journal_transaction_is_valid(p_s_sb, d_bh, &oldest_invalid_trans_id, &newest_mount_id) ;
     if (ret == 1) {
       desc = (struct reiserfs_journal_desc *)d_bh->b_data ;
@@ -1851,7 +1848,7 @@
       break ;
     }
     wake_up(&reiserfs_commit_thread_done) ;
-    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5) ;
+    interruptible_sleep_on_timeout(&reiserfs_commit_thread_wait, 5 * HZ) ;
   }
   unlock_kernel() ;
   wake_up(&reiserfs_commit_thread_done) ;
@@ -2543,7 +2540,7 @@
   int cleaned = 0 ;
   
   if (reiserfs_dont_log(th->t_super)) {
-    bh = get_hash_table(p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ;
+    bh = sb_get_hash_table(p_s_sb, blocknr) ;
     if (bh && buffer_dirty (bh)) {
       printk ("journal_mark_freed(dont_log): dirty buffer on hash list: %lx %ld\n", bh->b_state, blocknr);
       BUG ();
@@ -2551,7 +2548,7 @@
     brelse (bh);
     return 0 ;
   }
-  bh = get_hash_table(p_s_sb->s_dev, blocknr, p_s_sb->s_blocksize) ;
+  bh = sb_get_hash_table(p_s_sb, blocknr) ;
   /* if it is journal new, we just remove it from this transaction */
   if (bh && buffer_journal_new(bh)) {
     mark_buffer_notjournal_new(bh) ;
@@ -2758,7 +2755,7 @@
   
   rs = SB_DISK_SUPER_BLOCK(p_s_sb) ;
   /* setup description block */
-  d_bh = getblk(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start, p_s_sb->s_blocksize) ; 
+  d_bh = sb_getblk(p_s_sb, reiserfs_get_journal_block(p_s_sb) + SB_JOURNAL(p_s_sb)->j_start) ; 
   mark_buffer_uptodate(d_bh, 1) ;
   desc = (struct reiserfs_journal_desc *)(d_bh)->b_data ;
   memset(desc, 0, sizeof(struct reiserfs_journal_desc)) ;
@@ -2766,9 +2763,8 @@
   desc->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ;
 
   /* setup commit block.  Don't write (keep it clean too) this one until after everyone else is written */
-  c_bh = getblk(p_s_sb->s_dev,  reiserfs_get_journal_block(p_s_sb) + 
-  				        ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 1) % JOURNAL_BLOCK_COUNT), 
-					 p_s_sb->s_blocksize) ;
+  c_bh = sb_getblk(p_s_sb,  reiserfs_get_journal_block(p_s_sb) + 
+  				        ((SB_JOURNAL(p_s_sb)->j_start + SB_JOURNAL(p_s_sb)->j_len + 1) % JOURNAL_BLOCK_COUNT)) ;
   commit = (struct reiserfs_journal_commit *)c_bh->b_data ;
   memset(commit, 0, sizeof(struct reiserfs_journal_commit)) ;
   commit->j_trans_id = cpu_to_le32(SB_JOURNAL(p_s_sb)->j_trans_id) ;
@@ -2856,9 +2852,8 @@
     /* copy all the real blocks into log area.  dirty log blocks */
     if (test_bit(BH_JDirty, &cn->bh->b_state)) {
       struct buffer_head *tmp_bh ;
-      tmp_bh = getblk(p_s_sb->s_dev, reiserfs_get_journal_block(p_s_sb) + 
-		     ((cur_write_start + jindex) % JOURNAL_BLOCK_COUNT), 
-				       p_s_sb->s_blocksize) ;
+      tmp_bh = sb_getblk(p_s_sb, reiserfs_get_journal_block(p_s_sb) + 
+		     ((cur_write_start + jindex) % JOURNAL_BLOCK_COUNT)) ;
       mark_buffer_uptodate(tmp_bh, 1) ;
       memcpy(tmp_bh->b_data, cn->bh->b_data, cn->bh->b_size) ;  
       jindex++ ;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/namei.c linux/fs/reiserfs/namei.c
--- linux.orig/fs/reiserfs/namei.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/reiserfs/namei.c	Mon Feb  4 18:05:52 2002
@@ -309,9 +309,10 @@
 
     while (1) {
 	retval = search_by_entry_key (dir->i_sb, &key_to_search, path_to_entry, de);
-	if (retval == IO_ERROR)
-	    // FIXME: still has to be dealt with
-	    reiserfs_panic (dir->i_sb, "zam-7001: io error in " __FUNCTION__ "\n");
+	if (retval == IO_ERROR) {
+	    reiserfs_warning ("zam-7001: io error in " __FUNCTION__ "\n");
+	    return IO_ERROR;
+	}
 
 	/* compare names for all entries having given hash value */
 	retval = linear_search_in_dir_item (&key_to_search, de, name, namelen);
@@ -875,7 +876,7 @@
     }
 
     item_len = ROUND_UP (strlen (symname));
-    if (item_len > MAX_ITEM_LEN (dir->i_sb->s_blocksize)) {
+    if (item_len > MAX_DIRECT_ITEM_LEN (dir->i_sb->s_blocksize)) {
 	iput(inode) ;
 	return -ENAMETOOLONG;
     }
@@ -1042,7 +1043,7 @@
     INITIALIZE_PATH (old_entry_path);
     INITIALIZE_PATH (new_entry_path);
     INITIALIZE_PATH (dot_dot_entry_path);
-    struct item_head new_entry_ih, old_entry_ih ;
+    struct item_head new_entry_ih, old_entry_ih, dot_dot_ih ;
     struct reiserfs_dir_entry old_de, new_de, dot_dot_de;
     struct inode * old_inode, * new_inode;
     int windex ;
@@ -1131,6 +1132,8 @@
 
 	copy_item_head(&old_entry_ih, get_ih(&old_entry_path)) ;
 
+	reiserfs_prepare_for_journal(old_inode->i_sb, old_de.de_bh, 1) ;
+
 	// look for new name by reiserfs_find_entry
 	new_de.de_gen_number_bit_string = 0;
 	retval = reiserfs_find_entry (new_dir, new_dentry->d_name.name, new_dentry->d_name.len, 
@@ -1145,6 +1148,7 @@
 	if (S_ISDIR(old_inode->i_mode)) {
 	    if (search_by_entry_key (new_dir->i_sb, &dot_dot_de.de_entry_key, &dot_dot_entry_path, &dot_dot_de) != NAME_FOUND)
 		BUG ();
+	    copy_item_head(&dot_dot_ih, get_ih(&dot_dot_entry_path)) ;
 	    // node containing ".." gets into transaction
 	    reiserfs_prepare_for_journal(old_inode->i_sb, dot_dot_de.de_bh, 1) ;
 	}
@@ -1161,23 +1165,33 @@
 	** of the above checks could have scheduled.  We have to be
 	** sure our items haven't been shifted by another process.
 	*/
-	if (!entry_points_to_object(new_dentry->d_name.name, 
+	if (item_moved(&new_entry_ih, &new_entry_path) ||
+	    !entry_points_to_object(new_dentry->d_name.name, 
 	                            new_dentry->d_name.len,
 				    &new_de, new_inode) ||
-	    item_moved(&new_entry_ih, &new_entry_path) ||
 	    item_moved(&old_entry_ih, &old_entry_path) || 
 	    !entry_points_to_object (old_dentry->d_name.name, 
 	                             old_dentry->d_name.len,
 				     &old_de, old_inode)) {
 	    reiserfs_restore_prepared_buffer (old_inode->i_sb, new_de.de_bh);
+	    reiserfs_restore_prepared_buffer (old_inode->i_sb, old_de.de_bh);
 	    if (S_ISDIR(old_inode->i_mode))
 		reiserfs_restore_prepared_buffer (old_inode->i_sb, dot_dot_de.de_bh);
 	    continue;
 	}
+	if (S_ISDIR(old_inode->i_mode)) {
+	    if ( item_moved(&dot_dot_ih, &dot_dot_entry_path) ||
+		 !entry_points_to_object ( "..", 2, &dot_dot_de, old_dir) ) {
+		reiserfs_restore_prepared_buffer (old_inode->i_sb, old_de.de_bh);
+		reiserfs_restore_prepared_buffer (old_inode->i_sb, new_de.de_bh);
+		reiserfs_restore_prepared_buffer (old_inode->i_sb, dot_dot_de.de_bh);
+		continue;
+	    }
+	}
+
 
 	RFALSE( S_ISDIR(old_inode->i_mode) && 
-		(!entry_points_to_object ("..", 2, &dot_dot_de, old_dir) || 
-		 !reiserfs_buffer_prepared(dot_dot_de.de_bh)), "" );
+		!reiserfs_buffer_prepared(dot_dot_de.de_bh), "" );
 
 	break;
     }
@@ -1190,6 +1204,7 @@
     journal_mark_dirty (&th, old_dir->i_sb, new_de.de_bh);
 
     mark_de_hidden (old_de.de_deh + old_de.de_entry_num);
+    journal_mark_dirty (&th, old_dir->i_sb, old_de.de_bh);
     old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
     new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/procfs.c linux/fs/reiserfs/procfs.c
--- linux.orig/fs/reiserfs/procfs.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/procfs.c	Wed Feb 13 17:24:37 2002
@@ -79,7 +79,7 @@
 	struct super_block *sb;
 	char *format;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	if ( sb->u.reiserfs_sb.s_properties & (1 << REISERFS_3_6) ) {
@@ -143,7 +143,7 @@
 	struct reiserfs_sb_info *r;
 	int len = 0;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	r = &sb->u.reiserfs_sb;
@@ -223,7 +223,7 @@
 	int len = 0;
 	int level;
 	
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	r = &sb->u.reiserfs_sb;
@@ -302,7 +302,7 @@
 	struct reiserfs_sb_info *r = &sb->u.reiserfs_sb;
 	int len = 0;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	r = &sb->u.reiserfs_sb;
@@ -343,7 +343,7 @@
 	int hash_code;
 	int len = 0;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	sb_info = &sb->u.reiserfs_sb;
@@ -396,7 +396,7 @@
 	int len = 0;
 	int exact;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	sb_info = &sb->u.reiserfs_sb;
@@ -447,7 +447,7 @@
 	struct reiserfs_super_block *rs;
 	int len = 0;
     
-	sb = procinfo_prologue( ( kdev_t ) ( int ) data );
+	sb = procinfo_prologue( ( kdev_t ) ( long ) data );
 	if( sb == NULL )
 		return -ENOENT;
 	r = &sb->u.reiserfs_sb;
@@ -464,7 +464,7 @@
 			"s_journal_max_commit_age: \t%i\n"
 			"s_journal_max_trans_age: \t%i\n"
 			/* incore fields */
-			"j_state: \t%i\n"			
+			"j_state: \t%li\n"			
 			"j_trans_id: \t%lu\n"
 			"j_mount_id: \t%lu\n"
 			"j_start: \t%lu\n"
@@ -588,7 +588,7 @@
 {
 	return ( sb->u.reiserfs_sb.procdir ) ? create_proc_read_entry
 		( name, 0, sb->u.reiserfs_sb.procdir, func, 
-		  ( void * ) ( int ) sb -> s_dev ) : NULL;
+		  ( void * ) ( long ) sb -> s_dev ) : NULL;
 }
 
 void reiserfs_proc_unregister( struct super_block *sb, const char *name )
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/resize.c linux/fs/reiserfs/resize.c
--- linux.orig/fs/reiserfs/resize.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/resize.c	Wed Jan 23 20:15:01 2002
@@ -39,7 +39,7 @@
 	}
 
 	/* check the device size */
-	bh = bread(s->s_dev, block_count_new - 1, s->s_blocksize);
+	bh = sb_bread(s, block_count_new - 1);
 	if (!bh) {
 		printk("reiserfs_resize: can\'t read last block\n");
 		return -EINVAL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/stree.c linux/fs/reiserfs/stree.c
--- linux.orig/fs/reiserfs/stree.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/stree.c	Wed Feb 13 17:26:34 2002
@@ -126,19 +126,19 @@
   retval = comp_short_keys (le_key, cpu_key);
   if (retval)
       return retval;
-  if (le_key_k_offset (cpu_key->version, le_key) < cpu_key_k_offset (cpu_key))
+  if (le_key_k_offset (le_key_version(le_key), le_key) < cpu_key_k_offset (cpu_key))
       return -1;
-  if (le_key_k_offset (cpu_key->version, le_key) > cpu_key_k_offset (cpu_key))
+  if (le_key_k_offset (le_key_version(le_key), le_key) > cpu_key_k_offset (cpu_key))
       return 1;
 
   if (cpu_key->key_length == 3)
       return 0;
 
   /* this part is needed only when tail conversion is in progress */
-  if (le_key_k_type (cpu_key->version, le_key) < cpu_key_k_type (cpu_key))
+  if (le_key_k_type (le_key_version(le_key), le_key) < cpu_key_k_type (cpu_key))
     return -1;
 
-  if (le_key_k_type (cpu_key->version, le_key) > cpu_key_k_type (cpu_key))
+  if (le_key_k_type (le_key_version(le_key), le_key) > cpu_key_k_type (cpu_key))
     return 1;
 
   return 0;
@@ -1700,8 +1700,7 @@
     }
 
     if ( n_file_size == 0 || n_file_size < n_new_file_size ) {
-	pathrelse(&s_search_path);
-	return;
+	goto update_and_out ;
     }
 
     /* Update key to search for the last file item. */
@@ -1754,6 +1753,7 @@
 	    "PAP-5680: truncate did not finish: new_file_size %Ld, current %Ld, oid %d\n",
 	    n_new_file_size, n_file_size, s_item_key.on_disk_key.k_objectid);
 
+update_and_out:
     if (update_timestamps) {
 	// this is truncate, not file closing
 	p_s_inode->i_mtime = p_s_inode->i_ctime = CURRENT_TIME;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/reiserfs/super.c linux/fs/reiserfs/super.c
--- linux.orig/fs/reiserfs/super.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/reiserfs/super.c	Wed Feb 13 17:26:22 2002
@@ -86,7 +86,7 @@
    protecting unlink is bigger that a key lf "save link" which
    protects truncate), so there left no items to make truncate
    completion on */
-static void remove_save_link_only (struct super_block * s, struct key * key)
+static void remove_save_link_only (struct super_block * s, struct key * key, int oid_free)
 {
     struct reiserfs_transaction_handle th;
 
@@ -94,7 +94,7 @@
      journal_begin (&th, s, JOURNAL_PER_BALANCE_CNT);
  
      reiserfs_delete_solid_item (&th, key);
-     if (is_direct_le_key (KEY_FORMAT_3_5, key))
+     if (oid_free)
         /* removals are protected by direct items */
         reiserfs_release_objectid (&th, le32_to_cpu (key->k_objectid));
 
@@ -167,7 +167,7 @@
 	       "save" link and release objectid */
             reiserfs_warning ("vs-2180: finish_unfinished: iget failed for %K\n",
                               &obj_key);
-            remove_save_link_only (s, &save_link_key);
+            remove_save_link_only (s, &save_link_key, 1);
             continue;
         }
 
@@ -175,9 +175,21 @@
 	    /* file is not unlinked */
             reiserfs_warning ("vs-2185: finish_unfinished: file %K is not unlinked\n",
                               &obj_key);
-            remove_save_link_only (s, &save_link_key);
+            remove_save_link_only (s, &save_link_key, 0);
             continue;
 	}
+
+	if (truncate && S_ISDIR (inode->i_mode) ) {
+	    /* We got a truncate request for a dir which is impossible.
+	       The only imaginable way is to execute unfinished truncate request
+	       then boot into old kernel, remove the file and create dir with
+	       the same key. */
+	    reiserfs_warning("green-2101: impossible truncate on a directory %k. Please report\n", INODE_PKEY (inode));
+	    remove_save_link_only (s, &save_link_key, 0);
+	    truncate = 0;
+	    iput (inode); 
+	    continue;
+	}
  
         if (truncate) {
             inode -> u.reiserfs_i.i_flags |= i_link_saved_truncate_mask;
@@ -243,6 +255,8 @@
 			   4/*length*/, 0xffff/*free space*/);
     } else {
 	/* truncate */
+	if (S_ISDIR (inode->i_mode))
+	    reiserfs_warning("green-2102: Adding a truncate savelink for a directory %k! Please report\n", INODE_PKEY(inode));
 	set_cpu_key_k_offset (&key, 1);
 	set_cpu_key_k_type (&key, TYPE_INDIRECT);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/romfs/inode.c linux/fs/romfs/inode.c
--- linux.orig/fs/romfs/inode.c	Fri Nov 16 17:45:47 2001
+++ linux/fs/romfs/inode.c	Wed Jan 23 20:15:01 2002
@@ -108,7 +108,7 @@
 	s->u.generic_sbp = (void *) 0;
 	s->s_maxbytes = 0xFFFFFFFF;
 
-	bh = bread(dev, 0, ROMBSIZE);
+	bh = sb_bread(s, 0);
 	if (!bh) {
 		/* XXX merge with other printk? */
                 printk ("romfs: unable to read superblock\n");
@@ -188,7 +188,7 @@
 	if (count > maxsize || offset+count > maxsize)
 		count = maxsize-offset;
 
-	bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
+	bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 	if (!bh)
 		return -1;		/* error */
 
@@ -203,7 +203,7 @@
 	while (res < count) {
 		offset += maxsize;
 
-		bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
+		bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 		if (!bh)
 			return -1;
 		maxsize = min_t(unsigned long, count - res, ROMBSIZE);
@@ -226,7 +226,7 @@
 	if (offset >= maxsize || count > maxsize || offset+count>maxsize)
 		return -1;
 
-	bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
+	bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 	if (!bh)
 		return -1;		/* error */
 
@@ -241,7 +241,7 @@
 		offset += maxsize;
 		dest += maxsize;
 
-		bh = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
+		bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
 		if (!bh)
 			return -1;
 		maxsize = min_t(unsigned long, count - res, ROMBSIZE);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/smbfs/ChangeLog linux/fs/smbfs/ChangeLog
--- linux.orig/fs/smbfs/ChangeLog	Tue Jan 22 17:51:23 2002
+++ linux/fs/smbfs/ChangeLog	Mon Jan 14 18:10:06 2002
@@ -1,5 +1,15 @@
 ChangeLog for smbfs.
 
+2001-12-31 René Scharfe <l.s.r@web.de>
+
+	* inode.c: added smb_show_options to show mount options in /proc/mounts
+	* inode.c, getopt.c, getopt.h: merged flag and has_arg in struct option
+	* inode.c: use S_IRWXUGO where appropriate
+
+2001-12-22 Urban Widmark <urban@teststation.com>
+
+	* file.c, proc.c: Fix problems triggered by the "fsx test"
+
 2001-09-17 Urban Widmark <urban@teststation.com>
 
 	* proc.c: Use 4096 (was 512) as the blocksize for better write
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/smbfs/file.c linux/fs/smbfs/file.c
--- linux.orig/fs/smbfs/file.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/smbfs/file.c	Mon Jan 14 18:10:06 2002
@@ -270,7 +270,6 @@
 static int smb_prepare_write(struct file *file, struct page *page, 
 			     unsigned offset, unsigned to)
 {
-	kmap(page);
 	return 0;
 }
 
@@ -283,7 +282,6 @@
 	lock_kernel();
 	status = smb_updatepage(file, page, offset, to-offset);
 	unlock_kernel();
-	kunmap(page);
 	return status;
 }
 
@@ -349,8 +347,14 @@
 smb_file_release(struct inode *inode, struct file * file)
 {
 	lock_kernel();
-	if (!--inode->u.smbfs_i.openers)
+	if (!--inode->u.smbfs_i.openers) {
+		/* We must flush any dirty pages now as we won't be able to
+		   write anything after close. mmap can trigger this.
+		   "openers" should perhaps include mmap'ers ... */
+		filemap_fdatasync(inode->i_mapping);
+		filemap_fdatawait(inode->i_mapping);
 		smb_close(inode);
+	}
 	unlock_kernel();
 	return 0;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/smbfs/getopt.c linux/fs/smbfs/getopt.c
--- linux.orig/fs/smbfs/getopt.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/smbfs/getopt.c	Mon Jan 14 18:10:06 2002
@@ -46,7 +46,7 @@
 
 	for (i = 0; opts[i].name != NULL; i++) {
 		if (!strcmp(opts[i].name, token)) {
-			if (opts[i].has_arg && (!val || !*val)) {
+			if (!opts[i].flag && (!val || !*val)) {
 				printk("%s: the %s option requires an argument\n",
 				       caller, token);
 				return -1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/smbfs/getopt.h linux/fs/smbfs/getopt.h
--- linux.orig/fs/smbfs/getopt.h	Tue Jan 22 17:51:23 2002
+++ linux/fs/smbfs/getopt.h	Mon Jan 14 18:10:06 2002
@@ -3,7 +3,6 @@
 
 struct option {
 	const char *name;
-	int has_arg;
 	unsigned long flag;
 	int val;
 };
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/smbfs/inode.c linux/fs/smbfs/inode.c
--- linux.orig/fs/smbfs/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/smbfs/inode.c	Mon Jan 14 18:10:06 2002
@@ -22,6 +22,7 @@
 #include <linux/dcache.h>
 #include <linux/smp_lock.h>
 #include <linux/nls.h>
+#include <linux/seq_file.h>
 
 #include <linux/smb_fs.h>
 #include <linux/smbno.h>
@@ -41,9 +42,12 @@
 #define SMB_NLS_REMOTE ""
 #endif
 
+#define SMB_TTL_DEFAULT 1000
+
 static void smb_delete_inode(struct inode *);
 static void smb_put_super(struct super_block *);
 static int  smb_statfs(struct super_block *, struct statfs *);
+static int  smb_show_options(struct seq_file *, struct vfsmount *);
 
 static struct super_operations smb_sops =
 {
@@ -51,6 +55,7 @@
 	delete_inode:	smb_delete_inode,
 	put_super:	smb_put_super,
 	statfs:		smb_statfs,
+	show_options:	smb_show_options,
 };
 
 
@@ -259,21 +264,20 @@
 	clear_inode(ino);
 }
 
-/* FIXME: flags and has_arg could probably be merged. */
 static struct option opts[] = {
-	{ "version",	1, 0, 'v' },
-	{ "win95",	0, SMB_MOUNT_WIN95, 1 },
-	{ "oldattr",	0, SMB_MOUNT_OLDATTR, 1 },
-	{ "dirattr",	0, SMB_MOUNT_DIRATTR, 1 },
-	{ "case",	0, SMB_MOUNT_CASE, 1 },
-	{ "uid",	1, 0, 'u' },
-	{ "gid",	1, 0, 'g' },
-	{ "file_mode",	1, 0, 'f' },
-	{ "dir_mode",	1, 0, 'd' },
-	{ "iocharset",	1, 0, 'i' },
-	{ "codepage",	1, 0, 'c' },
-	{ "ttl",	1, 0, 't' },
-	{ NULL,		0, 0, 0}
+	{ "version",	0, 'v' },
+	{ "win95",	SMB_MOUNT_WIN95, 1 },
+	{ "oldattr",	SMB_MOUNT_OLDATTR, 1 },
+	{ "dirattr",	SMB_MOUNT_DIRATTR, 1 },
+	{ "case",	SMB_MOUNT_CASE, 1 },
+	{ "uid",	0, 'u' },
+	{ "gid",	0, 'g' },
+	{ "file_mode",	0, 'f' },
+	{ "dir_mode",	0, 'd' },
+	{ "iocharset",	0, 'i' },
+	{ "codepage",	0, 'c' },
+	{ "ttl",	0, 't' },
+	{ NULL,		0, 0}
 };
 
 static int
@@ -310,12 +314,10 @@
 			mnt->gid = value;
 			break;
 		case 'f':
-			mnt->file_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
-			mnt->file_mode |= S_IFREG;
+			mnt->file_mode = (value & S_IRWXUGO) | S_IFREG;
 			break;
 		case 'd':
-			mnt->dir_mode = value & (S_IRWXU | S_IRWXG | S_IRWXO);
-			mnt->dir_mode |= S_IFDIR;
+			mnt->dir_mode = (value & S_IRWXUGO) | S_IFDIR;
 			break;
 		case 'i':
 			strncpy(mnt->codepage.local_name, optarg, 
@@ -338,6 +340,45 @@
 	return c;
 }
 
+/*
+ * smb_show_options() is for displaying mount options in /proc/mounts.
+ * It tries to avoid showing settings that were not changed from their
+ * defaults.
+ */
+static int
+smb_show_options(struct seq_file *s, struct vfsmount *m)
+{
+	struct smb_mount_data_kernel *mnt = m->mnt_sb->u.smbfs_sb.mnt;
+	int i;
+
+	for (i = 0; opts[i].name != NULL; i++)
+		if (mnt->flags & opts[i].flag)
+			seq_printf(s, ",%s", opts[i].name);
+
+	if (mnt->uid != 0)
+		seq_printf(s, ",uid=%d", mnt->uid);
+	if (mnt->gid != 0)
+		seq_printf(s, ",gid=%d", mnt->gid);
+	if (mnt->mounted_uid != 0)
+		seq_printf(s, ",mounted_uid=%d", mnt->mounted_uid);
+
+	/* 
+	 * Defaults for file_mode and dir_mode are unknown to us; they
+	 * depend on the current umask of the user doing the mount.
+	 */
+	seq_printf(s, ",file_mode=%04o", mnt->file_mode & S_IRWXUGO);
+	seq_printf(s, ",dir_mode=%04o", mnt->dir_mode & S_IRWXUGO);
+
+	if (strcmp(mnt->codepage.local_name, CONFIG_NLS_DEFAULT))
+		seq_printf(s, ",iocharset=%s", mnt->codepage.local_name);
+	if (strcmp(mnt->codepage.remote_name, SMB_NLS_REMOTE))
+		seq_printf(s, ",codepage=%s", mnt->codepage.remote_name);
+
+	if (mnt->ttl != SMB_TTL_DEFAULT)
+		seq_printf(s, ",ttl=%d", mnt->ttl);
+
+	return 0;
+}
 
 static void
 smb_put_super(struct super_block *sb)
@@ -425,7 +466,7 @@
 	strncpy(mnt->codepage.remote_name, SMB_NLS_REMOTE,
 		SMB_NLS_MAXNAMELEN);
 
-	mnt->ttl = 1000;
+	mnt->ttl = SMB_TTL_DEFAULT;
 	if (ver == SMB_MOUNT_OLDVERSION) {
 		mnt->version = oldmnt->version;
 
@@ -434,12 +475,8 @@
 		mnt->uid = oldmnt->uid;
 		mnt->gid = oldmnt->gid;
 
-		mnt->file_mode =
-			oldmnt->file_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
-		mnt->dir_mode =
-			oldmnt->dir_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
-		mnt->file_mode |= S_IFREG;
-		mnt->dir_mode  |= S_IFDIR;
+		mnt->file_mode = (oldmnt->file_mode & S_IRWXUGO) | S_IFREG;
+		mnt->dir_mode = (oldmnt->dir_mode & S_IRWXUGO) | S_IFDIR;
 
 		mnt->flags = (oldmnt->file_mode >> 9);
 	} else {
@@ -510,7 +547,7 @@
 {
 	struct inode *inode = dentry->d_inode;
 	struct smb_sb_info *server = server_from_dentry(dentry);
-	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO);
+	unsigned int mask = (S_IFREG | S_IFDIR | S_IRWXUGO);
 	int error, changed, refresh = 0;
 	struct smb_fattr fattr;
 
@@ -535,6 +572,10 @@
 		VERBOSE("changing %s/%s, old size=%ld, new size=%ld\n",
 			DENTRY_PATH(dentry),
 			(long) inode->i_size, (long) attr->ia_size);
+
+		filemap_fdatasync(inode->i_mapping);
+		filemap_fdatawait(inode->i_mapping);
+
 		error = smb_open(dentry, O_WRONLY);
 		if (error)
 			goto out;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/super.c linux/fs/super.c
--- linux.orig/fs/super.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/super.c	Wed Jan 23 21:21:17 2002
@@ -271,6 +271,160 @@
 	return fs;
 }
 
+/**
+ *	alloc_super	-	create new superblock
+ *
+ *	Allocates and initializes a new &struct super_block.  alloc_super()
+ *	returns a pointer new superblock or %NULL if allocation had failed.
+ */
+static struct super_block *alloc_super(void)
+{
+	struct super_block *s = kmalloc(sizeof(struct super_block),  GFP_USER);
+	if (s) {
+		memset(s, 0, sizeof(struct super_block));
+		INIT_LIST_HEAD(&s->s_dirty);
+		INIT_LIST_HEAD(&s->s_locked_inodes);
+		INIT_LIST_HEAD(&s->s_files);
+		INIT_LIST_HEAD(&s->s_instances);
+		init_rwsem(&s->s_umount);
+		sema_init(&s->s_lock, 1);
+		down_write(&s->s_umount);
+		s->s_count = S_BIAS;
+		atomic_set(&s->s_active, 1);
+		sema_init(&s->s_vfs_rename_sem,1);
+		sema_init(&s->s_nfsd_free_path_sem,1);
+		sema_init(&s->s_dquot.dqio_sem, 1);
+		sema_init(&s->s_dquot.dqoff_sem, 1);
+		s->s_maxbytes = MAX_NON_LFS;
+	}
+	return s;
+}
+
+/**
+ *	destroy_super	-	frees a superblock
+ *	@s: superblock to free
+ *
+ *	Frees a superblock.
+ */
+static inline void destroy_super(struct super_block *s)
+{
+	kfree(s);
+}
+
+/* Superblock refcounting  */
+
+/**
+ *	deactivate_super	-	turn an active reference into temporary
+ *	@s: superblock to deactivate
+ *
+ *	Turns an active reference into temporary one.  Returns 0 if there are
+ *	other active references, 1 if we had deactivated the last one.
+ */
+static inline int deactivate_super(struct super_block *s)
+{
+	if (!atomic_dec_and_lock(&s->s_active, &sb_lock))
+		return 0;
+	s->s_count -= S_BIAS-1;
+	spin_unlock(&sb_lock);
+	return 1;
+}
+
+/**
+ *	put_super	-	drop a temporary reference to superblock
+ *	@s: superblock in question
+ *
+ *	Drops a temporary reference, frees superblock if there's no
+ *	references left.
+ */
+static inline void put_super(struct super_block *s)
+{
+	spin_lock(&sb_lock);
+	if (!--s->s_count)
+		destroy_super(s);
+	spin_unlock(&sb_lock);
+}
+
+/**
+ *	grab_super	- acquire an active reference
+ *	@s	- reference we are trying to make active
+ *
+ *	Tries to acquire an active reference.  grab_super() is used when we
+ * 	had just found a superblock in super_blocks or fs_type->fs_supers
+ *	and want to turn it into a full-blown active reference.  grab_super()
+ *	is called with sb_lock held and drops it.  Returns 1 in case of
+ *	success, 0 if we had failed (superblock contents was already dead or
+ *	dying when grab_super() had been called).
+ */
+static int grab_super(struct super_block *s)
+{
+	s->s_count++;
+	spin_unlock(&sb_lock);
+	down_write(&s->s_umount);
+	if (s->s_root) {
+		spin_lock(&sb_lock);
+		if (s->s_count > S_BIAS) {
+			atomic_inc(&s->s_active);
+			s->s_count--;
+			spin_unlock(&sb_lock);
+			return 1;
+		}
+		spin_unlock(&sb_lock);
+	}
+	up_write(&s->s_umount);
+	put_super(s);
+	return 0;
+}
+ 
+/**
+ *	insert_super	-	put superblock on the lists
+ *	@s:	superblock in question
+ *	@type:	filesystem type it will belong to
+ *
+ *	Associates superblock with fs type and puts it on per-type and global
+ *	superblocks' lists.  Should be called with sb_lock held; drops it.
+ */
+static void insert_super(struct super_block *s, struct file_system_type *type)
+{
+	s->s_type = type;
+	list_add(&s->s_list, super_blocks.prev);
+	list_add(&s->s_instances, &type->fs_supers);
+	spin_unlock(&sb_lock);
+	get_filesystem(type);
+}
+
+void put_unnamed_dev(kdev_t dev);	/* should become static */
+
+/**
+ *	remove_super	-	makes superblock unreachable
+ *	@s:	superblock in question
+ *
+ *	Removes superblock from the lists, unlocks it, drop the reference
+ *	and releases the hosting device.  @s should have no active
+ *	references by that time and after remove_super() it's essentially
+ *	in rundown mode - all remaining references are temporary, no new
+ *	reference of any sort are going to appear and all holders of
+ *	temporary ones will eventually drop them.  At that point superblock
+ *	itself will be destroyed; all its contents is already gone.
+ */
+static void remove_super(struct super_block *s)
+{
+	kdev_t dev = s->s_dev;
+	struct block_device *bdev = s->s_bdev;
+	struct file_system_type *fs = s->s_type;
+
+	spin_lock(&sb_lock);
+	list_del(&s->s_list);
+	list_del(&s->s_instances);
+	spin_unlock(&sb_lock);
+	up_write(&s->s_umount);
+	put_super(s);
+	put_filesystem(fs);
+	if (bdev)
+		blkdev_put(bdev, BDEV_FS);
+	else
+		put_unnamed_dev(dev);
+}
+
 struct vfsmount *alloc_vfsmnt(void);
 void free_vfsmnt(struct vfsmount *mnt);
 void set_devname(struct vfsmount *mnt, const char *name);
@@ -279,14 +433,6 @@
 extern struct vfsmount *root_vfsmnt;
 extern int graft_tree(struct vfsmount *mnt, struct nameidata *nd);
 
-static inline void __put_super(struct super_block *sb)
-{
-	spin_lock(&sb_lock);
-	if (!--sb->s_count)
-		kfree(sb);
-	spin_unlock(&sb_lock);
-}
-
 static inline struct super_block * find_super(kdev_t dev)
 {
 	struct list_head *p;
@@ -304,14 +450,7 @@
 void drop_super(struct super_block *sb)
 {
 	up_read(&sb->s_umount);
-	__put_super(sb);
-}
-
-static void put_super(struct super_block *sb)
-{
-	atomic_dec(&sb->s_active);
-	up_write(&sb->s_umount);
-	__put_super(sb);
+	put_super(sb);
 }
 
 static inline void write_super(struct super_block *sb)
@@ -322,7 +461,7 @@
 			sb->s_op->write_super(sb);
 	unlock_super(sb);
 }
- 
+
 /*
  * Note: check the dirty flag before waiting, so we don't
  * hold up the sync while mounting a device. (The newly
@@ -410,37 +549,6 @@
 	return err;
 }
 
-/**
- *	get_empty_super	-	find empty superblocks
- *
- *	Find a superblock with no device assigned. A free superblock is 
- *	found and returned. If neccessary new superblocks are allocated.
- *	%NULL is returned if there are insufficient resources to complete
- *	the request.
- */
- 
-static struct super_block *alloc_super(void)
-{
-	struct super_block *s = kmalloc(sizeof(struct super_block),  GFP_USER);
-	if (s) {
-		memset(s, 0, sizeof(struct super_block));
-		INIT_LIST_HEAD(&s->s_dirty);
-		INIT_LIST_HEAD(&s->s_locked_inodes);
-		INIT_LIST_HEAD(&s->s_files);
-		INIT_LIST_HEAD(&s->s_instances);
-		init_rwsem(&s->s_umount);
-		sema_init(&s->s_lock, 1);
-		s->s_count = 1;
-		atomic_set(&s->s_active, 1);
-		sema_init(&s->s_vfs_rename_sem,1);
-		sema_init(&s->s_nfsd_free_path_sem,1);
-		sema_init(&s->s_dquot.dqio_sem, 1);
-		sema_init(&s->s_dquot.dqoff_sem, 1);
-		s->s_maxbytes = MAX_NON_LFS;
-	}
-	return s;
-}
-
 static struct super_block * read_super(kdev_t dev, struct block_device *bdev,
 				       struct file_system_type *type, int flags,
 				       void *data)
@@ -452,13 +560,8 @@
 	s->s_dev = dev;
 	s->s_bdev = bdev;
 	s->s_flags = flags;
-	s->s_type = type;
 	spin_lock(&sb_lock);
-	list_add (&s->s_list, super_blocks.prev);
-	list_add (&s->s_instances, &type->fs_supers);
-	s->s_count += S_BIAS;
-	spin_unlock(&sb_lock);
-	down_write(&s->s_umount);
+	insert_super(s, type);
 	lock_super(s);
 	if (!type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
 		goto out_fail;
@@ -471,16 +574,9 @@
 	return s;
 
 out_fail:
-	s->s_dev = 0;
-	s->s_bdev = 0;
-	s->s_type = NULL;
 	unlock_super(s);
-	spin_lock(&sb_lock);
-	list_del(&s->s_list);
-	list_del(&s->s_instances);
-	s->s_count -= S_BIAS;
-	spin_unlock(&sb_lock);
-	put_super(s);
+	deactivate_super(s);
+	remove_super(s);
 	return NULL;
 }
 
@@ -512,25 +608,6 @@
 			kdevname(dev));
 }
 
-static int grab_super(struct super_block *sb)
-{
-	sb->s_count++;
-	atomic_inc(&sb->s_active);
-	spin_unlock(&sb_lock);
-	down_write(&sb->s_umount);
-	if (sb->s_root) {
-		spin_lock(&sb_lock);
-		if (sb->s_count > S_BIAS) {
-			sb->s_count--;
-			spin_unlock(&sb_lock);
-			return 1;
-		}
-		spin_unlock(&sb_lock);
-	}
-	put_super(sb);
-	return 0;
-}
-
 static struct super_block *get_sb_bdev(struct file_system_type *fs_type,
 	char *dev_name, int flags, void * data)
 {
@@ -574,14 +651,17 @@
 		goto out;
 	check_disk_change(dev);
 	error = -EACCES;
-	if (!(flags & MS_RDONLY) && is_read_only(dev))
-		goto out1;
+	if (!(flags & MS_RDONLY) && is_read_only(dev)) {
+		blkdev_put(bdev, BDEV_FS);
+		goto out;
+	}
 
 	error = -ENOMEM;
 	s = alloc_super();
-	if (!s)
-		goto out1;
-	down_write(&s->s_umount);
+	if (!s) {
+		blkdev_put(bdev, BDEV_FS);
+		goto out;
+	}
 
 	error = -EBUSY;
 restart:
@@ -594,12 +674,13 @@
 		if (old->s_type != fs_type ||
 		    ((flags ^ old->s_flags) & MS_RDONLY)) {
 			spin_unlock(&sb_lock);
-			put_super(s);
-			goto out1;
+			destroy_super(s);
+			blkdev_put(bdev, BDEV_FS);
+			goto out;
 		}
 		if (!grab_super(old))
 			goto restart;
-		put_super(s);
+		destroy_super(s);
 		blkdev_put(bdev, BDEV_FS);
 		path_release(&nd);
 		return old;
@@ -607,12 +688,7 @@
 	s->s_dev = dev;
 	s->s_bdev = bdev;
 	s->s_flags = flags;
-	s->s_type = fs_type;
-	list_add (&s->s_list, super_blocks.prev);
-	list_add (&s->s_instances, &fs_type->fs_supers);
-	s->s_count += S_BIAS;
-
-	spin_unlock(&sb_lock);
+	insert_super(s, fs_type);
 
 	error = -EINVAL;
 	lock_super(s);
@@ -620,23 +696,13 @@
 		goto out_fail;
 	s->s_flags |= MS_ACTIVE;
 	unlock_super(s);
-	get_filesystem(fs_type);
 	path_release(&nd);
 	return s;
 
 out_fail:
-	s->s_dev = 0;
-	s->s_bdev = 0;
-	s->s_type = NULL;
 	unlock_super(s);
-	spin_lock(&sb_lock);
-	list_del(&s->s_list);
-	list_del(&s->s_instances);
-	s->s_count -= S_BIAS;
-	spin_unlock(&sb_lock);
-	put_super(s);
-out1:
-	blkdev_put(bdev, BDEV_FS);
+	deactivate_super(s);
+	remove_super(s);
 out:
 	path_release(&nd);
 	return ERR_PTR(error);
@@ -652,11 +718,8 @@
 		struct super_block * sb;
 		error = -EINVAL;
 		sb = read_super(dev, NULL, fs_type, flags, data);
-		if (sb) {
-			get_filesystem(fs_type);
+		if (sb)
 			return sb;
-		}
-		put_unnamed_dev(dev);
 	}
 	return ERR_PTR(error);
 }
@@ -667,7 +730,6 @@
 	struct super_block * s = alloc_super();
 	if (!s)
 		return ERR_PTR(-ENOMEM);
-	down_write(&s->s_umount);
 	/*
 	 * Get the superblock of kernel-wide instance, but
 	 * keep the reference to fs_type.
@@ -680,61 +742,43 @@
 				s_instances);
 		if (!grab_super(old))
 			goto retry;
-		put_super(s);
+		destroy_super(s);
 		do_remount_sb(old, flags, data);
 		return old;
 	} else {
 		kdev_t dev = get_unnamed_dev();
 		if (!dev) {
 			spin_unlock(&sb_lock);
-			put_super(s);
+			destroy_super(s);
 			return ERR_PTR(-EMFILE);
 		}
 		s->s_dev = dev;
 		s->s_flags = flags;
-		s->s_type = fs_type;
-		list_add (&s->s_list, super_blocks.prev);
-		list_add (&s->s_instances, &fs_type->fs_supers);
-		s->s_count += S_BIAS;
-		spin_unlock(&sb_lock);
+		insert_super(s, fs_type);
 		lock_super(s);
 		if (!fs_type->read_super(s, data, flags & MS_VERBOSE ? 1 : 0))
 			goto out_fail;
 		s->s_flags |= MS_ACTIVE;
 		unlock_super(s);
-		get_filesystem(fs_type);
 		return s;
 
 	out_fail:
-		s->s_dev = 0;
-		s->s_bdev = 0;
-		s->s_type = NULL;
 		unlock_super(s);
-		spin_lock(&sb_lock);
-		list_del(&s->s_list);
-		list_del(&s->s_instances);
-		s->s_count -= S_BIAS;
-		spin_unlock(&sb_lock);
-		put_super(s);
-		put_unnamed_dev(dev);
+		deactivate_super(s);
+		remove_super(s);
 		return ERR_PTR(-EINVAL);
 	}
 }
 
 void kill_super(struct super_block *sb)
 {
-	struct block_device *bdev;
-	kdev_t dev;
 	struct dentry *root = sb->s_root;
 	struct file_system_type *fs = sb->s_type;
 	struct super_operations *sop = sb->s_op;
 
-	if (!atomic_dec_and_lock(&sb->s_active, &sb_lock))
+	if (!deactivate_super(sb))
 		return;
 
-	sb->s_count -= S_BIAS;
-	spin_unlock(&sb_lock);
-
 	down_write(&sb->s_umount);
 	lock_kernel();
 	sb->s_root = NULL;
@@ -760,24 +804,9 @@
 			"Self-destruct in 5 seconds.  Have a nice day...\n");
 	}
 
-	dev = sb->s_dev;
-	sb->s_dev = 0;		/* Free the superblock */
-	bdev = sb->s_bdev;
-	sb->s_bdev = NULL;
-	put_filesystem(fs);
-	sb->s_type = NULL;
-	unlock_super(sb);
 	unlock_kernel();
-	if (bdev)
-		blkdev_put(bdev, BDEV_FS);
-	else
-		put_unnamed_dev(dev);
-	spin_lock(&sb_lock);
-	list_del(&sb->s_list);
-	list_del(&sb->s_instances);
-	spin_unlock(&sb_lock);
-	atomic_inc(&sb->s_active);
-	put_super(sb);
+	unlock_super(sb);
+	remove_super(sb);
 }
 
 /*
@@ -1018,6 +1047,7 @@
 		 * Allow the user to distinguish between failed open
 		 * and bad superblock on root device.
 		 */
+Eio:
 		printk ("VFS: Cannot open root device \"%s\" or %s\n",
 			root_device_name, kdevname (ROOT_DEV));
 		printk ("Please append a correct \"root=\" boot option\n");
@@ -1040,11 +1070,17 @@
 		struct file_system_type * fs_type = get_fs_type(p);
 		if (!fs_type)
   			continue;
+		atomic_inc(&bdev->bd_count);
+		retval = blkdev_get(bdev, mode, 0, BDEV_FS);
+		if (retval)
+			goto Eio;
   		sb = read_super(ROOT_DEV, bdev, fs_type,
 				root_mountflags, root_mount_data);
-		if (sb) 
-			goto mount_it;
 		put_filesystem(fs_type);
+		if (sb) {
+			blkdev_put(bdev, BDEV_FS);
+			goto mount_it;
+		}
 	}
 	panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));
 
@@ -1056,6 +1092,7 @@
 	putname(fs_names);
 	if (path_start >= 0) {
 		name = path + path_start;
+		devfs_unregister (devfs_find_handle(NULL, "root", 0, 0, 0, 0));
 		devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
 				  name + 5, NULL, NULL);
 		memcpy (name, "/dev/", 5);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/ChangeLog linux/fs/sysv/ChangeLog
--- linux.orig/fs/sysv/ChangeLog	Fri Dec 21 17:42:03 2001
+++ linux/fs/sysv/ChangeLog	Mon Feb  4 17:38:23 2002
@@ -1,4 +1,10 @@
-Sat Dec 15 2001  Christoph Hellwig  <hch@caldera.de>
+Thu Jan 10 2002  Andrew Morton  <akpm@zip.com.au>
+
+	* dir_commit_chunk(): call writeout_one_page() as well as
+	  waitfor_one_page() for IS_SYNC directories, so that we
+	  actually do sync the directory.
+
+Sat Dec 15 2001  Christoph Hellwig  <hch@infradead.org>
 
 	* inode.c (sysv_read_inode): Mark inode as bad in case of failure.
 	* super.c (complete_read_super): Check for bad root inode.
@@ -7,16 +13,17 @@
 
 	* file.c (sysv_sync_file): Call fsync_inode_data_buffers.
 
-Fri Oct 26 2001  Christoph Hellwig  <hch@caldera.de>
+Fri Oct 26 2001  Christoph Hellwig  <hch@infradead.org>
 
 	* dir.c, ialloc.c, namei.c, include/linux/sysv_fs_i.h:
 	Implement per-Inode lookup offset cache.
 	Modelled after Ted's ext2 patch.
 
-Fri Oct 26 2001  Christoph Hellwig  <hch@caldera.de>
+Fri Oct 26 2001  Christoph Hellwig  <hch@infradead.org>
 
 	* inode.c, super.c, include/linux/sysv_fs.h,
 	  include/linux/sysv_fs_sb.h:
 	Remove symlink faking.	Noone really wants to use these as
 	linux filesystems and native OSes don't support it anyway.
+
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/balloc.c linux/fs/sysv/balloc.c
--- linux.orig/fs/sysv/balloc.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/sysv/balloc.c	Wed Jan 23 20:15:01 2002
@@ -73,7 +73,7 @@
 	 */
 	if (count == sb->sv_flc_size || count == 0) {
 		block += sb->sv_block_base;
-		bh = getblk(sb->s_dev, block, sb->s_blocksize);
+		bh = sb_getblk(sb, block);
 		if (!bh) {
 			printk("sysv_free_block: getblk() failed\n");
 			unlock_super(sb);
@@ -125,7 +125,7 @@
 		unsigned count;
 
 		block += sb->sv_block_base;
-		if (!(bh = bread(sb->s_dev, block, sb->s_blocksize))) {
+		if (!(bh = sb_bread(sb, block))) {
 			printk("sysv_new_block: cannot read free-list block\n");
 			/* retry this same block next time */
 			*sb->sv_bcache_count = cpu_to_fs16(sb, 1);
@@ -196,7 +196,7 @@
 		if (block < sb->sv_firstdatazone || block >= sb->sv_nzones)
 			goto Einval;
 		block += sb->sv_block_base;
-		bh = bread(sb->s_dev, block, sb->s_blocksize);
+		bh = sb_bread(sb, block);
 		if (!bh)
 			goto Eio;
 		n = fs16_to_cpu(sb, *(u16*)bh->b_data);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/dir.c linux/fs/sysv/dir.c
--- linux.orig/fs/sysv/dir.c	Fri Nov  9 21:45:35 2001
+++ linux/fs/sysv/dir.c	Tue Jan 22 23:04:46 2002
@@ -43,8 +43,13 @@
 
 	dir->i_version = ++event;
 	page->mapping->a_ops->commit_write(NULL, page, from, to);
-	if (IS_SYNC(dir))
-		err = waitfor_one_page(page);
+	if (IS_SYNC(dir)) {
+		int err2;
+		err = writeout_one_page(page);
+		err2 = waitfor_one_page(page);
+		if (err == 0)
+			err = err2;
+	}
 	return err;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/ialloc.c linux/fs/sysv/ialloc.c
--- linux.orig/fs/sysv/ialloc.c	Fri Nov  9 21:45:35 2001
+++ linux/fs/sysv/ialloc.c	Wed Jan 23 20:15:01 2002
@@ -55,7 +55,7 @@
 	struct sysv_inode *res;
 	int block = sb->sv_firstinodezone + sb->sv_block_base;
 	block += (ino-1) >> sb->sv_inodes_per_block_bits;
-	*bh = bread(sb->s_dev, block, sb->s_blocksize);
+	*bh = sb_bread(sb, block);
 	if (!*bh)
 		return NULL;
 	res = (struct sysv_inode *) (*bh)->b_data;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/itree.c linux/fs/sysv/itree.c
--- linux.orig/fs/sysv/itree.c	Mon Jul  2 21:03:04 2001
+++ linux/fs/sysv/itree.c	Wed Jan 23 20:15:01 2002
@@ -86,8 +86,7 @@
 			    Indirect chain[],
 			    int *err)
 {
-	kdev_t dev = inode->i_dev;
-	int size = inode->i_sb->s_blocksize;
+	struct super_block *sb = inode->i_sb;
 	Indirect *p = chain;
 	struct buffer_head *bh;
 
@@ -96,8 +95,8 @@
 	if (!p->key)
 		goto no_block;
 	while (--depth) {
-		int block = block_to_cpu(inode->i_sb, p->key);
-		bh = bread(dev, block, size);
+		int block = block_to_cpu(sb, p->key);
+		bh = sb_bread(sb, block);
 		if (!bh)
 			goto failure;
 		if (!verify_chain(chain, p))
@@ -139,7 +138,7 @@
 		 * the pointer to new one, then send parent to disk.
 		 */
 		parent = block_to_cpu(inode->i_sb, branch[n-1].key);
-		bh = getblk(inode->i_dev, parent, blocksize);
+		bh = sb_getblk(inode->i_sb, parent);
 		lock_buffer(bh);
 		memset(bh->b_data, 0, blocksize);
 		branch[n].bh = bh;
@@ -336,7 +335,7 @@
 				continue;
 			*p = 0;
 			block = block_to_cpu(sb, nr);
-			bh = bread(inode->i_dev, block, sb->s_blocksize);
+			bh = sb_bread(sb, block);
 			if (!bh)
 				continue;
 			free_branches(inode, (u32*)bh->b_data,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/super.c linux/fs/sysv/super.c
--- linux.orig/fs/sysv/super.c	Fri Dec 21 17:42:03 2001
+++ linux/fs/sysv/super.c	Wed Jan 23 20:15:01 2002
@@ -362,11 +362,12 @@
 	if (64 != sizeof (struct sysv_inode))
 		panic("sysv fs: bad i-node size");
 	set_blocksize(dev,BLOCK_SIZE);
+	sb->s_blocksize = BLOCK_SIZE;
 	sb->sv_block_base = 0;
 
 	for (i = 0; i < sizeof(flavours)/sizeof(flavours[0]) && !size; i++) {
 		brelse(bh);
-		bh = bread(dev, flavours[i].block, BLOCK_SIZE);
+		bh = sb_bread(sb, flavours[i].block);
 		if (!bh)
 			continue;
 		size = flavours[i].test(sb, bh);
@@ -380,8 +381,9 @@
 			blocknr = bh->b_blocknr << 1;
 			brelse(bh);
 			set_blocksize(dev, 512);
-			bh1 = bread(dev, blocknr, 512);
-			bh = bread(dev, blocknr + 1, 512);
+			sb->s_blocksize = 512;
+			bh1 = sb_bread(sb, blocknr);
+			bh = sb_bread(sb, blocknr + 1);
 			break;
 		case 2:
 			bh1 = bh;
@@ -390,7 +392,8 @@
 			blocknr = bh->b_blocknr >> 1;
 			brelse(bh);
 			set_blocksize(dev, 2048);
-			bh1 = bh = bread(dev, blocknr, 2048);
+			sb->s_blocksize = 2048;
+			bh1 = bh = sb_bread(sb, blocknr);
 			break;
 		default:
 			goto Ebadsize;
@@ -441,8 +444,9 @@
 	sb->sv_bytesex = BYTESEX_PDP;
 
 	set_blocksize(dev, 512);
+	sb->s_blocksize = 512;
 
-	if ((bh = bread(dev, 1, 512)) == NULL) {
+	if ((bh = sb_bread(sb, 1)) == NULL) {
 		if (!silent)
 			printk("VFS: unable to read V7 FS superblock on "
 			       "device %s.\n", bdevname(dev));
@@ -458,7 +462,7 @@
 
 	/* plausibility check on root inode: it is a directory,
 	   with a nonzero size that is a multiple of 16 */
-	if ((bh2 = bread(dev, 2, 512)) == NULL)
+	if ((bh2 = sb_bread(sb, 2)) == NULL)
 		goto failed;
 	v7i = (struct sysv_inode *)(bh2->b_data + 64);
 	if ((fs16_to_cpu(sb,v7i->i_mode) & ~0777) != S_IFDIR ||
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/sysv/symlink.c linux/fs/sysv/symlink.c
--- linux.orig/fs/sysv/symlink.c	Sun Sep  2 17:34:36 2001
+++ linux/fs/sysv/symlink.c	Mon Feb  4 17:38:23 2002
@@ -2,7 +2,7 @@
  *  linux/fs/sysv/symlink.c
  *
  *  Handling of System V filesystem fast symlinks extensions.
- *  Aug 2001, Christoph Hellwig (hch@caldera.de)
+ *  Aug 2001, Christoph Hellwig (hch@infradead.org)
  */
 
 #include <linux/fs.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/balloc.c linux/fs/udf/balloc.c
--- linux.orig/fs/udf/balloc.c	Thu Oct 11 15:59:24 2001
+++ linux/fs/udf/balloc.c	Wed Jan 23 20:15:01 2002
@@ -98,7 +98,7 @@
 	loc.logicalBlockNum = bitmap->s_extPosition;
 	loc.partitionReferenceNum = UDF_SB_PARTITION(sb);
 
-	bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block), sb->s_blocksize);
+	bh = udf_tread(sb, udf_get_lb_pblock(sb, loc, block));
 	if (!bh)
 	{
 		retval = -EIO;
@@ -463,7 +463,7 @@
 	elen = 0;
 	obloc = nbloc = UDF_I_LOCATION(table);
 
-	obh = nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0), sb->s_blocksize);
+	obh = nbh = udf_tread(sb, udf_get_lb_pblock(sb, nbloc, 0));
 	atomic_inc(&nbh->b_count);
 
 	while (count && (etype =
@@ -571,8 +571,7 @@
 			elen -= sb->s_blocksize;
 
 			if (!(nbh = udf_tread(sb,
-				udf_get_lb_pblock(sb, nbloc, 0),
-				sb->s_blocksize)))
+				udf_get_lb_pblock(sb, nbloc, 0))))
 			{
 				udf_release_data(obh);
 				goto error_return;
@@ -689,7 +688,7 @@
 	extoffset = sizeof(struct UnallocatedSpaceEntry);
 	bloc = UDF_I_LOCATION(table);
 
-	bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0), sb->s_blocksize);
+	bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0));
 	eloc.logicalBlockNum = 0xFFFFFFFF;
 
 	while (first_block != eloc.logicalBlockNum && (etype =
@@ -766,7 +765,7 @@
 	extoffset = sizeof(struct UnallocatedSpaceEntry);
 	bloc = UDF_I_LOCATION(table);
 
-	goal_bh = bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0), sb->s_blocksize);
+	goal_bh = bh = udf_tread(sb, udf_get_lb_pblock(sb, bloc, 0));
 	atomic_inc(&goal_bh->b_count);
 
 	while (spread && (etype =
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/dir.c linux/fs/udf/dir.c
--- linux.orig/fs/udf/dir.c	Thu Oct 11 15:59:24 2001
+++ linux/fs/udf/dir.c	Wed Jan 23 20:15:01 2002
@@ -146,7 +146,7 @@
 		return -ENOENT;
 	}
 
-	if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+	if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
 	{
 		udf_release_data(bh);
 		return -EIO;
@@ -160,7 +160,7 @@
 		for (num=0; i>0; i--)
 		{
 			block = udf_get_lb_pblock(dir->i_sb, eloc, offset+i);
-			tmp = udf_tgetblk(dir->i_sb, block, dir->i_sb->s_blocksize);
+			tmp = udf_tgetblk(dir->i_sb, block);
 			if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
 				bha[num++] = tmp;
 			else
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/directory.c linux/fs/udf/directory.c
--- linux.orig/fs/udf/directory.c	Tue Jun 12 02:15:27 2001
+++ linux/fs/udf/directory.c	Wed Jan 23 20:15:01 2002
@@ -60,7 +60,7 @@
 		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
 		if (!block)
 			return NULL;
-		if (!(*bh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+		if (!(*bh = udf_tread(dir->i_sb, block)))
 			return NULL;
 	}
 	else if (*offset > dir->i_sb->s_blocksize)
@@ -74,7 +74,7 @@
 		block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
 		if (!block)
 			return NULL;
-		if (!((*bh) = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+		if (!((*bh) = udf_tread(dir->i_sb, block)))
 			return NULL;
 
 		memcpy((Uint8 *)ad + remainder, (*bh)->b_data, ad_size - remainder);
@@ -117,7 +117,7 @@
 			*extoffset = lextoffset;
 
 		udf_release_data(fibh->sbh);
-		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
 			return NULL;
 		fibh->soffset = fibh->eoffset = 0;
 
@@ -129,7 +129,7 @@
 			for (num=0; i>0; i--)
 			{
 				block = udf_get_lb_pblock(dir->i_sb, *eloc, *offset+i);
-				tmp = udf_tgetblk(dir->i_sb, block, dir->i_sb->s_blocksize);
+				tmp = udf_tgetblk(dir->i_sb, block);
 				if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
 					bha[num++] = tmp;
 				else
@@ -183,7 +183,7 @@
 		fibh->soffset -= dir->i_sb->s_blocksize;
 		fibh->eoffset -= dir->i_sb->s_blocksize;
 
-		if (!(fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+		if (!(fibh->ebh = udf_tread(dir->i_sb, block)))
 			return NULL;
 
 		if (sizeof(struct FileIdentDesc) > - fibh->soffset)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/file.c linux/fs/udf/file.c
--- linux.orig/fs/udf/file.c	Thu Oct 11 15:59:24 2001
+++ linux/fs/udf/file.c	Wed Jan 23 20:15:01 2002
@@ -57,7 +57,7 @@
 	kaddr = kmap(page);
 	memset(kaddr, 0, PAGE_CACHE_SIZE);
 	block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
-	bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize);
+	bh = sb_bread(inode->i_sb, block);
 	memcpy(kaddr, bh->b_data + udf_ext0_offset(inode), inode->i_size);
 	brelse(bh);
 	flush_dcache_page(page);
@@ -80,7 +80,7 @@
 
 	kaddr = kmap(page);
 	block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
-	bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize);
+	bh = sb_bread(inode->i_sb, block);
 	memcpy(bh->b_data + udf_ext0_offset(inode), kaddr, inode->i_size);
 	mark_buffer_dirty(bh);
 	brelse(bh);
@@ -105,7 +105,7 @@
 	char *kaddr = page_address(page);
 
 	block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
-	bh = bread (inode->i_dev, block, inode->i_sb->s_blocksize);
+	bh = sb_bread(inode->i_sb, block);
 	memcpy(bh->b_data + udf_file_entry_alloc_offset(inode) + offset,
 		kaddr + offset, to-offset);
 	mark_buffer_dirty(bh);
@@ -246,8 +246,7 @@
 
 	/* ok, we need to read the inode */
 	bh = udf_tread(inode->i_sb,
-		udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0),
-		inode->i_sb->s_blocksize);
+		udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0));
 
 	if (!bh)
 	{
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/inode.c linux/fs/udf/inode.c
--- linux.orig/fs/udf/inode.c	Fri Oct 12 20:48:42 2001
+++ linux/fs/udf/inode.c	Wed Jan 23 20:15:01 2002
@@ -184,7 +184,7 @@
 	}
 
 	block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
-	bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize);
+	bh = udf_tread(inode->i_sb, block);
 	if (!bh)
 		return;
 	page = grab_cache_page(inode->i_mapping, 0);
@@ -251,10 +251,10 @@
 		UDF_I_LOCATION(inode).partitionReferenceNum, 0);
 	if (!newblock)
 		return NULL;
-	sbh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
+	sbh = udf_tread(inode->i_sb, inode->i_ino);
 	if (!sbh)
 		return NULL;
-	dbh = udf_tgetblk(inode->i_sb, newblock, inode->i_sb->s_blocksize);
+	dbh = udf_tgetblk(inode->i_sb, newblock);
 	if (!dbh)
 		return NULL;
 	lock_buffer(dbh);
@@ -382,7 +382,7 @@
 	if (!*err && buffer_mapped(&dummy))
 	{
 		struct buffer_head *bh;
-		bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize);
+		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
 		if (buffer_new(&dummy))
 		{
 			lock_buffer(bh);
@@ -886,8 +886,7 @@
 				udf_file_entry_alloc_offset(inode);
 
 			if ((bh = udf_tread(inode->i_sb,
-				udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0),
-				inode->i_sb->s_blocksize)))
+				udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0))))
 			{
 				memset(bh->b_data + offset, 0x00, inode->i_sb->s_blocksize - offset);
 				mark_buffer_dirty(bh);
@@ -1322,8 +1321,7 @@
 	int err = 0;
 
 	bh = udf_tread(inode->i_sb,
-		udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0),
-		inode->i_sb->s_blocksize);
+		udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0));
 
 	if (!bh)
 	{
@@ -1624,8 +1622,7 @@
 	if (!(*bh))
 	{
 		if (!(*bh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, *bloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, *bloc, 0));
@@ -1653,7 +1650,7 @@
 			return -1;
 		}
 		if (!(nbh = udf_tgetblk(inode->i_sb, udf_get_lb_pblock(inode->i_sb,
-			*bloc, 0), inode->i_sb->s_blocksize)))
+			*bloc, 0))))
 		{
 			return -1;
 		}
@@ -1759,8 +1756,7 @@
 	if (!(bh))
 	{
 		if (!(bh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, bloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, bloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, bloc, 0));
@@ -1828,8 +1824,7 @@
 	if (!(*bh))
 	{
 		if (!(*bh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, *bloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, *bloc, 0));
@@ -1951,8 +1946,7 @@
 	if (!(*bh))
 	{
 		if (!(*bh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, *bloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, *bloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, *bloc, 0));
@@ -2033,8 +2027,7 @@
 	if (!bh)
 	{
 		if (!(bh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, bloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, bloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, bloc, 0));
@@ -2068,8 +2061,7 @@
 	if (!(nbh))
 	{
 		if (!(nbh = udf_tread(inode->i_sb,
-			udf_get_lb_pblock(inode->i_sb, nbloc, 0),
-			inode->i_sb->s_blocksize)))
+			udf_get_lb_pblock(inode->i_sb, nbloc, 0))))
 		{
 			udf_debug("reading block %d failed!\n",
 				udf_get_lb_pblock(inode->i_sb, nbloc, 0));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/misc.c linux/fs/udf/misc.c
--- linux.orig/fs/udf/misc.c	Tue Jun 12 02:15:27 2001
+++ linux/fs/udf/misc.c	Wed Jan 23 20:15:01 2002
@@ -67,21 +67,21 @@
 #if defined(__linux__) && defined(__KERNEL__)
 
 extern struct buffer_head *
-udf_tgetblk(struct super_block *sb, int block, int size)
+udf_tgetblk(struct super_block *sb, int block)
 {
 	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
-		return getblk(sb->s_dev, udf_fixed_to_variable(block), size);
+		return sb_getblk(sb, udf_fixed_to_variable(block));
 	else
-		return getblk(sb->s_dev, block, size);
+		return sb_getblk(sb, block);
 }
 
 extern struct buffer_head *
-udf_tread(struct super_block *sb, int block, int size)
+udf_tread(struct super_block *sb, int block)
 {
 	if (UDF_QUERY_FLAG(sb, UDF_FLAG_VARCONV))
-		return bread(sb->s_dev, udf_fixed_to_variable(block), size);
+		return sb_bread(sb, udf_fixed_to_variable(block));
 	else
-		return bread(sb->s_dev, block, size);
+		return sb_bread(sb, block);
 }
 
 extern struct GenericAttrFormat *
@@ -92,7 +92,7 @@
 	long_ad eaicb;
 	int offset;
 
-	*bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
+	*bh = udf_tread(inode->i_sb, inode->i_ino);
 
 	if (UDF_I_EXTENDED_FE(inode) == 0)
 	{
@@ -208,7 +208,7 @@
 	long_ad eaicb;
 	Uint32 offset;
 
-	*bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
+	*bh = udf_tread(inode->i_sb, inode->i_ino);
 
 	if (UDF_I_EXTENDED_FE(inode) == 0)
 	{
@@ -273,7 +273,7 @@
 	struct buffer_head *bh = NULL;
 
 	/* Read the block */
-	bh = udf_tread(sb, block+offset, sb->s_blocksize);
+	bh = udf_tread(sb, block+offset);
 	if (!bh)
 	{
 		printk(KERN_ERR "udf: udf_read_untagged(%p,%d,%d) failed\n",
@@ -305,7 +305,7 @@
 	if (block == 0xFFFFFFFF)
 		return NULL;
 
-	bh = udf_tread(sb, block, sb->s_blocksize);
+	bh = udf_tread(sb, block);
 	if (!bh)
 	{
 		udf_debug("block=%d, location=%d: read failed\n", block, location);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/namei.c linux/fs/udf/namei.c
--- linux.orig/fs/udf/namei.c	Thu Oct 11 15:59:24 2001
+++ linux/fs/udf/namei.c	Wed Jan 23 20:15:01 2002
@@ -183,7 +183,7 @@
 		return NULL;
 	}
 
-	if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+	if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
 	{
 		udf_release_data(bh);
 		return NULL;
@@ -404,7 +404,7 @@
 		else
 			offset = 0;
 
-		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+		if (!(fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block)))
 		{
 			udf_release_data(bh);
 			*err = -EIO;
@@ -488,7 +488,7 @@
 		block = udf_get_lb_pblock(dir->i_sb, UDF_I_LOCATION(dir), 0);
 		if (UDF_I_ALLOCTYPE(dir) == ICB_FLAG_AD_IN_ICB)
 		{
-			fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize);
+			fibh->sbh = fibh->ebh = udf_tread(dir->i_sb, block);
 			fibh->soffset = fibh->eoffset = udf_file_entry_alloc_offset(dir);
 		}
 		else
@@ -803,7 +803,7 @@
 		return 0;
 	}
 
-	if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block, dir->i_sb->s_blocksize)))
+	if (!(fibh.sbh = fibh.ebh = udf_tread(dir->i_sb, block)))
 		return 0;
 
 	while ( (f_pos < size) )
@@ -964,7 +964,7 @@
 
 		block = udf_get_pblock(inode->i_sb, block,
 			UDF_I_LOCATION(inode).partitionReferenceNum, 0);
-		bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize);
+		bh = udf_tread(inode->i_sb, block);
 		lock_buffer(bh);
 		memset(bh->b_data, 0x00, inode->i_sb->s_blocksize);
 		mark_buffer_uptodate(bh, 1);
@@ -974,7 +974,7 @@
 	else
 	{
 		block = udf_get_lb_pblock(inode->i_sb, UDF_I_LOCATION(inode), 0);
-		bh = udf_tread(inode->i_sb, block, inode->i_sb->s_blocksize);
+		bh = udf_tread(inode->i_sb, block);
 	}
 	ea = bh->b_data + udf_ext0_offset(inode);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/partition.c linux/fs/udf/partition.c
--- linux.orig/fs/udf/partition.c	Tue Jun 12 02:15:27 2001
+++ linux/fs/udf/partition.c	Wed Jan 23 20:15:01 2002
@@ -76,7 +76,7 @@
 
 	loc = udf_block_map(UDF_SB_VAT(sb), newblock);
 
-	if (!(bh = bread(sb->s_dev, loc, sb->s_blocksize)))
+	if (!(bh = sb_bread(sb, loc)))
 	{
 		udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%d,%d) VAT: %d[%d]\n",
 			sb, block, partition, loc, index);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/super.c linux/fs/udf/super.c
--- linux.orig/fs/udf/super.c	Thu Oct 25 07:02:26 2001
+++ linux/fs/udf/super.c	Wed Jan 23 20:15:01 2002
@@ -412,7 +412,7 @@
 	for (;!nsr02 && !nsr03; sector += sectorsize)
 	{
 		/* Read a block */
-		bh = udf_tread(sb, sector >> sb->s_blocksize_bits, sb->s_blocksize);
+		bh = udf_tread(sb, sector >> sb->s_blocksize_bits);
 		if (!bh)
 			break;
 
@@ -525,7 +525,7 @@
 
 		for (i=0; (!lastblock && i<sizeof(last)/sizeof(int)); i++)
 		{
-			if (last[i] < 0 || !(bh = bread(sb->s_dev, last[i], sb->s_blocksize)))
+			if (last[i] < 0 || !(bh = sb_bread(sb, last[i])))
 			{
 				ident = location = 0;
 			}
@@ -560,7 +560,7 @@
 			}
 			else
 			{
-				if (last[i] < 256 || !(bh = bread(sb->s_dev, last[i] - 256, sb->s_blocksize)))
+				if (last[i] < 256 || !(bh = sb_bread(sb, last[i] - 256)))
 				{
 					ident = location = 0;
 				}
@@ -579,8 +579,7 @@
 				}
 				else
 				{
-					if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = bread(sb->s_dev, last[i] - 312 - UDF_SB_SESSION(sb),
-						sb->s_blocksize)))
+					if (last[i] < 312 + UDF_SB_SESSION(sb) || !(bh = sb_bread(sb, last[i] - 312 - UDF_SB_SESSION(sb))))
 					{
 						ident = location = 0;
 					}
@@ -606,7 +605,7 @@
 	if (!lastblock)
 	{
 		/* We havn't found the lastblock. check 312 */
-		if ((bh = bread(sb->s_dev, 312 + UDF_SB_SESSION(sb), sb->s_blocksize)))
+		if ((bh = sb_bread(sb, 312 + UDF_SB_SESSION(sb))))
 		{
 			ident = le16_to_cpu(((tag *)bh->b_data)->tagIdent);
 			location = le32_to_cpu(((tag *)bh->b_data)->tagLocation);
@@ -1258,7 +1257,7 @@
 					Uint32 pos;
 
 					pos = udf_block_map(UDF_SB_VAT(sb), 0);
-					bh = bread(sb->s_dev, pos, sb->s_blocksize);
+					bh = sb_bread(sb, pos);
 					UDF_SB_TYPEVIRT(sb,i).s_start_offset =
 						le16_to_cpu(((struct VirtualAllocationTable20 *)bh->b_data + udf_ext0_offset(UDF_SB_VAT(sb)))->lengthHeader) +
 							udf_ext0_offset(UDF_SB_VAT(sb));
@@ -1728,7 +1727,7 @@
 		{
 			udf_release_data(bh);
 			newblock = udf_get_lb_pblock(sb, loc, ++block);
-			bh = udf_tread(sb, newblock, sb->s_blocksize);
+			bh = udf_tread(sb, newblock);
 			if (!bh)
 			{
 				udf_debug("read failed\n");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/symlink.c linux/fs/udf/symlink.c
--- linux.orig/fs/udf/symlink.c	Tue Jun 12 02:15:27 2001
+++ linux/fs/udf/symlink.c	Wed Jan 23 20:15:01 2002
@@ -88,7 +88,7 @@
 	lock_kernel();
 	if (UDF_I_ALLOCTYPE(inode) == ICB_FLAG_AD_IN_ICB)
 	{
-		bh = udf_tread(inode->i_sb, inode->i_ino, inode->i_sb->s_blocksize);
+		bh = udf_tread(inode->i_sb, inode->i_ino);
 
 		if (!bh)
 			goto out;
@@ -97,8 +97,7 @@
 	}
 	else
 	{
-		bh = bread(inode->i_dev, udf_block_map(inode, 0),
-				inode->i_sb->s_blocksize);
+		bh = sb_bread(inode->i_sb, udf_block_map(inode, 0));
 
 		if (!bh)
 			goto out;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/udf/udfdecl.h linux/fs/udf/udfdecl.h
--- linux.orig/fs/udf/udfdecl.h	Tue Oct 16 01:49:21 2001
+++ linux/fs/udf/udfdecl.h	Wed Jan 23 20:15:01 2002
@@ -139,8 +139,8 @@
 
 /* misc.c */
 extern int udf_read_tagged_data(char *, int size, int fd, int block, int partref);
-extern struct buffer_head *udf_tgetblk(struct super_block *, int, int);
-extern struct buffer_head *udf_tread(struct super_block *, int, int);
+extern struct buffer_head *udf_tgetblk(struct super_block *, int);
+extern struct buffer_head *udf_tread(struct super_block *, int);
 extern struct GenericAttrFormat *udf_add_extendedattr(struct inode *, Uint32, Uint32, Uint8, struct buffer_head **);
 extern struct GenericAttrFormat *udf_get_extendedattr(struct inode *, Uint32, Uint8, struct buffer_head **);
 extern struct buffer_head *udf_read_tagged(struct super_block *, Uint32, Uint32, Uint16 *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/balloc.c linux/fs/ufs/balloc.c
--- linux.orig/fs/ufs/balloc.c	Mon Nov 19 22:55:46 2001
+++ linux/fs/ufs/balloc.c	Wed Jan 23 20:15:01 2002
@@ -223,7 +223,7 @@
 
 #define NULLIFY_FRAGMENTS \
 	for (i = oldcount; i < newcount; i++) { \
-		bh = getblk (sb->s_dev, result + i, sb->s_blocksize); \
+		bh = sb_getblk(sb, result + i); \
 		memset (bh->b_data, 0, sb->s_blocksize); \
 		mark_buffer_uptodate(bh, 1); \
 		mark_buffer_dirty (bh); \
@@ -284,16 +284,16 @@
 			return 0;
 		}
 	}
-	
+
 	/*
 	 * There is not enough space for user on the device
 	 */
-	if (!fsuser() && ufs_freespace(usb1, UFS_MINFREE) <= 0) {
+	if (!capable(CAP_SYS_RESOURCE) && ufs_freespace(usb1, UFS_MINFREE) <= 0) {
 		unlock_super (sb);
 		UFSD(("EXIT (FAILED)\n"))
 		return 0;
-	} 
-	
+	}
+
 	if (goal >= uspi->s_size) 
 		goal = 0;
 	if (goal == 0) 
@@ -357,7 +357,7 @@
 	result = ufs_alloc_fragments (inode, cgno, goal, request, err);
 	if (result) {
 		for (i = 0; i < oldcount; i++) {
-			bh = bread (sb->s_dev, tmp + i, sb->s_blocksize);
+			bh = sb_bread(sb, tmp + i);
 			if(bh)
 			{
 				mark_buffer_clean (bh);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/cylinder.c linux/fs/ufs/cylinder.c
--- linux.orig/fs/ufs/cylinder.c	Mon Nov 19 22:55:46 2001
+++ linux/fs/ufs/cylinder.c	Wed Jan 23 20:15:01 2002
@@ -54,7 +54,7 @@
 	 */
 	UCPI_UBH->bh[0] = sb->u.ufs_sb.s_ucg[cgno];
 	for (i = 1; i < UCPI_UBH->count; i++)
-		if (!(UCPI_UBH->bh[i] = bread (sb->s_dev, UCPI_UBH->fragment + i, sb->s_blocksize)))
+		if (!(UCPI_UBH->bh[i] = sb_bread(sb, UCPI_UBH->fragment + i)))
 			goto failed;
 	sb->u.ufs_sb.s_cgno[bitmap_nr] = cgno;
 			
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/dir.c linux/fs/ufs/dir.c
--- linux.orig/fs/ufs/dir.c	Mon Nov 19 23:00:26 2001
+++ linux/fs/ufs/dir.c	Wed Jan 23 20:15:01 2002
@@ -74,7 +74,7 @@
 	while (!error && !stored && filp->f_pos < inode->i_size) {
 		lblk = (filp->f_pos) >> sb->s_blocksize_bits;
 		blk = ufs_frag_map(inode, lblk);
-		if (!blk || !(bh = bread (sb->s_dev, blk, sb->s_blocksize))) {
+		if (!blk || !(bh = sb_bread(sb, blk))) {
 			/* XXX - error - skip to the next block */
 			printk("ufs_readdir: "
 			       "dir inode %lu has a hole at offset %lu\n",
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/inode.c linux/fs/ufs/inode.c
--- linux.orig/fs/ufs/inode.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/ufs/inode.c	Wed Jan 23 20:15:01 2002
@@ -106,8 +106,7 @@
 		struct buffer_head *bh;
 		int n = *p++;
 
-		bh = bread(sb->s_dev, uspi->s_sbbase + fs32_to_cpu(sb, block)+(n>>shift),
-				sb->s_blocksize);
+		bh = sb_bread(sb, uspi->s_sbbase + fs32_to_cpu(sb, block)+(n>>shift));
 		if (!bh)
 			goto out;
 		block = ((u32*) bh->b_data)[n & mask];
@@ -147,8 +146,7 @@
 	lastfrag = inode->u.ufs_i.i_lastfrag;
 	if (tmp && fragment < lastfrag) {
 		if (metadata) {
-			result = getblk (sb->s_dev, uspi->s_sbbase + tmp + blockoff,
-					 sb->s_blocksize);
+			result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
 			if (tmp == fs32_to_cpu(sb, *p)) {
 				UFSD(("EXIT, result %u\n", tmp + blockoff))
 				return result;
@@ -216,7 +214,7 @@
 	 * now. -DaveM
 	 */
 	if (metadata) {
-		result = getblk (inode->i_dev, tmp + blockoff, sb->s_blocksize);
+		result = sb_getblk(inode->i_sb, tmp + blockoff);
 	} else {
 		*phys = tmp;
 		result = NULL;
@@ -264,8 +262,7 @@
 	tmp = fs32_to_cpu(sb, *p);
 	if (tmp) {
 		if (metadata) {
-			result = getblk (bh->b_dev, uspi->s_sbbase + tmp + blockoff,
-					 sb->s_blocksize);
+			result = sb_getblk(sb, uspi->s_sbbase + tmp + blockoff);
 			if (tmp == fs32_to_cpu(sb, *p))
 				goto out;
 			brelse (result);
@@ -292,7 +289,7 @@
 	 * now. -DaveM
 	 */
 	if (metadata) {
-		result = getblk (bh->b_dev, tmp + blockoff, sb->s_blocksize);
+		result = sb_getblk(sb, tmp + blockoff);
 	} else {
 		*phys = tmp;
 		*new = 1;
@@ -425,7 +422,7 @@
 	*err = error;
 	if (!error && buffer_mapped(&dummy)) {
 		struct buffer_head *bh;
-		bh = getblk(dummy.b_dev, dummy.b_blocknr, inode->i_sb->s_blocksize);
+		bh = sb_getblk(inode->i_sb, dummy.b_blocknr);
 		if (buffer_new(&dummy)) {
 			memset(bh->b_data, 0, inode->i_sb->s_blocksize);
 			mark_buffer_uptodate(bh, 1);
@@ -497,13 +494,13 @@
 	if (inode->i_ino < UFS_ROOTINO || 
 	    inode->i_ino > (uspi->s_ncg * uspi->s_ipg)) {
 		ufs_warning (sb, "ufs_read_inode", "bad inode number (%lu)\n", inode->i_ino);
-		return;
+		goto bad_inode;
 	}
 	
-	bh = bread (sb->s_dev, uspi->s_sbbase + ufs_inotofsba(inode->i_ino), sb->s_blocksize);
+	bh = sb_bread(sb, uspi->s_sbbase + ufs_inotofsba(inode->i_ino));
 	if (!bh) {
 		ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
-		return;
+		goto bad_inode;
 	}
 	ufs_inode = (struct ufs_inode *) (bh->b_data + sizeof(struct ufs_inode) * ufs_inotofsbo(inode->i_ino));
 
@@ -568,6 +565,11 @@
 	brelse (bh);
 
 	UFSD(("EXIT\n"))
+	return;
+
+bad_inode:
+	make_bad_inode(inode);
+	return;
 }
 
 static int ufs_update_inode(struct inode * inode, int do_sync)
@@ -591,7 +593,7 @@
 		return -1;
 	}
 
-	bh = bread (sb->s_dev, ufs_inotofsba(inode->i_ino), sb->s_blocksize);
+	bh = sb_bread(sb, ufs_inotofsba(inode->i_ino));
 	if (!bh) {
 		ufs_warning (sb, "ufs_read_inode", "unable to read inode %lu\n", inode->i_ino);
 		return -1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/super.c linux/fs/ufs/super.c
--- linux.orig/fs/ufs/super.c	Tue Jan 22 17:51:23 2002
+++ linux/fs/ufs/super.c	Wed Jan 23 20:15:01 2002
@@ -339,7 +339,7 @@
 		size = uspi->s_bsize;
 		if (i + uspi->s_fpb > blks)
 			size = (blks - i) * uspi->s_fsize;
-		ubh = ubh_bread(sb->s_dev, uspi->s_csaddr + i, size);
+		ubh = ubh_bread(sb, uspi->s_csaddr + i, size);
 		if (!ubh)
 			goto failed;
 		ubh_ubhcpymem (space, ubh, size);
@@ -363,7 +363,7 @@
 	}
 	for (i = 0; i < uspi->s_ncg; i++) {
 		UFSD(("read cg %u\n", i))
-		if (!(sb->u.ufs_sb.s_ucg[i] = bread (sb->s_dev, ufs_cgcmin(i), sb->s_blocksize)))
+		if (!(sb->u.ufs_sb.s_ucg[i] = sb_bread(sb, ufs_cgcmin(i))))
 			goto failed;
 		if (!ufs_cg_chkmagic (sb, (struct ufs_cylinder_group *) sb->u.ufs_sb.s_ucg[i]->b_data))
 			goto failed;
@@ -414,7 +414,7 @@
 		size = uspi->s_bsize;
 		if (i + uspi->s_fpb > blks)
 			size = (blks - i) * uspi->s_fsize;
-		ubh = ubh_bread (sb->s_dev, uspi->s_csaddr + i, size);
+		ubh = ubh_bread(sb, uspi->s_csaddr + i, size);
 		ubh_memcpyubh (ubh, space, size);
 		space += size;
 		ubh_mark_buffer_uptodate (ubh, 1);
@@ -442,6 +442,7 @@
 	struct ufs_super_block_second * usb2;
 	struct ufs_super_block_third * usb3;
 	struct ufs_buffer_head * ubh;	
+	struct inode *inode;
 	unsigned block_size, super_block_size;
 	unsigned flags;
 
@@ -597,11 +598,12 @@
 	
 again:	
 	set_blocksize (sb->s_dev, block_size);
+	sb->s_blocksize = block_size;
 
 	/*
 	 * read ufs super block from device
 	 */
-	ubh = ubh_bread_uspi (uspi, sb->s_dev, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size);
+	ubh = ubh_bread_uspi (uspi, sb, uspi->s_sbbase + UFS_SBLOCK/block_size, super_block_size);
 	if (!ubh) 
 		goto failed;
 	
@@ -789,7 +791,13 @@
 		    fs32_to_cpu(sb, usb3->fs_u2.fs_44.fs_maxsymlinklen);
 	
 	sb->u.ufs_sb.s_flags = flags;
-	sb->s_root = d_alloc_root(iget(sb, UFS_ROOTINO));
+
+	inode = iget(sb, UFS_ROOTINO);
+	if (!inode || is_bad_inode(inode))
+		goto failed;
+	sb->s_root = d_alloc_root(inode);
+	if (!sb->s_root)
+		goto dalloc_failed;
 
 
 	/*
@@ -802,6 +810,8 @@
 	UFSD(("EXIT\n"))
 	return(sb);
 
+dalloc_failed:
+	iput(inode);
 failed:
 	if (ubh) ubh_brelse_uspi (uspi);
 	if (uspi) kfree (uspi);
@@ -972,3 +982,4 @@
 
 module_init(init_ufs_fs)
 module_exit(exit_ufs_fs)
+MODULE_LICENSE("GPL");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/swab.h linux/fs/ufs/swab.h
--- linux.orig/fs/ufs/swab.h	Mon Nov 19 22:55:46 2001
+++ linux/fs/ufs/swab.h	Mon Feb  4 17:38:23 2002
@@ -3,7 +3,7 @@
  *
  * Copyright (C) 1997, 1998 Francois-Rene Rideau <fare@tunes.org>
  * Copyright (C) 1998 Jakub Jelinek <jj@ultra.linux.cz>
- * Copyright (C) 2001 Christoph Hellwig <hch@caldera.de>
+ * Copyright (C) 2001 Christoph Hellwig <hch@infradead.org>
  */
 
 #ifndef _UFS_SWAB_H
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/truncate.c linux/fs/ufs/truncate.c
--- linux.orig/fs/ufs/truncate.c	Mon Nov 19 22:55:46 2001
+++ linux/fs/ufs/truncate.c	Wed Jan 23 20:15:01 2002
@@ -114,7 +114,7 @@
 	frag1 = ufs_fragnum (frag1);
 	frag2 = ufs_fragnum (frag2);
 	for (j = frag1; j < frag2; j++) {
-		bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
+		bh = sb_get_hash_table (sb, tmp + j);
 		if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
 			retry = 1;
 			brelse (bh);
@@ -137,7 +137,7 @@
 		if (!tmp)
 			continue;
 		for (j = 0; j < uspi->s_fpb; j++) {
-			bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
+			bh = sb_get_hash_table(sb, tmp + j);
 			if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
 				retry = 1;
 				brelse (bh);
@@ -176,7 +176,7 @@
 		ufs_panic(sb, "ufs_truncate_direct", "internal error");
 	frag4 = ufs_fragnum (frag4);
 	for (j = 0; j < frag4; j++) {
-		bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
+		bh = sb_get_hash_table (sb, tmp + j);
 		if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *p)) {
 			retry = 1;
 			brelse (bh);
@@ -218,7 +218,7 @@
 	tmp = fs32_to_cpu(sb, *p);
 	if (!tmp)
 		return 0;
-	ind_ubh = ubh_bread (sb->s_dev, tmp, uspi->s_bsize);
+	ind_ubh = ubh_bread(sb, tmp, uspi->s_bsize);
 	if (tmp != fs32_to_cpu(sb, *p)) {
 		ubh_brelse (ind_ubh);
 		return 1;
@@ -235,7 +235,7 @@
 		if (!tmp)
 			continue;
 		for (j = 0; j < uspi->s_fpb; j++) {
-			bh = get_hash_table (sb->s_dev, tmp + j, uspi->s_fsize);
+			bh = sb_get_hash_table(sb, tmp + j);
 			if ((bh && DATA_BUFFER_USED(bh)) || tmp != fs32_to_cpu(sb, *ind)) {
 				retry = 1;
 				brelse (bh);
@@ -312,7 +312,7 @@
 	tmp = fs32_to_cpu(sb, *p);
 	if (!tmp)
 		return 0;
-	dind_bh = ubh_bread (inode->i_dev, tmp, uspi->s_bsize);
+	dind_bh = ubh_bread(sb, tmp, uspi->s_bsize);
 	if (tmp != fs32_to_cpu(sb, *p)) {
 		ubh_brelse (dind_bh);
 		return 1;
@@ -378,7 +378,7 @@
 	p = inode->u.ufs_i.i_u1.i_data + UFS_TIND_BLOCK;
 	if (!(tmp = fs32_to_cpu(sb, *p)))
 		return 0;
-	tind_bh = ubh_bread (sb->s_dev, tmp, uspi->s_bsize);
+	tind_bh = ubh_bread (sb, tmp, uspi->s_bsize);
 	if (tmp != fs32_to_cpu(sb, *p)) {
 		ubh_brelse (tind_bh);
 		return 1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/util.c linux/fs/ufs/util.c
--- linux.orig/fs/ufs/util.c	Mon Sep 10 16:04:53 2001
+++ linux/fs/ufs/util.c	Wed Jan 23 20:15:01 2002
@@ -23,7 +23,7 @@
 
 
 struct ufs_buffer_head * _ubh_bread_ (struct ufs_sb_private_info * uspi,
-	kdev_t dev, unsigned fragment, unsigned size)
+	struct super_block *sb, unsigned fragment, unsigned size)
 {
 	struct ufs_buffer_head * ubh;
 	unsigned i, j, count;
@@ -39,7 +39,7 @@
 	ubh->fragment = fragment;
 	ubh->count = count;
 	for (i = 0; i < count; i++)
-		if (!(ubh->bh[i] = bread (dev, fragment + i, uspi->s_fsize)))
+		if (!(ubh->bh[i] = sb_bread(sb, fragment + i)))
 			goto failed;
 	for (; i < UFS_MAXFRAG; i++)
 		ubh->bh[i] = NULL;
@@ -51,7 +51,7 @@
 }
 
 struct ufs_buffer_head * ubh_bread_uspi (struct ufs_sb_private_info * uspi,
-	kdev_t dev, unsigned fragment, unsigned size)
+	struct super_block *sb, unsigned fragment, unsigned size)
 {
 	unsigned i, j, count;
 	if (size & ~uspi->s_fmask)
@@ -62,7 +62,7 @@
 	USPI_UBH->fragment = fragment;
 	USPI_UBH->count = count;
 	for (i = 0; i < count; i++)
-		if (!(USPI_UBH->bh[i] = bread (dev, fragment + i, uspi->s_fsize)))
+		if (!(USPI_UBH->bh[i] = sb_bread(sb, fragment + i)))
 			goto failed;
 	for (; i < UFS_MAXFRAG; i++)
 		USPI_UBH->bh[i] = NULL;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/fs/ufs/util.h linux/fs/ufs/util.h
--- linux.orig/fs/ufs/util.h	Mon Nov 19 23:25:45 2001
+++ linux/fs/ufs/util.h	Wed Jan 23 20:15:01 2002
@@ -226,9 +226,9 @@
 /*
  * These functions manipulate ufs buffers
  */
-#define ubh_bread(dev,fragment,size) _ubh_bread_(uspi,dev,fragment,size)  
-extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
-extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, kdev_t, unsigned, unsigned);
+#define ubh_bread(sb,fragment,size) _ubh_bread_(uspi,sb,fragment,size)  
+extern struct ufs_buffer_head * _ubh_bread_(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned);
+extern struct ufs_buffer_head * ubh_bread_uspi(struct ufs_sb_private_info *, struct super_block *, unsigned, unsigned);
 extern void ubh_brelse (struct ufs_buffer_head *);
 extern void ubh_brelse_uspi (struct ufs_sb_private_info *);
 extern void ubh_mark_buffer_dirty (struct ufs_buffer_head *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-alpha/pci.h linux/include/asm-alpha/pci.h
--- linux.orig/include/asm-alpha/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-alpha/pci.h	Wed Jan 16 20:56:50 2002
@@ -101,6 +101,20 @@
 extern void pci_unmap_single(struct pci_dev *, dma_addr_t, size_t, int);
 extern void pci_unmap_page(struct pci_dev *, dma_addr_t, size_t, int);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming mode for
    PCI DMA.  This is the scather-gather version of the above
    pci_map_single interface.  Here the scatter gather list elements
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-arm/pci.h linux/include/asm-arm/pci.h
--- linux.orig/include/asm-arm/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-arm/pci.h	Wed Jan 16 20:56:50 2002
@@ -87,6 +87,31 @@
 	/* nothing to do */
 }
 
+/* Whether pci_unmap_{single,page} is a nop depends upon the
+ * configuration.
+ */
+#ifdef CONFIG_SA1111
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else /* !(CONFIG_SA1111) */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif /* CONFIG_SA1111 */
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/bitops.h linux/include/asm-cris/bitops.h
--- linux.orig/include/asm-cris/bitops.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/bitops.h	Tue Jan  8 16:00:08 2002
@@ -331,6 +331,17 @@
 #define find_first_zero_bit(addr, size) \
         find_next_zero_bit((addr), (size), 0)
 
+/*
+ * hweightN - returns the hamming weight of a N-bit word
+ * @x: the word to weigh
+ *
+ * The Hamming Weight of a number is the total number of bits set in it.
+ */
+
+#define hweight32(x) generic_hweight32(x)
+#define hweight16(x) generic_hweight16(x)
+#define hweight8(x) generic_hweight8(x)
+
 #define ext2_set_bit                 test_and_set_bit
 #define ext2_clear_bit               test_and_clear_bit
 #define ext2_test_bit                test_bit
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/elf.h linux/include/asm-cris/elf.h
--- linux.orig/include/asm-cris/elf.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/elf.h	Tue Jan  8 16:00:08 2002
@@ -9,8 +9,9 @@
 
 typedef unsigned long elf_greg_t;
 
-/* These probably need fixing.  */
-#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+/* Note that NGREG is defined to ELF_NGREG in include/linux/elfcore.h, and is
+   thus exposed to user-space. */
+#define ELF_NGREG (sizeof (struct user_regs_struct) / sizeof(elf_greg_t))
 typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 
 /* A placeholder; CRIS does not have any fp regs.  */
@@ -45,7 +46,52 @@
 	(_r)->r1 = 0;  (_r)->r0 = 0;  (_r)->mof = 0; (_r)->srp = 0; \
 } while (0)
 
-#undef USE_ELF_CORE_DUMP
+#define USE_ELF_CORE_DUMP
+
+/* The additional layer below is because the stack pointer is missing in 
+   the pt_regs struct, but needed in a core dump. pr_reg is a elf_gregset_t,
+   and should be filled in according to the layout of the user_regs_struct
+   struct; regs is a pt_regs struct. We dump all registers, though several are
+   obviously unnecessary. That way there's less need for intelligence at 
+   the receiving end (i.e. gdb). */
+#define ELF_CORE_COPY_REGS(pr_reg, regs)                   \
+	pr_reg[0] = regs->r0;                              \
+	pr_reg[1] = regs->r1;                              \
+	pr_reg[2] = regs->r2;                              \
+	pr_reg[3] = regs->r3;                              \
+	pr_reg[4] = regs->r4;                              \
+	pr_reg[5] = regs->r5;                              \
+	pr_reg[6] = regs->r6;                              \
+	pr_reg[7] = regs->r7;                              \
+	pr_reg[8] = regs->r8;                              \
+	pr_reg[9] = regs->r9;                              \
+	pr_reg[10] = regs->r10;                            \
+	pr_reg[11] = regs->r11;                            \
+	pr_reg[12] = regs->r12;                            \
+	pr_reg[13] = regs->r13;                            \
+	pr_reg[14] = rdusp();               /* sp */       \
+	pr_reg[15] = regs->irp;             /* pc */       \
+	pr_reg[16] = 0;                     /* p0 */       \
+	pr_reg[17] = rdvr();                /* vr */       \
+	pr_reg[18] = 0;                     /* p2 */       \
+	pr_reg[19] = 0;                     /* p3 */       \
+	pr_reg[20] = 0;                     /* p4 */       \
+	pr_reg[21] = (regs->dccr & 0xffff); /* ccr */      \
+	pr_reg[22] = 0;                     /* p6 */       \
+	pr_reg[23] = regs->mof;             /* mof */      \
+	pr_reg[24] = 0;                     /* p8 */       \
+	pr_reg[25] = 0;                     /* ibr */      \
+	pr_reg[26] = 0;                     /* irp */      \
+	pr_reg[27] = regs->srp;             /* srp */      \
+	pr_reg[28] = 0;                     /* bar */      \
+	pr_reg[29] = regs->dccr;            /* dccr */     \
+	pr_reg[30] = 0;                     /* brp */      \
+	pr_reg[31] = rdusp();               /* usp */      \
+	pr_reg[32] = 0;                     /* csrinstr */ \
+	pr_reg[33] = 0;                     /* csraddr */  \
+	pr_reg[34] = 0;                     /* csrdata */
+
+
 #define ELF_EXEC_PAGESIZE	8192
 
 /* This is the location that an ET_DYN program is loaded if exec'ed.  Typical
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/ethernet.h linux/include/asm-cris/ethernet.h
--- linux.orig/include/asm-cris/ethernet.h	Thu Jan  1 00:00:00 1970
+++ linux/include/asm-cris/ethernet.h	Tue Feb  5 17:03:01 2002
@@ -0,0 +1,18 @@
+/*  
+ * ioctl defines for ethernet driver
+ *
+ * Copyright (c) 2001 Axis Communications AB
+ * 
+ * Author: Mikael Starvik 
+ *
+ */
+
+#ifndef _CRIS_ETHERNET_H
+#define _CRIS_ETHERNET_H
+#define SET_ETH_SPEED_AUTO      SIOCDEVPRIVATE          /* Auto neg speed */
+#define SET_ETH_SPEED_10        SIOCDEVPRIVATE+1        /* 10 Mbps */
+#define SET_ETH_SPEED_100       SIOCDEVPRIVATE+2        /* 100 Mbps. */
+#define SET_ETH_DUPLEX_AUTO     SIOCDEVPRIVATE+3        /* Auto neg duplex */
+#define SET_ETH_DUPLEX_HALF     SIOCDEVPRIVATE+4        /* Full duplex */
+#define SET_ETH_DUPLEX_FULL     SIOCDEVPRIVATE+5        /* Half duplex */
+#endif /* _CRIS_ETHERNET_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/irq.h linux/include/asm-cris/irq.h
--- linux.orig/include/asm-cris/irq.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/irq.h	Tue Jan  8 16:00:08 2002
@@ -126,6 +126,9 @@
   /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
    * do_IRQ (with irq disabled still). after that it unblocks and jumps to
    * ret_from_intr (entry.S)
+   *
+   * The reason the IRQ is blocked is to allow an sti() before the handler which
+   * will acknowledge the interrupt is run.
    */
 
 #define BUILD_IRQ(nr,mask) \
@@ -151,6 +154,41 @@
           "reti\n\t" \
           "nop\n");
 
+/* This is subtle. The timer interrupt is crucial and it should not be disabled for 
+ * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
+ * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
+ * If the softirq's take too much time to run, the timer irq won't run and the 
+ * watchdog will kill us.
+ *
+ * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
+ * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
+ * it here, we would not get the multiple_irq at all.
+ *
+ * The non-blocking here is based on the knowledge that the timer interrupt is 
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
+ * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ */
+
+#define BUILD_TIMER_IRQ(nr,mask) \
+void IRQ_NAME(nr); \
+void sIRQ_NAME(nr); \
+void BAD_IRQ_NAME(nr); \
+__asm__ ( \
+          ".text\n\t" \
+          "IRQ" #nr "_interrupt:\n\t" \
+	  SAVE_ALL \
+	  "sIRQ" #nr "_interrupt:\n\t" /* shortcut for the multiple irq handler */ \
+	  "moveq "#nr",$r10\n\t" \
+	  "move.d $sp,$r11\n\t" \
+	  "jsr do_IRQ\n\t" /* irq.c, r10 and r11 are arguments */ \
+	  "moveq 0,$r9\n\t" /* make ret_from_intr realise we came from an irq */ \
+	  "jump ret_from_intr\n\t" \
+          "bad_IRQ" #nr "_interrupt:\n\t" \
+	  "push $r0\n\t" \
+	  BLOCK_IRQ(mask,nr) \
+	  "pop $r0\n\t" \
+          "reti\n\t" \
+          "nop\n");
 
 #endif  /* _ASM_IRQ_H */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/pgtable.h linux/include/asm-cris/pgtable.h
--- linux.orig/include/asm-cris/pgtable.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/pgtable.h	Tue Jan  8 16:00:08 2002
@@ -3,6 +3,12 @@
  * HISTORY:
  *
  * $Log: pgtable.h,v $
+ * Revision 1.14  2001/12/10 03:08:50  bjornw
+ * Added pgtable_cache_init dummy
+ *
+ * Revision 1.13  2001/11/12 18:05:38  pkj
+ * Added declaration of paging_init().
+ *
  * Revision 1.12  2001/08/11 00:28:00  bjornw
  * PAGE_CHG_MASK and PAGE_NONE had somewhat untraditional values
  *
@@ -106,6 +112,8 @@
  * the CRIS page table tree.
  */
 
+extern void paging_init(void);
+
 /* The cache doesn't need to be flushed when TLB entries change because 
  * the cache is mapped to physical memory, not virtual memory
  */
@@ -507,6 +515,6 @@
 /*
  * No page table caches to initialise
  */
-#define pgtable_cache_init()	do { } while (0)
+#define pgtable_cache_init()   do { } while (0)
 
 #endif /* _CRIS_PGTABLE_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/processor.h linux/include/asm-cris/processor.h
--- linux.orig/include/asm-cris/processor.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/processor.h	Tue Jan  8 16:00:08 2002
@@ -142,6 +142,6 @@
 #define init_task       (init_task_union.task)
 #define init_stack      (init_task_union.stack)
 
-#define cpu_relax()	do { } while (0)
+#define cpu_relax()     do { } while (0)
 
 #endif /* __ASM_CRIS_PROCESSOR_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/unistd.h linux/include/asm-cris/unistd.h
--- linux.orig/include/asm-cris/unistd.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/unistd.h	Tue Jan  8 16:00:08 2002
@@ -378,6 +378,7 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,count)
 
   /* the following are just while developing the elinux port! */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-cris/user.h linux/include/asm-cris/user.h
--- linux.orig/include/asm-cris/user.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-cris/user.h	Tue Jan  8 16:00:08 2002
@@ -28,8 +28,51 @@
  *	to write an integer number of pages.
  */
 
+/* User mode registers, used for core dumps. In order to keep ELF_NGREG
+   sensible we let all registers be 32 bits. The csr registers are included
+   for future use. */
+struct user_regs_struct {
+        unsigned long r0;       /* General registers. */
+        unsigned long r1;
+        unsigned long r2;
+        unsigned long r3;
+        unsigned long r4;
+        unsigned long r5;
+        unsigned long r6;
+        unsigned long r7;
+        unsigned long r8;
+        unsigned long r9;
+        unsigned long r10;
+        unsigned long r11;
+        unsigned long r12;
+        unsigned long r13;
+        unsigned long sp;       /* Stack pointer. */
+        unsigned long pc;       /* Program counter. */
+        unsigned long p0;       /* Constant zero (only 8 bits). */
+        unsigned long vr;       /* Version register (only 8 bits). */
+        unsigned long p2;       /* Reserved. */
+        unsigned long p3;       /* Reserved. */
+        unsigned long p4;       /* Constant zero (only 16 bits). */
+        unsigned long ccr;      /* Condition code register (only 16 bits). */
+        unsigned long p6;       /* Reserved. */
+        unsigned long mof;      /* Multiply overflow register. */
+        unsigned long p8;       /* Constant zero. */
+        unsigned long ibr;      /* Not accessible. */
+        unsigned long irp;      /* Not accessible. */
+        unsigned long srp;      /* Subroutine return pointer. */
+        unsigned long bar;      /* Not accessible. */
+        unsigned long dccr;     /* Dword condition code register. */
+        unsigned long brp;      /* Not accessible. */
+        unsigned long usp;      /* User-mode stack pointer. Same as sp when 
+                                   in user mode. */
+        unsigned long csrinstr; /* Internal status registers. */
+        unsigned long csraddr;
+        unsigned long csrdata;
+};
+
+        
 struct user {
-	struct pt_regs	regs;			/* entire machine state */
+	struct user_regs_struct	regs;		/* entire machine state */
 	size_t		u_tsize;		/* text size (pages) */
 	size_t		u_dsize;		/* data size (pages) */
 	size_t		u_ssize;		/* stack size (pages) */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/io_apic.h linux/include/asm-i386/io_apic.h
--- linux.orig/include/asm-i386/io_apic.h	Thu Nov 22 19:46:18 2001
+++ linux/include/asm-i386/io_apic.h	Wed Feb 13 17:44:06 2002
@@ -15,7 +15,8 @@
 #define APIC_MISMATCH_DEBUG
 
 #define IO_APIC_BASE(idx) \
-		((volatile int *)__fix_to_virt(FIX_IO_APIC_BASE_0 + idx))
+		((volatile int *)(__fix_to_virt(FIX_IO_APIC_BASE_0 + idx) \
+		+ (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK)))
 
 /*
  * The structure of the IO-APIC:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/irq.h linux/include/asm-i386/irq.h
--- linux.orig/include/asm-i386/irq.h	Thu Nov 22 19:46:18 2001
+++ linux/include/asm-i386/irq.h	Wed Feb 13 17:43:18 2002
@@ -33,6 +33,7 @@
 extern void disable_irq(unsigned int);
 extern void disable_irq_nosync(unsigned int);
 extern void enable_irq(unsigned int);
+extern void release_x86_irqs(struct task_struct *);
 
 #ifdef CONFIG_X86_LOCAL_APIC
 #define ARCH_HAS_NMI_WATCHDOG		/* See include/linux/nmi.h */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/page.h linux/include/asm-i386/page.h
--- linux.orig/include/asm-i386/page.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/page.h	Wed Dec 26 17:05:14 2001
@@ -80,6 +80,12 @@
 
 #define __PAGE_OFFSET		(0xC0000000)
 
+/*
+ * This much address space is reserved for vmalloc() and iomap()
+ * as well as fixmap mappings.
+ */
+#define __VMALLOC_RESERVE	(128 << 20)
+
 #ifndef __ASSEMBLY__
 
 /*
@@ -118,6 +124,9 @@
 #endif /* __ASSEMBLY__ */
 
 #define PAGE_OFFSET		((unsigned long)__PAGE_OFFSET)
+#define VMALLOC_RESERVE		((unsigned long)__VMALLOC_RESERVE)
+#define __MAXMEM		(-__PAGE_OFFSET-__VMALLOC_RESERVE)
+#define MAXMEM			((unsigned long)(-PAGE_OFFSET-VMALLOC_RESERVE))
 #define __pa(x)			((unsigned long)(x)-PAGE_OFFSET)
 #define __va(x)			((void *)((unsigned long)(x)+PAGE_OFFSET))
 #define virt_to_page(kaddr)	(mem_map + (__pa(kaddr) >> PAGE_SHIFT))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/pci.h linux/include/asm-i386/pci.h
--- linux.orig/include/asm-i386/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/pci.h	Mon Feb  4 18:00:29 2002
@@ -114,6 +114,14 @@
 	/* Nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/rwlock.h linux/include/asm-i386/rwlock.h
--- linux.orig/include/asm-i386/rwlock.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/rwlock.h	Thu Jan 10 20:08:20 2002
@@ -17,6 +17,8 @@
 #ifndef _ASM_I386_RWLOCK_H
 #define _ASM_I386_RWLOCK_H
 
+#include <linux/stringify.h>
+
 #define RW_LOCK_BIAS		 0x01000000
 #define RW_LOCK_BIAS_STR	"0x01000000"
 
@@ -24,23 +26,29 @@
 	asm volatile(LOCK "subl $1,(%0)\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     ::"a" (rw) : "memory")
 
 #define __build_read_lock_const(rw, helper)   \
 	asm volatile(LOCK "subl $1,%0\n\t" \
 		     "js 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
 		     "popl %%eax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_read_lock(rw, helper)	do { \
@@ -54,23 +62,29 @@
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tcall " helper "\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     ::"a" (rw) : "memory")
 
 #define __build_write_lock_const(rw, helper) \
 	asm volatile(LOCK "subl $" RW_LOCK_BIAS_STR ",(%0)\n\t" \
 		     "jnz 2f\n" \
 		     "1:\n" \
-		     ".section .text.lock,\"ax\"\n" \
+		     ".subsection 1\n" \
+		     ".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+		     "_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+		     ".endif\n" \
 		     "2:\tpushl %%eax\n\t" \
 		     "leal %0,%%eax\n\t" \
 		     "call " helper "\n\t" \
 		     "popl %%eax\n\t" \
 		     "jmp 1b\n" \
-		     ".previous" \
+		     ".subsection 0\n" \
 		     :"=m" (*(volatile int *)rw) : : "memory")
 
 #define __build_write_lock(rw, helper)	do { \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/rwsem.h linux/include/asm-i386/rwsem.h
--- linux.orig/include/asm-i386/rwsem.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/rwsem.h	Thu Jan 10 20:08:20 2002
@@ -40,6 +40,7 @@
 
 #include <linux/list.h>
 #include <linux/spinlock.h>
+#include <linux/stringify.h>
 
 struct rwsem_waiter;
 
@@ -101,7 +102,10 @@
 LOCK_PREFIX	"  incl      (%%eax)\n\t" /* adds 0x00000001, returns the old value */
 		"  js        2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  pushl     %%edx\n\t"
@@ -109,7 +113,7 @@
 		"  popl      %%edx\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous"
+		".subsection 0\n"
 		"# ending down_read\n\t"
 		: "+m"(sem->count)
 		: "a"(sem)
@@ -130,13 +134,16 @@
 		"  testl     %0,%0\n\t" /* was the count 0 before? */
 		"  jnz       2f\n\t" /* jump if we weren't granted the lock */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  pushl     %%ecx\n\t"
 		"  call      rwsem_down_write_failed\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending down_write"
 		: "+d"(tmp), "+m"(sem->count)
 		: "a"(sem)
@@ -154,7 +161,10 @@
 LOCK_PREFIX	"  xadd      %%edx,(%%eax)\n\t" /* subtracts 1, returns the old value */
 		"  js        2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* do nothing if still outstanding active readers */
 		"  jnz       1b\n\t"
@@ -162,7 +172,7 @@
 		"  call      rwsem_wake\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending __up_read\n"
 		: "+m"(sem->count), "+d"(tmp)
 		: "a"(sem)
@@ -180,7 +190,10 @@
 LOCK_PREFIX	"  xaddl     %%edx,(%%eax)\n\t" /* tries to transition 0xffff0001 -> 0x00000000 */
 		"  jnz       2f\n\t" /* jump if the lock is being waited upon */
 		"1:\n\t"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\n\t"
 		"  decw      %%dx\n\t" /* did the active count reduce to 0? */
 		"  jnz       1b\n\t" /* jump back if not */
@@ -188,7 +201,7 @@
 		"  call      rwsem_wake\n\t"
 		"  popl      %%ecx\n\t"
 		"  jmp       1b\n"
-		".previous\n"
+		".subsection 0\n"
 		"# ending __up_write\n"
 		: "+m"(sem->count)
 		: "a"(sem), "i"(-RWSEM_ACTIVE_WRITE_BIAS)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/semaphore.h linux/include/asm-i386/semaphore.h
--- linux.orig/include/asm-i386/semaphore.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/semaphore.h	Thu Jan 10 20:08:20 2002
@@ -40,6 +40,7 @@
 #include <asm/atomic.h>
 #include <linux/wait.h>
 #include <linux/rwsem.h>
+#include <linux/stringify.h>
 
 struct semaphore {
 	atomic_t count;
@@ -122,10 +123,13 @@
 		LOCK "decl %0\n\t"     /* --sem->count */
 		"js 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -149,10 +153,13 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed_interruptible\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=a" (result), "=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -177,10 +184,13 @@
 		"js 2f\n\t"
 		"xorl %0,%0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __down_failed_trylock\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=a" (result), "=m" (sem->count)
 		:"c" (sem)
 		:"memory");
@@ -203,10 +213,13 @@
 		LOCK "incl %0\n\t"     /* ++sem->count */
 		"jle 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tcall __up_wakeup\n\t"
 		"jmp 1b\n"
-		".previous"
+		".subsection 0\n"
 		:"=m" (sem->count)
 		:"c" (sem)
 		:"memory");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/softirq.h linux/include/asm-i386/softirq.h
--- linux.orig/include/asm-i386/softirq.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/softirq.h	Thu Jan 10 20:08:20 2002
@@ -3,6 +3,7 @@
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
+#include <linux/stringify.h>
 
 #define __cpu_bh_enable(cpu) \
 		do { barrier(); local_bh_count(cpu)--; } while (0)
@@ -33,12 +34,15 @@
 			"jnz 2f;"					\
 			"1:;"						\
 									\
-			".section .text.lock,\"ax\";"			\
+			".subsection 1;"				\
+			".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"		\
+			"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"		\
+			".endif\n"					\
 			"2: pushl %%eax; pushl %%ecx; pushl %%edx;"	\
 			"call %c1;"					\
 			"popl %%edx; popl %%ecx; popl %%eax;"		\
 			"jmp 1b;"					\
-			".previous;"					\
+			".subsection 0;"				\
 									\
 		: /* no output */					\
 		: "r" (ptr), "i" (do_softirq)				\
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/spinlock.h linux/include/asm-i386/spinlock.h
--- linux.orig/include/asm-i386/spinlock.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-i386/spinlock.h	Thu Jan 10 20:08:20 2002
@@ -5,6 +5,7 @@
 #include <asm/rwlock.h>
 #include <asm/page.h>
 #include <linux/config.h>
+#include <linux/stringify.h>
 
 extern int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
@@ -56,13 +57,16 @@
 	"\n1:\t" \
 	"lock ; decb %0\n\t" \
 	"js 2f\n" \
-	".section .text.lock,\"ax\"\n" \
+	".subsection 1\n" \
+	".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n" \
+	"_text_lock_" __stringify(KBUILD_BASENAME) ":\n" \
+	".endif\n" \
 	"2:\t" \
 	"cmpb $0,%0\n\t" \
 	"rep;nop\n\t" \
 	"jle 2b\n\t" \
 	"jmp 1b\n" \
-	".previous"
+	".subsection 0\n"
 
 /*
  * This works. Despite all the confusion.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/timex.h linux/include/asm-i386/timex.h
--- linux.orig/include/asm-i386/timex.h	Thu Nov 22 19:46:18 2001
+++ linux/include/asm-i386/timex.h	Wed Jan 23 19:52:23 2002
@@ -9,7 +9,12 @@
 #include <linux/config.h>
 #include <asm/msr.h>
 
-#define CLOCK_TICK_RATE	1193180 /* Underlying HZ */
+#ifdef CONFIG_MELAN
+#  define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
+#else
+#  define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
+#endif
+
 #define CLOCK_TICK_FACTOR	20	/* Factor of both 1000000 and CLOCK_TICK_RATE */
 #define FINETUNE ((((((long)LATCH * HZ - CLOCK_TICK_RATE) << SHIFT_HZ) * \
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-i386/unistd.h linux/include/asm-i386/unistd.h
--- linux.orig/include/asm-i386/unistd.h	Wed Oct 17 17:03:03 2001
+++ linux/include/asm-i386/unistd.h	Mon Feb  4 17:24:05 2002
@@ -230,6 +230,18 @@
 #define __NR_security		223	/* syscall for security modules */
 #define __NR_gettid		224
 #define __NR_readahead		225
+#define __NR_setxattr		226
+#define __NR_lsetxattr		227
+#define __NR_fsetxattr		228
+#define __NR_getxattr		229
+#define __NR_lgetxattr		230
+#define __NR_fgetxattr		231
+#define __NR_listxattr		232
+#define __NR_llistxattr		233
+#define __NR_flistxattr		234
+#define __NR_removexattr	235
+#define __NR_lremovexattr	236
+#define __NR_fremovexattr	237
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ia64/pci.h linux/include/asm-ia64/pci.h
--- linux.orig/include/asm-ia64/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ia64/pci.h	Wed Jan 16 20:56:50 2002
@@ -46,6 +46,20 @@
 #define pci_dma_sync_sg			platform_pci_dma_sync_sg
 #define sg_dma_address			platform_pci_dma_address
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /*
  * Return whether the given PCI device DMA address mask can be supported properly.  For
  * example, if your device can only drive the low 24-bits during PCI bus mastering, then
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-m68k/semaphore.h linux/include/asm-m68k/semaphore.h
--- linux.orig/include/asm-m68k/semaphore.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-m68k/semaphore.h	Thu Jan 10 20:08:20 2002
@@ -9,6 +9,7 @@
 #include <linux/wait.h>
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
+#include <linux/stringify.h>
 
 #include <asm/system.h>
 #include <asm/atomic.h>
@@ -94,11 +95,14 @@
 		"subql #1,%0@\n\t"
 		"jmi 2f\n\t"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed\n"
-		".previous"
+		".subsection 0\n"
 		: /* no outputs */
 		: "a" (sem1)
 		: "memory");
@@ -119,11 +123,14 @@
 		"jmi 2f\n\t"
 		"clrl %0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed_interruptible\n"
-		".previous"
+		".subsection 0\n"
 		: "=d" (result)
 		: "a" (sem1)
 		: "memory");
@@ -145,11 +152,14 @@
 		"jmi 2f\n\t"
 		"clrl %0\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\tpea 1b\n\t"
 		"jbra __down_failed_trylock\n"
-		".previous"
+		".subsection 0\n"
 		: "=d" (result)
 		: "a" (sem1)
 		: "memory");
@@ -175,12 +185,15 @@
 		"addql #1,%0@\n\t"
 		"jle 2f\n"
 		"1:\n"
-		".section .text.lock,\"ax\"\n"
+		".subsection 1\n"
 		".even\n"
+		".ifndef _text_lock_" __stringify(KBUILD_BASENAME) "\n"
+		"_text_lock_" __stringify(KBUILD_BASENAME) ":\n"
+		".endif\n"
 		"2:\t"
 		"pea 1b\n\t"
 		"jbra __up_wakeup\n"
-		".previous"
+		".subsection 0\n"
 		: /* no outputs */
 		: "a" (sem1)
 		: "memory");
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-mips/pci.h linux/include/asm-mips/pci.h
--- linux.orig/include/asm-mips/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-mips/pci.h	Wed Jan 16 20:56:50 2002
@@ -114,6 +114,14 @@
 	/* Nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+
 /*
  * Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-mips64/pci.h linux/include/asm-mips64/pci.h
--- linux.orig/include/asm-mips64/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-mips64/pci.h	Wed Jan 16 20:56:50 2002
@@ -86,6 +86,20 @@
 extern void pci_dma_sync_sg(struct pci_dev *hwdev, struct scatterlist *sg,
                             int nelems, int direction);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 #else /* CONFIG_MAPPED_PCI_IO  */
 
 /*
@@ -123,6 +137,14 @@
 
 	/* Nothing to do */
 }
+
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
 
 /*
  * Map a set of buffers described by scatterlist in streaming
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-parisc/pci.h linux/include/asm-parisc/pci.h
--- linux.orig/include/asm-parisc/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-parisc/pci.h	Wed Jan 16 20:56:50 2002
@@ -172,6 +172,20 @@
 #define pci_map_sg(p, sg, n, d)		hppa_dma_ops->map_sg(p, sg, n, d)
 #define pci_unmap_sg(p, sg, n, d)	hppa_dma_ops->unmap_sg(p, sg, n, d)
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* For U2/Astro/Ike based platforms (which are fully I/O coherent)
 ** dma_sync is a NOP. Let's keep the performance path short here.
 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-parisc/semaphore.h linux/include/asm-parisc/semaphore.h
--- linux.orig/include/asm-parisc/semaphore.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-parisc/semaphore.h	Thu Jan 10 20:08:20 2002
@@ -12,10 +12,6 @@
  *
  */
 
-/* if you're going to use out-of-line slowpaths, use .section .lock.text,
- * not .text.lock or the -ffunction-sections monster will eat you alive
- */
-
 #include <linux/spinlock.h>
 #include <linux/rwsem.h>
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-parisc/spinlock.h linux/include/asm-parisc/spinlock.h
--- linux.orig/include/asm-parisc/spinlock.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-parisc/spinlock.h	Thu Jan 10 20:08:20 2002
@@ -3,10 +3,6 @@
 
 #include <asm/system.h>
 
-/* if you're going to use out-of-line slowpaths, use .section .lock.text,
- * not .text.lock or the -ffunction-sections monster will eat you alive
- */
-
 /* we seem to be the only architecture that uses 0 to mean locked - but we
  * have to.  prumpf */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/ans-lcd.h linux/include/asm-ppc/ans-lcd.h
--- linux.orig/include/asm-ppc/ans-lcd.h	Thu Jan  1 00:00:00 1970
+++ linux/include/asm-ppc/ans-lcd.h	Wed Dec 26 16:36:12 2001
@@ -0,0 +1,11 @@
+#ifndef _PPC_ANS_LCD_H
+#define _PPC_ANS_LCD_H
+
+#define ANSLCD_MINOR		156
+
+#define ANSLCD_CLEAR		0x01
+#define ANSLCD_SENDCTRL		0x02
+#define ANSLCD_SETSHORTDELAY	0x03
+#define ANSLCD_SETLONGDELAY	0x04
+
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/dbdma.h linux/include/asm-ppc/dbdma.h
--- linux.orig/include/asm-ppc/dbdma.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/dbdma.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.dbdma.h 1.5 05/17/01 18:14:24 cort
+ * BK Id: SCCS/s.dbdma.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * Definitions for using the Apple Descriptor-Based DMA controller
@@ -93,5 +93,13 @@
 /* Align an address for a DBDMA command structure */
 #define DBDMA_ALIGN(x)	(((unsigned)(x) + sizeof(struct dbdma_cmd) - 1) \
 			 & -sizeof(struct dbdma_cmd))
+
+/* Useful macros */
+#define DBDMA_DO_STOP(regs) do {				\
+	out_le32(&((regs)->control), (RUN|FLUSH)<<16);		\
+	while(in_le32(&((regs)->status)) & (ACTIVE|FLUSH))	\
+		;						\
+} while(0)
+
 #endif /* _ASM_DBDMA_H_ */
 #endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/feature.h linux/include/asm-ppc/feature.h
--- linux.orig/include/asm-ppc/feature.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/feature.h	Thu Jan  1 00:00:00 1970
@@ -1,111 +0,0 @@
-/*
- * BK Id: SCCS/s.feature.h 1.13 08/19/01 22:23:04 paulus
- */
-/*
- * Definitions for accessing the Feature Control Register (FCR)
- * on Power Macintoshes and similar machines.  The FCR lets us
- * enable/disable, reset, and power up/down various peripherals.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 1998 Paul Mackerras &
- *                    Ben. Herrenschmidt.
- *
- * 
- */
-#ifdef __KERNEL__
-#ifndef __ASM_PPC_FEATURE_H
-#define __ASM_PPC_FEATURE_H
-
-/*
- * The FCR selector for particular features vary somewhat between
- * different machines.  So we abstract a list of features here
- * and let the feature_* routines map them to the actual bits.
- */
-enum system_feature {
-	FEATURE_null,
-	FEATURE_Serial_reset,
-	FEATURE_Serial_enable,
-	FEATURE_Serial_IO_A,
-	FEATURE_Serial_IO_B,
-	FEATURE_SWIM3_enable,
-	FEATURE_MESH_enable,
-	FEATURE_IDE0_enable,		/* Internal IDE */
-	FEATURE_IDE0_reset,		/* Internal IDE */
-	FEATURE_IOBUS_enable,		/* Internal IDE */
-	FEATURE_Mediabay_reset,
-	FEATURE_Mediabay_power,
-	FEATURE_Mediabay_PCI_enable,
-	FEATURE_IDE1_enable,		/* MediaBay IDE */
-	FEATURE_IDE1_reset,		/* MediaBay IDE */
-	FEATURE_Mediabay_floppy_enable,
-	FEATURE_BMac_reset,
-	FEATURE_BMac_IO_enable,
-	FEATURE_Modem_power,
-	FEATURE_Slow_SCC_PCLK,
-	FEATURE_Sound_power,
-	FEATURE_Sound_CLK_enable,
-	FEATURE_IDE2_enable,
-	FEATURE_IDE2_reset,
-	FEATURE_Mediabay_IDE_switch,	/* MB IDE bus switch */
-	FEATURE_Mediabay_content,	/* MB content indicator enable */
-	FEATURE_Airport_reset,		/* Is it actually a reset ? */
-	FEATURE_last,
-};
-
-/* Note about the device parameter: Each device gives it's own entry. If NULL,
-   the feature function will just do nothing and return -EINVAL.
-   The feature management will walk up the device tree until in reaches a recognized
-   chip for which features can be changed and it will then apply the necessary
-   features to that chip. If it's not found, -ENODEV is returned.
-   Note also that feature_test/set/clear are interrupt-safe provided that they are
-   called _after_ feature_init() is completed.
- */
-
-/* Test whether a particular feature is enabled. May return -ENODEV*/
-extern int	feature_test(struct device_node* device, enum system_feature f);
-
-/* Set a particular feature. Returns 0 or -ENODEV */
-extern int	feature_set(struct device_node* device, enum system_feature f);
-
-/* Clear a particular feature */
-extern int	feature_clear(struct device_node* device, enum system_feature f);
-
-/* Initialize feature stuff */
-extern void	feature_init(void);
-
-
-/*
- * Additional functions related to Core99 machines. We should extend the
- * feature mecanism to make those fit into it. For now, they are still
- * separate functions.
- */
-extern void	feature_set_gmac_power(struct device_node* device, int power);
-
-	/* use constants in KeyLargo.h for the reset parameter */
-extern void	feature_gmac_phy_reset(struct device_node* device);
-
-extern void	feature_set_usb_power(struct device_node* device, int power);
-
-extern void 	feature_set_firewire_power(struct device_node* device, int power);
-extern void 	feature_set_firewire_cable_power(struct device_node* device, int power);
-
-extern void	feature_set_modem_power(struct device_node* device, int power);
-
-extern void	feature_set_airport_power(struct device_node* device, int power);
-
-extern void	feature_core99_kick_cpu(int cpu_nr);
-
-/*
- * Sleep related functions. At term, they should be high-priority notifiers,
- * but this would require some changes to the current sleep scheme that won't
- * be done in 2.4.
- */
-extern void	feature_prepare_for_sleep(void);
-extern void	feature_wake_up(void);
-extern int	feature_can_sleep(void);
-
-#endif /* __ASM_PPC_FEATURE_H */
-#endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/heathrow.h linux/include/asm-ppc/heathrow.h
--- linux.orig/include/asm-ppc/heathrow.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/heathrow.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.heathrow.h 1.7 05/17/01 18:14:24 cort
+ * BK Id: SCCS/s.heathrow.h 1.10 12/01/01 20:09:11 benh
  */
 /*
  * heathrow.h: definitions for using the "Heathrow" I/O controller chip.
@@ -17,7 +17,9 @@
 #define HEATHROW_CONTRAST_CNTL		0x33
 
 /* offset from ohare base for feature control register */
-#define HEATHROW_FEATURE_REG		0x38
+#define HEATHROW_MBCR			0x34	/* Media bay control */
+#define HEATHROW_FCR			0x38	/* Feature control */
+#define HEATHROW_AUX_CNTL_REG		0x3c	/* Aux control */
 
 /*
  * Bits in feature control register.
@@ -30,6 +32,7 @@
 #define HRW_BAY_FLOPPY_ENABLE	0x00000010
 #define HRW_IDE0_ENABLE		0x00000020
 #define HRW_IDE0_RESET_N	0x00000040
+#define HRW_BAY_DEV_MASK	0x0000001c
 #define HRW_BAY_RESET_N		0x00000080
 #define HRW_IOBUS_ENABLE	0x00000100	/* Internal IDE ? */
 #define HRW_SCC_ENABLE		0x00000200
@@ -45,7 +48,7 @@
 #define HRW_SWIM_CLONE_FLOPPY	0x00080000	/* ??? (0) */
 #define HRW_AUD_RUN22		0x00100000	/* ??? (1) */
 #define HRW_SCSI_LINK_MODE	0x00200000	/* Read ??? (1) */
-#define HRW_ARB_BYPASS		0x00400000	/* ??? (0 on main, 1 on gatwick) */
+#define HRW_ARB_BYPASS		0x00400000	/* Disable internal PCI arbitrer */
 #define HRW_IDE1_RESET_N	0x00800000	/* Media bay */
 #define HRW_SLOW_SCC_PCLK	0x01000000	/* ??? (0) */
 #define HRW_MODEM_POWER_N	0x02000000	/* Used by internal modem on wallstreet */
@@ -60,3 +63,7 @@
 /* Those seem to be different on paddington */
 #define PADD_MODEM_POWER_N	0x00000001	/* modem power on paddington */
 #define PADD_RESET_SCC		0x02000000	/* check this please */
+
+/* Looks like Heathrow has some sort of GPIOs as well... */
+#define HRW_GPIO_MODEM_RESET	0x6d
+
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/keylargo.h linux/include/asm-ppc/keylargo.h
--- linux.orig/include/asm-ppc/keylargo.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/keylargo.h	Mon Feb  4 18:47:24 2002
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.keylargo.h 1.13 08/19/01 22:23:04 paulus
+ * BK Id: SCCS/s.keylargo.h 1.17 01/20/02 23:53:12 benh
  */
 /*
  * keylargo.h: definitions for using the "KeyLargo" I/O controller chip.
@@ -27,14 +27,18 @@
 #define KEYLARGO_GPIO_EXTINT_CNT	18
 #define KEYLARGO_GPIO_0			0x6A
 #define KEYLARGO_GPIO_CNT		17
+#define KEYLARGO_GPIO_EXTINT_DUAL_EDGE	0x80
 #define KEYLARGO_GPIO_OUTPUT_ENABLE	0x04
 #define KEYLARGO_GPIO_OUTOUT_DATA	0x01
+#define KEYLARGO_GPIO_INPUT_DATA	0x02
 
 /* Specific GPIO regs */
 
-#define KL_GPIO_MODEM_RESET		(KEYLARGO_GPIO_0+0x03) /* Pangea */
+#define KL_GPIO_MODEM_RESET		(KEYLARGO_GPIO_0+0x03)
 #define KL_GPIO_MODEM_POWER		(KEYLARGO_GPIO_0+0x02) /* Pangea */
 
+#define KL_GPIO_SOUND_POWER		(KEYLARGO_GPIO_0+0x05)
+
 /* Hrm... this one is only to be used on Pismo. It seeem to also
  * control the timebase enable on other machines. Still to be
  * experimented... --BenH.
@@ -54,7 +58,9 @@
 #define KL_GPIO_RESET_CPU3		(KEYLARGO_GPIO_EXTINT_0+0x10)
 
 #define KL_GPIO_PMU_MESSAGE_IRQ		(KEYLARGO_GPIO_EXTINT_0+0x09)
-#define KL_GPIO_PMU_MESSAGE_BIT		0x02
+#define KL_GPIO_PMU_MESSAGE_BIT		KEYLARGO_GPIO_INPUT_DATA
+
+#define KL_GPIO_MEDIABAY_IRQ		(KEYLARGO_GPIO_EXTINT_0+0x0e)
 
 #define KL_GPIO_AIRPORT_0		(KEYLARGO_GPIO_EXTINT_0+0x0a)
 #define KL_GPIO_AIRPORT_1		(KEYLARGO_GPIO_EXTINT_0+0x0d)
@@ -65,43 +71,47 @@
 /*
  * Bits in feature control register
  */
-#define KL_MBCR_MB0_DEV_ENABLE		0x00001000
+#define KL_MBCR_MB0_PCI_ENABLE		0x00000800	/* exist ? */
+#define KL_MBCR_MB0_IDE_ENABLE		0x00001000
+#define KL_MBCR_MB0_FLOPPY_ENABLE	0x00002000	/* exist ? */
+#define KL_MBCR_MB0_SOUND_ENABLE	0x00004000	/* hrm... */
+#define KL_MBCR_MB0_DEV_MASK		0x00007800
 #define KL_MBCR_MB0_DEV_POWER		0x00000400
 #define KL_MBCR_MB0_DEV_RESET		0x00000200
 #define KL_MBCR_MB0_ENABLE		0x00000100
-#define KL_MBCR_MB1_DEV_ENABLE		0x10000000
+#define KL_MBCR_MB1_PCI_ENABLE		0x08000000	/* exist ? */
+#define KL_MBCR_MB1_IDE_ENABLE		0x10000000
+#define KL_MBCR_MB1_FLOPPY_ENABLE	0x20000000	/* exist ? */
+#define KL_MBCR_MB1_SOUND_ENABLE	0x40000000	/* hrm... */
+#define KL_MBCR_MB1_DEV_MASK		0x78000000
 #define KL_MBCR_MB1_DEV_POWER		0x04000000
 #define KL_MBCR_MB1_DEV_RESET		0x02000000
 #define KL_MBCR_MB1_ENABLE		0x01000000
 
-#define KL0_SCC_B_INTF_ENABLE		0x00000001	/* ??? */
-#define KL0_SCC_A_INTF_ENABLE		0x00000002	/* ??? */
+#define KL0_SCC_B_INTF_ENABLE		0x00000001
+#define KL0_SCC_A_INTF_ENABLE		0x00000002
 #define KL0_SCC_SLOWPCLK		0x00000004
 #define KL0_SCC_RESET			0x00000008
 #define KL0_SCCA_ENABLE			0x00000010
 #define KL0_SCCB_ENABLE			0x00000020
 #define KL0_SCC_CELL_ENABLE		0x00000040
+#define KL0_IRDA_HIGH_BAND		0x00000100
+#define KL0_IRDA_SOURCE2_SEL		0x00000200
+#define KL0_IRDA_SOURCE1_SEL		0x00000400
+#define KL0_IRDA_RESET			0x00000800
+#define KL0_IRDA_DEFAULT1		0x00001000
+#define KL0_IRDA_DEFAULT0		0x00002000
+#define KL0_IRDA_FAST_CONNECT		0x00004000
+#define KL0_IRDA_ENABLE			0x00008000
+#define KL0_IRDA_CLK32_ENABLE		0x00010000
+#define KL0_IRDA_CLK19_ENABLE		0x00020000
 #define KL0_USB0_PAD_SUSPEND0		0x00040000
 #define KL0_USB0_PAD_SUSPEND1		0x00080000
 #define KL0_USB0_CELL_ENABLE		0x00100000
 #define KL0_USB1_PAD_SUSPEND0		0x00400000
 #define KL0_USB1_PAD_SUSPEND1		0x00800000
 #define KL0_USB1_CELL_ENABLE		0x01000000
-/* KL id 0x22 only */
 #define KL0_USB_REF_SUSPEND		0x10000000
-#define KL0_IRDA_ENABLE			0x00008000
-#define KL0_IRDA_CLK32_ENABLE		0x00010000
-#define KL0_IRDA_CLK19_ENABLE		0x00020000
-/* KL id 0x25 (pangea) only */
-#define KL0_USB1_PAD_SUSPEND_SEL	0x00020000
-#define KL0_USB1_REF_SUSPEND		0x00010000
-#define KL0_USB1_REF_SUSPEND_SEL	0x00008000
-#define KL0_USB1_PMI			0x00004000
-#define KL0_USB0_PAD_SUSPEND_SEL	0x00002000
-#define KL0_USB0_REF_SUSPEND		0x00001000
-#define KL0_USB0_REF_SUSPEND_SEL	0x00000800
-#define KL0_USB0_PMI			0x00000400
-
 
 #define KL0_SERIAL_ENABLE		(KL0_SCC_B_INTF_ENABLE | \
 					KL0_SCC_SLOWPCLK | \
@@ -128,8 +138,9 @@
 #define KL2_IOBUS_ENABLE		0x00000002
 #define KL2_SLEEP_STATE_BIT		0x00000100
 #define KL2_MPIC_ENABLE			0x00020000
-#define KL2_MODEM_POWER_N		0x02000000
-#define KL2_AIRPORT_RESET_N		0x08000000	/* Or power ? */
+#define KL2_ALT_DATA_OUT		0x02000000
+#define KL2_MEM_IS_BIG			0x04000000
+#define KL2_CARDSEL_16			0x08000000
 
 #define KL3_SHUTDOWN_PLL_TOTAL		0x00000001
 #define KL3_SHUTDOWN_PLLKW6		0x00000002
@@ -149,11 +160,11 @@
 #define KL3_STOPPING33_ENABLED		0x00080000
 
 /* Port 0,1 : bus 0, port 2,3 : bus 1 */
-#define KL4_SET_PORT_ENABLE(p)		(0x00000008 << ((p)<<3))
-#define KL4_SET_PORT_RESUME(p)		(0x00000004 << ((p)<<3))
-#define KL4_SET_PORT_CONNECT(p)		(0x00000002 << ((p)<<3))
-#define KL4_SET_PORT_DISCONNECT(p)	(0x00000001 << ((p)<<3))
-#define KL4_GET_PORT_RESUME(p)		(0x00000040 << ((p)<<3))
-#define KL4_GET_PORT_CONNECT(p)		(0x00000020 << ((p)<<3))
-#define KL4_GET_PORT_DISCONNECT(p)	(0x00000010 << ((p)<<3))
+#define KL4_PORT_WAKEUP_ENABLE(p)	(0x00000008 << ((p)<<3))
+#define KL4_PORT_RESUME_WAKE_EN(p)	(0x00000004 << ((p)<<3))
+#define KL4_PORT_CONNECT_WAKE_EN(p)	(0x00000002 << ((p)<<3))
+#define KL4_PORT_DISCONNECT_WAKE_EN(p)	(0x00000001 << ((p)<<3))
+#define KL4_PORT_RESUME_STAT(p)		(0x00000040 << ((p)<<3))
+#define KL4_PORT_CONNECT_STAT(p)	(0x00000020 << ((p)<<3))
+#define KL4_PORT_DISCONNECT_STAT(p)	(0x00000010 << ((p)<<3))
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/machdep.h linux/include/asm-ppc/machdep.h
--- linux.orig/include/asm-ppc/machdep.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/machdep.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.machdep.h 1.25 11/13/01 21:26:07 paulus
+ * BK Id: SCCS/s.machdep.h 1.27 12/01/01 20:09:11 benh
  */
 #ifdef __KERNEL__
 #ifndef _PPC_MACHDEP_H
@@ -16,6 +16,11 @@
 struct pci_dev;
 struct seq_file;
 
+/* We export this macro for external modules like Alsa to know if
+ * ppc_md.feature_call is implemented or not
+ */
+#define CONFIG_PPC_HAS_FEATURE_CALLS
+
 struct machdep_calls {
 	void		(*setup_arch)(void);
 	/* Optional, may be NULL. */
@@ -94,6 +99,12 @@
 
 	/* this is for modules, since _machine can be a define -- Cort */
 	int ppc_machine;
+
+	/* Motherboard/chipset features. This is a kind of general purpose
+	 * hook used to control some machine specific features (like reset
+	 * lines, chip power control, etc...).
+	 */
+	int (*feature_call)(unsigned int feature, ...);
 
 #ifdef CONFIG_SMP
 	/* functions for dealing with other cpus */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/mediabay.h linux/include/asm-ppc/mediabay.h
--- linux.orig/include/asm-ppc/mediabay.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/mediabay.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.mediabay.h 1.5 05/17/01 18:14:25 cort
+ * BK Id: SCCS/s.mediabay.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * mediabay.h: definitions for using the media bay
@@ -12,10 +12,13 @@
 
 #ifdef __KERNEL__
 
-#define MB_FD	0		/* media bay contains floppy drive */
-#define MB_FD1	1		/* media bay contains floppy drive */
-#define MB_CD	3		/* media bay contains ATA drive such as CD */
-#define MB_NO	7		/* media bay contains nothing */
+#define MB_FD		0	/* media bay contains floppy drive (automatic eject ?) */
+#define MB_FD1		1	/* media bay contains floppy drive (manual eject ?) */
+#define MB_SOUND	2	/* sound device ? */
+#define MB_CD		3	/* media bay contains ATA drive such as CD or ZIP */
+#define MB_PCI		5	/* media bay contains a PCI device */
+#define MB_POWER	6	/* media bay contains a Power device (???) */
+#define MB_NO		7	/* media bay contains nothing */
 
 void media_bay_init(void);
 int check_media_bay(struct device_node *which_bay, int what);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/ohare.h linux/include/asm-ppc/ohare.h
--- linux.orig/include/asm-ppc/ohare.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/ohare.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ohare.h 1.5 05/17/01 18:14:25 cort
+ * BK Id: SCCS/s.ohare.h 1.8 12/01/01 20:09:11 benh
  */
 /*
  * ohare.h: definitions for using the "O'Hare" I/O controller chip.
@@ -11,7 +11,8 @@
  */
 
 /* offset from ohare base for feature control register */
-#define OHARE_FEATURE_REG	0x38
+#define OHARE_MBCR	0x34
+#define OHARE_FCR	0x38
 
 /*
  * Bits in feature control register.
@@ -25,6 +26,7 @@
 #define OH_BAY_FLOPPY_ENABLE	0x10
 #define OH_IDE0_ENABLE		0x20
 #define OH_IDE0_RESET_N		0x40	/* a guess */
+#define OH_BAY_DEV_MASK		0x1c
 #define OH_BAY_RESET_N		0x80
 #define OH_IOBUS_ENABLE		0x100	/* IOBUS seems to be IDE */
 #define OH_SCC_ENABLE		0x200
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/pci.h linux/include/asm-ppc/pci.h
--- linux.orig/include/asm-ppc/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/pci.h	Wed Jan 16 20:56:50 2002
@@ -102,6 +102,13 @@
 	/* nothing to do */
 }
 
+/* pci_unmap_{page,single} is a nop so... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
 
 /*
  * pci_{map,unmap}_single_page maps a kernel page to a dma_addr_t. identical
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/pmac_feature.h linux/include/asm-ppc/pmac_feature.h
--- linux.orig/include/asm-ppc/pmac_feature.h	Thu Jan  1 00:00:00 1970
+++ linux/include/asm-ppc/pmac_feature.h	Mon Feb  4 18:47:24 2002
@@ -0,0 +1,252 @@
+/*
+ * Definition of platform feature hooks for PowerMacs
+ * 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1998 Paul Mackerras &
+ *                    Ben. Herrenschmidt.
+ *                    
+ *                    
+ * Note: I removed media-bay details from the feature stuff, I beleive it's
+ *       not worth it, the media-bay driver can directly use the mac-io
+ *       ASIC registers.
+ *       
+ * Implementation note: Currently, none of these functions will block.
+ * However, they may internally protect themselves with a spinlock
+ * for way too long. Be prepared for at least some of these to block
+ * in the future.
+ * 
+ * Unless specifically defined, the result code is assumed to be an
+ * error when negative, 0 is the default success result. Some functions
+ * may return additional positive result values.
+ * 
+ * To keep implementation simple, all feature calls are assumed to have
+ * the prototype parameters (struct device_node* node, int value).
+ * When either is not used, pass 0.
+ */
+
+#ifdef __KERNEL__
+#ifndef __PPC_ASM_PMAC_FEATURE_H
+#define __PPC_ASM_PMAC_FEATURE_H
+
+/*
+ * Known Mac motherboard models
+ * 
+ * Please, report any error here to benh@kernel.crashing.org, thanks !
+ */
+
+/* PowerSurge are the first generation of PCI Pmacs. This include
+ * all of the Grand-Central based machines
+ */
+#define PMAC_TYPE_PSURGE		0x10	/* PowerSurge */
+
+/* Here is the infamous serie of OHare based machines
+ */
+#define PMAC_TYPE_COMET			0x20	/* Beleived to be PowerBook 2400 */
+#define PMAC_TYPE_HOOPER		0x21	/* Beleived to be PowerBook 3400 */
+#define PMAC_TYPE_KANGA			0x22	/* PowerBook 3500 (first G3) */
+#define PMAC_TYPE_ALCHEMY		0x23	/* Alchemy motherboard base */
+#define PMAC_TYPE_GAZELLE		0x24	/* Spartacus, some 5xxx/6xxx */
+#define PMAC_TYPE_UNKNOWN_OHARE		0x2f	/* Unknown, but OHare based */
+
+/* Here are the Heathrow based machines
+ * FIXME: Differenciate wallstreet,mainstreet,wallstreetII
+ */
+#define PMAC_TYPE_GOSSAMER		0x30	/* Gossamer motherboard */
+#define PMAC_TYPE_SILK			0x31	/* Desktop PowerMac G3 */
+#define PMAC_TYPE_WALLSTREET		0x32	/* Wallstreet/Mainstreet PowerBook*/
+#define PMAC_TYPE_UNKNOWN_HEATHROW	0x3f	/* Unknown but heathrow based */
+
+/* Here are newworld machines based on Paddington (heathrow derivative)
+ */
+#define PMAC_TYPE_101_PBOOK		0x40	/* 101 PowerBook (aka Lombard) */
+#define PMAC_TYPE_ORIG_IMAC		0x41	/* First generation iMac */
+#define PMAC_TYPE_YOSEMITE		0x42	/* B&W G3 */
+#define PMAC_TYPE_YIKES			0x43	/* Yikes G4 (PCI graphics) */
+#define PMAC_TYPE_UNKNOWN_PADDINGTON	0x4f	/* Unknown but paddington based */
+
+/* Core99 machines based on UniNorth 1.0 and 1.5
+ * 
+ * Note: A single entry here may cover several actual models according
+ * to the device-tree. (Sawtooth is most tower G4s, FW_IMAC is most
+ * FireWire based iMacs, etc...). Those machines are too similar to be
+ * distinguished here, when they need to be differencied, use the
+ * device-tree "model" or "compatible" property.
+ */
+#define PMAC_TYPE_ORIG_IBOOK		0x40	/* First iBook model (no firewire) */
+#define PMAC_TYPE_SAWTOOTH		0x41	/* Desktop G4s */
+#define PMAC_TYPE_FW_IMAC		0x42	/* FireWire iMacs (except Pangea based) */
+#define PMAC_TYPE_FW_IBOOK		0x43	/* FireWire iBooks (except iBook2) */
+#define PMAC_TYPE_CUBE			0x44	/* Cube PowerMac */
+#define PMAC_TYPE_QUICKSILVER		0x45	/* QuickSilver G4s */
+#define PMAC_TYPE_PISMO			0x46	/* Pismo PowerBook */
+#define PMAC_TYPE_TITANIUM		0x47	/* Titanium PowerBook */
+#define PMAC_TYPE_TITANIUM2		0x48	/* Titanium II PowerBook */
+#define PMAC_TYPE_UNKNOWN_CORE99	0x5f
+
+/* MacRISC2 machines based on the Pangea chipset
+ */
+#define PMAC_TYPE_PANGEA_IMAC		0x100	/* Flower Power iMac */
+#define PMAC_TYPE_IBOOK2		0x101	/* iBook2 (polycarbonate) */
+#define PMAC_TYPE_UNKNOWN_PANGEA	0x10f
+
+/*
+ * Motherboard flags
+ */
+
+#define PMAC_MB_CAN_SLEEP		0x00000001
+#define PMAC_MB_HAS_FW_POWER		0x00000002
+
+/*
+ * Feature calls supported on pmac
+ * 	
+ */
+
+/*
+ * Use this inline wrapper
+ */
+struct device_node;
+
+static inline int pmac_call_feature(int selector, struct device_node* node,
+					int param, int value)
+{
+	if (!ppc_md.feature_call)
+		return -ENODEV;
+	return ppc_md.feature_call(selector, node, param, value);
+}
+
+/* PMAC_FTR_SERIAL_ENABLE	(struct device_node* node, int param, int value)
+ * enable/disable an SCC side. Pass the node corresponding to the
+ * channel side as a parameter.
+ * param is the type of port
+ * if param is ored with PMAC_SCC_FLAG_XMON, then the SCC is locked enabled
+ * for use by xmon.
+ */
+#define PMAC_FTR_SCC_ENABLE		PMAC_FTR_DEF(0)
+	#define PMAC_SCC_ASYNC		0
+	#define PMAC_SCC_IRDA		1
+	#define PMAC_SCC_I2S1		2
+	#define PMAC_SCC_FLAG_XMON	0x00001000
+
+/* PMAC_FTR_MODEM_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the internal modem.
+ */
+#define PMAC_FTR_MODEM_ENABLE		PMAC_FTR_DEF(1)
+
+/* PMAC_FTR_SWIM3_ENABLE	(struct device_node* node, 0,int value)
+ * enable/disable the swim3 (floppy) cell of a mac-io ASIC
+ */
+#define PMAC_FTR_SWIM3_ENABLE		PMAC_FTR_DEF(2)
+
+/* PMAC_FTR_MESH_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the mesh (scsi) cell of a mac-io ASIC
+ */
+#define PMAC_FTR_MESH_ENABLE		PMAC_FTR_DEF(3)
+
+/* PMAC_FTR_IDE_ENABLE		(struct device_node* node, int busID, int value)
+ * enable/disable an IDE port of a mac-io ASIC
+ * pass the busID parameter
+ */
+#define PMAC_FTR_IDE_ENABLE		PMAC_FTR_DEF(4)
+
+/* PMAC_FTR_IDE_RESET		(struct device_node* node, int busID, int value)
+ * assert(1)/release(0) an IDE reset line (mac-io IDE only)
+ */
+#define PMAC_FTR_IDE_RESET		PMAC_FTR_DEF(5)
+
+/* PMAC_FTR_BMAC_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the bmac (ethernet) cell of a mac-io ASIC, also drive
+ * it's reset line
+ */
+#define PMAC_FTR_BMAC_ENABLE		PMAC_FTR_DEF(6)
+
+/* PMAC_FTR_GMAC_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the gmac (ethernet) cell of an uninorth ASIC. This
+ * control the cell's clock.
+ */
+#define PMAC_FTR_GMAC_ENABLE		PMAC_FTR_DEF(7)
+
+/* PMAC_FTR_GMAC_PHY_RESET	(struct device_node* node, 0, 0)
+ * Perform a HW reset of the PHY connected to a gmac controller.
+ * Pass the gmac device node, not the PHY node.
+ */
+#define PMAC_FTR_GMAC_PHY_RESET		PMAC_FTR_DEF(8)
+
+/* PMAC_FTR_SOUND_CHIP_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the sound chip, whatever it is and provided it can
+ * acually be controlled
+ */
+#define PMAC_FTR_SOUND_CHIP_ENABLE	PMAC_FTR_DEF(9)
+
+/* -- add various tweaks related to sound routing -- */
+ 
+/* PMAC_FTR_AIRPORT_ENABLE	(struct device_node* node, 0, int value)
+ * enable/disable the airport card
+ */
+#define PMAC_FTR_AIRPORT_ENABLE		PMAC_FTR_DEF(10)
+
+/* PMAC_FTR_RESET_CPU		(NULL, int cpu_nr, 0)
+ * toggle the reset line of a CPU on an uninorth-based SMP machine
+ */
+#define PMAC_FTR_RESET_CPU		PMAC_FTR_DEF(11)
+
+/* PMAC_FTR_USB_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable an USB cell, along with the power of the USB "pad"
+ * on keylargo based machines
+ */
+#define PMAC_FTR_USB_ENABLE		PMAC_FTR_DEF(12)
+
+/* PMAC_FTR_1394_ENABLE		(struct device_node* node, 0, int value)
+ * enable/disable the firewire cell of an uninorth ASIC.
+ */
+#define PMAC_FTR_1394_ENABLE		PMAC_FTR_DEF(13)
+
+/* PMAC_FTR_1394_CABLE_POWER	(struct device_node* node, 0, int value)
+ * enable/disable the firewire cable power supply of the uninorth
+ * firewire cell
+ */
+#define PMAC_FTR_1394_CABLE_POWER	PMAC_FTR_DEF(14)
+
+/* PMAC_FTR_SLEEP_STATE		(struct device_node* node, 0, int value)
+ * set the sleep state of the motherboard.
+ * Pass -1 as value to query for sleep capability
+ */
+#define PMAC_FTR_SLEEP_STATE		PMAC_FTR_DEF(15)
+
+/* PMAC_FTR_GET_MB_INFO		(NULL, selector, 0)
+ * 
+ * returns some motherboard infos.
+ * selector: 0  - model id
+ *           1  - model flags (capabilities)
+ *           2  - model name (cast to const char *)
+ */
+#define PMAC_FTR_GET_MB_INFO		PMAC_FTR_DEF(16)
+#define   PMAC_MB_INFO_MODEL	0
+#define   PMAC_MB_INFO_FLAGS	1
+#define   PMAC_MB_INFO_NAME	2
+
+/* PMAC_FTR_READ_GPIO		(NULL, int index, 0)
+ *
+ * read a GPIO from a mac-io controller of type KeyLargo or Pangea.
+ * the value returned is a byte (positive), or a negative error code
+ */
+#define PMAC_FTR_READ_GPIO		PMAC_FTR_DEF(17)
+
+/* PMAC_FTR_WRITE_GPIO		(NULL, int index, int value)
+ *
+ * write a GPIO of a mac-io controller of type KeyLargo or Pangea.
+ */
+#define PMAC_FTR_WRITE_GPIO		PMAC_FTR_DEF(18)
+
+
+/* Don't use those directly, they are for the sake of pmac_setup.c */
+extern int pmac_do_feature_call(unsigned int selector, ...);
+extern void pmac_feature_init(void);
+extern void pmac_feature_late_init(void);
+
+#define PMAC_FTR_DEF(x) ((_MACH_Pmac << 16) | (x))
+
+#endif /* __PPC_ASM_PMAC_FEATURE_H */
+#endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- linux.orig/include/asm-ppc/processor.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/processor.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.processor.h 1.31 10/05/01 16:26:22 paulus
+ * BK Id: SCCS/s.processor.h 1.33 12/01/01 20:09:11 benh
  */
 #ifdef __KERNEL__
 #ifndef __ASM_PPC_PROCESSOR_H
@@ -209,8 +209,11 @@
 #define	  HID0_DFCA	(1<<6)		/* Data Cache Flush Assist */
 #define   HID0_BTIC	(1<<5)		/* Branch Target Instruction Cache Enable */
 #define   HID0_ABE	(1<<3)		/* Address Broadcast Enable */
+#define   HID0_FOLD	(1<<3)		/* Branch Folding enable - 7450 */
 #define	  HID0_BHTE	(1<<2)		/* Branch History Table Enable */
 #define	  HID0_BTCD	(1<<1)		/* Branch target cache disable */
+#define	  HID0_NOPDST	(1<<1)		/* No-op dst, dstt, etc. instr. */
+#define	  HID0_NOPTI	(1<<0)		/* No-op dcbt and dcbst instr. */
 #define	SPRN_HID1	0x3F1	/* Hardware Implementation Register 1 */
 #define	SPRN_IABR	0x3F2	/* Instruction Address Breakpoint Register */
 #define	SPRN_IAC1	0x3F4	/* Instruction Address Compare 1 */
@@ -261,6 +264,14 @@
 #define L2CR_L2DF		0x00004000	/* L2 differential clock */
 #define L2CR_L2BYP		0x00002000	/* L2 DLL bypass */
 #define L2CR_L2IP		0x00000001	/* L2 GI in progress */
+#define SPRN_L2CR2      0x3f8
+#define	SPRN_L3CR	0x3FA	/* Level 3 Cache Control Regsiter (7450) */
+#define L3CR_L3E		0x80000000	/* L3 enable */
+#define SPRN_MSSCR0	0x3f6	/* Memory Subsystem Control Register 0 */
+#define SPRN_MSSSR0	0x3f7	/* Memory Subsystem Status Register 1 */
+#define SPRN_ICTRL	0x3f3	/* Instruction Cache & Interrupt control reg */
+#define SPRN_LDSTCR	0x3f8	/* Load/Store control register */
+#define SPRN_LDSTDB	0x3f4	/* */
 #define	SPRN_LR		0x008	/* Link Register */
 #define	SPRN_MMCR0	0x3B8	/* Monitor Mode Control Register 0 */
 #define	SPRN_MMCR1	0x3BC	/* Monitor Mode Control Register 1 */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-ppc/prom.h linux/include/asm-ppc/prom.h
--- linux.orig/include/asm-ppc/prom.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-ppc/prom.h	Wed Dec 26 16:36:12 2001
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prom.h 1.19 08/17/01 15:23:17 paulus
+ * BK Id: SCCS/s.prom.h 1.21 12/01/01 20:09:11 benh
  */
 /*
  * Definitions for talking to the Open Firmware PROM on
@@ -85,6 +85,10 @@
 extern void prom_get_irq_senses(unsigned char *, int, int);
 extern int prom_n_addr_cells(struct device_node* np);
 extern int prom_n_size_cells(struct device_node* np);
+
+extern struct resource*
+request_OF_resource(struct device_node* node, int index, const char* name_postfix);
+extern int release_OF_resource(struct device_node* node, int index);
 
 extern void print_properties(struct device_node *node);
 extern int call_rtas(const char *service, int nargs, int nret,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/idals.h linux/include/asm-s390/idals.h
--- linux.orig/include/asm-s390/idals.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/idals.h	Fri Dec 21 16:25:31 2001
@@ -10,7 +10,7 @@
 #include <linux/config.h>
 #include <asm/irq.h>
 
-#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
+#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
 #define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG)
 
 static inline addr_t *
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/lowcore.h linux/include/asm-s390/lowcore.h
--- linux.orig/include/asm-s390/lowcore.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/lowcore.h	Fri Dec 21 16:25:31 2001
@@ -45,6 +45,9 @@
 #define __LC_CPUID                      0xC60
 #define __LC_CPUADDR                    0xC68
 #define __LC_IPLDEV                     0xC7C
+
+#define __LC_JIFFY_TIMER		0xC80
+
 #define __LC_PANIC_MAGIC                0xE00
 
 #define __LC_PFAULT_INTPARM             0x080
@@ -161,7 +164,7 @@
 	/* entry.S sensitive area end */
 
         /* SMP info area: defined by DJB */
-        __u64        jiffy_timer_cc;           /* 0xc80 */
+        __u64        jiffy_timer;              /* 0xc80 */
 	atomic_t     ext_call_fast;            /* 0xc88 */
         __u8         pad11[0xe00-0xc8c];       /* 0xc8c */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/processor.h linux/include/asm-s390/processor.h
--- linux.orig/include/asm-s390/processor.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/processor.h	Fri Dec 21 16:25:31 2001
@@ -74,8 +74,6 @@
 
 struct thread_struct
  {
-
-        struct pt_regs *regs;         /* the user registers can be found on*/
 	s390_fp_regs fp_regs;
         __u32   ar2;                   /* kernel access register 2         */
         __u32   ar4;                   /* kernel access register 4         */
@@ -95,8 +93,7 @@
 
 typedef struct thread_struct thread_struct;
 
-#define INIT_THREAD { (struct pt_regs *) 0,                       \
-                    { 0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
+#define INIT_THREAD {{0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
 			    {0},{0},{0},{0},{0},{0}}},            \
                      0, 0,                                        \
                     sizeof(init_stack) + (__u32) &init_stack,     \
@@ -126,16 +123,25 @@
 #define release_segments(mm)            do { } while (0)
 
 /*
- * Return saved PC of a blocked thread. used in kernel/sched
+ * Return saved PC of a blocked thread. used in kernel/sched.
+ * resume in entry.S does not create a new stack frame, it
+ * just stores the registers %r6-%r15 to the frame given by
+ * schedule. We want to return the address of the caller of
+ * schedule, so we have to walk the backchain one time to
+ * find the frame schedule() store its return address.
  */
 extern inline unsigned long thread_saved_pc(struct thread_struct *t)
 {
-        return (t->regs) ? ((unsigned long)t->regs->psw.addr) : 0;
+	unsigned long bc;
+	bc = *((unsigned long *) t->ksp);
+	return *((unsigned long *) (bc+56));
 }
 
 unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk)   ((tsk)->thread.regs->psw.addr)
-#define KSTK_ESP(tsk)   ((tsk)->thread.ksp)
+#define __KSTK_PTREGS(tsk) \
+	((struct pt_regs *)((unsigned long) tsk+THREAD_SIZE) - 1)
+#define KSTK_EIP(tsk)	(__KSTK_PTREGS(tsk)->psw.addr)
+#define KSTK_ESP(tsk)	(__KSTK_PTREGS(tsk)->gprs[15])
 
 /* Allocation and freeing of basic task resources. */
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/ptrace.h linux/include/asm-s390/ptrace.h
--- linux.orig/include/asm-s390/ptrace.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/ptrace.h	Fri Dec 21 16:25:31 2001
@@ -118,7 +118,7 @@
 {
         __u32   mask;
         __u32   addr;
-} psw_t __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8))) psw_t;
 
 #ifdef __KERNEL__
 #define FIX_PSW(addr) ((unsigned long)(addr)|0x80000000UL)
@@ -150,8 +150,8 @@
 #define FPC_VALID_MASK          0xF8F8FF03
 
 /*
- * The first entries in pt_regs, gdb_pt_regs and user_regs_struct
- * are common for all three structures. The s390_regs structure
+ * The first entries in pt_regs and user_regs_struct
+ * are common for the two structures. The s390_regs structure
  * covers the common parts. It simplifies copying the common part
  * between the three structures.
  */
@@ -178,30 +178,12 @@
 };
 
 /*
- * The gdb_pt_regs struct is used instead of the pt_regs structure
- * if kernel remote debugging is used.
- */
-#if CONFIG_REMOTE_DEBUG
-struct gdb_pt_regs
-{
-	psw_t psw;
-	__u32 gprs[NUM_GPRS];
-	__u32 acrs[NUM_ACRS];
-	__u32 orig_gpr2;
-	__u32 trap;
-	__u32 crs[16];
-	s390_fp_regs fp_regs;
-	__u32 old_ilc;
-};
-#endif
-
-/*
  * Now for the program event recording (trace) definitions.
  */
 typedef struct
 {
 	__u32 cr[3];
-} per_cr_words  __attribute__((packed));
+} per_cr_words;
 
 #define PER_EM_MASK 0xE8000000
 
@@ -223,14 +205,14 @@
 	unsigned                       : 21;
 	addr_t   starting_addr;
 	addr_t   ending_addr;
-} per_cr_bits  __attribute__((packed));
+} per_cr_bits;
 
 typedef struct
 {
 	__u16          perc_atmid;          /* 0x096 */
 	__u32          address;             /* 0x098 */
 	__u8           access_id;           /* 0x0a1 */
-} per_lowcore_words  __attribute__((packed));
+} per_lowcore_words;
 
 typedef struct
 {
@@ -249,14 +231,14 @@
 	addr_t   address;                     /* 0x098 */
 	unsigned                         : 4; /* 0x0a1 */
 	unsigned access_id               : 4;
-} per_lowcore_bits __attribute__((packed));
+} per_lowcore_bits;
 
 typedef struct
 {
 	union {
 		per_cr_words   words;
 		per_cr_bits    bits;
-	} control_regs  __attribute__((packed));
+	} control_regs;
 	/*
 	 * Use these flags instead of setting em_instruction_fetch
 	 * directly they are used so that single stepping can be
@@ -275,7 +257,7 @@
 		per_lowcore_words words;
 		per_lowcore_bits  bits;
 	} lowcore; 
-} per_struct __attribute__((packed));
+} per_struct;
 
 typedef struct
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/s390-gdbregs.h linux/include/asm-s390/s390-gdbregs.h
--- linux.orig/include/asm-s390/s390-gdbregs.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/s390-gdbregs.h	Thu Jan  1 00:00:00 1970
@@ -1,85 +0,0 @@
-/*
- *  include/asm-s390/s390-gdbregs.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  used both by the linux kernel for remote debugging & gdb 
- */
-
-#ifndef _S390_GDBREGS_H
-#define _S390_GDBREGS_H
-
-#ifdef __KERNEL__
-#include <asm/s390-regs-common.h>
-#else
-#include <s390/s390-regs-common.h>
-#endif
-#define S390_MAX_INSTR_SIZE 6
-#define NUM_REGS      (2+NUM_GPRS+NUM_ACRS+NUM_CRS+1+NUM_FPRS)
-#define FIRST_ACR     (2+NUM_GPRS)
-#define LAST_ACR      (FIRST_ACR+NUM_ACRS-1)
-#define FIRST_CR      (FIRST_ACR+NUM_ACRS)
-#define LAST_CR       (FIRST_CR+NUM_CRS-1)
-
-#define PSWM_REGNUM    0
-#define PC_REGNUM      1
-#define	GP0_REGNUM     2		    /* GPR register 0 */ 
-#define GP_LAST_REGNUM (GP0_REGNUM+NUM_GPRS-1)
-#define RETADDR_REGNUM (GP0_REGNUM+14)                   /* Usually return address */
-#define SP_REGNUM      (GP0_REGNUM+15)	    /* Contains address of top of stack */
-#define FP_REGNUM     SP_REGNUM /* needed in findvar.c still */
-#define FRAME_REGNUM  (GP0_REGNUM+11)
-#define FPC_REGNUM    (GP0_REGNUM+NUM_GPRS+NUM_ACRS+NUM_CRS)
-#define FP0_REGNUM    (FPC_REGNUM+1) /* FPR (Floating point) register 0 */
-#define FPLAST_REGNUM (FP0_REGNUM+NUM_FPRS-1)	/* Last floating point register */
-
-/* The top of this structure is as similar as possible to a pt_regs structure to */
-/* simplify code */
-typedef struct
-{
-	S390_REGS_COMMON
-	__u32         crs[NUM_CRS];
-	s390_fp_regs  fp_regs;
-} s390_gdb_regs __attribute__((packed));
-
-#define REGISTER_NAMES                                           \
-{                                                                \
-"pswm","pswa",                                                   \
-"gpr0","gpr1","gpr2","gpr3","gpr4","gpr5","gpr6","gpr7",         \
-"gpr8","gpr9","gpr10","gpr11","gpr12","gpr13","gpr14","gpr15",   \
-"acr0","acr1","acr2","acr3","acr4","acr5","acr6","acr7",         \
-"acr8","acr9","acr10","acr11","acr12","acr13","acr14","acr15",   \
-"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7",                 \
-"cr8","cr9","cr10","cr11","cr12","cr13","cr14","cr15",           \
-"fpc",                                                           \
-"fpr0","fpr1","fpr2","fpr3","fpr4","fpr5","fpr6","fpr7",         \
-"fpr8","fpr9","fpr10","fpr11","fpr12","fpr13","fpr14","fpr15"    \
-}
-
-/* Index within `registers' of the first byte of the space for
-   register N.  */
-
-#define FP0_OFFSET ((PSW_MASK_SIZE+PSW_ADDR_SIZE)+ \
-(GPR_SIZE*NUM_GPRS)+(ACR_SIZE+NUM_ACRS)+ \
-(CR_SIZE*NUM_CRS)+(FPC_SIZE+FPC_PAD_SIZE))
-
-#define REGISTER_BYTES    \
-((FP0_OFFSET)+(FPR_SIZE*NUM_FPRS))
-
-#define REGISTER_BYTE(N)  ((N) < FP0_REGNUM ? (N)*4:(FP0_OFFSET+((N)-FP0_REGNUM)*FPR_SIZE))
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/scatterlist.h linux/include/asm-s390/scatterlist.h
--- linux.orig/include/asm-s390/scatterlist.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/scatterlist.h	Fri Dec 21 16:25:31 2001
@@ -1,8 +1,16 @@
-#ifndef _ASMS390X_SCATTERLIST_H
-#define _ASMS390X_SCATTERLIST_H
+#ifndef _ASMS390_SCATTERLIST_H
+#define _ASMS390_SCATTERLIST_H
 
 struct scatterlist {
-    char *  address;    /* Location data is to be transferred to */
+    /* This will disappear in 2.5.x */
+    char *address;
+
+    /* These two are only valid if ADDRESS member of this
+     * struct is NULL.
+     */
+    struct page *page;
+    unsigned int offset;
+
     unsigned int length;
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/setup.h linux/include/asm-s390/setup.h
--- linux.orig/include/asm-s390/setup.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/setup.h	Fri Dec 21 16:25:31 2001
@@ -13,7 +13,7 @@
 #define RAMDISK_ORIGIN		0x800000
 #define RAMDISK_SIZE		0x800000
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 #define IPL_DEVICE        (*(unsigned long *)  (0x10404))
 #define INITRD_START      (*(unsigned long *)  (0x1040C))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/siginfo.h linux/include/asm-s390/siginfo.h
--- linux.orig/include/asm-s390/siginfo.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/siginfo.h	Fri Dec 21 16:25:31 2001
@@ -111,7 +111,7 @@
 #define SI_USER		0	/* sent by kill, sigsend, raise */
 #define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
 #define SI_QUEUE	-1	/* sent by sigqueue */
-#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
 #define SI_MESGQ	-3	/* sent by real time mesq state change */
 #define SI_ASYNCIO	-4	/* sent by AIO completion */
 #define SI_SIGIO	-5	/* sent by queued SIGIO */
@@ -122,71 +122,71 @@
 /*
  * SIGILL si_codes
  */
-#define ILL_ILLOPC	1	/* illegal opcode */
-#define ILL_ILLOPN	2	/* illegal operand */
-#define ILL_ILLADR	3	/* illegal addressing mode */
-#define ILL_ILLTRP	4	/* illegal trap */
-#define ILL_PRVOPC	5	/* privileged opcode */
-#define ILL_PRVREG	6	/* privileged register */
-#define ILL_COPROC	7	/* coprocessor error */
-#define ILL_BADSTK	8	/* internal stack error */
+#define ILL_ILLOPC	(__SI_FAULT|1)	/* illegal opcode */
+#define ILL_ILLOPN	(__SI_FAULT|2)	/* illegal operand */
+#define ILL_ILLADR	(__SI_FAULT|3)	/* illegal addressing mode */
+#define ILL_ILLTRP	(__SI_FAULT|4)	/* illegal trap */
+#define ILL_PRVOPC	(__SI_FAULT|5)	/* privileged opcode */
+#define ILL_PRVREG	(__SI_FAULT|6)	/* privileged register */
+#define ILL_COPROC	(__SI_FAULT|7)	/* coprocessor error */
+#define ILL_BADSTK	(__SI_FAULT|8)	/* internal stack error */
 #define NSIGILL		8
 
 /*
  * SIGFPE si_codes
  */
-#define FPE_INTDIV	1	/* integer divide by zero */
-#define FPE_INTOVF	2	/* integer overflow */
-#define FPE_FLTDIV	3	/* floating point divide by zero */
-#define FPE_FLTOVF	4	/* floating point overflow */
-#define FPE_FLTUND	5	/* floating point underflow */
-#define FPE_FLTRES	6	/* floating point inexact result */
-#define FPE_FLTINV	7	/* floating point invalid operation */
-#define FPE_FLTSUB	8	/* subscript out of range */
+#define FPE_INTDIV	(__SI_FAULT|1)	/* integer divide by zero */
+#define FPE_INTOVF	(__SI_FAULT|2)	/* integer overflow */
+#define FPE_FLTDIV	(__SI_FAULT|3)	/* floating point divide by zero */
+#define FPE_FLTOVF	(__SI_FAULT|4)	/* floating point overflow */
+#define FPE_FLTUND	(__SI_FAULT|5)	/* floating point underflow */
+#define FPE_FLTRES	(__SI_FAULT|6)	/* floating point inexact result */
+#define FPE_FLTINV	(__SI_FAULT|7)	/* floating point invalid operation */
+#define FPE_FLTSUB	(__SI_FAULT|8)	/* subscript out of range */
 #define NSIGFPE		8
 
 /*
  * SIGSEGV si_codes
  */
-#define SEGV_MAPERR	1	/* address not mapped to object */
-#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_MAPERR	(__SI_FAULT|1)	/* address not mapped to object */
+#define SEGV_ACCERR	(__SI_FAULT|2)	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
  * SIGBUS si_codes
  */
-#define BUS_ADRALN	1	/* invalid address alignment */
-#define BUS_ADRERR	2	/* non-existant physical address */
-#define BUS_OBJERR	3	/* object specific hardware error */
+#define BUS_ADRALN	(__SI_FAULT|1)	/* invalid address alignment */
+#define BUS_ADRERR	(__SI_FAULT|2)	/* non-existant physical address */
+#define BUS_OBJERR	(__SI_FAULT|3)	/* object specific hardware error */
 #define NSIGBUS		3
 
 /*
  * SIGTRAP si_codes
  */
-#define TRAP_BRKPT	1	/* process breakpoint */
-#define TRAP_TRACE	2	/* process trace trap */
+#define TRAP_BRKPT	(__SI_FAULT|1)	/* process breakpoint */
+#define TRAP_TRACE	(__SI_FAULT|2)	/* process trace trap */
 #define NSIGTRAP	2
 
 /*
  * SIGCHLD si_codes
  */
-#define CLD_EXITED	1	/* child has exited */
-#define CLD_KILLED	2	/* child was killed */
-#define CLD_DUMPED	3	/* child terminated abnormally */
-#define CLD_TRAPPED	4	/* traced child has trapped */
-#define CLD_STOPPED	5	/* child has stopped */
-#define CLD_CONTINUED	6	/* stopped child has continued */
+#define CLD_EXITED	(__SI_CHLD|1)	/* child has exited */
+#define CLD_KILLED	(__SI_CHLD|2)	/* child was killed */
+#define CLD_DUMPED	(__SI_CHLD|3)	/* child terminated abnormally */
+#define CLD_TRAPPED	(__SI_CHLD|4)	/* traced child has trapped */
+#define CLD_STOPPED	(__SI_CHLD|5)	/* child has stopped */
+#define CLD_CONTINUED	(__SI_CHLD|6)	/* stopped child has continued */
 #define NSIGCHLD
 
 /*
  * SIGPOLL si_codes
  */
-#define POLL_IN		1	/* data input available */
-#define POLL_OUT	2	/* output buffers available */
-#define POLL_MSG	3	/* input message available */
-#define POLL_ERR	4	/* i/o error */
-#define POLL_PRI	5	/* high priority input available */
-#define POLL_HUP	6	/* device disconnected */
+#define POLL_IN		(__SI_POLL|1)	/* data input available */
+#define POLL_OUT	(__SI_POLL|2)	/* output buffers available */
+#define POLL_MSG	(__SI_POLL|3)	/* input message available */
+#define POLL_ERR	(__SI_POLL|4)	/* i/o error */
+#define POLL_PRI	(__SI_POLL|5)	/* high priority input available */
+#define POLL_HUP	(__SI_POLL|6)	/* device disconnected */
 #define NSIGPOLL	6
 
 /*
@@ -224,7 +224,7 @@
 #ifdef __KERNEL__
 #include <linux/string.h>
 
-extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
+static inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
 {
 	if (from->si_code < 0)
 		memcpy(to, from, sizeof(siginfo_t));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/timex.h linux/include/asm-s390/timex.h
--- linux.orig/include/asm-s390/timex.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/timex.h	Fri Dec 21 16:25:31 2001
@@ -17,13 +17,16 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
-typedef unsigned long cycles_t;
+typedef unsigned long long cycles_t;
 
 extern cycles_t cacheflush_time;
 
 static inline cycles_t get_cycles(void)
 {
-        return 0;
+	cycles_t cycles;
+
+	__asm__("stck  %0" : "=m" (cycles) : : "cc");
+	return cycles >> 2;
 }
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390/uaccess.h linux/include/asm-s390/uaccess.h
--- linux.orig/include/asm-s390/uaccess.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390/uaccess.h	Fri Dec 21 16:25:31 2001
@@ -379,34 +379,11 @@
  * access register are set up, that 4 points to secondary (user) , 2 to primary (kernel)
  */
 
-asmlinkage void __copy_from_user_fixup(void /* special calling convention */);
-asmlinkage void __copy_to_user_fixup(void /* special calling convention */);
-
-extern inline unsigned long
-__copy_to_user_asm(void* to, const void* from,  long n)
-{
-
-        __asm__ __volatile__ (  "   lr    2,%2\n"
-                                "   lr    4,%1\n"
-                                "   lr    3,%0\n"
-                                "   lr    5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lr    %0,3\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
+extern long __copy_to_user_asm(const void *from, long n, const void *to);
 
 #define __copy_to_user(to, from, n)                             \
 ({                                                              \
-        __copy_to_user_asm(to,from,n);                          \
+        __copy_to_user_asm(from, n, to);                        \
 })
 
 #define copy_to_user(to, from, n)                               \
@@ -414,38 +391,18 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(to,__n)) {                              \
-                err = __copy_to_user_asm(to,from,__n);          \
+                err = __copy_to_user_asm(from, __n, to);        \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
         err;                                                    \
 })
 
-extern inline unsigned long
-__copy_from_user_asm(void* to, const void* from,  long n)
-{
-        __asm__ __volatile__ (  "   lr    2,%1\n"
-                                "   lr    4,%2\n"
-                                "   lr    3,%0\n"
-                                "   lr    5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 2,4,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lr    %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,__copy_from_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
-
+extern long __copy_from_user_asm(void *to, long n, const void *from);
 
 #define __copy_from_user(to, from, n)                           \
 ({                                                              \
-        __copy_from_user_asm(to,from,n);                        \
+        __copy_from_user_asm(to, n, from);                      \
 })
 
 #define copy_from_user(to, from, n)                             \
@@ -453,7 +410,7 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(from,__n)) {                            \
-                err = __copy_from_user_asm(to,from,__n);        \
+                err = __copy_from_user_asm(to, __n, from);      \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
@@ -550,38 +507,12 @@
  * Zero Userspace
  */
 
-static inline unsigned long
-__clear_user(void *to, unsigned long n)
-{
-        __asm__ __volatile__ (  "   sacf  512\n"
-                                "   lr    4,%1\n"
-                                "   lr    5,%0\n"
-                                "   sr    2,2\n"
-                                "   sr    3,3\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "1: lr    %0,3\n"
-                                ".section .fixup,\"ax\"\n"
-                                "2: lhi   5,-4096\n"
-                                "   n     5,0x90\n"
-                                "   sr    5,4\n"
-                                "   mvcle 4,2,0\n"
-                                "   sacf  0\n"
-                                "   basr  4,0\n"
-                                "   l     4,3f-.(4)\n"
-                                "   br    4\n"
-                                "3: .long 1b\n"
-                                ".previous\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 4\n"
-				"   .long  0b,2b\n"
-				".previous"
-                                : "+&a" (n)
-                                : "a"   (to)
-                                : "cc", "2", "3", "4", "5" );
-        return n;
-}
+extern long __clear_user_asm(void *to, long n);
+
+#define __clear_user(to, n)                                     \
+({                                                              \
+        __clear_user_asm(to, n);                                \
+})
 
 static inline unsigned long
 clear_user(void *to, unsigned long n)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/lowcore.h linux/include/asm-s390x/lowcore.h
--- linux.orig/include/asm-s390x/lowcore.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/lowcore.h	Fri Dec 21 16:25:31 2001
@@ -45,6 +45,8 @@
 #define __LC_CPUADDR                    0xD98
 #define __LC_IPLDEV                     0xDB8
 
+#define __LC_JIFFY_TIMER		0xDC0
+
 #define __LC_PANIC_MAGIC                0xE00
 
 #define __LC_AREGS_SAVE_AREA            0x1340
@@ -158,7 +160,7 @@
 	/* entry.S sensitive area end */
 
         /* SMP info area: defined by DJB */
-        __u64        jiffy_timer_cc;           /* 0xdc0 */
+        __u64        jiffy_timer;              /* 0xdc0 */
 	__u64        ext_call_fast;            /* 0xdc8 */
         __u8         pad12[0xe00-0xdd0];       /* 0xdd0 */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/processor.h linux/include/asm-s390x/processor.h
--- linux.orig/include/asm-s390x/processor.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/processor.h	Fri Dec 21 16:25:31 2001
@@ -80,7 +80,6 @@
 
 struct thread_struct
  {
-        struct pt_regs *regs;         /* the user registers can be found on*/
 	s390_fp_regs fp_regs;
         __u32   ar2;                   /* kernel access register 2         */
         __u32   ar4;                   /* kernel access register 4         */
@@ -99,8 +98,7 @@
 
 typedef struct thread_struct thread_struct;
 
-#define INIT_THREAD { (struct pt_regs *) 0,                       \
-                    { 0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
+#define INIT_THREAD {{0,{{0},{0},{0},{0},{0},{0},{0},{0},{0},{0}, \
 			    {0},{0},{0},{0},{0},{0}}},            \
                      0, 0,                                        \
                     sizeof(init_stack) + (addr_t) &init_stack,    \
@@ -137,15 +135,24 @@
 
 /*
  * Return saved PC of a blocked thread. used in kernel/sched
+ * resume in entry.S does not create a new stack frame, it
+ * just stores the registers %r6-%r15 to the frame given by
+ * schedule. We want to return the address of the caller of
+ * schedule, so we have to walk the backchain one time to
+ * find the frame schedule() store its return address.
  */
 extern inline unsigned long thread_saved_pc(struct thread_struct *t)
 {
-        return (t->regs) ? ((unsigned long)t->regs->psw.addr) : 0;
+	unsigned long bc;
+	bc = *((unsigned long *) t->ksp);
+	return *((unsigned long *) (bc+112));
 }
 
 unsigned long get_wchan(struct task_struct *p);
-#define KSTK_EIP(tsk)   ((tsk)->thread.regs->psw.addr)
-#define KSTK_ESP(tsk)   ((tsk)->thread.ksp)
+#define __KSTK_PTREGS(tsk) \
+	((struct pt_regs *)((unsigned long) tsk+THREAD_SIZE) - 1)
+#define KSTK_EIP(tsk)	(__KSTK_PTREGS(tsk)->psw.addr)
+#define KSTK_ESP(tsk)	(__KSTK_PTREGS(tsk)->gprs[15])
 
 /* Allocation and freeing of basic task resources. */
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/ptrace.h linux/include/asm-s390x/ptrace.h
--- linux.orig/include/asm-s390x/ptrace.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/ptrace.h	Fri Dec 21 16:25:31 2001
@@ -98,7 +98,7 @@
 {
         __u64   mask;
         __u64   addr;
-} psw_t __attribute__ ((aligned(8)));
+} __attribute__ ((aligned(8))) psw_t;
 
 #ifdef __KERNEL__
 #define FIX_PSW(addr) ((unsigned long)(addr))
@@ -130,8 +130,8 @@
 #define FPC_VALID_MASK          0xF8F8FF03
 
 /*
- * The first entries in pt_regs, gdb_pt_regs and user_regs_struct
- * are common for all three structures. The s390_regs structure
+ * The first entries in pt_regs and user_regs_struct
+ * are common for the two structures. The s390_regs structure
  * covers the common parts. It simplifies copying the common part
  * between the three structures.
  */
@@ -158,29 +158,12 @@
 } __attribute__ ((packed));
 
 /*
- * The gdb_pt_regs struct is used instead of the pt_regs structure
- * if kernel remote debugging is used.
- */
-#if CONFIG_REMOTE_DEBUG
-struct gdb_pt_regs
-{
-	psw_t psw;
-	__u64 gprs[NUM_GPRS];
-	__u32 acrs[NUM_ACRS];
-	__u64 orig_gpr2;
-	__u32 trap;
-	__u32 crs[16];
-	s390_fp_regs fp_regs;
-};
-#endif
-
-/*
  * Now for the program event recording (trace) definitions.
  */
 typedef struct
 {
 	__u64 cr[3];
-} per_cr_words  __attribute__((packed));
+} per_cr_words;
 
 #define PER_EM_MASK 0x00000000E8000000UL
 
@@ -203,14 +186,14 @@
 	unsigned                       : 21;
 	addr_t   starting_addr;
 	addr_t   ending_addr;
-} per_cr_bits  __attribute__((packed));
+} per_cr_bits;
 
 typedef struct
 {
 	__u16          perc_atmid;
 	addr_t         address;
 	__u8           access_id;
-} per_lowcore_words  __attribute__((packed));
+} per_lowcore_words;
 
 typedef struct
 {
@@ -230,14 +213,14 @@
 	addr_t   address;                     /* 0x098 */
 	unsigned                         : 4; /* 0x0a1 */
 	unsigned access_id               : 4;
-} per_lowcore_bits __attribute__((packed));
+} per_lowcore_bits;
 
 typedef struct
 {
 	union {
 		per_cr_words   words;
 		per_cr_bits    bits;
-	} control_regs  __attribute__((packed));
+	} control_regs;
 	/*
 	 * Use these flags instead of setting em_instruction_fetch
 	 * directly they are used so that single stepping can be
@@ -256,7 +239,7 @@
 		per_lowcore_words words;
 		per_lowcore_bits  bits;
 	} lowcore; 
-} per_struct __attribute__((packed));
+} per_struct;
 
 typedef struct
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/s390-gdbregs.h linux/include/asm-s390x/s390-gdbregs.h
--- linux.orig/include/asm-s390x/s390-gdbregs.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/s390-gdbregs.h	Thu Jan  1 00:00:00 1970
@@ -1,89 +0,0 @@
-/*
- *  include/asm-s390/s390-gdbregs.h
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  used both by the linux kernel for remote debugging & gdb 
- */
-
-#ifndef _S390_GDBREGS_H
-#define _S390_GDBREGS_H
-
-#ifdef __KERNEL__
-#include <asm/s390-regs-common.h>
-#else
-#include <s390x/s390-regs-common.h>
-#endif
-#define S390_MAX_INSTR_SIZE 6
-#define NUM_REGS      (2+NUM_GPRS+NUM_ACRS+NUM_CRS+1+NUM_FPRS)
-#define FIRST_ACR     (2+NUM_GPRS)
-#define LAST_ACR      (FIRST_ACR+NUM_ACRS-1)
-#define FIRST_CR      (FIRST_ACR+NUM_ACRS)
-#define LAST_CR       (FIRST_CR+NUM_CRS-1)
-
-#define PSWM_REGNUM    0
-#define PC_REGNUM      1
-#define	GP0_REGNUM     2		    /* GPR register 0 */ 
-#define GP_LAST_REGNUM (GP0_REGNUM+NUM_GPRS-1)
-#define RETADDR_REGNUM (GP0_REGNUM+14)                   /* Usually return address */
-#define SP_REGNUM      (GP0_REGNUM+15)	    /* Contains address of top of stack */
-#define FP_REGNUM     SP_REGNUM /* needed in findvar.c still */
-#define FRAME_REGNUM  (GP0_REGNUM+11)
-#define FPC_REGNUM    (GP0_REGNUM+NUM_GPRS+NUM_ACRS+NUM_CRS)
-#define FP0_REGNUM    (FPC_REGNUM+1) /* FPR (Floating point) register 0 */
-#define FPLAST_REGNUM (FP0_REGNUM+NUM_FPRS-1)	/* Last floating point register */
-
-/* The top of this structure is as similar as possible to a pt_regs structure to */
-/* simplify code */
-typedef struct
-{
-	S390_REGS_COMMON
-	__u32         crs[NUM_CRS];
-	s390_fp_regs  fp_regs;
-} s390_gdb_regs __attribute__((packed));
-
-#define REGISTER_NAMES                                           \
-{                                                                \
-"pswm","pswa",                                                   \
-"gpr0","gpr1","gpr2","gpr3","gpr4","gpr5","gpr6","gpr7",         \
-"gpr8","gpr9","gpr10","gpr11","gpr12","gpr13","gpr14","gpr15",   \
-"acr0","acr1","acr2","acr3","acr4","acr5","acr6","acr7",         \
-"acr8","acr9","acr10","acr11","acr12","acr13","acr14","acr15",   \
-"cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7",                 \
-"cr8","cr9","cr10","cr11","cr12","cr13","cr14","cr15",           \
-"fpc",                                                           \
-"fpr0","fpr1","fpr2","fpr3","fpr4","fpr5","fpr6","fpr7",         \
-"fpr8","fpr9","fpr10","fpr11","fpr12","fpr13","fpr14","fpr15"    \
-}
-
-/* Index within `registers' of the first byte of the space for
-   register N.  */
-
-#define ACR0_OFFSET ((PSW_MASK_SIZE+PSW_ADDR_SIZE)+(GPR_SIZE*NUM_GPRS))
-#define CR0_OFFSET (ACR0_OFFSET+(ACR_SIZE+NUM_ACRS))
-#define FPC_OFFSET (CR0_OFFSET+(CR_SIZE*NUM_CRS))
-#define FP0_OFFSET (FPC_OFFSET+(FPC_SIZE+FPC_PAD_SIZE))
-
-#define REGISTER_BYTES    \
-((FP0_OFFSET)+(FPR_SIZE*NUM_FPRS))
-
-#define REGISTER_BYTE(N)  ((N)<=GP_LAST_REGNUM ? (N)*8: \
-(N) <= LAST_ACR ? (ACR0_OFFSET+(((N)-FIRST_ACR)*ACR_SIZE)): \
-(N) <= LAST_CR ? (CR0_OFFSET+(((N)-FIRST_CR)*CR_SIZE)): \
-(N) == FPC_REGNUM ? FPC_OFFSET:(FP0_OFFSET+(((N)-FP0_REGNUM)*FPR_SIZE)))
-
-#endif
-
-
-
-
-
-
-
-
-
-
-
-
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/scatterlist.h linux/include/asm-s390x/scatterlist.h
--- linux.orig/include/asm-s390x/scatterlist.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/scatterlist.h	Fri Dec 21 16:25:31 2001
@@ -2,7 +2,15 @@
 #define _ASMS390X_SCATTERLIST_H
 
 struct scatterlist {
-    char *  address;    /* Location data is to be transferred to */
+    /* This will disappear in 2.5.x */
+    char *address;
+
+    /* These two are only valid if ADDRESS member of this
+     * struct is NULL.
+     */
+    struct page *page;
+    unsigned int offset;
+
     unsigned int length;
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/setup.h linux/include/asm-s390x/setup.h
--- linux.orig/include/asm-s390x/setup.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/setup.h	Fri Dec 21 16:25:31 2001
@@ -13,7 +13,7 @@
 #define RAMDISK_ORIGIN		0x800000
 #define RAMDISK_SIZE		0x800000
 
-#ifndef __ASSEMBLER__
+#ifndef __ASSEMBLY__
 
 #define IPL_DEVICE        (*(unsigned long *)  (0x10400))
 #define INITRD_START      (*(unsigned long *)  (0x10408))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/siginfo.h linux/include/asm-s390x/siginfo.h
--- linux.orig/include/asm-s390x/siginfo.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/siginfo.h	Fri Dec 21 16:25:31 2001
@@ -111,7 +111,7 @@
 #define SI_USER		0	/* sent by kill, sigsend, raise */
 #define SI_KERNEL	0x80	/* sent by the kernel from somewhere */
 #define SI_QUEUE	-1	/* sent by sigqueue */
-#define SI_TIMER	-2	/* sent by timer expiration */
+#define SI_TIMER __SI_CODE(__SI_TIMER,-2) /* sent by timer expiration */
 #define SI_MESGQ	-3	/* sent by real time mesq state change */
 #define SI_ASYNCIO	-4	/* sent by AIO completion */
 #define SI_SIGIO	-5	/* sent by queued SIGIO */
@@ -122,71 +122,71 @@
 /*
  * SIGILL si_codes
  */
-#define ILL_ILLOPC	1	/* illegal opcode */
-#define ILL_ILLOPN	2	/* illegal operand */
-#define ILL_ILLADR	3	/* illegal addressing mode */
-#define ILL_ILLTRP	4	/* illegal trap */
-#define ILL_PRVOPC	5	/* privileged opcode */
-#define ILL_PRVREG	6	/* privileged register */
-#define ILL_COPROC	7	/* coprocessor error */
-#define ILL_BADSTK	8	/* internal stack error */
+#define ILL_ILLOPC	(__SI_FAULT|1)	/* illegal opcode */
+#define ILL_ILLOPN	(__SI_FAULT|2)	/* illegal operand */
+#define ILL_ILLADR	(__SI_FAULT|3)	/* illegal addressing mode */
+#define ILL_ILLTRP	(__SI_FAULT|4)	/* illegal trap */
+#define ILL_PRVOPC	(__SI_FAULT|5)	/* privileged opcode */
+#define ILL_PRVREG	(__SI_FAULT|6)	/* privileged register */
+#define ILL_COPROC	(__SI_FAULT|7)	/* coprocessor error */
+#define ILL_BADSTK	(__SI_FAULT|8)	/* internal stack error */
 #define NSIGILL		8
 
 /*
  * SIGFPE si_codes
  */
-#define FPE_INTDIV	1	/* integer divide by zero */
-#define FPE_INTOVF	2	/* integer overflow */
-#define FPE_FLTDIV	3	/* floating point divide by zero */
-#define FPE_FLTOVF	4	/* floating point overflow */
-#define FPE_FLTUND	5	/* floating point underflow */
-#define FPE_FLTRES	6	/* floating point inexact result */
-#define FPE_FLTINV	7	/* floating point invalid operation */
-#define FPE_FLTSUB	8	/* subscript out of range */
+#define FPE_INTDIV	(__SI_FAULT|1)	/* integer divide by zero */
+#define FPE_INTOVF	(__SI_FAULT|2)	/* integer overflow */
+#define FPE_FLTDIV	(__SI_FAULT|3)	/* floating point divide by zero */
+#define FPE_FLTOVF	(__SI_FAULT|4)	/* floating point overflow */
+#define FPE_FLTUND	(__SI_FAULT|5)	/* floating point underflow */
+#define FPE_FLTRES	(__SI_FAULT|6)	/* floating point inexact result */
+#define FPE_FLTINV	(__SI_FAULT|7)	/* floating point invalid operation */
+#define FPE_FLTSUB	(__SI_FAULT|8)	/* subscript out of range */
 #define NSIGFPE		8
 
 /*
  * SIGSEGV si_codes
  */
-#define SEGV_MAPERR	1	/* address not mapped to object */
-#define SEGV_ACCERR	2	/* invalid permissions for mapped object */
+#define SEGV_MAPERR	(__SI_FAULT|1)	/* address not mapped to object */
+#define SEGV_ACCERR	(__SI_FAULT|2)	/* invalid permissions for mapped object */
 #define NSIGSEGV	2
 
 /*
  * SIGBUS si_codes
  */
-#define BUS_ADRALN	1	/* invalid address alignment */
-#define BUS_ADRERR	2	/* non-existant physical address */
-#define BUS_OBJERR	3	/* object specific hardware error */
+#define BUS_ADRALN	(__SI_FAULT|1)	/* invalid address alignment */
+#define BUS_ADRERR	(__SI_FAULT|2)	/* non-existant physical address */
+#define BUS_OBJERR	(__SI_FAULT|3)	/* object specific hardware error */
 #define NSIGBUS		3
 
 /*
  * SIGTRAP si_codes
  */
-#define TRAP_BRKPT	1	/* process breakpoint */
-#define TRAP_TRACE	2	/* process trace trap */
+#define TRAP_BRKPT	(__SI_FAULT|1)	/* process breakpoint */
+#define TRAP_TRACE	(__SI_FAULT|2)	/* process trace trap */
 #define NSIGTRAP	2
 
 /*
  * SIGCHLD si_codes
  */
-#define CLD_EXITED	1	/* child has exited */
-#define CLD_KILLED	2	/* child was killed */
-#define CLD_DUMPED	3	/* child terminated abnormally */
-#define CLD_TRAPPED	4	/* traced child has trapped */
-#define CLD_STOPPED	5	/* child has stopped */
-#define CLD_CONTINUED	6	/* stopped child has continued */
+#define CLD_EXITED	(__SI_CHLD|1)	/* child has exited */
+#define CLD_KILLED	(__SI_CHLD|2)	/* child was killed */
+#define CLD_DUMPED	(__SI_CHLD|3)	/* child terminated abnormally */
+#define CLD_TRAPPED	(__SI_CHLD|4)	/* traced child has trapped */
+#define CLD_STOPPED	(__SI_CHLD|5)	/* child has stopped */
+#define CLD_CONTINUED	(__SI_CHLD|6)	/* stopped child has continued */
 #define NSIGCHLD
 
 /*
  * SIGPOLL si_codes
  */
-#define POLL_IN		1	/* data input available */
-#define POLL_OUT	2	/* output buffers available */
-#define POLL_MSG	3	/* input message available */
-#define POLL_ERR	4	/* i/o error */
-#define POLL_PRI	5	/* high priority input available */
-#define POLL_HUP	6	/* device disconnected */
+#define POLL_IN		(__SI_POLL|1)	/* data input available */
+#define POLL_OUT	(__SI_POLL|2)	/* output buffers available */
+#define POLL_MSG	(__SI_POLL|3)	/* input message available */
+#define POLL_ERR	(__SI_POLL|4)	/* i/o error */
+#define POLL_PRI	(__SI_POLL|5)	/* high priority input available */
+#define POLL_HUP	(__SI_POLL|6)	/* device disconnected */
 #define NSIGPOLL	6
 
 /*
@@ -224,7 +224,7 @@
 #ifdef __KERNEL__
 #include <linux/string.h>
 
-extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
+static inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
 {
 	if (from->si_code < 0)
 		memcpy(to, from, sizeof(siginfo_t));
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/timex.h linux/include/asm-s390x/timex.h
--- linux.orig/include/asm-s390x/timex.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/timex.h	Fri Dec 21 16:25:31 2001
@@ -17,13 +17,16 @@
 	(1000000/CLOCK_TICK_FACTOR) / (CLOCK_TICK_RATE/CLOCK_TICK_FACTOR)) \
 		<< (SHIFT_SCALE-SHIFT_HZ)) / HZ)
 
-typedef unsigned long cycles_t;
+typedef unsigned long long cycles_t;
 
 extern cycles_t cacheflush_time;
 
 static inline cycles_t get_cycles(void)
 {
-        return 0;
+	cycles_t cycles;
+
+	__asm__("stck  %0" : "=m" (cycles) : : "cc");
+	return cycles >> 2;
 }
 
 #endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-s390x/uaccess.h linux/include/asm-s390x/uaccess.h
--- linux.orig/include/asm-s390x/uaccess.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-s390x/uaccess.h	Fri Dec 21 16:25:31 2001
@@ -336,34 +336,11 @@
  * access register are set up, that 4 points to secondary (user) , 2 to primary (kernel)
  */
 
-asmlinkage void __copy_from_user_fixup(void /* special calling convention */);
-asmlinkage void __copy_to_user_fixup(void /* special calling convention */);
-
-extern inline unsigned long
-__copy_to_user_asm(void* to, const void* from,  long n)
-{
-
-        __asm__ __volatile__ (  "   lgr   2,%2\n"
-                                "   lgr   4,%1\n"
-                                "   lgr   3,%0\n"
-                                "   lgr   5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lgr   %0,3\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
+extern long __copy_to_user_asm(const void *from, long n, void *to);
 
 #define __copy_to_user(to, from, n)                             \
 ({                                                              \
-        __copy_to_user_asm(to,from,n);                          \
+        __copy_to_user_asm(from, n, to);                        \
 })
 
 #define copy_to_user(to, from, n)                               \
@@ -371,38 +348,18 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(to,__n)) {                              \
-                err = __copy_to_user_asm(to,from,__n);          \
+                err = __copy_to_user_asm(from, __n, to);        \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
         err;                                                    \
 })
 
-extern inline unsigned long
-__copy_from_user_asm(void* to, const void* from,  long n)
-{
-        __asm__ __volatile__ (  "   lgr   2,%1\n"
-                                "   lgr   4,%2\n"
-                                "   lgr   3,%0\n"
-                                "   lgr   5,3\n"
-                                "   sacf  512\n"
-                                "0: mvcle 2,4,0\n"
-                                "   jo    0b\n"
-                                "   sacf  0\n"
-                                "   lgr   %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_from_user_fixup\n"
-				".previous"
-                                : "+&d" (n) : "d" (to), "d" (from)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
-
+extern long __copy_from_user_asm(void *to, long n, const void *from);
 
 #define __copy_from_user(to, from, n)                           \
 ({                                                              \
-        __copy_from_user_asm(to,from,n);                        \
+        __copy_from_user_asm(to, n, from);                      \
 })
 
 #define copy_from_user(to, from, n)                             \
@@ -410,7 +367,7 @@
         long err = 0;                                           \
         __typeof__(n) __n = (n);                                \
         if (__access_ok(from,__n)) {                            \
-                err = __copy_from_user_asm(to,from,__n);        \
+                err = __copy_from_user_asm(to, __n, from);      \
         }                                                       \
         else                                                    \
                 err = __n;                                      \
@@ -520,27 +477,12 @@
  * Zero Userspace
  */
 
-static inline unsigned long
-__clear_user(void *to, unsigned long n)
-{
-        __asm__ __volatile__ (  "   sacf  512\n"
-                                "   lgr   4,%1\n"
-                                "   lgr   5,%0\n"
-                                "   sgr   2,2\n"
-                                "   sgr   3,3\n"
-                                "0: mvcle 4,2,0\n"
-                                "   jo    0b\n"
-                                "1: sacf  0\n"
-                                "   lgr   %0,5\n"
-				".section __ex_table,\"a\"\n"
-				"   .align 8\n"
-				"   .quad  0b,__copy_to_user_fixup\n"
-				".previous"
-                                : "+&a" (n)
-                                : "a"   (to)
-                                : "cc", "1", "2", "3", "4", "5" );
-        return n;
-}
+extern long __clear_user_asm(void *to, long n);
+
+#define __clear_user(to, n)                                     \
+({                                                              \
+        __clear_user_asm(to, n);                                \
+})
 
 static inline unsigned long
 clear_user(void *to, unsigned long n)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/pci.h linux/include/asm-sh/pci.h
--- linux.orig/include/asm-sh/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sh/pci.h	Wed Jan 16 20:56:50 2002
@@ -89,6 +89,31 @@
 	return virt_to_bus(ptr);
 }
 
+/* pci_unmap_{single,page} being a nop depends upon the
+ * configuration.
+ */
+#ifdef CONFIG_SH_PCIDMA_NONCOHERENT
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+#else
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)
+#define pci_unmap_addr(PTR, ADDR_NAME)		(0)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)	do { } while (0)
+#define pci_unmap_len(PTR, LEN_NAME)		(0)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)	do { } while (0)
+#endif
+
 /* Unmap a single streaming mode DMA translation.  The dma_addr and size
  * must match what was provided for in a previous pci_map_single call.  All
  * other usages are undefined.
@@ -195,6 +220,11 @@
 {
 	return 1;
 }
+
+/* Not supporting more than 32-bit PCI bus addresses now, but
+ * must satisfy references to this function.  Change if needed.
+ */
+#define pci_dac_dma_supported(pci_dev, mask) (0)
 
 /* Return the index of the PCI controller for device PDEV. */
 #define pci_controller_num(PDEV)	(0)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/stat.h linux/include/asm-sh/stat.h
--- linux.orig/include/asm-sh/stat.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sh/stat.h	Fri Dec 21 18:00:38 2001
@@ -42,8 +42,16 @@
  * insane amounts of padding around dev_t's.
  */
 struct stat64 {
+#if defined(__BIG_ENDIAN__)
+	unsigned char   __pad0b[6];
 	unsigned short	st_dev;
-	unsigned char	__pad0[10];
+#elif defined(__LITTLE_ENDIAN__)
+	unsigned short	st_dev;
+	unsigned char	__pad0b[6];
+#else
+#error Must know endian to build stat64 structure!
+#endif
+	unsigned char	__pad0[4];
 
 	unsigned long	st_ino;
 	unsigned int	st_mode;
@@ -52,14 +60,25 @@
 	unsigned long	st_uid;
 	unsigned long	st_gid;
 
+#if defined(__BIG_ENDIAN__)
+	unsigned char	__pad3b[6];
+	unsigned short	st_rdev;
+#else /* Must be little */
 	unsigned short	st_rdev;
-	unsigned char	__pad3[10];
+	unsigned char	__pad3b[6];
+#endif
+	unsigned char	__pad3[4];
 
 	long long	st_size;
 	unsigned long	st_blksize;
 
+#if defined(__BIG_ENDIAN__)
+	unsigned long	__pad4;		/* Future possible st_blocks hi bits */
+	unsigned long	st_blocks;	/* Number 512-byte blocks allocated. */
+#else /* Must be little */
 	unsigned long	st_blocks;	/* Number 512-byte blocks allocated. */
-	unsigned long	__pad4;		/* future possible st_blocks high bits */
+	unsigned long	__pad4;		/* Future possible st_blocks hi bits */
+#endif
 
 	unsigned long	st_atime;
 	unsigned long	__pad5;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sh/uaccess.h linux/include/asm-sh/uaccess.h
--- linux.orig/include/asm-sh/uaccess.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sh/uaccess.h	Fri Dec 21 18:00:38 2001
@@ -216,6 +216,7 @@
 	: "r" (val), "m" (__m(addr)), "i" (-EFAULT) \
         : "memory"); })
 #else
+#define __put_user_u64(val,addr,retval) \
 ({ \
 __asm__ __volatile__( \
 	"1:\n\t" \
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc/oplib.h linux/include/asm-sparc/oplib.h
--- linux.orig/include/asm-sparc/oplib.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc/oplib.h	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.21 2000/08/26 02:38:04 anton Exp $
+/* $Id: oplib.h,v 1.21.2.2 2001/12/21 00:52:47 davem Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -298,15 +298,7 @@
 
 /* Dorking with Bus ranges... */
 
-/* Adjust reg values with the passed ranges. */
-extern void prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
-			     struct linux_prom_ranges *rangep, int nranges);
-
-/* Adjust child ranges with the passed parent ranges. */
-extern void prom_adjust_ranges(struct linux_prom_ranges *cranges, int ncranges,
-			       struct linux_prom_ranges *pranges, int npranges);
-
-/* Apply promlib probed OBIO ranges to registers. */
+/* Apply promlib probes OBIO ranges to registers. */
 extern void prom_apply_obio_ranges(struct linux_prom_registers *obioregs, int nregs);
 
 /* Apply ranges of any prom node (and optionally parent node as well) to registers. */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc/pci.h linux/include/asm-sparc/pci.h
--- linux.orig/include/asm-sparc/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc/pci.h	Wed Jan 16 20:56:50 2002
@@ -63,6 +63,20 @@
  */
 extern void pci_unmap_single(struct pci_dev *hwdev, dma_addr_t dma_addr, size_t size, int direction);
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc/string.h linux/include/asm-sparc/string.h
--- linux.orig/include/asm-sparc/string.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc/string.h	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: string.h,v 1.35 2000/05/02 01:47:01 davem Exp $
+/* $Id: string.h,v 1.35.2.1 2001/12/21 00:52:47 davem Exp $
  * string.h: External definitions for optimized assembly string
  *           routines for the Linux Kernel.
  *
@@ -126,9 +126,11 @@
 })
 
 #define __HAVE_ARCH_MEMCMP
+extern int memcmp(const void *,const void *,__kernel_size_t);
 
 /* Now the str*() stuff... */
 #define __HAVE_ARCH_STRLEN
+extern __kernel_size_t strlen(const char *);
 
 #define __HAVE_ARCH_STRNCMP
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/delay.h linux/include/asm-sparc64/delay.h
--- linux.orig/include/asm-sparc64/delay.h	Fri Apr 27 05:17:26 2001
+++ linux/include/asm-sparc64/delay.h	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/* $Id: delay.h,v 1.12 2001/04/24 01:09:12 davem Exp $
+/* $Id: delay.h,v 1.12.2.1 2002/02/02 02:11:52 kanoj Exp $
  * delay.h: Linux delay routines on the V9.
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu).
@@ -9,9 +9,13 @@
 
 #include <linux/config.h>
 #include <linux/param.h>
+
+#ifndef __ASSEMBLY__
+
 #ifdef CONFIG_SMP
-#include <linux/sched.h>
 #include <asm/smp.h>
+#else
+extern unsigned long loops_per_jiffy;
 #endif 
 
 extern __inline__ void __delay(unsigned long loops)
@@ -48,5 +52,7 @@
 #endif
 
 #define udelay(usecs) __udelay((usecs),__udelay_val)
+
+#endif /* !__ASSEMBLY__ */
 
 #endif /* defined(__SPARC64_DELAY_H) */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/elf.h linux/include/asm-sparc64/elf.h
--- linux.orig/include/asm-sparc64/elf.h	Fri Sep  7 18:01:20 2001
+++ linux/include/asm-sparc64/elf.h	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/* $Id: elf.h,v 1.30 2001/08/30 23:35:38 kanoj Exp $ */
+/* $Id: elf.h,v 1.30.2.1 2002/02/04 22:37:47 davem Exp $ */
 #ifndef __ASM_SPARC64_ELF_H
 #define __ASM_SPARC64_ELF_H
 
@@ -75,24 +75,7 @@
 	else						\
 		flags &= ~SPARC_FLAG_32BIT;		\
 	if (flags != current->thread.flags) {		\
-		unsigned long pgd_cache = 0UL;		\
-		if (flags & SPARC_FLAG_32BIT) {		\
-		  pgd_t *pgd0 = &current->mm->pgd[0];	\
-		  if (pgd_none (*pgd0)) {		\
-		    pmd_t *page = pmd_alloc_one_fast(NULL, 0);	\
-		    if (!page)				\
-		      page = pmd_alloc_one(NULL, 0);	\
-                    pgd_set(pgd0, page);		\
-		  }					\
-		  pgd_cache = pgd_val(*pgd0) << 11UL;	\
-		}					\
-		__asm__ __volatile__(			\
-			"stxa\t%0, [%1] %2\n\t"		\
-			"membar #Sync"			\
-			: /* no outputs */		\
-			: "r" (pgd_cache),		\
-			  "r" (TSB_REG),		\
-			  "i" (ASI_DMMU));		\
+		/* flush_thread will update pgd cache */\
 		current->thread.flags = flags;		\
 	}						\
 							\
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/io.h linux/include/asm-sparc64/io.h
--- linux.orig/include/asm-sparc64/io.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc64/io.h	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: io.h,v 1.40 2001/11/10 09:24:56 davem Exp $ */
+/* $Id: io.h,v 1.41.2.1 2001/12/11 22:50:52 davem Exp $ */
 #ifndef __SPARC64_IO_H
 #define __SPARC64_IO_H
 
@@ -18,6 +18,9 @@
 extern unsigned long bus_to_virt_not_defined_use_pci_map(volatile void *addr);
 #define bus_to_virt bus_to_virt_not_defined_use_pci_map
 
+extern unsigned long phys_base;
+#define page_to_phys(page)	((((page) - mem_map) << PAGE_SHIFT)+phys_base)
+
 /* Different PCI controllers we support have their PCI MEM space
  * mapped to an either 2GB (Psycho) or 4GB (Sabre) aligned area,
  * so need to chop off the top 33 or 32 bits.
@@ -252,6 +255,7 @@
 #define __raw_readb(__addr)		(_raw_readb((unsigned long)(__addr)))
 #define __raw_readw(__addr)		(_raw_readw((unsigned long)(__addr)))
 #define __raw_readl(__addr)		(_raw_readl((unsigned long)(__addr)))
+#define __raw_readq(__addr)		(_raw_readq((unsigned long)(__addr)))
 #define __raw_writeb(__b, __addr)	(_raw_writeb((u8)(__b), (unsigned long)(__addr)))
 #define __raw_writew(__w, __addr)	(_raw_writew((u16)(__w), (unsigned long)(__addr)))
 #define __raw_writel(__l, __addr)	(_raw_writel((u32)(__l), (unsigned long)(__addr)))
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/oplib.h linux/include/asm-sparc64/oplib.h
--- linux.orig/include/asm-sparc64/oplib.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc64/oplib.h	Tue Jan 15 19:08:06 2002
@@ -1,4 +1,4 @@
-/* $Id: oplib.h,v 1.13 2000/05/09 17:40:15 davem Exp $
+/* $Id: oplib.h,v 1.13.2.1 2001/12/19 00:16:21 davem Exp $
  * oplib.h:  Describes the interface and available routines in the
  *           Linux Prom library.
  *
@@ -326,20 +326,6 @@
 /* Client interface level routines. */
 extern void prom_set_trap_table(unsigned long tba);
 
-/* Dorking with Bus ranges... */
-
-/* Adjust reg values with the passed ranges. */
-extern void prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
-			     struct linux_prom_ranges *rangep, int nranges);
-
-/* Adjust child ranges with the passed parent ranges. */
-extern void prom_adjust_ranges(struct linux_prom_ranges *cranges, int ncranges,
-			       struct linux_prom_ranges *pranges, int npranges);
-
-/* Apply ranges of any prom node (and optionally parent node as well) to registers. */
-extern void prom_apply_generic_ranges(int node, int parent, 
-				      struct linux_prom_registers *sbusregs, int nregs);
-				      
 extern long p1275_cmd (char *, long, ...);
 				   
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/pci.h linux/include/asm-sparc64/pci.h
--- linux.orig/include/asm-sparc64/pci.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc64/pci.h	Wed Jan 16 20:56:50 2002
@@ -77,6 +77,20 @@
 	pci_map_single(dev, (page_address(page) + (off)), size, dir)
 #define pci_unmap_page(dev,addr,sz,dir) pci_unmap_single(dev,addr,sz,dir)
 
+/* pci_unmap_{single,page} is not a nop, thus... */
+#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME)	\
+	dma_addr_t ADDR_NAME;
+#define DECLARE_PCI_UNMAP_LEN(LEN_NAME)		\
+	__u32 LEN_NAME;
+#define pci_unmap_addr(PTR, ADDR_NAME)			\
+	((PTR)->ADDR_NAME)
+#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL)		\
+	(((PTR)->ADDR_NAME) = (VAL))
+#define pci_unmap_len(PTR, LEN_NAME)			\
+	((PTR)->LEN_NAME)
+#define pci_unmap_len_set(PTR, LEN_NAME, VAL)		\
+	(((PTR)->LEN_NAME) = (VAL))
+
 /* Map a set of buffers described by scatterlist in streaming
  * mode for DMA.  This is the scather-gather version of the
  * above pci_map_single interface.  Here the scatter gather list
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/pgalloc.h linux/include/asm-sparc64/pgalloc.h
--- linux.orig/include/asm-sparc64/pgalloc.h	Tue Jan 22 17:51:23 2002
+++ linux/include/asm-sparc64/pgalloc.h	Tue Jan 15 19:08:06 2002
@@ -35,8 +35,10 @@
 extern void flush_dcache_page_impl(struct page *page);
 #ifdef CONFIG_SMP
 extern void smp_flush_dcache_page_impl(struct page *page, int cpu);
+extern void flush_dcache_page_all(struct mm_struct *mm, struct page *page);
 #else
 #define smp_flush_dcache_page_impl(page,cpu) flush_dcache_page_impl(page)
+#define flush_dcache_page_all(mm,page) flush_dcache_page_impl(page)
 #endif
 
 extern void flush_dcache_page(struct page *page);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/asm-sparc64/processor.h linux/include/asm-sparc64/processor.h
--- linux.orig/include/asm-sparc64/processor.h	Fri Dec 21 17:42:03 2001
+++ linux/include/asm-sparc64/processor.h	Tue Feb  5 17:28:24 2002
@@ -1,4 +1,4 @@
-/* $Id: processor.h,v 1.80 2001/11/17 00:10:48 davem Exp $
+/* $Id: processor.h,v 1.80.2.1 2002/02/02 02:11:52 kanoj Exp $
  * include/asm-sparc64/processor.h
  *
  * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu)
@@ -21,6 +21,7 @@
 #include <asm/signal.h>
 #include <asm/segment.h>
 #include <asm/page.h>
+#include <asm/delay.h>
 
 /* Bus types */
 #define EISA_bus 0
@@ -304,7 +305,7 @@
 #define init_task	(init_task_union.task)
 #define init_stack	(init_task_union.stack)
 
-#define cpu_relax()	do { } while (0)
+#define cpu_relax()	udelay(1 + smp_processor_id())
 
 #endif /* __KERNEL__ */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/amigaffs.h linux/include/linux/amigaffs.h
--- linux.orig/include/linux/amigaffs.h	Tue Sep 11 15:20:30 2001
+++ linux/include/linux/amigaffs.h	Wed Jan 23 20:15:01 2002
@@ -31,7 +31,7 @@
 {
 	pr_debug(KERN_DEBUG "affs_bread: %d\n", block);
 	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
-		return bread(sb->s_dev, block, sb->s_blocksize);
+		return sb_bread(sb, block);
 	return NULL;
 }
 static inline struct buffer_head *
@@ -39,7 +39,7 @@
 {
 	pr_debug(KERN_DEBUG "affs_getblk: %d\n", block);
 	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size)
-		return getblk(sb->s_dev, block, sb->s_blocksize);
+		return sb_getblk(sb, block);
 	return NULL;
 }
 static inline struct buffer_head *
@@ -48,10 +48,11 @@
 	struct buffer_head *bh;
 	pr_debug(KERN_DEBUG "affs_getzeroblk: %d\n", block);
 	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
-		bh = getblk(sb->s_dev, block, sb->s_blocksize);
-		wait_on_buffer(bh);
+		bh = sb_getblk(sb, block);
+		lock_buffer(bh);
 		memset(bh->b_data, 0 , sb->s_blocksize);
 		mark_buffer_uptodate(bh, 1);
+		unlock_buffer(bh);
 		return bh;
 	}
 	return NULL;
@@ -62,7 +63,7 @@
 	struct buffer_head *bh;
 	pr_debug(KERN_DEBUG "affs_getemptyblk: %d\n", block);
 	if (block >= AFFS_SB->s_reserved && block < AFFS_SB->s_partition_size) {
-		bh = getblk(sb->s_dev, block, sb->s_blocksize);
+		bh = sb_getblk(sb, block);
 		wait_on_buffer(bh);
 		mark_buffer_uptodate(bh, 1);
 		return bh;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/capi.h linux/include/linux/capi.h
--- linux.orig/include/linux/capi.h	Sun Sep 30 19:26:42 2001
+++ linux/include/linux/capi.h	Mon Feb  4 17:55:44 2002
@@ -1,4 +1,4 @@
-/* $Id: capi.h,v 1.4.6.1 2001/09/23 22:25:05 kai Exp $
+/* $Id: capi.h,v 1.1.4.1 2001/11/20 14:19:38 kai Exp $
  * 
  * CAPI 2.0 Interface for Linux
  * 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/coda_fs_i.h linux/include/linux/coda_fs_i.h
--- linux.orig/include/linux/coda_fs_i.h	Thu Nov 22 19:46:19 2001
+++ linux/include/linux/coda_fs_i.h	Mon Feb  4 17:44:52 2002
@@ -33,6 +33,7 @@
 #define C_PURGE       0x8
 
 int coda_cnode_make(struct inode **, struct ViceFid *, struct super_block *);
+struct inode *coda_iget(struct super_block *sb, struct ViceFid *fid, struct coda_vattr *attr);
 int coda_cnode_makectl(struct inode **inode, struct super_block *sb);
 struct inode *coda_fid_to_inode(ViceFid *fid, struct super_block *sb);
 void coda_replace_fid(struct inode *, ViceFid *, ViceFid *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/cyclades.h linux/include/linux/cyclades.h
--- linux.orig/include/linux/cyclades.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/cyclades.h	Tue Jan  8 16:18:54 2002
@@ -514,10 +514,13 @@
     int nports;		/* Number of ports in the card */
     int bus_index;	/* address shift - 0 for ISA, 1 for PCI */
     int	intr_enabled;	/* FW Interrupt flag - 0 disabled, 1 enabled */
+    struct resource *resource;
+    unsigned long res_start;
+    unsigned long res_len;
 #ifdef __KERNEL__
     spinlock_t card_lock;
 #else
-    uclong filler;
+    unsigned long filler;
 #endif
 };
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/ext3_fs.h linux/include/linux/ext3_fs.h
--- linux.orig/include/linux/ext3_fs.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/ext3_fs.h	Thu Jan 17 21:23:51 2002
@@ -36,8 +36,8 @@
 /*
  * The second extended file system version
  */
-#define EXT3FS_DATE		"02 Dec 2001"
-#define EXT3FS_VERSION		"2.4-0.9.16"
+#define EXT3FS_DATE		"10 Jan 2002"
+#define EXT3FS_VERSION		"2.4-0.9.17"
 
 /*
  * Debug code
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/ext3_fs_sb.h linux/include/linux/ext3_fs_sb.h
--- linux.orig/include/linux/ext3_fs_sb.h	Thu Nov 22 19:46:19 2001
+++ linux/include/linux/ext3_fs_sb.h	Thu Jan 17 21:23:51 2002
@@ -61,6 +61,7 @@
 	int s_desc_per_block_bits;
 	int s_inode_size;
 	int s_first_ino;
+	u32 s_next_generation;
 
 	/* Journaling */
 	struct inode * s_journal_inode;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/fs.h linux/include/linux/fs.h
--- linux.orig/include/linux/fs.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/fs.h	Wed Jan 23 21:21:19 2002
@@ -108,6 +108,7 @@
 #define MS_NOATIME	1024	/* Do not update access times. */
 #define MS_NODIRATIME	2048	/* Do not update directory access times */
 #define MS_BIND		4096
+#define MS_MOVE		8192
 #define MS_REC		16384
 #define MS_VERBOSE	32768
 #define MS_ACTIVE	(1<<30)
@@ -1212,8 +1213,8 @@
 extern int fsync_inode_buffers(struct inode *);
 extern int fsync_inode_data_buffers(struct inode *);
 extern int inode_has_buffers(struct inode *);
-extern void filemap_fdatasync(struct address_space *);
-extern void filemap_fdatawait(struct address_space *);
+extern int filemap_fdatasync(struct address_space *);
+extern int filemap_fdatawait(struct address_space *);
 extern void sync_supers(kdev_t);
 extern int bmap(struct inode *, int);
 extern int notify_change(struct dentry *, struct iattr *);
@@ -1365,6 +1366,18 @@
 }
 extern int set_blocksize(kdev_t, int);
 extern struct buffer_head * bread(kdev_t, int, int);
+static inline struct buffer_head * sb_bread(struct super_block *sb, int block)
+{
+	return bread(sb->s_dev, block, sb->s_blocksize);
+}
+static inline struct buffer_head * sb_getblk(struct super_block *sb, int block)
+{
+	return getblk(sb->s_dev, block, sb->s_blocksize);
+}
+static inline struct buffer_head * sb_get_hash_table(struct super_block *sb, int block)
+{
+	return get_hash_table(sb->s_dev, block, sb->s_blocksize);
+}
 extern void wakeup_bdflush(void);
 extern void put_unused_buffer_head(struct buffer_head * bh);
 extern struct buffer_head * get_unused_buffer_head(int async);
@@ -1384,6 +1397,7 @@
 extern int block_prepare_write(struct page*, unsigned, unsigned, get_block_t*);
 extern int cont_prepare_write(struct page*, unsigned, unsigned, get_block_t*,
 				unsigned long *);
+extern int generic_cont_expand(struct inode *inode, loff_t size) ;
 extern int block_commit_write(struct page *page, unsigned from, unsigned to);
 extern int block_sync_page(struct page *);
 
@@ -1391,8 +1405,9 @@
 int generic_commit_write(struct file *, struct page *, unsigned, unsigned);
 int block_truncate_page(struct address_space *, loff_t, get_block_t *);
 extern int generic_direct_IO(int, struct inode *, struct kiobuf *, unsigned long, int, get_block_t *);
+extern int waitfor_one_page(struct page *);
+extern int writeout_one_page(struct page *);
 
-extern int waitfor_one_page(struct page*);
 extern int generic_file_mmap(struct file *, struct vm_area_struct *);
 extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
 extern ssize_t generic_file_read(struct file *, char *, size_t, loff_t *);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/generic_serial.h linux/include/linux/generic_serial.h
--- linux.orig/include/linux/generic_serial.h	Fri Sep  7 16:28:38 2001
+++ linux/include/linux/generic_serial.h	Mon Feb  4 17:22:39 2002
@@ -12,9 +12,6 @@
 #ifndef GENERIC_SERIAL_H
 #define GENERIC_SERIAL_H
 
-
-
-
 struct real_driver {
   void                    (*disable_tx_interrupts) (void *);
   void                    (*enable_tx_interrupts) (void *);
@@ -98,7 +95,7 @@
                      struct termios * old_termios);
 int  gs_init_port(struct gs_port *port);
 int  gs_setserial(struct gs_port *port, struct serial_struct *sp);
-void gs_getserial(struct gs_port *port, struct serial_struct *sp);
+int  gs_getserial(struct gs_port *port, struct serial_struct *sp);
 void gs_got_break(struct gs_port *port);
 
 extern int gs_debug;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/genhd.h linux/include/linux/genhd.h
--- linux.orig/include/linux/genhd.h	Thu Nov 22 19:47:05 2001
+++ linux/include/linux/genhd.h	Wed Jan 16 18:51:15 2002
@@ -37,6 +37,7 @@
 /* Ours is not to wonder why.. */
 	BSD_PARTITION =	FREEBSD_PARTITION,
 	MINIX_PARTITION = 0x81,  /* Minix Partition ID */
+	PLAN9_PARTITION = 0x39,  /* Plan 9 Partition ID */
 	UNIXWARE_PARTITION = 0x63,		/* Partition ID, same as */
 						/* GNU_HURD and SCO Unix */
 };
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/highmem.h linux/include/linux/highmem.h
--- linux.orig/include/linux/highmem.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/highmem.h	Mon Jan 14 18:33:59 2002
@@ -56,17 +56,6 @@
 	kunmap(page);
 }
 
-static inline void memclear_highpage(struct page *page, unsigned int offset, unsigned int size)
-{
-	char *kaddr;
-
-	if (offset + size > PAGE_SIZE)
-		BUG();
-	kaddr = kmap(page);
-	memset(kaddr + offset, 0, size);
-	kunmap(page);
-}
-
 /*
  * Same but also flushes aliased cache contents to RAM.
  */
@@ -78,6 +67,7 @@
 		BUG();
 	kaddr = kmap(page);
 	memset(kaddr + offset, 0, size);
+	flush_dcache_page(page);
 	flush_page_to_ram(page);
 	kunmap(page);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/i2o.h linux/include/linux/i2o.h
--- linux.orig/include/linux/i2o.h	Fri Nov  9 22:11:15 2001
+++ linux/include/linux/i2o.h	Wed Feb 13 17:44:49 2002
@@ -252,34 +252,34 @@
  */
 static inline u32 I2O_POST_READ32(struct i2o_controller *c)
 {
-	return *c->post_port;
+	return readl(c->post_port);
 }
 
-static inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 Val)
+static inline void I2O_POST_WRITE32(struct i2o_controller *c, u32 val)
 {
-	*c->post_port = Val;
+	writel(val, c->post_port);
 }
 
 
 static inline u32 I2O_REPLY_READ32(struct i2o_controller *c)
 {
-	return *c->reply_port;
+	return readl(c->reply_port);
 }
 
-static inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 Val)
+static inline void I2O_REPLY_WRITE32(struct i2o_controller *c, u32 val)
 {
-	*c->reply_port = Val;
+	writel(val, c->reply_port);
 }
 
 
 static inline u32 I2O_IRQ_READ32(struct i2o_controller *c)
 {
-	return *c->irq_mask;
+	return readl(c->irq_mask);
 }
 
-static inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 Val)
+static inline void I2O_IRQ_WRITE32(struct i2o_controller *c, u32 val)
 {
-	*c->irq_mask = Val;
+	writel(val, c->irq_mask);
 }
 
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/if_arp.h linux/include/linux/if_arp.h
--- linux.orig/include/linux/if_arp.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/if_arp.h	Mon Jan 14 16:24:07 2002
@@ -82,6 +82,7 @@
 	/* 787->799 reserved for fibrechannel media types */
 #define ARPHRD_IEEE802_TR 800		/* Magic type ident for TR	*/
 #define ARPHRD_IEEE80211 801		/* IEEE 802.11			*/
+#define ARPHRD_IEEE80211_PRISM 802	/* IEEE 802.11 + Prism2 header  */
 
 #define ARPHRD_VOID	  0xFFFF	/* Void type, nothing is known */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/if_bonding.h linux/include/linux/if_bonding.h
--- linux.orig/include/linux/if_bonding.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/if_bonding.h	Mon Jan 14 16:24:07 2002
@@ -79,6 +79,15 @@
 	u32 link_failure_count;
 } slave_t;
 
+/*
+ * Here are the locking policies for the two bonding locks:
+ *
+ * 1) Get bond->lock when reading/writing slave list.
+ * 2) Get bond->ptrlock when reading/writing bond->current_slave.
+ *    (It is unnecessary when the write-lock is put with bond->lock.)
+ * 3) When we lock with bond->ptrlock, we must lock with bond->lock
+ *    beforehand.
+ */
 typedef struct bonding {
 	slave_t *next;
 	slave_t *prev;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/intermezzo_fs.h linux/include/linux/intermezzo_fs.h
--- linux.orig/include/linux/intermezzo_fs.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/intermezzo_fs.h	Mon Jan  7 14:09:06 2002
@@ -135,6 +135,7 @@
         int fset_permit_count;
         int fset_permit_cookie;
         int fset_chunkbits; 
+        int fset_data; /* replaces the dentry d_data field for fsetroots */
 	struct  kml_fsdata *fset_kmldata;
 	loff_t  fset_file_maxio;  /* writing more than this causes a close */ 
 };
@@ -201,9 +202,6 @@
 void presto_frob_dop(struct dentry *de) ;
 char * presto_path(struct dentry *dentry, struct dentry *root,
                    char *buffer, int buflen);
-void presto_set_dd(struct dentry *);
-void presto_init_ddata_cache(void);
-void presto_cleanup_ddata_cache(void);
 extern struct dentry_operations presto_dentry_ops;
 
 
@@ -231,14 +229,6 @@
 };
 
 
-struct presto_dentry_data { 
-        int dd_count; /* how mnay dentries are using this dentry */
-        struct presto_file_set *dd_fset;
-        loff_t dd_kml_offset;
-        int dd_flags;
-
-}; 
-
 struct presto_file_data { 
         int fd_do_lml;
         loff_t fd_lml_offset;
@@ -259,7 +249,6 @@
         __u64 pv_ctime;
         __u64 pv_size;
 };
-inline struct presto_dentry_data *presto_d2d(struct dentry *);
 int presto_walk(const char *name, struct nameidata *nd);
 int presto_clear_fsetroot(char *path);
 int presto_clear_all_fsetroots(char *path);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/iso_fs.h linux/include/linux/iso_fs.h
--- linux.orig/include/linux/iso_fs.h	Thu Nov 22 19:47:11 2001
+++ linux/include/linux/iso_fs.h	Wed Jan 23 21:46:36 2002
@@ -219,7 +219,7 @@
 int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
 
 extern struct dentry *isofs_lookup(struct inode *, struct dentry *);
-extern struct buffer_head *isofs_bread(struct inode *, unsigned int, unsigned int);
+extern struct buffer_head *isofs_bread(struct inode *inode, unsigned int block);
 extern int isofs_get_blocks(struct inode *, long, struct buffer_head **, unsigned long);
 
 extern struct inode_operations isofs_dir_inode_operations;
@@ -230,11 +230,11 @@
 #ifdef LEAK_CHECK
 #define free_s leak_check_free_s
 #define malloc leak_check_malloc
-#define bread leak_check_bread
+#define sb_bread leak_check_bread
 #define brelse leak_check_brelse
 extern void * leak_check_malloc(unsigned int size);
 extern void leak_check_free_s(void * obj, int size);
-extern struct buffer_head * leak_check_bread(int dev, int block, int size);
+extern struct buffer_head * leak_check_bread(struct super_block *sb, int block);
 extern void leak_check_brelse(struct buffer_head * bh);
 #endif /* LEAK_CHECK */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/jbd.h linux/include/linux/jbd.h
--- linux.orig/include/linux/jbd.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/jbd.h	Thu Jan 17 21:23:51 2002
@@ -567,6 +567,7 @@
 int journal_next_log_block(journal_t *, unsigned long *);
 
 /* Commit management */
+void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate);
 extern void journal_commit_transaction(journal_t *);
 
 /* Checkpoint list management */
@@ -799,8 +800,6 @@
 #define BJ_Reserved	8	/* Buffer is reserved for access by journal */
 #define BJ_Types	9
  
-extern int jbd_blocks_per_page(struct inode *inode);
-
 #ifdef __KERNEL__
 
 extern spinlock_t jh_splice_lock;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/kdev_t.h linux/include/linux/kdev_t.h
--- linux.orig/include/linux/kdev_t.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/kdev_t.h	Mon Jan 21 20:27:04 2002
@@ -75,6 +75,14 @@
 
 extern const char * kdevname(kdev_t);	/* note: returns pointer to static data! */
 
+/* 2.5.x compatibility */
+
+#define mk_kdev(a,b) MKDEV(a,b)
+#define major(d) MAJOR(d)
+#define minor(d) MINOR(d)
+#define kdev_same(a,b) (a==b)
+#define kdev_none(d) (!(d))
+
 /*
 As long as device numbers in the outside world have 16 bits only,
 we use these conversions.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/kernel.h linux/include/linux/kernel.h
--- linux.orig/include/linux/kernel.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/kernel.h	Wed Dec 26 15:25:28 2001
@@ -36,6 +36,13 @@
 #define	KERN_INFO	"<6>"	/* informational			*/
 #define	KERN_DEBUG	"<7>"	/* debug-level messages			*/
 
+extern int console_printk[];
+
+#define console_loglevel (console_printk[0])
+#define default_message_loglevel (console_printk[1])
+#define minimum_console_loglevel (console_printk[2])
+#define default_console_loglevel (console_printk[3])
+
 # define NORET_TYPE    /**/
 # define ATTRIB_NORET  __attribute__((noreturn))
 # define NORET_AND     noreturn,
@@ -81,8 +88,6 @@
 
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
-
-extern int console_loglevel;
 
 static inline void console_silent(void)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/kernelcapi.h linux/include/linux/kernelcapi.h
--- linux.orig/include/linux/kernelcapi.h	Thu Feb  8 18:40:25 2001
+++ linux/include/linux/kernelcapi.h	Mon Feb  4 17:55:44 2002
@@ -1,10 +1,12 @@
-/*
- * $Id: kernelcapi.h,v 1.8.6.2 2001/02/07 11:31:31 kai Exp $
+/* $Id: kernelcapi.h,v 1.1.4.2 2002/01/28 18:25:10 kai Exp $
  * 
  * Kernel CAPI 2.0 Interface for Linux
  * 
  * (c) Copyright 1997 by Carsten Paeth (calle@calle.in-berlin.de)
  * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
  */
 
 #ifndef __KERNELCAPI_H__
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/limits.h linux/include/linux/limits.h
--- linux.orig/include/linux/limits.h	Wed Jul 28 17:30:10 1999
+++ linux/include/linux/limits.h	Wed Jan 23 02:53:39 2002
@@ -11,7 +11,7 @@
 #define MAX_CANON        255	/* size of the canonical input queue */
 #define MAX_INPUT        255	/* size of the type-ahead buffer */
 #define NAME_MAX         255	/* # chars in a file name */
-#define PATH_MAX        4095	/* # chars in a path name */
+#define PATH_MAX        4096	/* # chars in a path name including nul */
 #define PIPE_BUF        4096	/* # bytes in atomic write to a pipe */
 
 #define RTSIG_MAX	  32
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/mii.h linux/include/linux/mii.h
--- linux.orig/include/linux/mii.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/mii.h	Mon Jan 14 17:27:24 2002
@@ -70,6 +70,8 @@
 #define ADVERTISE_LPACK         0x4000  /* Ack link partners response  */
 #define ADVERTISE_NPAGE         0x8000  /* Next page bit               */
 
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+			ADVERTISE_CSMA)
 #define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
                        ADVERTISE_100HALF | ADVERTISE_100FULL)
 
@@ -100,6 +102,27 @@
 #define NWAYTEST_RESV1          0x00ff  /* Unused...                   */
 #define NWAYTEST_LOOPBACK       0x0100  /* Enable loopback for N-way   */
 #define NWAYTEST_RESV2          0xfe00  /* Unused...                   */
+
+
+struct mii_if_info {
+	int phy_id;
+	int advertising;
+
+	unsigned int full_duplex : 1;
+	unsigned int duplex_lock : 1;
+
+	struct net_device *dev;
+	int (*mdio_read) (struct net_device *dev, int phy_id, int location);
+	void (*mdio_write) (struct net_device *dev, int phy_id, int location, int val);
+};
+
+struct ethtool_cmd;
+
+int mii_link_ok (struct mii_if_info *mii);
+int mii_nway_restart (struct mii_if_info *mii);
+int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd);
+int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd);
+
 
 /* This structure is used in all SIOCxMIIxxx ioctl calls */
 struct mii_ioctl_data {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netdevice.h linux/include/linux/netdevice.h
--- linux.orig/include/linux/netdevice.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/netdevice.h	Fri Dec 21 18:56:21 2001
@@ -663,6 +663,8 @@
 	NETIF_MSG_TX_DONE	= 0x0400,
 	NETIF_MSG_RX_STATUS	= 0x0800,
 	NETIF_MSG_PKTDATA	= 0x1000,
+	NETIF_MSG_HW		= 0x2000,
+	NETIF_MSG_WOL		= 0x4000,
 };
 
 #define netif_msg_drv(p)	((p)->msg_enable & NETIF_MSG_DRV)
@@ -678,6 +680,8 @@
 #define netif_msg_tx_done(p)	((p)->msg_enable & NETIF_MSG_TX_DONE)
 #define netif_msg_rx_status(p)	((p)->msg_enable & NETIF_MSG_RX_STATUS)
 #define netif_msg_pktdata(p)	((p)->msg_enable & NETIF_MSG_PKTDATA)
+#define netif_msg_hw(p)		((p)->msg_enable & NETIF_MSG_HW)
+#define netif_msg_wol(p)	((p)->msg_enable & NETIF_MSG_WOL)
 
 /* These functions live elsewhere (drivers/net/net_init.c, but related) */
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ip_conntrack.h linux/include/linux/netfilter_ipv4/ip_conntrack.h
--- linux.orig/include/linux/netfilter_ipv4/ip_conntrack.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/netfilter_ipv4/ip_conntrack.h	Mon Jan 14 16:24:07 2002
@@ -27,6 +27,21 @@
 	IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
 };
 
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+	/* It's an expected connection: bit 0 set.  This bit never changed */
+	IPS_EXPECTED_BIT = 0,
+	IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+	/* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
+	IPS_SEEN_REPLY_BIT = 1,
+	IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+	/* Conntrack should never be early-expired. */
+	IPS_ASSURED_BIT = 2,
+	IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+};
+
 #ifdef __KERNEL__
 
 #include <linux/types.h>
@@ -46,21 +61,6 @@
 #else
 #define IP_NF_ASSERT(x)
 #endif
-
-/* Bitset representing status of connection. */
-enum ip_conntrack_status {
-	/* It's an expected connection: bit 0 set.  This bit never changed */
-	IPS_EXPECTED_BIT = 0,
-	IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
-
-	/* We've seen packets both ways: bit 1 set.  Can be set, not unset. */
-	IPS_SEEN_REPLY_BIT = 1,
-	IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
-
-	/* Conntrack should never be early-expired. */
-	IPS_ASSURED_BIT = 2,
-	IPS_ASSURED = (1 << IPS_ASSURED_BIT),
-};
 
 struct ip_conntrack_expect
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ip_conntrack_tuple.h linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h
--- linux.orig/include/linux/netfilter_ipv4/ip_conntrack_tuple.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/netfilter_ipv4/ip_conntrack_tuple.h	Mon Jan 14 16:24:07 2002
@@ -62,6 +62,13 @@
 	} dst;
 };
 
+enum ip_conntrack_dir
+{
+	IP_CT_DIR_ORIGINAL,
+	IP_CT_DIR_REPLY,
+	IP_CT_DIR_MAX
+};
+
 #ifdef __KERNEL__
 
 #define DUMP_TUPLE(tp)						\
@@ -75,13 +82,19 @@
 /* If we're the first tuple, it's the original dir. */
 #define DIRECTION(h) ((enum ip_conntrack_dir)(&(h)->ctrack->tuplehash[1] == (h)))
 
-enum ip_conntrack_dir
+/* Connections have two entries in the hash table: one for each way */
+struct ip_conntrack_tuple_hash
 {
-	IP_CT_DIR_ORIGINAL,
-	IP_CT_DIR_REPLY,
-	IP_CT_DIR_MAX
+	struct list_head list;
+
+	struct ip_conntrack_tuple tuple;
+
+	/* this == &ctrack->tuplehash[DIRECTION(this)]. */
+	struct ip_conntrack *ctrack;
 };
 
+#endif /* __KERNEL__ */
+
 static inline int ip_ct_tuple_src_equal(const struct ip_conntrack_tuple *t1,
 				        const struct ip_conntrack_tuple *t2)
 {
@@ -115,16 +128,4 @@
 		     & mask->dst.protonum));
 }
 
-/* Connections have two entries in the hash table: one for each way */
-struct ip_conntrack_tuple_hash
-{
-	struct list_head list;
-
-	struct ip_conntrack_tuple tuple;
-
-	/* this == &ctrack->tuplehash[DIRECTION(this)]. */
-	struct ip_conntrack *ctrack;
-};
-
-#endif /* __KERNEL__ */
 #endif /* _IP_CONNTRACK_TUPLE_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ip_tables.h linux/include/linux/netfilter_ipv4/ip_tables.h
--- linux.orig/include/linux/netfilter_ipv4/ip_tables.h	Thu Jul 26 20:58:46 2001
+++ linux/include/linux/netfilter_ipv4/ip_tables.h	Wed Jan 23 20:24:37 2002
@@ -428,6 +428,9 @@
 
 	/* Man behind the curtain... */
 	struct ipt_table_info *private;
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
 };
 
 extern int ipt_register_table(struct ipt_table *table);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ipt_ULOG.h linux/include/linux/netfilter_ipv4/ipt_ULOG.h
--- linux.orig/include/linux/netfilter_ipv4/ipt_ULOG.h	Thu Jan  1 00:00:00 1970
+++ linux/include/linux/netfilter_ipv4/ipt_ULOG.h	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,46 @@
+/* Header file for IP tables userspace logging, Version 1.8
+ *
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ * 
+ * Distributed under the terms of GNU GPL */
+
+#ifndef _IPT_ULOG_H
+#define _IPT_ULOG_H
+
+#ifndef NETLINK_NFLOG
+#define NETLINK_NFLOG 	5
+#endif
+
+#define ULOG_MAC_LEN	80
+#define ULOG_PREFIX_LEN	32
+
+#define ULOG_MAX_QLEN	50
+/* Why 50? Well... there is a limit imposed by the slab cache 131000
+ * bytes. So the multipart netlink-message has to be < 131000 bytes.
+ * Assuming a standard ethernet-mtu of 1500, we could define this up
+ * to 80... but even 50 seems to be big enough. */
+
+/* private data structure for each rule with a ULOG target */
+struct ipt_ulog_info {
+	unsigned int nl_group;
+	size_t copy_range;
+	size_t qthreshold;
+	char prefix[ULOG_PREFIX_LEN];
+};
+
+/* Format of the ULOG packets passed through netlink */
+typedef struct ulog_packet_msg {
+	unsigned long mark;
+	long timestamp_sec;
+	long timestamp_usec;
+	unsigned int hook;
+	char indev_name[IFNAMSIZ];
+	char outdev_name[IFNAMSIZ];
+	size_t data_len;
+	char prefix[ULOG_PREFIX_LEN];
+	unsigned char mac_len;
+	unsigned char mac[ULOG_MAC_LEN];
+	unsigned char payload[0];
+} ulog_packet_msg_t;
+
+#endif /*_IPT_ULOG_H*/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ipt_ah.h linux/include/linux/netfilter_ipv4/ipt_ah.h
--- linux.orig/include/linux/netfilter_ipv4/ipt_ah.h	Thu Jan  1 00:00:00 1970
+++ linux/include/linux/netfilter_ipv4/ipt_ah.h	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,16 @@
+#ifndef _IPT_AH_H
+#define _IPT_AH_H
+
+struct ipt_ah
+{
+	u_int32_t spis[2];			/* Security Parameter Index */
+	u_int8_t  invflags;			/* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_ah. */
+#define IPT_AH_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IPT_AH_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IPT_AH_H*/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4/ipt_esp.h linux/include/linux/netfilter_ipv4/ipt_esp.h
--- linux.orig/include/linux/netfilter_ipv4/ipt_esp.h	Thu Jan  1 00:00:00 1970
+++ linux/include/linux/netfilter_ipv4/ipt_esp.h	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,16 @@
+#ifndef _IPT_ESP_H
+#define _IPT_ESP_H
+
+struct ipt_esp
+{
+	u_int32_t spis[2];			/* Security Parameter Index */
+	u_int8_t  invflags;			/* Inverse flags */
+};
+
+
+
+/* Values for "invflags" field in struct ipt_esp. */
+#define IPT_ESP_INV_SPI		0x01	/* Invert the sense of spi. */
+#define IPT_ESP_INV_MASK	0x01	/* All possible flags. */
+
+#endif /*_IPT_ESP_H*/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv4.h linux/include/linux/netfilter_ipv4.h
--- linux.orig/include/linux/netfilter_ipv4.h	Thu Nov 22 19:48:57 2001
+++ linux/include/linux/netfilter_ipv4.h	Thu Feb  7 16:52:23 2002
@@ -59,18 +59,20 @@
 	NF_IP_PRI_LAST = INT_MAX,
 };
 
-#ifdef CONFIG_NETFILTER_DEBUG
+/* Arguments for setsockopt SOL_IP: */
+/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
+/* 2.2 firewalling (+ masq) went from 64 through 76 */
+/* 2.4 firewalling went 64 through 67. */
+#define SO_ORIGINAL_DST 80
+
 #ifdef __KERNEL__
+#ifdef CONFIG_NETFILTER_DEBUG
 void nf_debug_ip_local_deliver(struct sk_buff *skb);
 void nf_debug_ip_loopback_xmit(struct sk_buff *newskb);
 void nf_debug_ip_finish_output2(struct sk_buff *skb);
-#endif /*__KERNEL__*/
 #endif /*CONFIG_NETFILTER_DEBUG*/
 
-/* Arguments for setsockopt SOL_IP: */
-/* 2.0 firewalling went from 64 through 71 (and +256, +512, etc). */
-/* 2.2 firewalling (+ masq) went from 64 through 76 */
-/* 2.4 firewalling went 64 through 67. */
-#define SO_ORIGINAL_DST 80
+extern int ip_route_me_harder(struct sk_buff **pskb);
+#endif /*__KERNEL__*/
 
 #endif /*__LINUX_IP_NETFILTER_H*/
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netfilter_ipv6/ip6_tables.h linux/include/linux/netfilter_ipv6/ip6_tables.h
--- linux.orig/include/linux/netfilter_ipv6/ip6_tables.h	Thu Jul 26 20:59:06 2001
+++ linux/include/linux/netfilter_ipv6/ip6_tables.h	Wed Jan 23 20:24:37 2002
@@ -435,6 +435,9 @@
 
 	/* Man behind the curtain... */
 	struct ip6t_table_info *private;
+
+	/* Set this to THIS_MODULE if you are a module, otherwise NULL */
+	struct module *me;
 };
 
 extern int ip6t_register_table(struct ip6t_table *table);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/netlink.h linux/include/linux/netlink.h
--- linux.orig/include/linux/netlink.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/netlink.h	Wed Jan 23 20:24:37 2002
@@ -6,6 +6,7 @@
 #define NETLINK_USERSOCK	2	/* Reserved for user mode socket protocols 	*/
 #define NETLINK_FIREWALL	3	/* Firewalling hook				*/
 #define NETLINK_TCPDIAG		4	/* TCP socket monitoring			*/
+#define NETLINK_NFLOG		5	/* netfilter/iptables ULOG */
 #define NETLINK_ARPD		8
 #define NETLINK_ROUTE6		11	/* af_inet6 route comm channel */
 #define NETLINK_IP6_FW		13
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/pagemap.h linux/include/linux/pagemap.h
--- linux.orig/include/linux/pagemap.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/pagemap.h	Wed Jan  9 17:10:17 2002
@@ -29,7 +29,7 @@
 #define PAGE_CACHE_ALIGN(addr)	(((addr)+PAGE_CACHE_SIZE-1)&PAGE_CACHE_MASK)
 
 #define page_cache_get(x)	get_page(x)
-extern void FASTCALL(page_cache_release(struct page *));
+#define page_cache_release(x)	__free_page(x)
 
 static inline struct page *page_cache_alloc(struct address_space *x)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/pci_ids.h linux/include/linux/pci_ids.h
--- linux.orig/include/linux/pci_ids.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/pci_ids.h	Wed Feb 13 16:34:54 2002
@@ -355,6 +355,7 @@
 
 #define PCI_VENDOR_ID_WD		0x101c
 #define PCI_DEVICE_ID_WD_7197		0x3296
+#define PCI_DEVICE_ID_WD_90C		0xc24a
 
 #define PCI_VENDOR_ID_AMI		0x101e
 #define PCI_DEVICE_ID_AMI_MEGARAID3	0x1960
@@ -646,6 +647,12 @@
 #define PCI_DEVICE_ID_APPLE_KL_USB	0x0019
 #define PCI_DEVICE_ID_APPLE_UNI_N_AGP	0x0020
 #define PCI_DEVICE_ID_APPLE_UNI_N_GMAC	0x0021
+#define PCI_DEVICE_ID_APPLE_KEYLARGO	0x0022
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMACP	0x0024
+#define PCI_DEVICE_ID_APPLE_KEYLARGO_P	0x0025
+#define PCI_DEVICE_ID_APPLE_KL_USB_P	0x0026
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP_P	0x0027
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP15	0x002d
 #define PCI_DEVICE_ID_APPLE_UNI_N_FW2	0x0030
 
 #define PCI_VENDOR_ID_YAMAHA		0x1073
@@ -855,11 +862,17 @@
 #define PCI_DEVICE_ID_NVIDIA_QUADRO		0x0103
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX	0x0110
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2	0x0111
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO	0x0112
 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR	0x0113
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS	0x0150
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2	0x0151
 #define PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA	0x0152
 #define PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO	0x0153
+#define PCI_DEVICE_ID_NVIDIA_IGEFORCE2		0x01a0
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3		0x0200
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_1		0x0201
+#define PCI_DEVICE_ID_NVIDIA_GEFORCE3_2		0x0202
+#define PCI_DEVICE_ID_NVIDIA_QUADRO_DDC		0x0203
 
 #define PCI_VENDOR_ID_IMS		0x10e0
 #define PCI_DEVICE_ID_IMS_8849		0x8849
@@ -946,7 +959,9 @@
 #define PCI_DEVICE_ID_VIA_8233_7	0x3065
 #define PCI_DEVICE_ID_VIA_82C686_6	0x3068
 #define PCI_DEVICE_ID_VIA_8233_0	0x3074
+#define PCI_DEVICE_ID_VIA_8622		0x3102
 #define PCI_DEVICE_ID_VIA_8233C_0	0x3109
+#define PCI_DEVICE_ID_VIA_8361		0x3112
 #define PCI_DEVICE_ID_VIA_8633_0	0x3091
 #define PCI_DEVICE_ID_VIA_8367_0	0x3099
 #define PCI_DEVICE_ID_VIA_86C100A	0x6100
@@ -1109,6 +1124,8 @@
 #define PCI_DEVICE_ID_ARTOP_ATP850UF	0x0005
 #define PCI_DEVICE_ID_ARTOP_ATP860	0x0006
 #define PCI_DEVICE_ID_ARTOP_ATP860R	0x0007
+#define PCI_DEVICE_ID_ARTOP_ATP865	0x0008
+#define PCI_DEVICE_ID_ARTOP_ATP865R	0x0009
 #define PCI_DEVICE_ID_ARTOP_AEC7610	0x8002
 #define PCI_DEVICE_ID_ARTOP_AEC7612UW	0x8010
 #define PCI_DEVICE_ID_ARTOP_AEC7612U	0x8020
@@ -1482,6 +1499,9 @@
 #define PCI_VENDOR_ID_PANACOM		0x14d4
 #define PCI_DEVICE_ID_PANACOM_QUADMODEM	0x0400
 #define PCI_DEVICE_ID_PANACOM_DUALMODEM	0x0402
+
+#define PCI_VENDOR_ID_AFAVLAB		0x14db
+#define PCI_DEVICE_ID_AFAVLAB_P028	0x2180
 
 #define PCI_VENDOR_ID_BROADCOM		0x14e4
 #define PCI_DEVICE_ID_TIGON3_5700	0x1644
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/pmu.h linux/include/linux/pmu.h
--- linux.orig/include/linux/pmu.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/pmu.h	Wed Dec 26 16:36:12 2001
@@ -168,19 +168,41 @@
 
 /* priority levels in notifiers */
 #define SLEEP_LEVEL_VIDEO	100	/* Video driver (first wake) */
-#define SLEEP_LEVEL_SOUND	90	/* Sound driver */
-#define SLEEP_LEVEL_MEDIABAY	80	/* Media bay driver */
-#define SLEEP_LEVEL_BLOCK	70	/* IDE, SCSI */
-#define SLEEP_LEVEL_NET		60	/* bmac */
-#define SLEEP_LEVEL_ADB		50	/* ADB */
-#define SLEEP_LEVEL_MISC	30	/* Anything */
-#define SLEEP_LEVEL_LAST	0	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_MEDIABAY	90	/* Media bay driver */
+#define SLEEP_LEVEL_BLOCK	80	/* IDE, SCSI */
+#define SLEEP_LEVEL_NET		70	/* bmac, gmac */
+#define SLEEP_LEVEL_MISC	60	/* Anything else */
+#define SLEEP_LEVEL_USERLAND	55	/* Reserved for apm_emu */
+#define SLEEP_LEVEL_ADB		50	/* ADB (async) */
+#define SLEEP_LEVEL_SOUND	40	/* Sound driver (blocking) */
 
 /* special register notifier functions */
 int pmu_register_sleep_notifier(struct pmu_sleep_notifier* notifier);
 int pmu_unregister_sleep_notifier(struct pmu_sleep_notifier* notifier);
 
-#endif /* CONFIG_PMAC_PBOOK */
+#define PMU_MAX_BATTERIES	2
+
+/* values for pmu_power_flags */
+#define PMU_PWR_AC_PRESENT	0x00000001
+
+/* values for pmu_battery_info.flags */
+#define PMU_BATT_PRESENT	0x00000001
+#define PMU_BATT_CHARGING	0x00000002
 
+struct pmu_battery_info
+{
+	unsigned int	flags;
+	unsigned int	charge;		/* current charge */
+	unsigned int	max_charge;	/* maximum charge */
+	signed int	current;	/* current, positive if charging */
+	unsigned int	voltage;	/* voltage */
+	unsigned int	time_remaining;	/* remaining time */
+};
+
+extern int pmu_battery_count;
+extern struct pmu_battery_info pmu_batteries[PMU_MAX_BATTERIES];
+extern unsigned int pmu_power_flags;
+
+#endif /* CONFIG_PMAC_PBOOK */
 
 #endif	/* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/prctl.h linux/include/linux/prctl.h
--- linux.orig/include/linux/prctl.h	Fri Jul 20 03:39:57 2001
+++ linux/include/linux/prctl.h	Wed Jan 23 20:55:51 2002
@@ -20,4 +20,10 @@
 #define PR_GET_KEEPCAPS   7
 #define PR_SET_KEEPCAPS   8
 
+/* Get/set floating-point emulation control bits (if meaningful) */
+#define PR_GET_FPEMU  9
+#define PR_SET_FPEMU 10
+# define PR_FPEMU_NOPRINT	1	/* silently emulate fp operations accesses */
+# define PR_FPEMU_SIGFPE	2	/* don't emulate fp operations, send SIGFPE instead */
+
 #endif /* _LINUX_PRCTL_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/reiserfs_fs.h linux/include/linux/reiserfs_fs.h
--- linux.orig/include/linux/reiserfs_fs.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/reiserfs_fs.h	Wed Feb 13 17:24:37 2002
@@ -236,9 +236,9 @@
     __u64 linear;
 } __attribute__ ((__packed__)) offset_v2_esafe_overlay;
 
-static inline __u16 offset_v2_k_type( struct offset_v2 *v2 )
+static inline __u16 offset_v2_k_type( const struct offset_v2 *v2 )
 {
-    offset_v2_esafe_overlay tmp = *(offset_v2_esafe_overlay *)v2;
+    offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
     tmp.linear = le64_to_cpu( tmp.linear );
     return tmp.offset_v2.k_type;
 }
@@ -248,12 +248,12 @@
     offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2;
     tmp->linear = le64_to_cpu(tmp->linear);
     tmp->offset_v2.k_type = type;
-    tmp->linear = le64_to_cpu(tmp->linear);
+    tmp->linear = cpu_to_le64(tmp->linear);
 }
  
-static inline loff_t offset_v2_k_offset( struct offset_v2 *v2 )
+static inline loff_t offset_v2_k_offset( const struct offset_v2 *v2 )
 {
-    offset_v2_esafe_overlay tmp = *(offset_v2_esafe_overlay *)v2;
+    offset_v2_esafe_overlay tmp = *(const offset_v2_esafe_overlay *)v2;
     tmp.linear = le64_to_cpu( tmp.linear );
     return tmp.offset_v2.k_offset;
 }
@@ -262,7 +262,7 @@
     offset_v2_esafe_overlay *tmp = (offset_v2_esafe_overlay *)v2;
     tmp->linear = le64_to_cpu(tmp->linear);
     tmp->offset_v2.k_offset = offset;
-    tmp->linear = le64_to_cpu(tmp->linear);
+    tmp->linear = cpu_to_le64(tmp->linear);
 }
 #else
 # define offset_v2_k_type(v2)           ((v2)->k_type)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/reiserfs_fs_sb.h linux/include/linux/reiserfs_fs_sb.h
--- linux.orig/include/linux/reiserfs_fs_sb.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/reiserfs_fs_sb.h	Wed Feb 13 17:24:37 2002
@@ -201,7 +201,7 @@
   struct buffer_head *bh ;		 /* real buffer head */
   kdev_t dev ;				 /* dev of real buffer head */
   unsigned long blocknr ;		 /* block number of real buffer head, == 0 when buffer on disk */		 
-  int state ;
+  long state ;
   struct reiserfs_journal_list *jlist ;  /* journal list this cnode lives in */
   struct reiserfs_journal_cnode *next ;  /* next in transaction list */
   struct reiserfs_journal_cnode *prev ;  /* prev in transaction list */
@@ -264,7 +264,7 @@
   struct reiserfs_journal_cnode *j_last ; /* newest journal block */
   struct reiserfs_journal_cnode *j_first ; /*  oldest journal block.  start here for traverse */
 				
-  int j_state ;			
+  long j_state ;			
   unsigned long j_trans_id ;
   unsigned long j_mount_id ;
   unsigned long j_start ;             /* start of current waiting commit (index into j_ap_blocks) */
@@ -407,7 +407,7 @@
 				/* To be obsoleted soon by per buffer seals.. -Hans */
     atomic_t s_generation_counter; // increased by one every time the
     // tree gets re-balanced
-    unsigned int s_properties;    /* File system properties. Currently holds
+    unsigned long s_properties;    /* File system properties. Currently holds
 				     on-disk FS format */
     
     /* session statistics */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/rtnetlink.h linux/include/linux/rtnetlink.h
--- linux.orig/include/linux/rtnetlink.h	Fri Dec 21 17:42:03 2001
+++ linux/include/linux/rtnetlink.h	Wed Jan 23 20:15:38 2002
@@ -586,9 +586,9 @@
 extern void rtnetlink_init(void);
 
 #define ASSERT_RTNL() do { if (down_trylock(&rtnl_sem) == 0)  { up(&rtnl_sem); \
-printk("RTNL: assertion failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); } \
+printk("RTNL: assertion failed at " __FILE__ "(%d)\n", __LINE__); } \
 		   } while(0);
-#define BUG_TRAP(x) if (!(x)) { printk("KERNEL: assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); }
+#define BUG_TRAP(x) if (!(x)) { printk("KERNEL: assertion (" #x ") failed at " __FILE__ "(%d)\n", __LINE__); }
 
 
 #endif /* __KERNEL__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/sonypi.h linux/include/linux/sonypi.h
--- linux.orig/include/linux/sonypi.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/sonypi.h	Thu Feb  7 16:58:52 2002
@@ -73,11 +73,28 @@
 #define SONYPI_EVENT_BACK_PRESSED		35
 #define SONYPI_EVENT_LID_CLOSED			36
 #define SONYPI_EVENT_LID_OPENED			37
+#define SONYPI_EVENT_BLUETOOTH_ON		38
+#define SONYPI_EVENT_BLUETOOTH_OFF		39
 
+/* get/set brightness */
+#define SONYPI_IOCGBRT		_IOR('v', 0, __u8)
+#define SONYPI_IOCSBRT		_IOW('v', 0, __u8)
 
-/* brightness etc. ioctls */
-#define SONYPI_IOCGBRT	_IOR('v', 0, __u8)
-#define SONYPI_IOCSBRT	_IOW('v', 0, __u8)
+/* get battery full capacity/remaining capacity */
+#define SONYPI_IOCGBAT1CAP	_IOR('v', 2, __u16)
+#define SONYPI_IOCGBAT1REM	_IOR('v', 3, __u16)
+#define SONYPI_IOCGBAT2CAP	_IOR('v', 4, __u16)
+#define SONYPI_IOCGBAT2REM	_IOR('v', 5, __u16)
+
+/* get battery flags: battery1/battery2/ac adapter present */
+#define SONYPI_BFLAGS_B1	0x01
+#define SONYPI_BFLAGS_B2	0x02
+#define SONYPI_BFLAGS_AC	0x04
+#define SONYPI_IOCGBATFLAGS	_IOR('v', 7, __u8)
+
+/* get/set bluetooth subsystem state on/off */
+#define SONYPI_IOCGBLUE		_IOR('v', 8, __u8)
+#define SONYPI_IOCSBLUE		_IOW('v', 9, __u8)
 
 #ifdef __KERNEL__
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/stringify.h linux/include/linux/stringify.h
--- linux.orig/include/linux/stringify.h	Thu Jan  1 00:00:00 1970
+++ linux/include/linux/stringify.h	Tue Jan 15 00:40:01 2002
@@ -0,0 +1,12 @@
+#ifndef __LINUX_STRINGIFY_H
+#define __LINUX_STRINGIFY_H
+
+/* Indirect stringification.  Doing two levels allows the parameter to be a
+ * macro itself.  For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+
+#define __stringify_1(x)	#x
+#define __stringify(x)		__stringify_1(x)
+
+#endif	/* !__LINUX_STRINGIFY_H */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/sunrpc/clnt.h linux/include/linux/sunrpc/clnt.h
--- linux.orig/include/linux/sunrpc/clnt.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/sunrpc/clnt.h	Mon Jan  7 17:36:23 2002
@@ -136,7 +136,6 @@
 	xprt_set_timeout(&clnt->cl_timeout, retr, incr);
 }
 
-extern void rpciod_tcp_dispatcher(void);
 extern void rpciod_wake_up(void);
 
 /*
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/sysrq.h linux/include/linux/sysrq.h
--- linux.orig/include/linux/sysrq.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/sysrq.h	Mon Jan 14 17:27:24 2002
@@ -10,6 +10,8 @@
  *	overhauled to use key registration
  *	based upon discusions in irc://irc.openprojects.net/#kernelnewbies
  */
+#ifndef __LINUX_SYSRQ_H__
+#define __LINUX_SYSRQ_H__
 
 #include <linux/config.h>
 
@@ -117,3 +119,5 @@
 #else
 #define CHECK_EMERGENCY_SYNC
 #endif
+
+#endif /* __LINUX_SYSRQ_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/threads.h linux/include/linux/threads.h
--- linux.orig/include/linux/threads.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/threads.h	Wed Dec 26 15:25:28 2001
@@ -5,7 +5,7 @@
 
 /*
  * The default limit for the nr of threads is now in
- * /proc/sys/kernel/max-threads.
+ * /proc/sys/kernel/threads-max.
  */
  
 #ifdef CONFIG_SMP
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/time.h linux/include/linux/time.h
--- linux.orig/include/linux/time.h	Thu Nov 22 19:46:18 2001
+++ linux/include/linux/time.h	Mon Feb  4 16:55:14 2002
@@ -100,8 +100,6 @@
 #ifdef __KERNEL__
 extern void do_gettimeofday(struct timeval *tv);
 extern void do_settimeofday(struct timeval *tv);
-extern void get_fast_time(struct timeval *tv);
-extern void (*do_get_fast_time)(struct timeval *);
 #endif
 
 #define FD_SETSIZE		__FD_SETSIZE
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/usb.h linux/include/linux/usb.h
--- linux.orig/include/linux/usb.h	Tue Jan 22 17:51:24 2002
+++ linux/include/linux/usb.h	Wed Dec 26 14:28:36 2001
@@ -470,40 +470,84 @@
 	iso_packet_descriptor_t iso_frame_desc[0];
 } urb_t, *purb_t;
 
-#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \
+/**
+ * FILL_CONTROL_URB - macro to help initialize a control urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @SETUP_PACKET: pointer to the setup_packet buffer
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ *
+ * Initializes a control urb with the proper information needed to submit
+ * it to a device.  This macro is depreciated, the usb_fill_control_urb()
+ * function should be used instead.
+ */
+#define FILL_CONTROL_URB(URB,DEV,PIPE,SETUP_PACKET,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->setup_packet=c;\
-	(a)->transfer_buffer=d;\
-	(a)->transfer_buffer_length=e;\
-	(a)->complete=f;\
-	(a)->context=g;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->setup_packet=SETUP_PACKET;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
     } while (0)
 
-#define FILL_BULK_URB(a,aa,b,c,d,e,f) \
+/**
+ * FILL_BULK_URB - macro to help initialize a bulk urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ *
+ * Initializes a bulk urb with the proper information needed to submit it
+ * to a device.  This macro is depreciated, the usb_fill_bulk_urb()
+ * function should be used instead.
+ */
+#define FILL_BULK_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->transfer_buffer=c;\
-	(a)->transfer_buffer_length=d;\
-	(a)->complete=e;\
-	(a)->context=f;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
     } while (0)
     
-#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \
+/**
+ * FILL_INT_URB - macro to help initialize a interrupt urb
+ * @URB: pointer to the urb to initialize.
+ * @DEV: pointer to the struct usb_device for this urb.
+ * @PIPE: the endpoint pipe
+ * @TRANSFER_BUFFER: pointer to the transfer buffer
+ * @BUFFER_LENGTH: length of the transfer buffer
+ * @COMPLETE: pointer to the usb_complete_t function
+ * @CONTEXT: what to set the urb context to.
+ * @INTERVAL: what to set the urb interval to.
+ *
+ * Initializes a interrupt urb with the proper information needed to submit
+ * it to a device.  This macro is depreciated, the usb_fill_int_urb()
+ * function should be used instead.
+ */
+#define FILL_INT_URB(URB,DEV,PIPE,TRANSFER_BUFFER,BUFFER_LENGTH,COMPLETE,CONTEXT,INTERVAL) \
     do {\
-	spin_lock_init(&(a)->lock);\
-	(a)->dev=aa;\
-	(a)->pipe=b;\
-	(a)->transfer_buffer=c;\
-	(a)->transfer_buffer_length=d;\
-	(a)->complete=e;\
-	(a)->context=f;\
-	(a)->interval=g;\
-	(a)->start_frame=-1;\
+	spin_lock_init(&(URB)->lock);\
+	(URB)->dev=DEV;\
+	(URB)->pipe=PIPE;\
+	(URB)->transfer_buffer=TRANSFER_BUFFER;\
+	(URB)->transfer_buffer_length=BUFFER_LENGTH;\
+	(URB)->complete=COMPLETE;\
+	(URB)->context=CONTEXT;\
+	(URB)->interval=INTERVAL;\
+	(URB)->start_frame=-1;\
     } while (0)
 
 #define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \
@@ -530,7 +574,105 @@
 	(a)->context=f;\
 	(a)->timeout=g;\
     } while (0)
+ 
+/**
+ * usb_fill_control_urb - initializes a control urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @setup_packet: pointer to the setup_packet buffer
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a control urb with the proper information needed to submit
+ * it to a device.
+ */
+static inline void usb_fill_control_urb (struct urb *urb,
+					 struct usb_device *dev,
+					 unsigned int pipe,
+					 unsigned char *setup_packet,
+					 void *transfer_buffer,
+					 int buffer_length,
+					 usb_complete_t complete,
+					 void *context)
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->setup_packet = setup_packet;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+}
+
+/**
+ * usb_fill_bulk_urb - macro to help initialize a bulk urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ *
+ * Initializes a bulk urb with the proper information needed to submit it
+ * to a device.
+ */
+static inline void usb_fill_bulk_urb (struct urb *urb,
+				      struct usb_device *dev,
+				      unsigned int pipe,
+				      void *transfer_buffer,
+				      int buffer_length,
+				      usb_complete_t complete,
+				      void *context)
+				      
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+}
     
+/**
+ * usb_fill_int_urb - macro to help initialize a interrupt urb
+ * @urb: pointer to the urb to initialize.
+ * @dev: pointer to the struct usb_device for this urb.
+ * @pipe: the endpoint pipe
+ * @transfer_buffer: pointer to the transfer buffer
+ * @buffer_length: length of the transfer buffer
+ * @complete: pointer to the usb_complete_t function
+ * @context: what to set the urb context to.
+ * @interval: what to set the urb interval to.
+ *
+ * Initializes a interrupt urb with the proper information needed to submit
+ * it to a device.
+ */
+static inline void usb_fill_int_urb (struct urb *urb,
+				     struct usb_device *dev,
+				     unsigned int pipe,
+				     void *transfer_buffer,
+				     int buffer_length,
+				     usb_complete_t complete,
+				     void *context,
+				     int interval)
+{
+	spin_lock_init(&urb->lock);
+	urb->dev = dev;
+	urb->pipe = pipe;
+	urb->transfer_buffer = transfer_buffer;
+	urb->transfer_buffer_length = buffer_length;
+	urb->complete = complete;
+	urb->context = context;
+	urb->interval = interval;
+	urb->start_frame = -1;
+}
+
 purb_t usb_alloc_urb(int iso_packets);
 void usb_free_urb (purb_t purb);
 int usb_submit_urb(purb_t purb);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/linux/watchdog.h linux/include/linux/watchdog.h
--- linux.orig/include/linux/watchdog.h	Fri Nov  9 22:11:15 2001
+++ linux/include/linux/watchdog.h	Thu Jan 17 22:00:03 2002
@@ -25,7 +25,8 @@
 #define	WDIOC_GETTEMP		_IOR(WATCHDOG_IOCTL_BASE, 3, int)
 #define	WDIOC_SETOPTIONS	_IOR(WATCHDOG_IOCTL_BASE, 4, int)
 #define	WDIOC_KEEPALIVE		_IOR(WATCHDOG_IOCTL_BASE, 5, int)
-#define	WDIOC_SETTIMEOUT        _IOW(WATCHDOG_IOCTL_BASE, 6, int)
+#define	WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)
+#define	WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)
 
 #define	WDIOF_UNKNOWN		-1	/* Unknown flag error */
 #define	WDIOS_UNKNOWN		-1	/* Unknown status error */
@@ -37,6 +38,7 @@
 #define	WDIOF_POWERUNDER	0x0010	/* Power bad/power fault */
 #define	WDIOF_CARDRESET		0x0020	/* Card previously reset the CPU */
 #define WDIOF_POWEROVER		0x0040	/* Power over voltage */
+#define WDIOF_SETTIMEOUT	0x0080  /* Set timeout (in seconds) */
 #define	WDIOF_KEEPALIVEPING	0x8000	/* Keep alive ping reply */
 
 #define	WDIOS_DISABLECARD	0x0001	/* Turn off the watchdog timer */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/double.h linux/include/math-emu/double.h
--- linux.orig/include/math-emu/double.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/double.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef    __MATH_EMU_DOUBLE_H__
+#define    __MATH_EMU_DOUBLE_H__
+
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel kid.  Go buy yourself a real computer."
 #endif
@@ -197,3 +200,6 @@
 #define _FP_FRAC_HIGH_RAW_D(X)	_FP_FRAC_HIGH_1(X)
 
 #endif /* W_TYPE_SIZE < 64 */
+
+
+#endif /* __MATH_EMU_DOUBLE_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/extended.h linux/include/math-emu/extended.h
--- linux.orig/include/math-emu/extended.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/extended.h	Mon Jan 14 16:19:56 2002
@@ -19,6 +19,10 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+
+#ifndef    __MATH_EMU_EXTENDED_H__
+#define    __MATH_EMU_EXTENDED_H__
+
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel, kid. Go buy yourself a real computer."
 #endif
@@ -388,3 +392,5 @@
 #define _FP_FRAC_HIGH_RAW_E(X)	(X##_f0)
 
 #endif /* not _FP_W_TYPE_SIZE < 64 */
+
+#endif /* __MATH_EMU_EXTENDED_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/op-1.h linux/include/math-emu/op-1.h
--- linux.orig/include/math-emu/op-1.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/op-1.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef    __MATH_EMU_OP_1_H__
+#define    __MATH_EMU_OP_1_H__
+
 #define _FP_FRAC_DECL_1(X)	_FP_W_TYPE X##_f
 #define _FP_FRAC_COPY_1(D,S)	(D##_f = S##_f)
 #define _FP_FRAC_SET_1(X,I)	(X##_f = I)
@@ -295,3 +298,5 @@
     else								\
       D##_f <<= _FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs;		\
   } while (0)
+
+#endif /* __MATH_EMU_OP_1_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/op-2.h linux/include/math-emu/op-2.h
--- linux.orig/include/math-emu/op-2.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/op-2.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef __MATH_EMU_OP_2_H__
+#define __MATH_EMU_OP_2_H__
+
 #define _FP_FRAC_DECL_2(X)	_FP_W_TYPE X##_f0, X##_f1
 #define _FP_FRAC_COPY_2(D,S)	(D##_f0 = S##_f0, D##_f1 = S##_f1)
 #define _FP_FRAC_SET_2(X,I)	__FP_FRAC_SET_2(X, I)
@@ -606,3 +609,4 @@
     _FP_FRAC_SLL_2(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
   } while (0)
 
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/op-4.h linux/include/math-emu/op-4.h
--- linux.orig/include/math-emu/op-4.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/op-4.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef __MATH_EMU_OP_4_H__
+#define __MATH_EMU_OP_4_H__
+
 #define _FP_FRAC_DECL_4(X)	_FP_W_TYPE X##_f[4]
 #define _FP_FRAC_COPY_4(D,S)			\
   (D##_f[0] = S##_f[0], D##_f[1] = S##_f[1],	\
@@ -659,3 +662,4 @@
      _FP_FRAC_SLL_4(D, (_FP_WFRACBITS_##dfs - _FP_WFRACBITS_##sfs));	\
    } while (0)
 
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/op-8.h linux/include/math-emu/op-8.h
--- linux.orig/include/math-emu/op-8.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/op-8.h	Mon Jan 14 16:19:56 2002
@@ -21,6 +21,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef __MATH_EMU_OP_8_H__
+#define __MATH_EMU_OP_8_H__
+
 /* We need just a few things from here for op-4, if we ever need some
    other macros, they can be added. */
 #define _FP_FRAC_DECL_8(X)	_FP_W_TYPE X##_f[8]
@@ -101,3 +104,4 @@
     X##_f[0] |= (_s != 0);						\
   } while (0)
 
+#endif
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/op-common.h linux/include/math-emu/op-common.h
--- linux.orig/include/math-emu/op-common.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/op-common.h	Mon Jan 14 16:19:56 2002
@@ -21,6 +21,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef __MATH_EMU_OP_COMMON_H__
+#define __MATH_EMU_OP_COMMON_H__
+
 #define _FP_DECL(wc, X)			\
   _FP_I_TYPE X##_c, X##_s, X##_e;	\
   _FP_FRAC_DECL_##wc(X)
@@ -846,3 +849,4 @@
     q = n / d, r = n % d;			\
   } while (0)
 
+#endif /* __MATH_EMU_OP_COMMON_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/quad.h linux/include/math-emu/quad.h
--- linux.orig/include/math-emu/quad.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/quad.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef  __MATH_EMU_QUAD_H__
+#define  __MATH_EMU_QUAD_H__
+
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel, kid. Go buy yourself a real computer."
 #endif
@@ -201,3 +204,5 @@
 #define _FP_FRAC_HIGH_RAW_Q(X)	_FP_FRAC_HIGH_2(X)
 
 #endif /* not _FP_W_TYPE_SIZE < 64 */
+
+#endif /* __MATH_EMU_QUAD_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/single.h linux/include/math-emu/single.h
--- linux.orig/include/math-emu/single.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/single.h	Mon Jan 14 16:19:56 2002
@@ -22,6 +22,9 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+#ifndef    __MATH_EMU_SINGLE_H__
+#define    __MATH_EMU_SINGLE_H__
+
 #if _FP_W_TYPE_SIZE < 32
 #error "Here's a nickel kid.  Go buy yourself a real computer."
 #endif
@@ -109,3 +112,5 @@
 
 #define _FP_FRAC_HIGH_S(X)	_FP_FRAC_HIGH_1(X)
 #define _FP_FRAC_HIGH_RAW_S(X)	_FP_FRAC_HIGH_1(X)
+
+#endif /* __MATH_EMU_SINGLE_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/math-emu/soft-fp.h linux/include/math-emu/soft-fp.h
--- linux.orig/include/math-emu/soft-fp.h	Tue Jan 22 17:51:24 2002
+++ linux/include/math-emu/soft-fp.h	Mon Jan 14 16:19:56 2002
@@ -21,8 +21,8 @@
    not, write to the Free Software Foundation, Inc.,
    59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
-#ifndef SOFT_FP_H
-#define SOFT_FP_H
+#ifndef __MATH_EMU_SOFT_FP_H__
+#define __MATH_EMU_SOFT_FP_H__
 
 #include <asm/sfp-machine.h>
 
@@ -178,4 +178,4 @@
 #include <stdlib/longlong.h>
 #endif
 
-#endif
+#endif /* __MATH_EMU_SOFT_FP_H__ */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/net/inetpeer.h linux/include/net/inetpeer.h
--- linux.orig/include/net/inetpeer.h	Thu Nov 22 19:47:11 2001
+++ linux/include/net/inetpeer.h	Wed Jan 23 20:13:08 2002
@@ -1,7 +1,7 @@
 /*
  *		INETPEER - A storage for permanent information about peers
  *
- *  Version:	$Id: inetpeer.h,v 1.1 2000/01/06 00:41:51 davem Exp $
+ *  Version:	$Id: inetpeer.h,v 1.1.2.1 2002/01/12 07:53:15 davem Exp $
  *
  *  Authors:	Andrey V. Savochkin <saw@msu.ru>
  */
@@ -38,7 +38,7 @@
 extern struct inet_peer *inet_peer_unused_head;
 extern struct inet_peer **inet_peer_unused_tailp;
 /* can be called from BH context or outside */
-extern inline void	inet_putpeer(struct inet_peer *p)
+static inline void	inet_putpeer(struct inet_peer *p)
 {
 	spin_lock_bh(&inet_peer_unused_lock);
 	if (atomic_dec_and_test(&p->refcnt)) {
@@ -53,7 +53,7 @@
 
 extern spinlock_t inet_peer_idlock;
 /* can be called with or without local BH being disabled */
-extern inline __u16	inet_getid(struct inet_peer *p)
+static inline __u16	inet_getid(struct inet_peer *p)
 {
 	__u16 id;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/net/route.h linux/include/net/route.h
--- linux.orig/include/net/route.h	Thu Nov 22 19:47:15 2001
+++ linux/include/net/route.h	Mon Feb  4 16:46:26 2002
@@ -30,6 +30,7 @@
 #include <linux/in_route.h>
 #include <linux/rtnetlink.h>
 #include <linux/route.h>
+#include <linux/ip.h>
 #include <linux/cache.h>
 
 #ifndef __KERNEL__
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/include/scsi/scsi.h linux/include/scsi/scsi.h
--- linux.orig/include/scsi/scsi.h	Fri Apr 27 20:59:19 2001
+++ linux/include/scsi/scsi.h	Mon Feb  4 19:11:16 2002
@@ -130,6 +130,7 @@
 
 #define TYPE_DISK           0x00
 #define TYPE_TAPE           0x01
+#define TYPE_PRINTER        0x02
 #define TYPE_PROCESSOR      0x03    /* HP scanners use this */
 #define TYPE_WORM           0x04    /* Treated as ROM by our system */
 #define TYPE_ROM            0x05
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/init/main.c linux/init/main.c
--- linux.orig/init/main.c	Fri Dec 21 17:42:04 2001
+++ linux/init/main.c	Wed Jan 23 20:55:51 2002
@@ -106,6 +106,9 @@
 #if defined(CONFIG_SYSVIPC)
 extern void ipc_init(void);
 #endif
+#ifdef CONFIG_PERFMON
+extern void perfmon_init(void);
+#endif
 
 /*
  * Boot command-line arguments
@@ -593,6 +596,9 @@
 	kmem_cache_sizes_init();
 	pgtable_cache_init();
 
+#ifdef CONFIG_PERFMON
+	perfmon_init();
+#endif
 	mempages = num_physpages;
 
 	fork_init(mempages);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/exec_domain.c linux/kernel/exec_domain.c
--- linux.orig/kernel/exec_domain.c	Fri Dec 21 17:42:04 2001
+++ linux/kernel/exec_domain.c	Mon Feb  4 17:38:23 2002
@@ -4,7 +4,7 @@
  * We group personalities into execution domains which have their
  * own handlers for kernel entry points, signal mapping, etc...
  *
- * 2001-05-06	Complete rewrite,  Christoph Hellwig (hch@caldera.de)
+ * 2001-05-06	Complete rewrite,  Christoph Hellwig (hch@infradead.org)
  */
 
 #include <linux/config.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/exit.c linux/kernel/exit.c
--- linux.orig/kernel/exit.c	Tue Jan 22 17:51:24 2002
+++ linux/kernel/exit.c	Wed Dec 26 16:16:25 2001
@@ -150,21 +150,34 @@
 }
 
 /*
- * When we die, we re-parent all our children to
+ * When we die, we re-parent all our children.
+ * Try to give them to another thread in our process
+ * group, and if no such member exists, give it to
  * the global child reaper process (ie "init")
  */
 static inline void forget_original_parent(struct task_struct * father)
 {
-	struct task_struct * p;
+	struct task_struct * p, *reaper;
 
 	read_lock(&tasklist_lock);
 
+	/* Next in our thread group */
+	reaper = next_thread(father);
+	if (reaper == father)
+		reaper = child_reaper;
+
 	for_each_task(p) {
 		if (p->p_opptr == father) {
 			/* We dont want people slaying init */
 			p->exit_signal = SIGCHLD;
 			p->self_exec_id++;
-			p->p_opptr = child_reaper;
+
+			/* Make sure we're not reparenting to ourselves */
+			if (p == reaper)
+				p->p_opptr = child_reaper;
+			else
+				p->p_opptr = reaper;
+
 			if (p->pdeath_signal) send_sig(p->pdeath_signal, p, 0);
 		}
 	}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/fork.c linux/kernel/fork.c
--- linux.orig/kernel/fork.c	Tue Jan 22 17:51:24 2002
+++ linux/kernel/fork.c	Thu Jan 10 19:46:27 2002
@@ -219,6 +219,7 @@
 	init_rwsem(&mm->mmap_sem);
 	mm->page_table_lock = SPIN_LOCK_UNLOCKED;
 	mm->pgd = pgd_alloc(mm);
+	mm->def_flags = 0;
 	if (mm->pgd)
 		return mm;
 	free_mm(mm);
@@ -585,7 +586,14 @@
 	*p = *current;
 
 	retval = -EAGAIN;
-	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur)
+	/*
+	 * Check if we are over our maximum process limit, but be sure to
+	 * exclude root. This is needed to make it possible for login and
+	 * friends to set the per-user process limit to something lower
+	 * than the amount of processes root is running. -- Rik
+	 */
+	if (atomic_read(&p->user->processes) >= p->rlim[RLIMIT_NPROC].rlim_cur
+	              && !capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RESOURCE))
 		goto bad_fork_free;
 
 	atomic_inc(&p->user->__count);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/ksyms.c linux/kernel/ksyms.c
--- linux.orig/kernel/ksyms.c	Tue Jan 22 17:51:24 2002
+++ linux/kernel/ksyms.c	Wed Feb  6 21:06:42 2002
@@ -95,7 +95,6 @@
 EXPORT_SYMBOL(alloc_pages_node);
 EXPORT_SYMBOL(__get_free_pages);
 EXPORT_SYMBOL(get_zeroed_page);
-EXPORT_SYMBOL(page_cache_release);
 EXPORT_SYMBOL(__free_pages);
 EXPORT_SYMBOL(free_pages);
 EXPORT_SYMBOL(num_physpages);
@@ -201,15 +200,16 @@
 EXPORT_SYMBOL(__wait_on_buffer);
 EXPORT_SYMBOL(___wait_on_page);
 EXPORT_SYMBOL(generic_direct_IO);
+EXPORT_SYMBOL(discard_bh_page);
 EXPORT_SYMBOL(block_write_full_page);
 EXPORT_SYMBOL(block_read_full_page);
 EXPORT_SYMBOL(block_prepare_write);
 EXPORT_SYMBOL(block_sync_page);
+EXPORT_SYMBOL(generic_cont_expand);
 EXPORT_SYMBOL(cont_prepare_write);
 EXPORT_SYMBOL(generic_commit_write);
 EXPORT_SYMBOL(block_truncate_page);
 EXPORT_SYMBOL(generic_block_bmap);
-EXPORT_SYMBOL(waitfor_one_page);
 EXPORT_SYMBOL(generic_file_read);
 EXPORT_SYMBOL(do_generic_file_read);
 EXPORT_SYMBOL(generic_file_write);
@@ -257,6 +257,7 @@
 EXPORT_SYMBOL(grab_cache_page);
 EXPORT_SYMBOL(grab_cache_page_nowait);
 EXPORT_SYMBOL(read_cache_page);
+EXPORT_SYMBOL(set_page_dirty);
 EXPORT_SYMBOL(vfs_readlink);
 EXPORT_SYMBOL(vfs_follow_link);
 EXPORT_SYMBOL(page_readlink);
@@ -354,8 +355,8 @@
 EXPORT_SYMBOL(del_timer);
 EXPORT_SYMBOL(request_irq);
 EXPORT_SYMBOL(free_irq);
-#if !defined(CONFIG_ARCH_S390)
-EXPORT_SYMBOL(irq_stat);	/* No separate irq_stat for s390, it is part of PSA */
+#if !defined(CONFIG_IA64)	/* irq_stat is part of struct cpuinfo_ia64 */
+EXPORT_SYMBOL(irq_stat);
 #endif
 
 /* waitqueue handling */
@@ -460,7 +461,9 @@
 EXPORT_SYMBOL(kdevname);
 EXPORT_SYMBOL(bdevname);
 EXPORT_SYMBOL(cdevname);
+EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(simple_strtoul);
+EXPORT_SYMBOL(simple_strtoull);
 EXPORT_SYMBOL(system_utsname);	/* UTS data */
 EXPORT_SYMBOL(uts_sem);		/* UTS semaphore */
 #ifndef __mips__
@@ -529,9 +532,6 @@
 
 /* binfmt_aout */
 EXPORT_SYMBOL(get_write_access);
-
-/* time */
-EXPORT_SYMBOL(get_fast_time);
 
 /* library functions */
 EXPORT_SYMBOL(strnicmp);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/printk.c linux/kernel/printk.c
--- linux.orig/kernel/printk.c	Tue Jan 22 17:51:24 2002
+++ linux/kernel/printk.c	Wed Dec 26 15:25:28 2001
@@ -16,6 +16,7 @@
  *	01Mar01 Andrew Morton <andrewm@uow.edu.au>
  */
 
+#include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/tty.h>
 #include <linux/tty_driver.h>
@@ -30,6 +31,8 @@
 
 #ifdef CONFIG_MULTIQUAD
 #define LOG_BUF_LEN	(65536)
+#elif defined(CONFIG_ARCH_S390)
+#define LOG_BUF_LEN	(131072)
 #elif defined(CONFIG_SMP)
 #define LOG_BUF_LEN	(32768)
 #else	
@@ -51,11 +54,12 @@
 
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
 
-/* Keep together for sysctl support */
-int console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
-int default_message_loglevel = DEFAULT_MESSAGE_LOGLEVEL;
-int minimum_console_loglevel = MINIMUM_CONSOLE_LOGLEVEL;
-int default_console_loglevel = DEFAULT_CONSOLE_LOGLEVEL;
+int console_printk[4] = {
+	DEFAULT_CONSOLE_LOGLEVEL,	/* console_loglevel */
+	DEFAULT_MESSAGE_LOGLEVEL,	/* default_message_loglevel */
+	MINIMUM_CONSOLE_LOGLEVEL,	/* minimum_console_loglevel */
+	DEFAULT_CONSOLE_LOGLEVEL,	/* default_console_loglevel */
+};
 
 int oops_in_progress;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/ptrace.c linux/kernel/ptrace.c
--- linux.orig/kernel/ptrace.c	Fri Dec 21 17:42:04 2001
+++ linux/kernel/ptrace.c	Mon Feb  4 19:12:49 2002
@@ -173,6 +173,7 @@
 		put_page(page);
 		len -= bytes;
 		buf += bytes;
+		addr += bytes;
 	}
 	up_read(&mm->mmap_sem);
 	mmput(mm);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/sys.c linux/kernel/sys.c
--- linux.orig/kernel/sys.c	Tue Sep 18 21:10:43 2001
+++ linux/kernel/sys.c	Wed Jan 23 20:55:51 2002
@@ -1245,6 +1245,22 @@
 #endif
 			break;
 
+	        case PR_SET_FPEMU:
+#ifdef SET_FPEMU_CTL
+			error = SET_FPEMU_CTL(current, arg2);
+#else
+			error = -EINVAL;
+#endif
+			break;
+
+	        case PR_GET_FPEMU:
+#ifdef GET_FPEMU_CTL
+			error = GET_FPEMU_CTL(current, arg2);
+#else
+			error = -EINVAL;
+#endif
+			break;
+
 		case PR_GET_KEEPCAPS:
 			if (current->keep_capabilities)
 				error = 1;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/kernel/time.c linux/kernel/time.c
--- linux.orig/kernel/time.c	Fri Dec 21 17:42:04 2001
+++ linux/kernel/time.c	Mon Feb  4 16:55:14 2002
@@ -36,24 +36,6 @@
  */
 struct timezone sys_tz;
 
-static void do_normal_gettime(struct timeval * tm)
-{
-        *tm=xtime;
-}
-
-void (*do_get_fast_time)(struct timeval *) = do_normal_gettime;
-
-/*
- * Generic way to access 'xtime' (the current time of day).
- * This can be changed if the platform provides a more accurate (and fast!) 
- * version.
- */
-
-void get_fast_time(struct timeval * t)
-{
-	do_get_fast_time(t);
-}
-
 /* The xtime_lock is not only serializing the xtime read/writes but it's also
    serializing all accesses to the global NTP variables now. */
 extern rwlock_t xtime_lock;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/filemap.c linux/mm/filemap.c
--- linux.orig/mm/filemap.c	Tue Jan 22 17:51:24 2002
+++ linux/mm/filemap.c	Tue Jan 22 23:04:47 2002
@@ -454,41 +454,6 @@
 	return page;
 }
 
-/*
- * By the time this is called, the page is locked and
- * we don't have to worry about any races any more.
- *
- * Start the IO..
- */
-static int writeout_one_page(struct page *page)
-{
-	struct buffer_head *bh, *head = page->buffers;
-
-	bh = head;
-	do {
-		if (buffer_locked(bh) || !buffer_dirty(bh) || !buffer_uptodate(bh))
-			continue;
-
-		bh->b_flushtime = jiffies;
-		ll_rw_block(WRITE, 1, &bh);	
-	} while ((bh = bh->b_this_page) != head);
-	return 0;
-}
-
-int waitfor_one_page(struct page *page)
-{
-	int error = 0;
-	struct buffer_head *bh, *head = page->buffers;
-
-	bh = head;
-	do {
-		wait_on_buffer(bh);
-		if (buffer_req(bh) && !buffer_uptodate(bh))
-			error = -EIO;
-	} while ((bh = bh->b_this_page) != head);
-	return error;
-}
-
 static int do_buffer_fdatasync(struct list_head *head, unsigned long start, unsigned long end, int (*fn)(struct page *))
 {
 	struct list_head *curr;
@@ -582,8 +547,9 @@
  *      @mapping: address space structure to write
  *
  */
-void filemap_fdatasync(struct address_space * mapping)
+int filemap_fdatasync(struct address_space * mapping)
 {
+	int ret = 0;
 	int (*writepage)(struct page *) = mapping->a_ops->writepage;
 
 	spin_lock(&pagecache_lock);
@@ -603,8 +569,11 @@
 		lock_page(page);
 
 		if (PageDirty(page)) {
+			int err;
 			ClearPageDirty(page);
-			writepage(page);
+			err = writepage(page);
+			if (err && !ret)
+				ret = err;
 		} else
 			UnlockPage(page);
 
@@ -612,6 +581,7 @@
 		spin_lock(&pagecache_lock);
 	}
 	spin_unlock(&pagecache_lock);
+	return ret;
 }
 
 /**
@@ -621,8 +591,10 @@
  *      @mapping: address space structure to wait for
  *
  */
-void filemap_fdatawait(struct address_space * mapping)
+int filemap_fdatawait(struct address_space * mapping)
 {
+	int ret = 0;
+
 	spin_lock(&pagecache_lock);
 
         while (!list_empty(&mapping->locked_pages)) {
@@ -638,11 +610,14 @@
 		spin_unlock(&pagecache_lock);
 
 		___wait_on_page(page);
+		if (PageError(page))
+			ret = -EIO;
 
 		page_cache_release(page);
 		spin_lock(&pagecache_lock);
 	}
 	spin_unlock(&pagecache_lock);
+	return ret;
 }
 
 /*
@@ -941,7 +916,6 @@
 	spin_unlock(&pagecache_lock);
 	if (!page) {
 		struct page *newpage = alloc_page(gfp_mask);
-		page = NULL;
 		if (newpage) {
 			spin_lock(&pagecache_lock);
 			page = __find_lock_page_helper(mapping, index, *hash);
@@ -1520,12 +1494,14 @@
 		goto out_free;
 
 	/*
-	 * Flush to disk exlusively the _data_, metadata must remains
+	 * Flush to disk exclusively the _data_, metadata must remain
 	 * completly asynchronous or performance will go to /dev/null.
 	 */
-	filemap_fdatasync(mapping);
-	retval = fsync_inode_data_buffers(inode);
-	filemap_fdatawait(mapping);
+	retval = filemap_fdatasync(mapping);
+	if (retval == 0)
+		retval = fsync_inode_data_buffers(inode);
+	if (retval == 0)
+		retval = filemap_fdatawait(mapping);
 	if (retval < 0)
 		goto out_free;
 
@@ -2142,26 +2118,45 @@
  * The msync() system call.
  */
 
+/*
+ * MS_SYNC syncs the entire file - including mappings.
+ *
+ * MS_ASYNC initiates writeout of just the dirty mapped data.
+ * This provides no guarantee of file integrity - things like indirect
+ * blocks may not have started writeout.  MS_ASYNC is primarily useful
+ * where the application knows that it has finished with the data and
+ * wishes to intelligently schedule its own I/O traffic.
+ */
 static int msync_interval(struct vm_area_struct * vma,
 	unsigned long start, unsigned long end, int flags)
 {
+	int ret = 0;
 	struct file * file = vma->vm_file;
+
 	if (file && (vma->vm_flags & VM_SHARED)) {
-		int error;
-		error = filemap_sync(vma, start, end-start, flags);
+		ret = filemap_sync(vma, start, end-start, flags);
 
-		if (!error && (flags & MS_SYNC)) {
+		if (!ret && (flags & (MS_SYNC|MS_ASYNC))) {
 			struct inode * inode = file->f_dentry->d_inode;
+
 			down(&inode->i_sem);
-			filemap_fdatasync(inode->i_mapping);
-			if (file->f_op && file->f_op->fsync)
-				error = file->f_op->fsync(file, file->f_dentry, 1);
-			filemap_fdatawait(inode->i_mapping);
+			ret = filemap_fdatasync(inode->i_mapping);
+			if (flags & MS_SYNC) {
+				int err;
+
+				if (file->f_op && file->f_op->fsync) {
+					err = file->f_op->fsync(file, file->f_dentry, 1);
+					if (err && !ret)
+						ret = err;
+				}
+				err = filemap_fdatawait(inode->i_mapping);
+				if (err && !ret)
+					ret = err;
+			}
 			up(&inode->i_sem);
 		}
-		return error;
 	}
-	return 0;
+	return ret;
 }
 
 asmlinkage long sys_msync(unsigned long start, size_t len, int flags)
@@ -3005,7 +3000,7 @@
 		kaddr = kmap(page);
 		status = mapping->a_ops->prepare_write(file, page, offset, offset+bytes);
 		if (status)
-			goto unlock;
+			goto sync_failure;
 		page_fault = __copy_from_user(kaddr+offset, buf, bytes);
 		flush_dcache_page(page);
 		status = mapping->a_ops->commit_write(file, page, offset, offset+bytes);
@@ -3030,6 +3025,7 @@
 		if (status < 0)
 			break;
 	} while (count);
+done:
 	*ppos = pos;
 
 	if (cached_page)
@@ -3051,6 +3047,18 @@
 fail_write:
 	status = -EFAULT;
 	goto unlock;
+
+sync_failure:
+	/*
+	 * If blocksize < pagesize, prepare_write() may have instantiated a
+	 * few blocks outside i_size.  Trim these off again.
+	 */
+	kunmap(page);
+	UnlockPage(page);
+	page_cache_release(page);
+	if (pos + bytes > inode->i_size)
+		vmtruncate(inode, inode->i_size);
+	goto done;
 
 o_direct:
 	written = generic_file_direct_IO(WRITE, file, (char *) buf, count, pos);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/mmap.c linux/mm/mmap.c
--- linux.orig/mm/mmap.c	Sun Nov  4 18:17:20 2001
+++ linux/mm/mmap.c	Wed Feb 13 19:11:55 2002
@@ -620,7 +620,7 @@
 {
 	if (flags & MAP_FIXED) {
 		if (addr > TASK_SIZE - len)
-			return -EINVAL;
+			return -ENOMEM;
 		if (addr & ~PAGE_MASK)
 			return -EINVAL;
 		return addr;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/page_alloc.c linux/mm/page_alloc.c
--- linux.orig/mm/page_alloc.c	Tue Jan 22 17:51:24 2002
+++ linux/mm/page_alloc.c	Wed Jan  9 17:10:17 2002
@@ -70,6 +70,12 @@
 	struct page *base;
 	zone_t *zone;
 
+	/* Yes, think what happens when other parts of the kernel take 
+	 * a reference to a page in order to pin it for io. -ben
+	 */
+	if (PageLRU(page))
+		lru_cache_del(page);
+
 	if (page->buffers)
 		BUG();
 	if (page->mapping)
@@ -424,15 +430,6 @@
 		return (unsigned long) address;
 	}
 	return 0;
-}
-
-void page_cache_release(struct page *page)
-{
-	if (!PageReserved(page) && put_page_testzero(page)) {
-		if (PageLRU(page))
-			lru_cache_del(page);
-		__free_pages_ok(page, 0);
-	}
 }
 
 void __free_pages(struct page *page, unsigned int order)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/swapfile.c linux/mm/swapfile.c
--- linux.orig/mm/swapfile.c	Fri Dec 21 17:42:05 2001
+++ linux/mm/swapfile.c	Mon Jan 21 17:13:47 2002
@@ -344,7 +344,7 @@
 	if (page) {
 		page_cache_get(page);
 		/* Only cache user (+us), or swap space full? Free it! */
-		if (page_count(page) == 2 || vm_swap_full()) {
+		if (page_count(page) - !!page->buffers == 2 || vm_swap_full()) {
 			delete_from_swap_cache(page);
 			SetPageDirty(page);
 		}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/vmalloc.c linux/mm/vmalloc.c
--- linux.orig/mm/vmalloc.c	Tue Jan 22 17:51:24 2002
+++ linux/mm/vmalloc.c	Wed Jan  9 17:10:17 2002
@@ -164,6 +164,7 @@
 		ret = 0;
 	} while (address && (address < end));
 	spin_unlock(&init_mm.page_table_lock);
+	flush_cache_all();
 	return ret;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/mm/vmscan.c linux/mm/vmscan.c
--- linux.orig/mm/vmscan.c	Tue Jan 22 17:51:24 2002
+++ linux/mm/vmscan.c	Tue Jan  8 15:39:44 2002
@@ -537,7 +537,7 @@
 
 	spin_lock(&pagemap_lru_lock);
 	entry = active_list.prev;
-	while (nr_pages-- && entry != &active_list) {
+	while (nr_pages && entry != &active_list) {
 		struct page * page;
 
 		page = list_entry(entry, struct page, lru);
@@ -547,6 +547,8 @@
 			list_add(&page->lru, &active_list);
 			continue;
 		}
+
+		nr_pages--;
 
 		del_page_from_active_list(page);
 		add_page_to_inactive_list(page);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/Config.in linux/net/Config.in
--- linux.orig/net/Config.in	Tue Jan 22 17:51:24 2002
+++ linux/net/Config.in	Mon Jan 14 16:24:07 2002
@@ -59,7 +59,7 @@
 if [ "$CONFIG_DECNET" != "n" ]; then
    source net/decnet/Config.in
 fi
-tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE
+dep_tristate '802.1d Ethernet Bridging' CONFIG_BRIDGE $CONFIG_INET
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
    tristate 'CCITT X.25 Packet Layer (EXPERIMENTAL)' CONFIG_X25
    tristate 'LAPB Data Link Driver (EXPERIMENTAL)' CONFIG_LAPB
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/Makefile linux/net/Makefile
--- linux.orig/net/Makefile	Fri Dec 21 17:42:05 2001
+++ linux/net/Makefile	Mon Feb  4 17:38:24 2002
@@ -1,7 +1,7 @@
 #
 # Makefile for the linux networking.
 #
-# 2 Sep 2000, Christoph Hellwig <hch@caldera.de>
+# 2 Sep 2000, Christoph Hellwig <hch@infradead.org>
 # Rewritten to use lists instead of if-statements.
 #
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ax25/af_ax25.c linux/net/ax25/af_ax25.c
--- linux.orig/net/ax25/af_ax25.c	Fri Sep 14 00:16:23 2001
+++ linux/net/ax25/af_ax25.c	Wed Feb  6 20:47:54 2002
@@ -102,6 +102,7 @@
  *			Joerg(DL1BKE)		Added support for SO_BINDTODEVICE
  *			Arnaldo C. Melo		s/suser/capable(CAP_NET_ADMIN)/, some more cleanups
  *			Michal Ostrowski	Module initialization cleanup.
+ *			Jeroen(PE1RXQ)		Use sock_orphan() on release.
  */
 
 #include <linux/config.h>
@@ -1018,7 +1019,7 @@
 				sk->state                = TCP_CLOSE;
 				sk->shutdown            |= SEND_SHUTDOWN;
 				sk->state_change(sk);
-				sk->dead                 = 1;
+				sock_orphan(sk);
 				sk->destroy              = 1;
 				break;
 
@@ -1029,7 +1030,7 @@
 		sk->state     = TCP_CLOSE;
 		sk->shutdown |= SEND_SHUTDOWN;
 		sk->state_change(sk);
-		sk->dead      = 1;
+		sock_orphan(sk);
 		ax25_destroy_socket(sk->protinfo.ax25);
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br.c linux/net/bridge/br.c
--- linux.orig/net/bridge/br.c	Tue Jan 22 17:51:24 2002
+++ linux/net/bridge/br.c	Mon Jan 14 16:24:07 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br.c,v 1.46 2001/10/02 02:22:36 davem Exp $
+ *	$Id: br.c,v 1.46.2.1 2001/12/24 00:56:13 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -43,9 +43,7 @@
 	printk(KERN_INFO "NET4: Ethernet Bridge 008 for NET4.0\n");
 
 	br_handle_frame_hook = br_handle_frame;
-#ifdef CONFIG_INET
 	br_ioctl_hook = br_ioctl_deviceless_stub;
-#endif
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
 	br_fdb_get_hook = br_fdb_get;
 	br_fdb_put_hook = br_fdb_put;
@@ -62,9 +60,7 @@
 
 static void __br_clear_ioctl_hook(void)
 {
-#ifdef CONFIG_INET
 	br_ioctl_hook = NULL;
-#endif	
 }
 
 static void __exit br_deinit(void)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br_device.c linux/net/bridge/br_device.c
--- linux.orig/net/bridge/br_device.c	Tue Jan 22 17:51:24 2002
+++ linux/net/bridge/br_device.c	Mon Jan 14 16:24:07 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_device.c,v 1.5 2001/08/14 22:05:57 davem Exp $
+ *	$Id: br_device.c,v 1.5.2.1 2001/12/24 00:59:27 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -71,7 +71,7 @@
 	return 0;
 }
 
-static int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct net_bridge *br;
 	int ret;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br_fdb.c linux/net/bridge/br_fdb.c
--- linux.orig/net/bridge/br_fdb.c	Thu Nov  9 23:57:53 2000
+++ linux/net/bridge/br_fdb.c	Thu Jan 17 19:42:16 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_fdb.c,v 1.5 2000/11/08 05:16:40 davem Exp $
+ *	$Id: br_fdb.c,v 1.5.2.1 2002/01/17 00:59:01 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -292,7 +292,8 @@
 	write_lock_bh(&br->hash_lock);
 	fdb = br->hash[hash];
 	while (fdb != NULL) {
-		if (!memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
+		if (!fdb->is_local &&
+		    !memcmp(fdb->addr.addr, addr, ETH_ALEN)) {
 			__fdb_possibly_replace(fdb, source, is_local);
 			write_unlock_bh(&br->hash_lock);
 			return;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br_if.c linux/net/bridge/br_if.c
--- linux.orig/net/bridge/br_if.c	Tue Jan 22 17:51:24 2002
+++ linux/net/bridge/br_if.c	Mon Jan 14 16:24:07 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_if.c,v 1.6 2001/11/24 17:51:03 davem Exp $
+ *	$Id: br_if.c,v 1.6.2.1 2001/12/24 00:59:27 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -225,6 +225,9 @@
 
 	if (dev->flags & IFF_LOOPBACK || dev->type != ARPHRD_ETHER)
 		return -EINVAL;
+
+	if (dev->hard_start_xmit == br_dev_xmit)
+		return -ELOOP;
 
 	dev_hold(dev);
 	write_lock_bh(&br->lock);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br_input.c linux/net/bridge/br_input.c
--- linux.orig/net/bridge/br_input.c	Tue Jan 22 17:51:24 2002
+++ linux/net/bridge/br_input.c	Mon Jan 14 16:24:07 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_input.c,v 1.9 2001/08/14 22:05:57 davem Exp $
+ *	$Id: br_input.c,v 1.9.2.1 2001/12/24 04:50:05 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -46,7 +46,7 @@
 			br_pass_frame_up_finish);
 }
 
-static void __br_handle_frame(struct sk_buff *skb)
+static int br_handle_frame_finish(struct sk_buff *skb)
 {
 	struct net_bridge *br;
 	unsigned char *dest;
@@ -57,103 +57,112 @@
 	dest = skb->mac.ethernet->h_dest;
 
 	p = skb->dev->br_port;
-	br = p->br;
-	passedup = 0;
+	if (p == NULL)
+		goto err_nolock;
 
-	if (!(br->dev.flags & IFF_UP) ||
-	    p->state == BR_STATE_DISABLED)
-		goto freeandout;
+	br = p->br;
+	read_lock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
 
+	passedup = 0;
 	if (br->dev.flags & IFF_PROMISC) {
 		struct sk_buff *skb2;
 
 		skb2 = skb_clone(skb, GFP_ATOMIC);
-		if (skb2) {
+		if (skb2 != NULL) {
 			passedup = 1;
 			br_pass_frame_up(br, skb2);
 		}
 	}
 
-	if (skb->mac.ethernet->h_source[0] & 1)
-		goto freeandout;
-
-	if (!passedup &&
-	    (dest[0] & 1) &&
-	    (br->dev.flags & IFF_ALLMULTI || br->dev.mc_list != NULL)) {
-		struct sk_buff *skb2;
-
-		skb2 = skb_clone(skb, GFP_ATOMIC);
-		if (skb2) {
-			passedup = 1;
-			br_pass_frame_up(br, skb2);
-		}
-	}
-
-	if (br->stp_enabled &&
-	    !memcmp(dest, bridge_ula, 5) &&
-	    !(dest[5] & 0xF0))
-		goto handle_special_frame;
-
-	if (p->state == BR_STATE_LEARNING ||
-	    p->state == BR_STATE_FORWARDING)
-		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
-
-	if (p->state != BR_STATE_FORWARDING)
-		goto freeandout;
-
 	if (dest[0] & 1) {
-		br_flood_forward(br, skb, 1);
+		br_flood_forward(br, skb, !passedup);
 		if (!passedup)
 			br_pass_frame_up(br, skb);
-		else
-			kfree_skb(skb);
-		return;
+		goto out;
 	}
 
 	dst = br_fdb_get(br, dest);
-
 	if (dst != NULL && dst->is_local) {
 		if (!passedup)
 			br_pass_frame_up(br, skb);
 		else
 			kfree_skb(skb);
 		br_fdb_put(dst);
-		return;
+		goto out;
 	}
 
 	if (dst != NULL) {
 		br_forward(dst->dst, skb);
 		br_fdb_put(dst);
-		return;
+		goto out;
 	}
 
 	br_flood_forward(br, skb, 0);
-	return;
 
- handle_special_frame:
-	if (!dest[5]) {
-		br_stp_handle_bpdu(skb);
-		return;
-	}
+out:
+	read_unlock(&br->lock);
+	return 0;
 
- freeandout:
+err:
+	read_unlock(&br->lock);
+err_nolock:
 	kfree_skb(skb);
+	return 0;
 }
 
-static int br_handle_frame_finish(struct sk_buff *skb)
+void br_handle_frame(struct sk_buff *skb)
 {
 	struct net_bridge *br;
+	unsigned char *dest;
+	struct net_bridge_port *p;
 
-	br = skb->dev->br_port->br;
+	dest = skb->mac.ethernet->h_dest;
+
+	p = skb->dev->br_port;
+	if (p == NULL)
+		goto err_nolock;
+
+	br = p->br;
 	read_lock(&br->lock);
-	__br_handle_frame(skb);
-	read_unlock(&br->lock);
+	if (skb->dev->br_port == NULL)
+		goto err;
 
-	return 0;
-}
+	if (!(br->dev.flags & IFF_UP) ||
+	    p->state == BR_STATE_DISABLED)
+		goto err;
 
-void br_handle_frame(struct sk_buff *skb)
-{
-	NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
+	if (skb->mac.ethernet->h_source[0] & 1)
+		goto err;
+
+	if (p->state == BR_STATE_LEARNING ||
+	    p->state == BR_STATE_FORWARDING)
+		br_fdb_insert(br, p, skb->mac.ethernet->h_source, 0);
+
+	if (br->stp_enabled &&
+	    !memcmp(dest, bridge_ula, 5) &&
+	    !(dest[5] & 0xF0))
+		goto handle_special_frame;
+
+	if (p->state == BR_STATE_FORWARDING) {
+		NF_HOOK(PF_BRIDGE, NF_BR_PRE_ROUTING, skb, skb->dev, NULL,
 			br_handle_frame_finish);
+		read_unlock(&br->lock);
+		return;
+	}
+
+err:
+	read_unlock(&br->lock);
+err_nolock:
+	kfree_skb(skb);
+	return;
+
+handle_special_frame:
+	if (!dest[5]) {
+		br_stp_handle_bpdu(skb);
+		return;
+	}
+
+	kfree_skb(skb);
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/bridge/br_private.h linux/net/bridge/br_private.h
--- linux.orig/net/bridge/br_private.h	Tue Jan 22 17:51:24 2002
+++ linux/net/bridge/br_private.h	Mon Jan 14 16:24:07 2002
@@ -4,7 +4,7 @@
  *	Authors:
  *	Lennert Buytenhek		<buytenh@gnu.org>
  *
- *	$Id: br_private.h,v 1.6 2001/06/01 09:28:28 davem Exp $
+ *	$Id: br_private.h,v 1.6.2.1 2001/12/24 00:59:27 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -121,6 +121,7 @@
 
 /* br_device.c */
 extern void br_dev_setup(struct net_device *dev);
+extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
 
 /* br_fdb.c */
 extern void br_fdb_changeaddr(struct net_bridge_port *p,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/core/dev.c linux/net/core/dev.c
--- linux.orig/net/core/dev.c	Wed Nov  7 22:39:36 2001
+++ linux/net/core/dev.c	Mon Feb  4 16:55:14 2002
@@ -238,7 +238,7 @@
 
 #ifdef CONFIG_NET_FASTROUTE
 	/* Hack to detect packet socket */
-	if (pt->data) {
+	if ((pt->data) && ((int)(pt->data)!=1)) {
 		netdev_fastroute_obstacles++;
 		dev_clear_fastroute(pt->dev);
 	}
@@ -875,7 +875,7 @@
 void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
 {
 	struct packet_type *ptype;
-	get_fast_time(&skb->stamp);
+	do_gettimeofday(&skb->stamp);
 
 	br_read_lock(BR_NETPROTO_LOCK);
 	for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next) 
@@ -1218,7 +1218,7 @@
 	unsigned long flags;
 
 	if (skb->stamp.tv_sec == 0)
-		get_fast_time(&skb->stamp);
+		do_gettimeofday(&skb->stamp);
 
 	/* The code is rearranged so that the path is the most
 	   short when CPU is congested, but is still operating.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/core/netfilter.c linux/net/core/netfilter.c
--- linux.orig/net/core/netfilter.c	Fri Apr 27 21:15:01 2001
+++ linux/net/core/netfilter.c	Thu Feb  7 16:52:23 2002
@@ -20,6 +20,10 @@
 #include <linux/if.h>
 #include <linux/netdevice.h>
 #include <linux/brlock.h>
+#include <linux/inetdevice.h>
+#include <net/sock.h>
+#include <net/route.h>
+#include <linux/ip.h>
 
 #define __KERNEL_SYSCALLS__
 #include <linux/unistd.h>
@@ -552,6 +556,73 @@
 	kfree(info);
 	return;
 }
+
+#ifdef CONFIG_INET
+/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
+int ip_route_me_harder(struct sk_buff **pskb)
+{
+	struct iphdr *iph = (*pskb)->nh.iph;
+	struct rtable *rt;
+	struct rt_key key = { dst:iph->daddr,
+			      src:iph->saddr,
+			      oif:(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			      tos:RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+			      fwmark:(*pskb)->nfmark
+#endif
+			    };
+	struct net_device *dev_src = NULL;
+	int err;
+
+	/* accomodate ip_route_output_slow(), which expects the key src to be
+	   0 or a local address; however some non-standard hacks like
+	   ipt_REJECT.c:send_reset() can cause packets with foreign
+           saddr to be appear on the NF_IP_LOCAL_OUT hook -MB */
+	if(key.src && !(dev_src = ip_dev_find(key.src)))
+		key.src = 0;
+
+	if ((err=ip_route_output_key(&rt, &key)) != 0) {
+		printk("route_me_harder: ip_route_output_key(dst=%u.%u.%u.%u, src=%u.%u.%u.%u, oif=%d, tos=0x%x, fwmark=0x%lx) error %d\n",
+			NIPQUAD(iph->daddr), NIPQUAD(iph->saddr),
+			(*pskb)->sk ? (*pskb)->sk->bound_dev_if : 0,
+			RT_TOS(iph->tos)|RTO_CONN,
+#ifdef CONFIG_IP_ROUTE_FWMARK
+			(*pskb)->nfmark,
+#else
+			0UL,
+#endif
+			err);
+		goto out;
+	}
+
+	/* Drop old route. */
+	dst_release((*pskb)->dst);
+
+	(*pskb)->dst = &rt->u.dst;
+
+	/* Change in oif may mean change in hh_len. */
+	if (skb_headroom(*pskb) < (*pskb)->dst->dev->hard_header_len) {
+		struct sk_buff *nskb;
+
+		nskb = skb_realloc_headroom(*pskb,
+					    (*pskb)->dst->dev->hard_header_len);
+		if (!nskb) {
+			err = -ENOMEM;
+			goto out;
+		}
+		if ((*pskb)->sk)
+			skb_set_owner_w(nskb, (*pskb)->sk);
+		kfree_skb(*pskb);
+		*pskb = nskb;
+	}
+
+out:
+	if (dev_src)
+		dev_put(dev_src);
+
+	return err;
+}
+#endif /*CONFIG_INET*/
 
 /* This does not belong here, but ipt_REJECT needs it if connection
    tracking in use: without this, connection may not be in hash table,
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/core/rtnetlink.c linux/net/core/rtnetlink.c
--- linux.orig/net/core/rtnetlink.c	Fri Dec 21 17:42:05 2001
+++ linux/net/core/rtnetlink.c	Wed Jan 23 19:57:38 2002
@@ -45,7 +45,6 @@
 #include <net/protocol.h>
 #include <net/arp.h>
 #include <net/route.h>
-#include <net/tcp.h>
 #include <net/udp.h>
 #include <net/sock.h>
 #include <net/pkt_sched.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/fib_semantics.c linux/net/ipv4/fib_semantics.c
--- linux.orig/net/ipv4/fib_semantics.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/fib_semantics.c	Wed Jan 23 20:13:08 2002
@@ -5,7 +5,7 @@
  *
  *		IPv4 Forwarding Information Base: semantics.
  *
- * Version:	$Id: fib_semantics.c,v 1.18 2001/10/31 21:55:54 davem Exp $
+ * Version:	$Id: fib_semantics.c,v 1.18.2.2 2002/01/12 07:54:15 davem Exp $
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
  *
@@ -134,7 +134,7 @@
 	write_unlock(&fib_info_lock);
 }
 
-extern __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
+static __inline__ int nh_comp(const struct fib_info *fi, const struct fib_info *ofi)
 {
 	const struct fib_nh *onh = ofi->fib_nh;
 
@@ -155,7 +155,7 @@
 	return 0;
 }
 
-extern __inline__ struct fib_info * fib_find_info(const struct fib_info *nfi)
+static __inline__ struct fib_info * fib_find_info(const struct fib_info *nfi)
 {
 	for_fib_info() {
 		if (fi->fib_nhs != nfi->fib_nhs)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- linux.orig/net/ipv4/icmp.c	Tue Jan 22 17:51:24 2002
+++ linux/net/ipv4/icmp.c	Tue Jan 22 18:10:05 2002
@@ -3,7 +3,7 @@
  *	
  *		Alan Cox, <alan@redhat.com>
  *
- *	Version: $Id: icmp.c,v 1.82 2001/11/01 23:44:31 davem Exp $
+ *	Version: $Id: icmp.c,v 1.82.2.1 2001/12/13 08:59:27 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *	modify it under the terms of the GNU General Public License
@@ -154,8 +154,8 @@
  * 	it's bit position.
  *
  *	default: 
- *	dest unreachable (0x03), source quench (0x04),
- *	time exceeded (0x11), parameter problem (0x12)
+ *	dest unreachable (3), source quench (4),
+ *	time exceeded (11), parameter problem (12)
  */
 
 int sysctl_icmp_ratelimit = 1*HZ;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- linux.orig/net/ipv4/ip_fragment.c	Fri Sep  7 18:01:21 2001
+++ linux/net/ipv4/ip_fragment.c	Wed Jan 23 20:13:08 2002
@@ -5,7 +5,7 @@
  *
  *		The IP fragmentation functionality.
  *		
- * Version:	$Id: ip_fragment.c,v 1.58 2001/09/01 00:31:50 davem Exp $
+ * Version:	$Id: ip_fragment.c,v 1.58.2.1 2002/01/12 07:53:15 davem Exp $
  *
  * Authors:	Fred N. van Kempen <waltje@uWalt.NL.Mugnet.ORG>
  *		Alan Cox <Alan.Cox@linux.org>
@@ -130,19 +130,19 @@
 atomic_t ip_frag_mem = ATOMIC_INIT(0);	/* Memory used for fragments */
 
 /* Memory Tracking Functions. */
-extern __inline__ void frag_kfree_skb(struct sk_buff *skb)
+static __inline__ void frag_kfree_skb(struct sk_buff *skb)
 {
 	atomic_sub(skb->truesize, &ip_frag_mem);
 	kfree_skb(skb);
 }
 
-extern __inline__ void frag_free_queue(struct ipq *qp)
+static __inline__ void frag_free_queue(struct ipq *qp)
 {
 	atomic_sub(sizeof(struct ipq), &ip_frag_mem);
 	kfree(qp);
 }
 
-extern __inline__ struct ipq *frag_alloc_queue(void)
+static __inline__ struct ipq *frag_alloc_queue(void)
 {
 	struct ipq *qp = kmalloc(sizeof(struct ipq), GFP_ATOMIC);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- linux.orig/net/ipv4/ip_input.c	Wed Nov  7 22:39:36 2001
+++ linux/net/ipv4/ip_input.c	Mon Feb  4 16:38:42 2002
@@ -5,7 +5,7 @@
  *
  *		The Internet Protocol (IP) module.
  *
- * Version:	$Id: ip_input.c,v 1.54 2001/11/06 22:33:52 davem Exp $
+ * Version:	$Id: ip_input.c,v 1.54.2.1 2002/01/12 07:39:23 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -224,8 +224,7 @@
 	nf_debug_ip_local_deliver(skb);
 #endif /*CONFIG_NETFILTER_DEBUG*/
 
-	/* Pull out additionl 8 bytes to save some space in protocols. */
-	if (!pskb_may_pull(skb, ihl+8))
+	if (!pskb_may_pull(skb, ihl))
 		goto out;
 	__skb_pull(skb, ihl);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/ipconfig.c linux/net/ipv4/ipconfig.c
--- linux.orig/net/ipv4/ipconfig.c	Tue Jan 22 17:51:24 2002
+++ linux/net/ipv4/ipconfig.c	Mon Jan 14 16:24:07 2002
@@ -1,5 +1,5 @@
 /*
- *  $Id: ipconfig.c,v 1.43 2001/11/21 20:27:34 davem Exp $
+ *  $Id: ipconfig.c,v 1.43.2.1 2001/12/13 10:39:53 davem Exp $
  *
  *  Automatic Configuration of IP -- use DHCP, BOOTP, RARP, or
  *  user-supplied information to configure own IP address and routes.
@@ -47,6 +47,7 @@
 #include <linux/route.h>
 #include <linux/udp.h>
 #include <linux/proc_fs.h>
+#include <linux/major.h>
 #include <net/arp.h>
 #include <net/ip.h>
 #include <net/ipconfig.h>
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/Config.in linux/net/ipv4/netfilter/Config.in
--- linux.orig/net/ipv4/netfilter/Config.in	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/netfilter/Config.in	Wed Jan 23 20:24:37 2002
@@ -21,6 +21,7 @@
   dep_tristate '  netfilter MARK match support' CONFIG_IP_NF_MATCH_MARK $CONFIG_IP_NF_IPTABLES
   dep_tristate '  Multiple port match support' CONFIG_IP_NF_MATCH_MULTIPORT $CONFIG_IP_NF_IPTABLES
   dep_tristate '  TOS match support' CONFIG_IP_NF_MATCH_TOS $CONFIG_IP_NF_IPTABLES
+  dep_tristate '  AH/ESP match support' CONFIG_IP_NF_MATCH_AH_ESP $CONFIG_IP_NF_IPTABLES
   dep_tristate '  LENGTH match support' CONFIG_IP_NF_MATCH_LENGTH $CONFIG_IP_NF_IPTABLES
   dep_tristate '  TTL match support' CONFIG_IP_NF_MATCH_TTL $CONFIG_IP_NF_IPTABLES
   dep_tristate '  tcpmss match support' CONFIG_IP_NF_MATCH_TCPMSS $CONFIG_IP_NF_IPTABLES
@@ -74,6 +75,9 @@
     dep_tristate '    MARK target support' CONFIG_IP_NF_TARGET_MARK $CONFIG_IP_NF_MANGLE
   fi
   dep_tristate '  LOG target support' CONFIG_IP_NF_TARGET_LOG $CONFIG_IP_NF_IPTABLES
+  if [ "$CONFIG_NETLINK" != "n" ]; then
+     dep_tristate '  ULOG target support' CONFIG_IP_NF_TARGET_ULOG $CONFIG_NETLINK $CONFIG_IP_NF_IPTABLES
+  fi
   dep_tristate '  TCPMSS target support' CONFIG_IP_NF_TARGET_TCPMSS $CONFIG_IP_NF_IPTABLES
 fi
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/Makefile linux/net/ipv4/netfilter/Makefile
--- linux.orig/net/ipv4/netfilter/Makefile	Tue Oct 30 23:08:12 2001
+++ linux/net/ipv4/netfilter/Makefile	Wed Jan 23 20:24:37 2002
@@ -56,6 +56,7 @@
 obj-$(CONFIG_IP_NF_MATCH_MULTIPORT) += ipt_multiport.o
 obj-$(CONFIG_IP_NF_MATCH_OWNER) += ipt_owner.o
 obj-$(CONFIG_IP_NF_MATCH_TOS) += ipt_tos.o
+obj-$(CONFIG_IP_NF_MATCH_AH_ESP) += ipt_ah.o ipt_esp.o
 
 obj-$(CONFIG_IP_NF_MATCH_LENGTH) += ipt_length.o
 
@@ -73,6 +74,7 @@
 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
 obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o
 obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o
+obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
 obj-$(CONFIG_IP_NF_TARGET_TCPMSS) += ipt_TCPMSS.o
 
 # backwards compatibility 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_conntrack_irc.c linux/net/ipv4/netfilter/ip_conntrack_irc.c
--- linux.orig/net/ipv4/netfilter/ip_conntrack_irc.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/netfilter/ip_conntrack_irc.c	Thu Feb  7 16:49:38 2002
@@ -1,8 +1,8 @@
-/* IRC extension for IP connection tracking, Version 1.20
- * (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
+/* IRC extension for IP connection tracking, Version 1.21
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
  * based on RR's ip_conntrack_ftp.c	
  *
- * ip_conntrack_irc.c,v 1.20 2001/12/06 07:42:10 laforge Exp
+ * ip_conntrack_irc.c,v 1.21 2002/02/05 14:49:26 laforge Exp
  *
  *      This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -112,9 +112,9 @@
 
 	struct ip_ct_irc *info = &ct->help.ct_irc_info;
 
-	memset(&mask, 0, sizeof(struct ip_conntrack_tuple));
-	mask.dst.u.tcp.port = 0xFFFF;
-	mask.dst.protonum = 0xFFFF;
+	mask = ((struct ip_conntrack_tuple)
+		{ { 0, { 0 } },
+		  { 0xFFFFFFFF, { 0xFFFF }, 0xFFFF }});
 
 	DEBUGP("entered\n");
 	/* Can't track connections formed before we registered */
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_conntrack_standalone.c linux/net/ipv4/netfilter/ip_conntrack_standalone.c
--- linux.orig/net/ipv4/netfilter/ip_conntrack_standalone.c	Tue Jan 22 17:51:24 2002
+++ linux/net/ipv4/netfilter/ip_conntrack_standalone.c	Mon Jan 14 16:24:07 2002
@@ -243,7 +243,7 @@
 
 	ret = nf_register_hook(&ip_conntrack_in_ops);
 	if (ret < 0) {
-		printk("ip_conntrack: can't register in hook.\n");
+		printk("ip_conntrack: can't register pre-routing hook.\n");
 		goto cleanup_proc;
 	}
 	ret = nf_register_hook(&ip_conntrack_local_out_ops);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_fw_compat_masq.c linux/net/ipv4/netfilter/ip_fw_compat_masq.c
--- linux.orig/net/ipv4/netfilter/ip_fw_compat_masq.c	Fri Sep 14 21:04:08 2001
+++ linux/net/ipv4/netfilter/ip_fw_compat_masq.c	Wed Jan 23 20:24:37 2002
@@ -157,7 +157,7 @@
 		/* Fall thru... */
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
-		IP_NF_ASSERT((skb->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
+		IP_NF_ASSERT(((*pskb)->nh.iph->frag_off & htons(IP_OFFSET)) == 0);
 
 		if (!get_tuple(iph, (*pskb)->len, &tuple, protocol)) {
 			if (net_ratelimit())
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_fw_compat_redir.c linux/net/ipv4/netfilter/ip_fw_compat_redir.c
--- linux.orig/net/ipv4/netfilter/ip_fw_compat_redir.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/netfilter/ip_fw_compat_redir.c	Wed Jan 23 20:24:37 2002
@@ -20,6 +20,9 @@
 
 #include <linux/netfilter_ipv4/lockhelp.h>
 
+/* Very simple timeout pushed back by each packet */
+#define REDIR_TIMEOUT (240*HZ)
+
 static DECLARE_LOCK(redir_lock);
 #define ASSERT_READ_LOCK(x) MUST_BE_LOCKED(&redir_lock)
 #define ASSERT_WRITE_LOCK(x) MUST_BE_LOCKED(&redir_lock)
@@ -150,6 +153,14 @@
 	skb->nfcache |= NFC_ALTERED;
 }
 
+static void destroyme(unsigned long me)
+{
+	LOCK_BH(&redir_lock);
+	LIST_DELETE(&redirs, (struct redir *)me);
+	UNLOCK_BH(&redir_lock);
+	kfree((struct redir *)me);
+}
+
 /* REDIRECT a packet. */
 unsigned int
 do_redirect(struct sk_buff *skb,
@@ -172,6 +183,10 @@
 		struct udphdr *udph = (struct udphdr *)((u_int32_t *)iph
 							+ iph->ihl);
 
+		/* Must have whole header */
+		if (skb->len < iph->ihl*4 + sizeof(*udph))
+			return NF_DROP;
+
 		if (udph->check) /* 0 is a special case meaning no checksum */
 			udph->check = cheat_check(~iph->daddr, newdst,
 					  cheat_check(udph->dest ^ 0xFFFF,
@@ -191,6 +206,10 @@
 		struct redir *redir;
 		int ret;
 
+		/* Must have whole header */
+		if (skb->len < iph->ihl*4 + sizeof(*tcph))
+			return NF_DROP;
+
 		DEBUGP("Doing tcp redirect. %08X:%u %08X:%u -> %08X:%u\n",
 		       iph->saddr, tcph->source, iph->daddr, tcph->dest,
 		       newdst, redirpt);
@@ -206,7 +225,9 @@
 			}
 			list_prepend(&redirs, redir);
 			init_timer(&redir->destroyme);
-			redir->destroyme.expires = jiffies + 75*HZ;
+			redir->destroyme.function = destroyme;
+			redir->destroyme.data = (unsigned long)redir;
+			redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
 			add_timer(&redir->destroyme);
 		}
 		/* In case mangling has changed, rewrite this part. */
@@ -227,13 +248,6 @@
 	}
 }
 
-static void destroyme(unsigned long me)
-{
-	LOCK_BH(&redir_lock);
-	LIST_DELETE(&redirs, (struct redir *)me);
-	UNLOCK_BH(&redir_lock);
-}
-
 /* Incoming packet: is it a reply to a masqueraded connection, or
    part of an already-redirected TCP connection? */
 void
@@ -247,15 +261,18 @@
 	if (iph->protocol != IPPROTO_TCP)
 		return;
 
+	/* Must have whole header */
+	if (skb->len < iph->ihl*4 + sizeof(*tcph))
+		return;
+
 	LOCK_BH(&redir_lock);
 	redir = find_redir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
 	if (redir) {
 		DEBUGP("Doing tcp redirect again.\n");
 		do_tcp_redir(skb, redir);
-		if (tcph->rst || tcph->fin) {
-			redir->destroyme.function = destroyme;
-			redir->destroyme.data = (unsigned long)redir;
-			mod_timer(&redir->destroyme, 75*HZ);
+		if (del_timer(&redir->destroyme)) {
+			redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
+			add_timer(&redir->destroyme);
 		}
 	}
 	UNLOCK_BH(&redir_lock);
@@ -272,15 +289,18 @@
 	if (iph->protocol != IPPROTO_TCP)
 		return;
 
+	/* Must have whole header */
+	if (skb->len < iph->ihl*4 + sizeof(*tcph))
+		return;
+
 	LOCK_BH(&redir_lock);
 	redir = find_unredir(iph->saddr, iph->daddr, tcph->source, tcph->dest);
 	if (redir) {
 		DEBUGP("Doing tcp unredirect.\n");
 		do_tcp_unredir(skb, redir);
-		if (tcph->rst || tcph->fin) {
-			redir->destroyme.function = destroyme;
-			redir->destroyme.data = (unsigned long)redir;
-			mod_timer(&redir->destroyme, 75*HZ);
+		if (del_timer(&redir->destroyme)) {
+			redir->destroyme.expires = jiffies + REDIR_TIMEOUT;
+			add_timer(&redir->destroyme);
 		}
 	}
 	UNLOCK_BH(&redir_lock);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_nat_rule.c linux/net/ipv4/netfilter/ip_nat_rule.c
--- linux.orig/net/ipv4/netfilter/ip_nat_rule.c	Fri Apr 27 21:15:01 2001
+++ linux/net/ipv4/netfilter/ip_nat_rule.c	Wed Jan 23 20:24:37 2002
@@ -104,7 +104,7 @@
 
 static struct ipt_table nat_table
 = { { NULL, NULL }, "nat", &nat_initial_table.repl,
-    NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+    NAT_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
 
 LIST_HEAD(nat_expect_list);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_nat_standalone.c linux/net/ipv4/netfilter/ip_nat_standalone.c
--- linux.orig/net/ipv4/netfilter/ip_nat_standalone.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/ip_nat_standalone.c	Thu Feb  7 16:52:23 2002
@@ -166,34 +166,6 @@
 	return ip_nat_fn(hooknum, pskb, in, out, okfn);
 }
 
-/* FIXME: change in oif may mean change in hh_len.  Check and realloc
-   --RR */
-static int
-route_me_harder(struct sk_buff *skb)
-{
-	struct iphdr *iph = skb->nh.iph;
-	struct rtable *rt;
-	struct rt_key key = { dst:iph->daddr,
-			      src:iph->saddr,
-			      oif:skb->sk ? skb->sk->bound_dev_if : 0,
-			      tos:RT_TOS(iph->tos)|RTO_CONN,
-#ifdef CONFIG_IP_ROUTE_FWMARK
-			      fwmark:skb->nfmark
-#endif
-			    };
-
-	if (ip_route_output_key(&rt, &key) != 0) {
-		printk("route_me_harder: No more route.\n");
-		return -EINVAL;
-	}
-
-	/* Drop old route. */
-	dst_release(skb->dst);
-
-	skb->dst = &rt->u.dst;
-	return 0;
-}
-
 static unsigned int
 ip_nat_local_fn(unsigned int hooknum,
 		struct sk_buff **pskb,
@@ -216,7 +188,7 @@
 	if (ret != NF_DROP && ret != NF_STOLEN
 	    && ((*pskb)->nh.iph->saddr != saddr
 		|| (*pskb)->nh.iph->daddr != daddr))
-		return route_me_harder(*pskb) == 0 ? ret : NF_DROP;
+		return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
 	return ret;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_queue.c linux/net/ipv4/netfilter/ip_queue.c
--- linux.orig/net/ipv4/netfilter/ip_queue.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/ip_queue.c	Thu Feb  7 16:52:23 2002
@@ -216,32 +216,6 @@
 	kfree(q);
 }
 
-/* With a chainsaw... */
-static int route_me_harder(struct sk_buff *skb)
-{
-	struct iphdr *iph = skb->nh.iph;
-	struct rtable *rt;
-
-	struct rt_key key = {
-				dst:iph->daddr, src:iph->saddr,
-				oif:skb->sk ? skb->sk->bound_dev_if : 0,
-				tos:RT_TOS(iph->tos)|RTO_CONN,
-#ifdef CONFIG_IP_ROUTE_FWMARK
-				fwmark:skb->nfmark
-#endif
-			};
-
-	if (ip_route_output_key(&rt, &key) != 0) {
-		printk("route_me_harder: No more route.\n");
-		return -EINVAL;
-	}
-
-	/* Drop old route. */
-	dst_release(skb->dst);
-	skb->dst = &rt->u.dst;
-	return 0;
-}
-
 static int ipq_mangle_ipv4(ipq_verdict_msg_t *v, ipq_queue_element_t *e)
 {
 	int diff;
@@ -287,7 +261,7 @@
 		if (!(iph->tos == e->rt_info.tos
 		      && iph->daddr == e->rt_info.daddr
 		      && iph->saddr == e->rt_info.saddr))
-			return route_me_harder(e->skb);
+			return ip_route_me_harder(&e->skb);
 	}
 	return 0;
 }
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ip_tables.c linux/net/ipv4/netfilter/ip_tables.c
--- linux.orig/net/ipv4/netfilter/ip_tables.c	Tue Oct 30 23:08:12 2001
+++ linux/net/ipv4/netfilter/ip_tables.c	Wed Jan 23 20:24:37 2002
@@ -2,6 +2,11 @@
  * Packet matching code.
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * Copyright (C) 2009-2002 Netfilter core team <coreteam@netfilter.org>
+ *
+ * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
+ * 	- increase module usage count as soon as we have rules inside
+ * 	  a table
  */
 #include <linux/config.h>
 #include <linux/skbuff.h>
@@ -84,6 +89,8 @@
 	unsigned int size;
 	/* Number of entries: FIXME. --RR */
 	unsigned int number;
+	/* Initial number of entries. Needed for module usage count */
+	unsigned int initial_entries;
 
 	/* Entry points and underflows */
 	unsigned int hook_entry[NF_IP_NUMHOOKS];
@@ -902,6 +909,7 @@
 	}
 	oldinfo = table->private;
 	table->private = newinfo;
+	newinfo->initial_entries = oldinfo->initial_entries;
 	write_unlock_bh(&table->lock);
 
 	return oldinfo;
@@ -1105,6 +1113,16 @@
 	if (!oldinfo)
 		goto free_newinfo_counters_untrans_unlock;
 
+	/* Update module usage count based on number of rules */
+	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+		oldinfo->number, oldinfo->initial_entries, newinfo->number);
+	if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
+ 	    (newinfo->number > oldinfo->initial_entries))
+		__MOD_INC_USE_COUNT(t->me);
+	else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
+	 	 (newinfo->number <= oldinfo->initial_entries))
+		__MOD_DEC_USE_COUNT(t->me);
+
 	/* Get the old counters. */
 	get_counters(oldinfo, counters);
 	/* Decrease module usage counts and free resource */
@@ -1363,7 +1381,7 @@
 	int ret;
 	struct ipt_table_info *newinfo;
 	static struct ipt_table_info bootstrap
-		= { 0, 0, { 0 }, { 0 }, { } };
+		= { 0, 0, 0, { 0 }, { 0 }, { } };
 
 	MOD_INC_USE_COUNT;
 	newinfo = vmalloc(sizeof(struct ipt_table_info)
@@ -1406,6 +1424,9 @@
 
 	duprintf("table->private->number = %u\n",
 		 table->private->number);
+	
+	/* save number of initial entries */
+	table->private->initial_entries = table->private->number;
 
 	table->lock = RW_LOCK_UNLOCKED;
 	list_prepend(&ipt_tables, table);
@@ -1746,7 +1767,7 @@
 	}
 #endif
 
-	printk("ip_tables: (c)2000 Netfilter core team\n");
+	printk("ip_tables: (C) 2000-2002 Netfilter core team\n");
 	return 0;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipchains_core.c linux/net/ipv4/netfilter/ipchains_core.c
--- linux.orig/net/ipv4/netfilter/ipchains_core.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/ipchains_core.c	Wed Jan 23 20:24:37 2002
@@ -838,6 +838,7 @@
 			i->branch->refcount--;
 		kfree(i);
 		i = tmp;
+		MOD_DEC_USE_COUNT;
 	}
 	return 0;
 }
@@ -875,13 +876,16 @@
 		 * interrupts is not necessary. */
 		chainptr->chain = rule;
 		if (rule->branch) rule->branch->refcount++;
-		return 0;
+		goto append_successful;
 	}
 
 	/* Find the rule before the end of the chain */
 	for (i = chainptr->chain; i->next; i = i->next);
 	i->next = rule;
 	if (rule->branch) rule->branch->refcount++;
+
+append_successful:
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -900,7 +904,7 @@
 		frwl->next = chainptr->chain;
 		if (frwl->branch) frwl->branch->refcount++;
 		chainptr->chain = frwl;
-		return 0;
+		goto insert_successful;
 	}
 	position--;
 	while (--position && f != NULL) f = f->next;
@@ -910,6 +914,9 @@
 	frwl->next = f->next;
 
 	f->next = frwl;
+
+insert_successful:
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -943,6 +950,8 @@
 		i->next = i->next->next;
 		kfree(tmp);
 	}
+
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1049,6 +1058,7 @@
 		else
 			chainptr->chain = ftmp->next;
 		kfree(ftmp);
+		MOD_DEC_USE_COUNT;
 		break;
 	}
 
@@ -1089,6 +1099,8 @@
 
 	tmp->next = tmp2->next;
 	kfree(tmp2);
+
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -1141,6 +1153,7 @@
 					      * user defined chain *
 					      * and therefore can be
 					      * deleted */
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipfwadm_core.c linux/net/ipv4/netfilter/ipfwadm_core.c
--- linux.orig/net/ipv4/netfilter/ipfwadm_core.c	Thu Sep 20 21:12:56 2001
+++ linux/net/ipv4/netfilter/ipfwadm_core.c	Mon Feb  4 16:43:43 2002
@@ -20,7 +20,7 @@
  *	license in recognition of the original copyright.
  *				-- Alan Cox.
  *
- *	$Id: ipfwadm_core.c,v 1.9 2001/09/18 22:29:10 davem Exp $
+ *	$Id: ipfwadm_core.c,v 1.9.2.2 2002/01/24 15:50:42 davem Exp $
  *
  *	Ported from BSD to Linux,
  *		Alan Cox 22/Nov/1994.
@@ -104,6 +104,7 @@
 #include <linux/sched.h>
 #include <linux/string.h>
 #include <linux/errno.h>
+#include <linux/module.h>
 
 #include <linux/socket.h>
 #include <linux/sockios.h>
@@ -687,6 +688,7 @@
 		ftmp = *chainptr;
 		*chainptr = ftmp->fw_next;
 		kfree(ftmp);
+		MOD_DEC_USE_COUNT;
 	}
 	restore_flags(flags);
 }
@@ -730,6 +732,7 @@
 	ftmp->fw_next = *chainptr;
        	*chainptr=ftmp;
 	restore_flags(flags);
+	MOD_INC_USE_COUNT;
 	return(0);
 }
 
@@ -780,6 +783,7 @@
 	else
         	*chainptr=ftmp;
 	restore_flags(flags);
+	MOD_INC_USE_COUNT;
 	return(0);
 }
 
@@ -853,9 +857,10 @@
 		 }
 	}
 	restore_flags(flags);
-	if (was_found)
+	if (was_found) {
+		MOD_DEC_USE_COUNT;
 		return 0;
-	else
+	} else
 		return(EINVAL);
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipt_LOG.c linux/net/ipv4/netfilter/ipt_LOG.c
--- linux.orig/net/ipv4/netfilter/ipt_LOG.c	Mon Nov  5 17:53:07 2001
+++ linux/net/ipv4/netfilter/ipt_LOG.c	Wed Jan 23 20:24:37 2002
@@ -217,7 +217,7 @@
 				printk("[");
 				dump_packet(info,
 					    (struct iphdr *)(icmph + 1),
-					    datalen-sizeof(struct iphdr),
+					    datalen-sizeof(struct icmphdr),
 					    0);
 				printk("] ");
 			}
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipt_REDIRECT.c linux/net/ipv4/netfilter/ipt_REDIRECT.c
--- linux.orig/net/ipv4/netfilter/ipt_REDIRECT.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/ipt_REDIRECT.c	Wed Jan 23 20:24:37 2002
@@ -74,10 +74,17 @@
 	/* Local packets: make them go to loopback */
 	if (hooknum == NF_IP_LOCAL_OUT)
 		newdst = htonl(0x7F000001);
-	else
+	else {
+		struct in_device *indev;
+
+		/* Device might not have an associated in_device. */
+		indev = (struct in_device *)(*pskb)->dev->ip_ptr;
+		if (indev == NULL)
+			return NF_DROP;
+
 		/* Grab first address on interface. */
-		newdst = (((struct in_device *)(*pskb)->dev->ip_ptr)
-			  ->ifa_list->ifa_local);
+		newdst = indev->ifa_list->ifa_local;
+	}
 
 	/* Transfer from original range. */
 	newrange = ((struct ip_nat_multi_range)
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipt_ULOG.c linux/net/ipv4/netfilter/ipt_ULOG.c
--- linux.orig/net/ipv4/netfilter/ipt_ULOG.c	Thu Jan  1 00:00:00 1970
+++ linux/net/ipv4/netfilter/ipt_ULOG.c	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,349 @@
+/*
+ * netfilter module for userspace packet logging daemons
+ *
+ * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
+ *
+ * 2000/09/22 ulog-cprange feature added
+ * 2001/01/04 in-kernel queue as proposed by Sebastian Zander 
+ * 						<zander@fokus.gmd.de>
+ * 2001/01/30 per-rule nlgroup conflicts with global queue. 
+ *            nlgroup now global (sysctl)
+ * 2001/04/19 ulog-queue reworked, now fixed buffer size specified at
+ * 	      module loadtime -HW
+ *
+ * Released under the terms of the GPL
+ *
+ * This module accepts two parameters: 
+ * 
+ * nlbufsiz:
+ *   The parameter specifies how big the buffer for each netlink multicast
+ * group is. e.g. If you say nlbufsiz=8192, up to eight kb of packets will
+ * get accumulated in the kernel until they are sent to userspace. It is
+ * NOT possible to allocate more than 128kB, and it is strongly discouraged,
+ * because atomically allocating 128kB inside the network rx softirq is not
+ * reliable. Please also keep in mind that this buffer size is allocated for
+ * each nlgroup you are using, so the total kernel memory usage increases
+ * by that factor.
+ *
+ * flushtimeout:
+ *   Specify, after how many clock ticks (intel: 100 per second) the queue
+ * should be flushed even if it is not full yet.
+ *
+ * ipt_ULOG.c,v 1.15 2002/01/18 21:33:19 laforge Exp
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/spinlock.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/netlink.h>
+#include <linux/netdevice.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv4/ipt_ULOG.h>
+#include <linux/netfilter_ipv4/lockhelp.h>
+#include <net/sock.h>
+
+MODULE_LICENSE("GPL");
+
+#define ULOG_NL_EVENT		111		/* Harald's favorite number */
+#define ULOG_MAXNLGROUPS	32		/* numer of nlgroups */
+
+#if 0
+#define DEBUGP(format, args...)	printk(__FILE__ ":" __FUNCTION__ ":" \
+				       format, ## args)
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define PRINTR(format, args...) do { if (net_ratelimit()) printk(format, ## args); } while (0);
+
+MODULE_AUTHOR("Harald Welte <laforge@gnumonks.org>");
+MODULE_DESCRIPTION("IP tables userspace logging module");
+
+
+static unsigned int nlbufsiz = 4096;
+MODULE_PARM(nlbufsiz, "i");
+MODULE_PARM_DESC(nlbufsiz, "netlink buffer size");
+
+static unsigned int flushtimeout = 10 * HZ;
+MODULE_PARM(flushtimeout, "i");
+MODULE_PARM_DESC(flushtimeout, "buffer flush timeout");
+
+/* global data structures */
+
+typedef struct {
+	unsigned int qlen;		/* number of nlmsgs' in the skb */
+	struct nlmsghdr *lastnlh;	/* netlink header of last msg in skb */
+	struct sk_buff *skb;		/* the pre-allocated skb */
+	struct timer_list timer;	/* the timer function */
+} ulog_buff_t;
+
+static ulog_buff_t ulog_buffers[ULOG_MAXNLGROUPS];	/* array of buffers */
+
+static struct sock *nflognl;	/* our socket */
+static size_t qlen;		/* current length of multipart-nlmsg */
+DECLARE_LOCK(ulog_lock);	/* spinlock */
+
+/* send one ulog_buff_t to userspace */
+static void ulog_send(unsigned int nlgroup)
+{
+	ulog_buff_t *ub = &ulog_buffers[nlgroup];
+
+	if (timer_pending(&ub->timer)) {
+		DEBUGP("ipt_ULOG: ulog_send: timer was pending, deleting\n");
+		del_timer(&ub->timer);
+	}
+
+	/* last nlmsg needs NLMSG_DONE */
+	if (ub->qlen > 1)
+		ub->lastnlh->nlmsg_type = NLMSG_DONE;
+
+	NETLINK_CB(ub->skb).dst_groups = nlgroup;
+	DEBUGP("ipt_ULOG: throwing %d packets to netlink mask %u\n",
+		ub->qlen, nlgroup);
+	netlink_broadcast(nflognl, ub->skb, 0, nlgroup, GFP_ATOMIC);
+
+	ub->qlen = 0;
+	ub->skb = NULL;
+	ub->lastnlh = NULL;
+
+}
+
+
+/* timer function to flush queue in ULOG_FLUSH_INTERVAL time */
+static void ulog_timer(unsigned long data)
+{
+	DEBUGP("ipt_ULOG: timer function called, calling ulog_send\n");
+
+	/* lock to protect against somebody modifying our structure
+	 * from ipt_ulog_target at the same time */
+	LOCK_BH(&ulog_lock);
+	ulog_send(data);
+	UNLOCK_BH(&ulog_lock);
+}
+
+static void nflog_rcv(struct sock *sk, int len)
+{
+	printk("ipt_ULOG:nflog_rcv() did receive netlink message ?!?\n");
+}
+
+struct sk_buff *ulog_alloc_skb(unsigned int size)
+{
+	struct sk_buff *skb;
+
+	/* alloc skb which should be big enough for a whole
+	 * multipart message. WARNING: has to be <= 131000
+	 * due to slab allocator restrictions */
+
+	skb = alloc_skb(nlbufsiz, GFP_ATOMIC);
+	if (!skb) {
+		PRINTR("ipt_ULOG: can't alloc whole buffer %ub!\n",
+			nlbufsiz);
+
+		/* try to allocate only as much as we need for 
+		 * current packet */
+
+		skb = alloc_skb(size, GFP_ATOMIC);
+		if (!skb)
+			PRINTR("ipt_ULOG: can't even allocate %ub\n", size);
+	}
+
+	return skb;
+}
+
+static unsigned int ipt_ulog_target(struct sk_buff **pskb,
+				    unsigned int hooknum,
+				    const struct net_device *in,
+				    const struct net_device *out,
+				    const void *targinfo, void *userinfo)
+{
+	ulog_buff_t *ub;
+	ulog_packet_msg_t *pm;
+	size_t size, copy_len;
+	struct nlmsghdr *nlh;
+	struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo;
+
+	/* calculate the size of the skb needed */
+	if ((loginfo->copy_range == 0) ||
+	    (loginfo->copy_range > (*pskb)->len)) {
+		copy_len = (*pskb)->len;
+	} else {
+		copy_len = loginfo->copy_range;
+	}
+
+	size = NLMSG_SPACE(sizeof(*pm) + copy_len);
+
+	ub = &ulog_buffers[loginfo->nl_group];
+	
+	LOCK_BH(&ulog_lock);
+
+	if (!ub->skb) {
+		if (!(ub->skb = ulog_alloc_skb(size)))
+			goto alloc_failure;
+	} else if (ub->qlen >= loginfo->qthreshold ||
+		   size > skb_tailroom(ub->skb)) {
+		/* either the queue len is too high or we don't have 
+		 * enough room in nlskb left. send it to userspace. */
+
+		ulog_send(loginfo->nl_group);
+
+		if (!(ub->skb = ulog_alloc_skb(size)))
+			goto alloc_failure;
+	}
+
+	DEBUGP("ipt_ULOG: qlen %d, qthreshold %d\n", ub->qlen, 
+		loginfo->qthreshold);
+
+	/* NLMSG_PUT contains a hidden goto nlmsg_failure !!! */
+	nlh = NLMSG_PUT(ub->skb, 0, ub->qlen, ULOG_NL_EVENT, 
+			size - sizeof(*nlh));
+	ub->qlen++;
+
+	pm = NLMSG_DATA(nlh);
+
+	/* copy hook, prefix, timestamp, payload, etc. */
+	pm->data_len = copy_len;
+	pm->timestamp_sec = (*pskb)->stamp.tv_sec;
+	pm->timestamp_usec = (*pskb)->stamp.tv_usec;
+	pm->mark = (*pskb)->nfmark;
+	pm->hook = hooknum;
+	if (loginfo->prefix[0] != '\0')
+		strncpy(pm->prefix, loginfo->prefix, sizeof(pm->prefix));
+	else
+		*(pm->prefix) = '\0';
+
+	if (in && in->hard_header_len > 0
+	    && (*pskb)->mac.raw != (void *) (*pskb)->nh.iph
+	    && in->hard_header_len <= ULOG_MAC_LEN) {
+		memcpy(pm->mac, (*pskb)->mac.raw, in->hard_header_len);
+		pm->mac_len = in->hard_header_len;
+	}
+
+	if (in)
+		strncpy(pm->indev_name, in->name, sizeof(pm->indev_name));
+	else
+		pm->indev_name[0] = '\0';
+
+	if (out)
+		strncpy(pm->outdev_name, out->name, sizeof(pm->outdev_name));
+	else
+		pm->outdev_name[0] = '\0';
+
+	if (copy_len)
+		memcpy(pm->payload, (*pskb)->data, copy_len);
+	
+	/* check if we are building multi-part messages */
+	if (ub->qlen > 1) {
+		ub->lastnlh->nlmsg_flags |= NLM_F_MULTI;
+	}
+
+	/* if threshold is reached, send message to userspace */
+	if (qlen >= loginfo->qthreshold) {
+		if (loginfo->qthreshold > 1)
+			nlh->nlmsg_type = NLMSG_DONE;
+	}
+
+	ub->lastnlh = nlh;
+
+	/* if timer isn't already running, start it */
+	if (!timer_pending(&ub->timer)) {
+		ub->timer.expires = jiffies + flushtimeout;
+		add_timer(&ub->timer);
+	}
+
+	UNLOCK_BH(&ulog_lock);
+
+	return IPT_CONTINUE;
+
+
+nlmsg_failure:
+	PRINTR("ipt_ULOG: error during NLMSG_PUT\n");
+
+alloc_failure:
+	PRINTR("ipt_ULOG: Error building netlink message\n");
+
+	UNLOCK_BH(&ulog_lock);
+
+	return IPT_CONTINUE;
+}
+
+static int ipt_ulog_checkentry(const char *tablename,
+			       const struct ipt_entry *e,
+			       void *targinfo,
+			       unsigned int targinfosize,
+			       unsigned int hookmask)
+{
+	struct ipt_ulog_info *loginfo = (struct ipt_ulog_info *) targinfo;
+
+	if (targinfosize != IPT_ALIGN(sizeof(struct ipt_ulog_info))) {
+		DEBUGP("ipt_ULOG: targinfosize %u != 0\n", targinfosize);
+		return 0;
+	}
+
+	if (loginfo->prefix[sizeof(loginfo->prefix) - 1] != '\0') {
+		DEBUGP("ipt_ULOG: prefix term %i\n",
+		       loginfo->prefix[sizeof(loginfo->prefix) - 1]);
+		return 0;
+	}
+
+	if (loginfo->qthreshold > ULOG_MAX_QLEN) {
+		DEBUGP("ipt_ULOG: queue threshold %i > MAX_QLEN\n",
+			loginfo->qthreshold);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_target ipt_ulog_reg =
+    { {NULL, NULL}, "ULOG", ipt_ulog_target, ipt_ulog_checkentry, NULL,
+THIS_MODULE
+};
+
+static int __init init(void)
+{
+	int i;
+
+	DEBUGP("ipt_ULOG: init module\n");
+
+	if (nlbufsiz >= 128*1024) {
+		printk("Netlink buffer has to be <= 128kB\n");
+		return -EINVAL;
+	}
+
+	/* initialize ulog_buffers */
+	for (i = 0; i < ULOG_MAXNLGROUPS; i++) {
+		memset(&ulog_buffers[i], 0, sizeof(ulog_buff_t));
+		init_timer(&ulog_buffers[i].timer);
+		ulog_buffers[i].timer.function = ulog_timer;
+		ulog_buffers[i].timer.data = i;
+	}
+
+	nflognl = netlink_kernel_create(NETLINK_NFLOG, nflog_rcv);
+	if (!nflognl)
+		return -ENOMEM;
+
+	if (ipt_register_target(&ipt_ulog_reg) != 0) {
+		sock_release(nflognl->socket);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void __exit fini(void)
+{
+	DEBUGP("ipt_ULOG: cleanup_module\n");
+
+	ipt_unregister_target(&ipt_ulog_reg);
+	sock_release(nflognl->socket);
+}
+
+module_init(init);
+module_exit(fini);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipt_ah.c linux/net/ipv4/netfilter/ipt_ah.c
--- linux.orig/net/ipv4/netfilter/ipt_ah.c	Thu Jan  1 00:00:00 1970
+++ linux/net/ipv4/netfilter/ipt_ah.c	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,105 @@
+/* Kernel module to match AH parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ipt_ah.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+
+#ifdef DEBUG_CONNTRACK
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+struct ahhdr {
+	__u32   spi;
+};
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+	int r=0;
+        duprintf("ah spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+        	min,spi,max);
+	r=(spi >= min && spi <= max) ^ invert;
+	duprintf(" result %s\n",r? "PASS" : "FAILED");
+	return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	const struct ahhdr *ah = hdr;
+	const struct ipt_ah *ahinfo = matchinfo;
+
+	if (offset == 0 && datalen < sizeof(struct ahhdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil AH tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& spi_match(ahinfo->spis[0], ahinfo->spis[1],
+			      ntohl(ah->spi),
+			      !!(ahinfo->invflags & IPT_AH_INV_SPI));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+	   const struct ipt_ip *ip,
+	   void *matchinfo,
+	   unsigned int matchinfosize,
+	   unsigned int hook_mask)
+{
+	const struct ipt_ah *ahinfo = matchinfo;
+
+	/* Must specify proto == AH, and no unknown invflags */
+	if (ip->proto != IPPROTO_AH || (ip->invflags & IPT_INV_PROTO)) {
+		duprintf("ipt_ah: Protocol %u != %u\n", ip->proto,
+			 IPPROTO_AH);
+		return 0;
+	}
+	if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_ah))) {
+		duprintf("ipt_ah: matchsize %u != %u\n",
+			 matchinfosize, IPT_ALIGN(sizeof(struct ipt_ah)));
+		return 0;
+	}
+	if (ahinfo->invflags & ~IPT_AH_INV_MASK) {
+		duprintf("ipt_ah: unknown flags %X\n",
+			 ahinfo->invflags);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_match ah_match
+= { { NULL, NULL }, "ah", &match, &checkentry, NULL, THIS_MODULE };
+
+int __init init(void)
+{
+	return ipt_register_match(&ah_match);
+}
+
+void __exit cleanup(void)
+{
+	ipt_unregister_match(&ah_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/ipt_esp.c linux/net/ipv4/netfilter/ipt_esp.c
--- linux.orig/net/ipv4/netfilter/ipt_esp.c	Thu Jan  1 00:00:00 1970
+++ linux/net/ipv4/netfilter/ipt_esp.c	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,105 @@
+/* Kernel module to match ESP parameters. */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+
+#include <linux/netfilter_ipv4/ipt_esp.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+
+EXPORT_NO_SYMBOLS;
+MODULE_LICENSE("GPL");
+
+#ifdef DEBUG_CONNTRACK
+#define duprintf(format, args...) printk(format , ## args)
+#else
+#define duprintf(format, args...)
+#endif
+
+struct esphdr {
+	__u32   spi;
+};
+
+/* Returns 1 if the spi is matched by the range, 0 otherwise */
+static inline int
+spi_match(u_int32_t min, u_int32_t max, u_int32_t spi, int invert)
+{
+	int r=0;
+        duprintf("esp spi_match:%c 0x%x <= 0x%x <= 0x%x",invert? '!':' ',
+        	min,spi,max);
+	r=(spi >= min && spi <= max) ^ invert;
+	duprintf(" result %s\n",r? "PASS" : "FAILED");
+	return r;
+}
+
+static int
+match(const struct sk_buff *skb,
+      const struct net_device *in,
+      const struct net_device *out,
+      const void *matchinfo,
+      int offset,
+      const void *hdr,
+      u_int16_t datalen,
+      int *hotdrop)
+{
+	const struct esphdr *esp = hdr;
+	const struct ipt_esp *espinfo = matchinfo;
+
+	if (offset == 0 && datalen < sizeof(struct esphdr)) {
+		/* We've been asked to examine this packet, and we
+		   can't.  Hence, no choice but to drop. */
+		duprintf("Dropping evil ESP tinygram.\n");
+		*hotdrop = 1;
+		return 0;
+	}
+
+	/* Must not be a fragment. */
+	return !offset
+		&& spi_match(espinfo->spis[0], espinfo->spis[1],
+			      ntohl(esp->spi),
+			      !!(espinfo->invflags & IPT_ESP_INV_SPI));
+}
+
+/* Called when user tries to insert an entry of this type. */
+static int
+checkentry(const char *tablename,
+	   const struct ipt_ip *ip,
+	   void *matchinfo,
+	   unsigned int matchinfosize,
+	   unsigned int hook_mask)
+{
+	const struct ipt_esp *espinfo = matchinfo;
+
+	/* Must specify proto == ESP, and no unknown invflags */
+	if (ip->proto != IPPROTO_ESP || (ip->invflags & IPT_INV_PROTO)) {
+		duprintf("ipt_esp: Protocol %u != %u\n", ip->proto,
+			 IPPROTO_ESP);
+		return 0;
+	}
+	if (matchinfosize != IPT_ALIGN(sizeof(struct ipt_esp))) {
+		duprintf("ipt_esp: matchsize %u != %u\n",
+			 matchinfosize, IPT_ALIGN(sizeof(struct ipt_esp)));
+		return 0;
+	}
+	if (espinfo->invflags & ~IPT_ESP_INV_MASK) {
+		duprintf("ipt_esp: unknown flags %X\n",
+			 espinfo->invflags);
+		return 0;
+	}
+
+	return 1;
+}
+
+static struct ipt_match esp_match
+= { { NULL, NULL }, "esp", &match, &checkentry, NULL, THIS_MODULE };
+
+static int __init init(void)
+{
+	return ipt_register_match(&esp_match);
+}
+
+static void __exit cleanup(void)
+{
+	ipt_unregister_match(&esp_match);
+}
+
+module_init(init);
+module_exit(cleanup);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/iptable_filter.c linux/net/ipv4/netfilter/iptable_filter.c
--- linux.orig/net/ipv4/netfilter/iptable_filter.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/iptable_filter.c	Wed Jan 23 20:24:37 2002
@@ -83,7 +83,7 @@
 
 static struct ipt_table packet_filter
 = { { NULL, NULL }, "filter", &initial_table.repl,
-    FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+    FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/netfilter/iptable_mangle.c linux/net/ipv4/netfilter/iptable_mangle.c
--- linux.orig/net/ipv4/netfilter/iptable_mangle.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv4/netfilter/iptable_mangle.c	Thu Feb  7 16:52:23 2002
@@ -2,6 +2,8 @@
  * This is the 1999 rewrite of IP Firewalling, aiming for kernel 2.3.x.
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ *
+ * Extended to all five netfilter hooks by Brad Chapman & Harald Welte
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -12,7 +14,11 @@
 #include <net/route.h>
 #include <linux/ip.h>
 
-#define MANGLE_VALID_HOOKS ((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))
+#define MANGLE_VALID_HOOKS ((1 << NF_IP_PRE_ROUTING) | \
+			    (1 << NF_IP_LOCAL_IN) | \
+			    (1 << NF_IP_FORWARD) | \
+			    (1 << NF_IP_LOCAL_OUT) | \
+			    (1 << NF_IP_POST_ROUTING))
 
 /* Standard entry. */
 struct ipt_standard
@@ -33,18 +39,25 @@
 	struct ipt_error_target target;
 };
 
+/* Ouch - five different hooks? Maybe this should be a config option..... -- BC */
 static struct
 {
 	struct ipt_replace repl;
-	struct ipt_standard entries[2];
+	struct ipt_standard entries[5];
 	struct ipt_error term;
 } initial_table __initdata
-= { { "mangle", MANGLE_VALID_HOOKS, 3,
-      sizeof(struct ipt_standard) * 2 + sizeof(struct ipt_error),
-      { [NF_IP_PRE_ROUTING] 0,
-	[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
-      { [NF_IP_PRE_ROUTING] 0,
-	[NF_IP_LOCAL_OUT] sizeof(struct ipt_standard) },
+= { { "mangle", MANGLE_VALID_HOOKS, 6,
+      sizeof(struct ipt_standard) * 5 + sizeof(struct ipt_error),
+      { [NF_IP_PRE_ROUTING] 	0,
+	[NF_IP_LOCAL_IN] 	sizeof(struct ipt_standard),
+	[NF_IP_FORWARD] 	sizeof(struct ipt_standard) * 2,
+	[NF_IP_LOCAL_OUT] 	sizeof(struct ipt_standard) * 3,
+	[NF_IP_POST_ROUTING] 	sizeof(struct ipt_standard) * 4 },
+      { [NF_IP_PRE_ROUTING] 	0,
+	[NF_IP_LOCAL_IN] 	sizeof(struct ipt_standard),
+	[NF_IP_FORWARD] 	sizeof(struct ipt_standard) * 2,
+	[NF_IP_LOCAL_OUT] 	sizeof(struct ipt_standard) * 3,
+	[NF_IP_POST_ROUTING]	sizeof(struct ipt_standard) * 4 },
       0, NULL, { } },
     {
 	    /* PRE_ROUTING */
@@ -55,6 +68,22 @@
 		0, { 0, 0 }, { } },
 	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
 		-NF_ACCEPT - 1 } },
+	    /* LOCAL_IN */
+ 	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ipt_entry),
+		sizeof(struct ipt_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
+	    /* FORWARD */
+ 	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ipt_entry),
+		sizeof(struct ipt_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
 	    /* LOCAL_OUT */
 	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
 		0,
@@ -62,7 +91,15 @@
 		sizeof(struct ipt_standard),
 		0, { 0, 0 }, { } },
 	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
-		-NF_ACCEPT - 1 } }
+		-NF_ACCEPT - 1 } },
+	    /* POST_ROUTING */
+	    { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ipt_entry),
+		sizeof(struct ipt_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IPT_ALIGN(sizeof(struct ipt_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
     },
     /* ERROR */
     { { { { 0 }, { 0 }, { 0 }, { 0 }, "", "", { 0 }, { 0 }, 0, 0, 0 },
@@ -79,11 +116,11 @@
 
 static struct ipt_table packet_mangler
 = { { NULL, NULL }, "mangle", &initial_table.repl,
-    MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+    MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ipt_hook(unsigned int hook,
+ipt_route_hook(unsigned int hook,
 	 struct sk_buff **pskb,
 	 const struct net_device *in,
 	 const struct net_device *out,
@@ -92,36 +129,8 @@
 	return ipt_do_table(pskb, hook, in, out, &packet_mangler, NULL);
 }
 
-/* FIXME: change in oif may mean change in hh_len.  Check and realloc
-   --RR */
-static int
-route_me_harder(struct sk_buff *skb)
-{
-	struct iphdr *iph = skb->nh.iph;
-	struct rtable *rt;
-	struct rt_key key = { dst:iph->daddr,
-			      src:iph->saddr,
-			      oif:skb->sk ? skb->sk->bound_dev_if : 0,
-			      tos:RT_TOS(iph->tos)|RTO_CONN,
-#ifdef CONFIG_IP_ROUTE_FWMARK
-			      fwmark:skb->nfmark
-#endif
-			    };
-
-	if (ip_route_output_key(&rt, &key) != 0) {
-		printk("route_me_harder: No more route.\n");
-		return -EINVAL;
-	}
-
-	/* Drop old route. */
-	dst_release(skb->dst);
-
-	skb->dst = &rt->u.dst;
-	return 0;
-}
-
 static unsigned int
-ipt_local_out_hook(unsigned int hook,
+ipt_local_hook(unsigned int hook,
 		   struct sk_buff **pskb,
 		   const struct net_device *in,
 		   const struct net_device *out,
@@ -153,15 +162,22 @@
 		|| (*pskb)->nh.iph->daddr != daddr
 		|| (*pskb)->nfmark != nfmark
 		|| (*pskb)->nh.iph->tos != tos))
-		return route_me_harder(*pskb) == 0 ? ret : NF_DROP;
+		return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
 
 	return ret;
 }
 
 static struct nf_hook_ops ipt_ops[]
-= { { { NULL, NULL }, ipt_hook, PF_INET, NF_IP_PRE_ROUTING, NF_IP_PRI_MANGLE },
-    { { NULL, NULL }, ipt_local_out_hook, PF_INET, NF_IP_LOCAL_OUT,
-		NF_IP_PRI_MANGLE }
+= { { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_PRE_ROUTING, 
+	NF_IP_PRI_MANGLE },
+    { { NULL, NULL }, ipt_local_hook, PF_INET, NF_IP_LOCAL_IN,
+	NF_IP_PRI_MANGLE },
+    { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_FORWARD,
+	NF_IP_PRI_MANGLE },
+    { { NULL, NULL }, ipt_local_hook, PF_INET, NF_IP_LOCAL_OUT,
+	NF_IP_PRI_MANGLE },
+    { { NULL, NULL }, ipt_route_hook, PF_INET, NF_IP_POST_ROUTING,
+	NF_IP_PRI_MANGLE }
 };
 
 static int __init init(void)
@@ -182,8 +198,26 @@
 	if (ret < 0)
 		goto cleanup_hook0;
 
+	ret = nf_register_hook(&ipt_ops[2]);
+	if (ret < 0)
+		goto cleanup_hook1;
+
+	ret = nf_register_hook(&ipt_ops[3]);
+	if (ret < 0)
+		goto cleanup_hook2;
+
+	ret = nf_register_hook(&ipt_ops[4]);
+	if (ret < 0)
+		goto cleanup_hook3;
+
 	return ret;
 
+ cleanup_hook3:
+        nf_unregister_hook(&ipt_ops[3]);
+ cleanup_hook2:
+        nf_unregister_hook(&ipt_ops[2]);
+ cleanup_hook1:
+	nf_unregister_hook(&ipt_ops[1]);
  cleanup_hook0:
 	nf_unregister_hook(&ipt_ops[0]);
  cleanup_table:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/route.c linux/net/ipv4/route.c
--- linux.orig/net/ipv4/route.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/route.c	Wed Jan 23 20:10:53 2002
@@ -5,7 +5,7 @@
  *
  *		ROUTE - implementation of the IP router.
  *
- * Version:	$Id: route.c,v 1.102 2001/10/31 21:55:54 davem Exp $
+ * Version:	$Id: route.c,v 1.102.2.1 2002/01/12 07:43:57 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -214,7 +214,7 @@
 {
 	int len = 0;
 	off_t pos = 128;
-	char temp[129];
+	char temp[256];
 	struct rtable *r;
 	int i;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- linux.orig/net/ipv4/tcp_ipv4.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv4/tcp_ipv4.c	Wed Jan 23 19:57:04 2002
@@ -5,7 +5,7 @@
  *
  *		Implementation of the Transmission Control Protocol(TCP).
  *
- * Version:	$Id: tcp_ipv4.c,v 1.237 2001/12/05 08:54:10 davem Exp $
+ * Version:	$Id: tcp_ipv4.c,v 1.237.2.1 2002/01/15 08:49:49 davem Exp $
  *
  *		IPv4 specific functions
  *
@@ -2064,7 +2064,7 @@
 	/* First, walk listening socket table. */
 	tcp_listen_lock();
 	for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
-		struct sock *sk = tcp_listening_hash[i];
+		struct sock *sk;
 		struct tcp_listen_opt *lopt;
 		int k;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv4/udp.c linux/net/ipv4/udp.c
--- linux.orig/net/ipv4/udp.c	Wed Oct 17 21:16:39 2001
+++ linux/net/ipv4/udp.c	Mon Feb  4 16:38:42 2002
@@ -5,7 +5,7 @@
  *
  *		The User Datagram Protocol (UDP).
  *
- * Version:	$Id: udp.c,v 1.100 2001/10/15 12:34:50 davem Exp $
+ * Version:	$Id: udp.c,v 1.100.2.1 2002/01/12 07:39:23 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -886,6 +886,9 @@
 	/*
 	 *	Validate the packet and the UDP length.
 	 */
+	if (!pskb_may_pull(skb, sizeof(struct udphdr)))
+		goto no_header;
+
 	ulen = ntohs(skb->h.uh->len);
 
 	if (ulen > len || ulen < sizeof(*uh))
@@ -926,6 +929,7 @@
 
 short_packet:
 	NETDEBUG(if (net_ratelimit()) printk(KERN_DEBUG "UDP: short packet: %d/%d\n", ulen, len));
+no_header:
 	UDP_INC_STATS_BH(UdpInErrors);
 	kfree_skb(skb);
 	return(0);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/ndisc.c linux/net/ipv6/ndisc.c
--- linux.orig/net/ipv6/ndisc.c	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv6/ndisc.c	Wed Jan 23 20:25:51 2002
@@ -957,6 +957,7 @@
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
 	struct neighbour *neigh;
 	struct inet6_ifaddr *ifp;
+	unsigned int payload_len;
 
 	__skb_push(skb, skb->data-skb->h.raw);
 
@@ -979,10 +980,11 @@
 	 *	(Some checking in ndisc_find_option)
 	 */
 
+	payload_len = ntohs(skb->nh.ipv6h->payload_len);
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
 		/* XXX: import nd_neighbor_solicit from glibc netinet/icmp6.h */
-		if (skb->nh.ipv6h->payload_len < 8+16) {
+		if (payload_len < 8+16) {
 			if (net_ratelimit())
 				printk(KERN_WARNING "ICMP NS: packet too short\n");
 			return 0;
@@ -1112,7 +1114,7 @@
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
 		/* XXX: import nd_neighbor_advert from glibc netinet/icmp6.h */
-		if (skb->nh.ipv6h->payload_len < 16+8 ) {
+		if (payload_len < 16+8 ) {
 			if (net_ratelimit())
 				printk(KERN_WARNING "ICMP NA: packet too short\n");
 			return 0;
@@ -1174,7 +1176,7 @@
 
 	case NDISC_ROUTER_ADVERTISEMENT:
 		/* XXX: import nd_router_advert from glibc netinet/icmp6.h */
-		if (skb->nh.ipv6h->payload_len < 8+4+4) {
+		if (payload_len < 8+4+4) {
 			if (net_ratelimit())
 				printk(KERN_WARNING "ICMP RA: packet too short\n");
 			return 0;
@@ -1184,7 +1186,7 @@
 
 	case NDISC_REDIRECT:
 		/* XXX: import nd_redirect from glibc netinet/icmp6.h */
-		if (skb->nh.ipv6h->payload_len < 8+16+16) {
+		if (payload_len < 8+16+16) {
 			if (net_ratelimit())
 				printk(KERN_WARNING "ICMP redirect: packet too short\n");
 			return 0;
@@ -1196,7 +1198,7 @@
 		/* No RS support in the kernel, but we do some required checks */
 
 		/* XXX: import nd_router_solicit from glibc netinet/icmp6.h */
-		if (skb->nh.ipv6h->payload_len < 8) {
+		if (payload_len < 8) {
 			if (net_ratelimit())
 				printk(KERN_WARNING "ICMP RS: packet too short\n");
 			return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/Config.in linux/net/ipv6/netfilter/Config.in
--- linux.orig/net/ipv6/netfilter/Config.in	Fri Dec 21 17:42:05 2001
+++ linux/net/ipv6/netfilter/Config.in	Wed Jan 23 20:24:37 2002
@@ -9,9 +9,10 @@
 #  dep_tristate '  FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK
 #fi
 
-#if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
-#  tristate 'Userspace queueing via NETLINK (EXPERIMENTAL)' CONFIG_IP6_NF_QUEUE
-#fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  tristate 'Userspace queueing via NETLINK (EXPERIMENTAL)' CONFIG_IP6_NF_QUEUE
+fi
+
 tristate 'IP6 tables support (required for filtering/masq/NAT)' CONFIG_IP6_NF_IPTABLES
 if [ "$CONFIG_IP6_NF_IPTABLES" != "n" ]; then
 # The simple matches.
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/Makefile linux/net/ipv6/netfilter/Makefile
--- linux.orig/net/ipv6/netfilter/Makefile	Tue Oct 30 23:08:12 2001
+++ linux/net/ipv6/netfilter/Makefile	Wed Jan 23 20:24:37 2002
@@ -21,6 +21,7 @@
 obj-$(CONFIG_IP6_NF_FILTER) += ip6table_filter.o
 obj-$(CONFIG_IP6_NF_MANGLE) += ip6table_mangle.o
 obj-$(CONFIG_IP6_NF_TARGET_MARK) += ip6t_MARK.o
+obj-$(CONFIG_IP6_NF_QUEUE) += ip6_queue.o
 obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
 
 include $(TOPDIR)/Rules.make
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/ip6_queue.c linux/net/ipv6/netfilter/ip6_queue.c
--- linux.orig/net/ipv6/netfilter/ip6_queue.c	Thu Jan  1 00:00:00 1970
+++ linux/net/ipv6/netfilter/ip6_queue.c	Mon Feb  4 16:43:43 2002
@@ -0,0 +1,721 @@
+/*
+ * This is a module which is used for queueing IPv6 packets and
+ * communicating with userspace via netlink.
+ *
+ * (C) 2001 Fernando Anton, this code is GPL.
+ *     IPv64 Project - Work based in IPv64 draft by Arturo Azcorra.
+ *     Universidad Carlos III de Madrid - Leganes (Madrid) - Spain
+ *     Universidad Politecnica de Alcala de Henares - Alcala de H. (Madrid) - Spain
+ *     email: fanton@it.uc3m.es
+ *
+ * 2001-11-06: First try. Working with ip_queue.c for IPv4 and trying
+ *             to adapt it to IPv6
+ *             HEAVILY based in ipqueue.c by James Morris. It's just
+ *             a little modified version of it, so he's nearly the
+ *             real coder of this.
+ *             Few changes needed, mainly the hard_routing code and
+ *             the netlink socket protocol (we're NETLINK_IP6_FW).
+ *
+ */
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/ipv6.h>
+#include <linux/notifier.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/netlink.h>
+#include <linux/spinlock.h>
+#include <linux/rtnetlink.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <net/sock.h>
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+
+/* We're still usign the following structs. No need to change them: */
+/*   ipq_packet_msg                                                 */
+/*   ipq_mode_msg                                                   */
+/*   ipq_verdict_msg                                                */
+/*   ipq_peer_msg                                                   */
+#include <linux/netfilter_ipv4/ip_queue.h>
+#include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
+
+#define IPQ_QMAX_DEFAULT 1024
+#define IPQ_PROC_FS_NAME "ip6_queue"
+#define NET_IPQ_QMAX 2088
+#define NET_IPQ_QMAX_NAME "ip6_queue_maxlen"
+
+typedef struct ip6q_rt_info {
+	struct in6_addr daddr;
+	struct in6_addr saddr;
+} ip6q_rt_info_t;
+
+typedef struct ip6q_queue_element {
+	struct list_head list;		/* Links element into queue */
+	int verdict;			/* Current verdict */
+	struct nf_info *info;		/* Extra info from netfilter */
+	struct sk_buff *skb;		/* Packet inside */
+	ip6q_rt_info_t rt_info;		/* May need post-mangle routing */
+} ip6q_queue_element_t;
+
+typedef int (*ip6q_send_cb_t)(ip6q_queue_element_t *e);
+
+typedef struct ip6q_peer {
+	pid_t pid;			/* PID of userland peer */
+	unsigned char died;		/* We think the peer died */
+	unsigned char copy_mode;	/* Copy packet as well as metadata? */
+	size_t copy_range;		/* Range past metadata to copy */
+	ip6q_send_cb_t send;		/* Callback for sending data to peer */
+} ip6q_peer_t;
+
+typedef struct ip6q_queue {
+ 	int len;			/* Current queue len */
+ 	int *maxlen;			/* Maximum queue len, via sysctl */
+ 	unsigned char flushing;		/* If queue is being flushed */
+ 	unsigned char terminate;	/* If the queue is being terminated */
+ 	struct list_head list;		/* Head of packet queue */
+ 	spinlock_t lock;		/* Queue spinlock */
+ 	ip6q_peer_t peer;		/* Userland peer */
+} ip6q_queue_t;
+
+/****************************************************************************
+ *
+ * Packet queue
+ *
+ ****************************************************************************/
+/* Dequeue a packet if matched by cmp, or the next available if cmp is NULL */
+static ip6q_queue_element_t *
+ip6q_dequeue(ip6q_queue_t *q,
+            int (*cmp)(ip6q_queue_element_t *, unsigned long),
+            unsigned long data)
+{
+	struct list_head *i;
+
+	spin_lock_bh(&q->lock);
+	for (i = q->list.prev; i != &q->list; i = i->prev) {
+		ip6q_queue_element_t *e = (ip6q_queue_element_t *)i;
+		
+		if (!cmp || cmp(e, data)) {
+			list_del(&e->list);
+			q->len--;
+			spin_unlock_bh(&q->lock);
+			return e;
+		}
+	}
+	spin_unlock_bh(&q->lock);
+	return NULL;
+}
+
+/* Flush all packets */
+static void ip6q_flush(ip6q_queue_t *q)
+{
+	ip6q_queue_element_t *e;
+	
+	spin_lock_bh(&q->lock);
+	q->flushing = 1;
+	spin_unlock_bh(&q->lock);
+	while ((e = ip6q_dequeue(q, NULL, 0))) {
+		e->verdict = NF_DROP;
+		nf_reinject(e->skb, e->info, e->verdict);
+		kfree(e);
+	}
+	spin_lock_bh(&q->lock);
+	q->flushing = 0;
+	spin_unlock_bh(&q->lock);
+}
+
+static ip6q_queue_t *ip6q_create_queue(nf_queue_outfn_t outfn,
+                                     ip6q_send_cb_t send_cb,
+                                     int *errp, int *sysctl_qmax)
+{
+	int status;
+	ip6q_queue_t *q;
+
+	*errp = 0;
+	q = kmalloc(sizeof(ip6q_queue_t), GFP_KERNEL);
+	if (q == NULL) {
+		*errp = -ENOMEM;
+		return NULL;
+	}
+	q->peer.pid = 0;
+	q->peer.died = 0;
+	q->peer.copy_mode = IPQ_COPY_NONE;
+	q->peer.copy_range = 0;
+	q->peer.send = send_cb;
+	q->len = 0;
+	q->maxlen = sysctl_qmax;
+	q->flushing = 0;
+	q->terminate = 0;
+	INIT_LIST_HEAD(&q->list);
+	spin_lock_init(&q->lock);
+	status = nf_register_queue_handler(PF_INET6, outfn, q);
+	if (status < 0) {
+		*errp = -EBUSY;
+		kfree(q);
+		return NULL;
+	}
+	return q;
+}
+
+static int ip6q_enqueue(ip6q_queue_t *q,
+                       struct sk_buff *skb, struct nf_info *info)
+{
+	ip6q_queue_element_t *e;
+	int status;
+	
+	e = kmalloc(sizeof(*e), GFP_ATOMIC);
+	if (e == NULL) {
+		printk(KERN_ERR "ip6_queue: OOM in enqueue\n");
+		return -ENOMEM;
+	}
+
+	e->verdict = NF_DROP;
+	e->info = info;
+	e->skb = skb;
+
+	if (e->info->hook == NF_IP_LOCAL_OUT) {
+		struct ipv6hdr *iph = skb->nh.ipv6h;
+
+		e->rt_info.daddr = iph->daddr;
+		e->rt_info.saddr = iph->saddr;
+	}
+
+	spin_lock_bh(&q->lock);
+	if (q->len >= *q->maxlen) {
+		spin_unlock_bh(&q->lock);
+		if (net_ratelimit()) 
+			printk(KERN_WARNING "ip6_queue: full at %d entries, "
+			       "dropping packet(s).\n", q->len);
+		goto free_drop;
+	}
+	if (q->flushing || q->peer.copy_mode == IPQ_COPY_NONE
+	    || q->peer.pid == 0 || q->peer.died || q->terminate) {
+		spin_unlock_bh(&q->lock);
+		goto free_drop;
+	}
+	status = q->peer.send(e);
+	if (status > 0) {
+		list_add(&e->list, &q->list);
+		q->len++;
+		spin_unlock_bh(&q->lock);
+		return status;
+	}
+	spin_unlock_bh(&q->lock);
+	if (status == -ECONNREFUSED) {
+		printk(KERN_INFO "ip6_queue: peer %d died, "
+		       "resetting state and flushing queue\n", q->peer.pid);
+			q->peer.died = 1;
+			q->peer.pid = 0;
+			q->peer.copy_mode = IPQ_COPY_NONE;
+			q->peer.copy_range = 0;
+			ip6q_flush(q);
+	}
+free_drop:
+	kfree(e);
+	return -EBUSY;
+}
+
+static void ip6q_destroy_queue(ip6q_queue_t *q)
+{
+	nf_unregister_queue_handler(PF_INET6);
+	spin_lock_bh(&q->lock);
+	q->terminate = 1;
+	spin_unlock_bh(&q->lock);
+	ip6q_flush(q);
+	kfree(q);
+}
+
+/*
+ * Taken from net/ipv6/ip6_output.c
+ *
+ * We should use the one there, but is defined static
+ * so we put this just here and let the things as
+ * they are now.
+ *
+ * If that one is modified, this one should be modified too.
+ */
+static int route6_me_harder(struct sk_buff *skb)
+{
+	struct ipv6hdr *iph = skb->nh.ipv6h;
+	struct dst_entry *dst;
+	struct flowi fl;
+
+	fl.proto = iph->nexthdr;
+	fl.fl6_dst = &iph->daddr;
+	fl.fl6_src = &iph->saddr;
+	fl.oif = skb->sk ? skb->sk->bound_dev_if : 0;
+	fl.fl6_flowlabel = 0;
+	fl.uli_u.ports.dport = 0;
+	fl.uli_u.ports.sport = 0;
+
+	dst = ip6_route_output(skb->sk, &fl);
+
+	if (dst->error) {
+		if (net_ratelimit())
+			printk(KERN_DEBUG "route6_me_harder: No more route.\n");
+		return -EINVAL;
+	}
+
+	/* Drop old route. */
+	dst_release(skb->dst);
+
+	skb->dst = dst;
+	return 0;
+}
+static int ip6q_mangle_ipv6(ipq_verdict_msg_t *v, ip6q_queue_element_t *e)
+{
+	int diff;
+	struct ipv6hdr *user_iph = (struct ipv6hdr *)v->payload;
+
+	if (v->data_len < sizeof(*user_iph))
+		return 0;
+	diff = v->data_len - e->skb->len;
+	if (diff < 0)
+		skb_trim(e->skb, v->data_len);
+	else if (diff > 0) {
+		if (v->data_len > 0xFFFF)
+			return -EINVAL;
+		if (diff > skb_tailroom(e->skb)) {
+			struct sk_buff *newskb;
+			
+			newskb = skb_copy_expand(e->skb,
+			                         skb_headroom(e->skb),
+			                         diff,
+			                         GFP_ATOMIC);
+			if (newskb == NULL) {
+				printk(KERN_WARNING "ip6_queue: OOM "
+				      "in mangle, dropping packet\n");
+				return -ENOMEM;
+			}
+			if (e->skb->sk)
+				skb_set_owner_w(newskb, e->skb->sk);
+			kfree_skb(e->skb);
+			e->skb = newskb;
+		}
+		skb_put(e->skb, diff);
+	}
+	memcpy(e->skb->data, v->payload, v->data_len);
+	e->skb->nfcache |= NFC_ALTERED;
+
+	/*
+	 * Extra routing may needed on local out, as the QUEUE target never
+	 * returns control to the table.
+         * Not a nice way to cmp, but works
+	 */
+	if (e->info->hook == NF_IP_LOCAL_OUT) {
+		struct ipv6hdr *iph = e->skb->nh.ipv6h;
+		if (!(   iph->daddr.in6_u.u6_addr32[0] == e->rt_info.daddr.in6_u.u6_addr32[0]
+                      && iph->daddr.in6_u.u6_addr32[1] == e->rt_info.daddr.in6_u.u6_addr32[1]
+                      && iph->daddr.in6_u.u6_addr32[2] == e->rt_info.daddr.in6_u.u6_addr32[2]
+                      && iph->daddr.in6_u.u6_addr32[3] == e->rt_info.daddr.in6_u.u6_addr32[3]
+		      && iph->saddr.in6_u.u6_addr32[0] == e->rt_info.saddr.in6_u.u6_addr32[0]
+		      && iph->saddr.in6_u.u6_addr32[1] == e->rt_info.saddr.in6_u.u6_addr32[1]
+		      && iph->saddr.in6_u.u6_addr32[2] == e->rt_info.saddr.in6_u.u6_addr32[2]
+		      && iph->saddr.in6_u.u6_addr32[3] == e->rt_info.saddr.in6_u.u6_addr32[3]))
+			return route6_me_harder(e->skb);
+	}
+	return 0;
+}
+
+static inline int id_cmp(ip6q_queue_element_t *e, unsigned long id)
+{
+	return (id == (unsigned long )e);
+}
+
+static int ip6q_set_verdict(ip6q_queue_t *q,
+                           ipq_verdict_msg_t *v, unsigned int len)
+{
+	ip6q_queue_element_t *e;
+
+	if (v->value > NF_MAX_VERDICT)
+		return -EINVAL;
+	e = ip6q_dequeue(q, id_cmp, v->id);
+	if (e == NULL)
+		return -ENOENT;
+	else {
+		e->verdict = v->value;
+		if (v->data_len && v->data_len == len)
+			if (ip6q_mangle_ipv6(v, e) < 0)
+				e->verdict = NF_DROP;
+		nf_reinject(e->skb, e->info, e->verdict);
+		kfree(e);
+		return 0;
+	}
+}
+
+static int ip6q_receive_peer(ip6q_queue_t* q, ipq_peer_msg_t *m,
+                            unsigned char type, unsigned int len)
+{
+
+	int status = 0;
+	int busy;
+		
+	spin_lock_bh(&q->lock);
+	busy = (q->terminate || q->flushing);
+	spin_unlock_bh(&q->lock);
+	if (busy)
+		return -EBUSY;
+	if (len < sizeof(ipq_peer_msg_t))
+		return -EINVAL;
+	switch (type) {
+		case IPQM_MODE:
+			switch (m->msg.mode.value) {
+				case IPQ_COPY_META:
+					q->peer.copy_mode = IPQ_COPY_META;
+					q->peer.copy_range = 0;
+					break;
+				case IPQ_COPY_PACKET:
+					q->peer.copy_mode = IPQ_COPY_PACKET;
+					q->peer.copy_range = m->msg.mode.range;
+					if (q->peer.copy_range > 0xFFFF)
+						q->peer.copy_range = 0xFFFF;
+					break;
+				default:
+					status = -EINVAL;
+			}
+			break;
+		case IPQM_VERDICT:
+			if (m->msg.verdict.value > NF_MAX_VERDICT)
+				status = -EINVAL;
+			else
+				status = ip6q_set_verdict(q,
+				                         &m->msg.verdict,
+				                         len - sizeof(*m));
+			break;
+		default:
+			 status = -EINVAL;
+	}
+	return status;
+}
+
+static inline int dev_cmp(ip6q_queue_element_t *e, unsigned long ifindex)
+{
+	if (e->info->indev)
+		if (e->info->indev->ifindex == ifindex)
+			return 1;
+	if (e->info->outdev)
+		if (e->info->outdev->ifindex == ifindex)
+			return 1;
+	return 0;
+}
+
+/* Drop any queued packets associated with device ifindex */
+static void ip6q_dev_drop(ip6q_queue_t *q, int ifindex)
+{
+	ip6q_queue_element_t *e;
+	
+	while ((e = ip6q_dequeue(q, dev_cmp, ifindex))) {
+		e->verdict = NF_DROP;
+		nf_reinject(e->skb, e->info, e->verdict);
+		kfree(e);
+	}
+}
+
+/****************************************************************************
+ *
+ * Netfilter interface
+ *
+ ****************************************************************************/
+
+/*
+ * Packets arrive here from netfilter for queuing to userspace.
+ * All of them must be fed back via nf_reinject() or Alexey will kill Rusty.
+ */
+static int netfilter6_receive(struct sk_buff *skb,
+                             struct nf_info *info, void *data)
+{
+	return ip6q_enqueue((ip6q_queue_t *)data, skb, info);
+}
+
+/****************************************************************************
+ *
+ * Netlink interface.
+ *
+ ****************************************************************************/
+
+static struct sock *nfnl = NULL;
+/* This is not a static one, so we should not repeat its name */
+ip6q_queue_t *nlq6 = NULL;
+
+static struct sk_buff *netlink_build_message(ip6q_queue_element_t *e, int *errp)
+{
+	unsigned char *old_tail;
+	size_t size = 0;
+	size_t data_len = 0;
+	struct sk_buff *skb;
+	ipq_packet_msg_t *pm;
+	struct nlmsghdr *nlh;
+
+	switch (nlq6->peer.copy_mode) {
+		size_t copy_range;
+
+		case IPQ_COPY_META:
+			size = NLMSG_SPACE(sizeof(*pm));
+			data_len = 0;
+			break;
+		case IPQ_COPY_PACKET:
+			copy_range = nlq6->peer.copy_range;
+			if (copy_range == 0 || copy_range > e->skb->len)
+				data_len = e->skb->len;
+			else
+				data_len = copy_range;
+			size = NLMSG_SPACE(sizeof(*pm) + data_len);
+			
+			break;
+		case IPQ_COPY_NONE:
+		default:
+			*errp = -EINVAL;
+			return NULL;
+	}
+	skb = alloc_skb(size, GFP_ATOMIC);
+	if (!skb)
+		goto nlmsg_failure;
+	old_tail = skb->tail;
+	nlh = NLMSG_PUT(skb, 0, 0, IPQM_PACKET, size - sizeof(*nlh));
+	pm = NLMSG_DATA(nlh);
+	memset(pm, 0, sizeof(*pm));
+	pm->packet_id = (unsigned long )e;
+	pm->data_len = data_len;
+	pm->timestamp_sec = e->skb->stamp.tv_sec;
+	pm->timestamp_usec = e->skb->stamp.tv_usec;
+	pm->mark = e->skb->nfmark;
+	pm->hook = e->info->hook;
+	if (e->info->indev) strcpy(pm->indev_name, e->info->indev->name);
+	else pm->indev_name[0] = '\0';
+	if (e->info->outdev) strcpy(pm->outdev_name, e->info->outdev->name);
+	else pm->outdev_name[0] = '\0';
+	pm->hw_protocol = e->skb->protocol;
+	if (e->info->indev && e->skb->dev) {
+		pm->hw_type = e->skb->dev->type;
+		if (e->skb->dev->hard_header_parse)
+			pm->hw_addrlen =
+				e->skb->dev->hard_header_parse(e->skb,
+				                               pm->hw_addr);
+	}
+	if (data_len)
+		memcpy(pm->payload, e->skb->data, data_len);
+	nlh->nlmsg_len = skb->tail - old_tail;
+	NETLINK_CB(skb).dst_groups = 0;
+	return skb;
+nlmsg_failure:
+	if (skb)
+		kfree_skb(skb);
+	*errp = 0;
+	printk(KERN_ERR "ip6_queue: error creating netlink message\n");
+	return NULL;
+}
+
+static int netlink_send_peer(ip6q_queue_element_t *e)
+{
+	int status = 0;
+	struct sk_buff *skb;
+
+	skb = netlink_build_message(e, &status);
+	if (skb == NULL)
+		return status;
+	return netlink_unicast(nfnl, skb, nlq6->peer.pid, MSG_DONTWAIT);
+}
+
+#define RCV_SKB_FAIL(err) do { netlink_ack(skb, nlh, (err)); return; } while (0);
+
+static __inline__ void netlink_receive_user_skb(struct sk_buff *skb)
+{
+	int status, type;
+	struct nlmsghdr *nlh;
+
+	if (skb->len < sizeof(struct nlmsghdr))
+		return;
+
+	nlh = (struct nlmsghdr *)skb->data;
+	if (nlh->nlmsg_len < sizeof(struct nlmsghdr)
+	    || skb->len < nlh->nlmsg_len)
+	    	return;
+
+	if(nlh->nlmsg_pid <= 0
+	    || !(nlh->nlmsg_flags & NLM_F_REQUEST)
+	    || nlh->nlmsg_flags & NLM_F_MULTI)
+		RCV_SKB_FAIL(-EINVAL);
+	if (nlh->nlmsg_flags & MSG_TRUNC)
+		RCV_SKB_FAIL(-ECOMM);
+	type = nlh->nlmsg_type;
+	if (type < NLMSG_NOOP || type >= IPQM_MAX)
+		RCV_SKB_FAIL(-EINVAL);
+	if (type <= IPQM_BASE)
+		return;
+	if(!cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN))
+		RCV_SKB_FAIL(-EPERM);
+	if (nlq6->peer.pid && !nlq6->peer.died
+	    && (nlq6->peer.pid != nlh->nlmsg_pid)) {
+	    	printk(KERN_WARNING "ip6_queue: peer pid changed from %d to "
+	    	      "%d, flushing queue\n", nlq6->peer.pid, nlh->nlmsg_pid);
+		ip6q_flush(nlq6);
+	}	
+	nlq6->peer.pid = nlh->nlmsg_pid;
+	nlq6->peer.died = 0;
+	status = ip6q_receive_peer(nlq6, NLMSG_DATA(nlh),
+	                          type, skb->len - NLMSG_LENGTH(0));
+	if (status < 0)
+		RCV_SKB_FAIL(status);
+	if (nlh->nlmsg_flags & NLM_F_ACK)
+		netlink_ack(skb, nlh, 0);
+        return;
+}
+
+/* Note: we are only dealing with single part messages at the moment. */
+static void netlink_receive_user_sk(struct sock *sk, int len)
+{
+	do {
+		struct sk_buff *skb;
+
+		if (rtnl_shlock_nowait())
+			return;
+		while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+			netlink_receive_user_skb(skb);
+			kfree_skb(skb);
+		}
+		up(&rtnl_sem);
+	} while (nfnl && nfnl->receive_queue.qlen);
+}
+
+/****************************************************************************
+ *
+ * System events
+ *
+ ****************************************************************************/
+
+static int receive_event(struct notifier_block *this,
+                         unsigned long event, void *ptr)
+{
+	struct net_device *dev = ptr;
+
+	/* Drop any packets associated with the downed device */
+	if (event == NETDEV_DOWN)
+		ip6q_dev_drop(nlq6, dev->ifindex);
+	return NOTIFY_DONE;
+}
+
+struct notifier_block ip6q_dev_notifier = {
+	receive_event,
+	NULL,
+	0
+};
+
+/****************************************************************************
+ *
+ * Sysctl - queue tuning.
+ *
+ ****************************************************************************/
+
+static int sysctl_maxlen = IPQ_QMAX_DEFAULT;
+
+static struct ctl_table_header *ip6q_sysctl_header;
+
+static ctl_table ip6q_table[] = {
+	{ NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &sysctl_maxlen,
+	  sizeof(sysctl_maxlen), 0644,  NULL, proc_dointvec },
+ 	{ 0 }
+};
+
+static ctl_table ip6q_dir_table[] = {
+	{NET_IPV6, "ipv6", NULL, 0, 0555, ip6q_table, 0, 0, 0, 0, 0},
+	{ 0 }
+};
+
+static ctl_table ip6q_root_table[] = {
+	{CTL_NET, "net", NULL, 0, 0555, ip6q_dir_table, 0, 0, 0, 0, 0},
+	{ 0 }
+};
+
+/****************************************************************************
+ *
+ * Procfs - debugging info.
+ *
+ ****************************************************************************/
+
+static int ip6q_get_info(char *buffer, char **start, off_t offset, int length)
+{
+	int len;
+
+	spin_lock_bh(&nlq6->lock);
+	len = sprintf(buffer,
+	              "Peer pid            : %d\n"
+	              "Peer died           : %d\n"
+	              "Peer copy mode      : %d\n"
+	              "Peer copy range     : %Zu\n"
+	              "Queue length        : %d\n"
+	              "Queue max. length   : %d\n"
+	              "Queue flushing      : %d\n"
+	              "Queue terminate     : %d\n",
+	              nlq6->peer.pid,
+	              nlq6->peer.died,
+	              nlq6->peer.copy_mode,
+	              nlq6->peer.copy_range,
+	              nlq6->len,
+	              *nlq6->maxlen,
+	              nlq6->flushing,
+	              nlq6->terminate);
+	spin_unlock_bh(&nlq6->lock);
+	*start = buffer + offset;
+	len -= offset;
+	if (len > length)
+		len = length;
+	else if (len < 0)
+		len = 0;
+	return len;
+}
+
+/****************************************************************************
+ *
+ * Module stuff.
+ *
+ ****************************************************************************/
+
+static int __init init(void)
+{
+	int status = 0;
+	struct proc_dir_entry *proc;
+	
+        /* We must create the NETLINK_IP6_FW protocol service */
+	nfnl = netlink_kernel_create(NETLINK_IP6_FW, netlink_receive_user_sk);
+	if (nfnl == NULL) {
+		printk(KERN_ERR "ip6_queue: initialisation failed: unable to "
+		       "create kernel netlink socket\n");
+		return -ENOMEM;
+	}
+	nlq6 = ip6q_create_queue(netfilter6_receive,
+	                       netlink_send_peer, &status, &sysctl_maxlen);
+	if (nlq6 == NULL) {
+		printk(KERN_ERR "ip6_queue: initialisation failed: unable to "
+		       "create queue\n");
+		sock_release(nfnl->socket);
+		return status;
+	}
+        /* The file will be /proc/net/ip6_queue */
+	proc = proc_net_create(IPQ_PROC_FS_NAME, 0, ip6q_get_info);
+	if (proc) proc->owner = THIS_MODULE;
+	else {
+		ip6q_destroy_queue(nlq6);
+		sock_release(nfnl->socket);
+		return -ENOMEM;
+	}
+	register_netdevice_notifier(&ip6q_dev_notifier);
+	ip6q_sysctl_header = register_sysctl_table(ip6q_root_table, 0);
+	return status;
+}
+
+static void __exit fini(void)
+{
+	unregister_sysctl_table(ip6q_sysctl_header);
+	proc_net_remove(IPQ_PROC_FS_NAME);
+	unregister_netdevice_notifier(&ip6q_dev_notifier);
+	ip6q_destroy_queue(nlq6);
+	sock_release(nfnl->socket);
+}
+
+MODULE_DESCRIPTION("IPv6 packet queue handler");
+MODULE_LICENSE("GPL");
+
+module_init(init);
+module_exit(fini);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/ip6_tables.c linux/net/ipv6/netfilter/ip6_tables.c
--- linux.orig/net/ipv6/netfilter/ip6_tables.c	Tue Oct 30 23:08:12 2001
+++ linux/net/ipv6/netfilter/ip6_tables.c	Wed Jan 23 20:24:37 2002
@@ -2,6 +2,11 @@
  * Packet matching code.
  *
  * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling
+ * Copyright (C) 2000-2002 Netfilter core team <coreteam@netfilter.org>
+ *
+ * 19 Jan 2002 Harald Welte <laforge@gnumonks.org>
+ * 	- increase module usage count as soon as we have rules inside
+ * 	  a table
  */
 #include <linux/config.h>
 #include <linux/skbuff.h>
@@ -86,6 +91,8 @@
 	unsigned int size;
 	/* Number of entries: FIXME. --RR */
 	unsigned int number;
+	/* Initial number of entries. Needed for module usage count */
+	unsigned int initial_entries;
 
 	/* Entry points and underflows */
 	unsigned int hook_entry[NF_IP6_NUMHOOKS];
@@ -949,6 +956,7 @@
 	}
 	oldinfo = table->private;
 	table->private = newinfo;
+	newinfo->initial_entries = oldinfo->initial_entries;
 	write_unlock_bh(&table->lock);
 
 	return oldinfo;
@@ -1148,6 +1156,16 @@
 	if (!oldinfo)
 		goto free_newinfo_counters_untrans_unlock;
 
+	/* Update module usage count based on number of rules */
+	duprintf("do_replace: oldnum=%u, initnum=%u, newnum=%u\n",
+		oldinfo->number, oldinfo->initial_entries, newinfo->number);
+	if (t->me && (oldinfo->number <= oldinfo->initial_entries) &&
+ 	    (newinfo->number > oldinfo->initial_entries))
+		__MOD_INC_USE_COUNT(t->me);
+	else if (t->me && (oldinfo->number > oldinfo->initial_entries) &&
+	 	 (newinfo->number <= oldinfo->initial_entries))
+		__MOD_DEC_USE_COUNT(t->me);
+
 	/* Get the old counters. */
 	get_counters(oldinfo, counters);
 	/* Decrease module usage counts and free resource */
@@ -1406,7 +1424,7 @@
 	int ret;
 	struct ip6t_table_info *newinfo;
 	static struct ip6t_table_info bootstrap
-		= { 0, 0, { 0 }, { 0 }, { }, { } };
+		= { 0, 0, 0, { 0 }, { 0 }, { }, { } };
 
 	MOD_INC_USE_COUNT;
 	newinfo = vmalloc(sizeof(struct ip6t_table_info)
@@ -1783,7 +1801,7 @@
 	}
 #endif
 
-	printk("ip6_tables: (c)2000 Netfilter core team\n");
+	printk("ip6_tables: (C) 2000-2002 Netfilter core team\n");
 	return 0;
 }
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/ip6t_MARK.c linux/net/ipv6/netfilter/ip6t_MARK.c
--- linux.orig/net/ipv6/netfilter/ip6t_MARK.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv6/netfilter/ip6t_MARK.c	Wed Jan 23 20:24:37 2002
@@ -51,7 +51,7 @@
 
 static int __init init(void)
 {
-	printk(KERN_DEBUG "registreing ipv6 mark target\n");
+	printk(KERN_DEBUG "registering ipv6 mark target\n");
 	if (ip6t_register_target(&ip6t_mark_reg))
 		return -EINVAL;
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/ip6table_filter.c linux/net/ipv6/netfilter/ip6table_filter.c
--- linux.orig/net/ipv6/netfilter/ip6table_filter.c	Sun Sep 30 19:26:08 2001
+++ linux/net/ipv6/netfilter/ip6table_filter.c	Wed Jan 23 20:24:37 2002
@@ -83,7 +83,7 @@
 
 static struct ip6t_table packet_filter
 = { { NULL, NULL }, "filter", &initial_table.repl,
-    FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+    FILTER_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/netfilter/ip6table_mangle.c linux/net/ipv6/netfilter/ip6table_mangle.c
--- linux.orig/net/ipv6/netfilter/ip6table_mangle.c	Sun Sep 30 19:26:09 2001
+++ linux/net/ipv6/netfilter/ip6table_mangle.c	Wed Jan 23 20:24:37 2002
@@ -1,14 +1,18 @@
 /*
  * IPv6 packet mangling table, a port of the IPv4 mangle table to IPv6
  *
- * Copyright (C) 2000 by Harald Welte <laforge@gnumonks.org>
+ * Copyright (C) 2000-2001 by Harald Welte <laforge@gnumonks.org>
  */
 #include <linux/module.h>
 #include <linux/netfilter_ipv6/ip6_tables.h>
 
-#define MANGLE_VALID_HOOKS ((1 << NF_IP6_PRE_ROUTING) | (1 << NF_IP6_LOCAL_OUT))
+#define MANGLE_VALID_HOOKS ((1 << NF_IP6_PRE_ROUTING) | \
+			    (1 << NF_IP6_LOCAL_IN) | \
+			    (1 << NF_IP6_FORWARD) | \
+			    (1 << NF_IP6_LOCAL_OUT) | \
+			    (1 << NF_IP6_POST_ROUTING))
 
-#if 1
+#if 0
 #define DEBUGP(x, args...)	printk(KERN_DEBUG x, ## args)
 #else
 #define DEBUGP(x, args...)
@@ -36,19 +40,41 @@
 static struct
 {
 	struct ip6t_replace repl;
-	struct ip6t_standard entries[2];
+	struct ip6t_standard entries[5];
 	struct ip6t_error term;
 } initial_table __initdata
-= { { "mangle", MANGLE_VALID_HOOKS, 3,
-      sizeof(struct ip6t_standard) * 2 + sizeof(struct ip6t_error),
-      { [NF_IP6_PRE_ROUTING] 0,
-	[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) },
-      { [NF_IP6_PRE_ROUTING] 0,
-	[NF_IP6_LOCAL_OUT] sizeof(struct ip6t_standard) },
+= { { "mangle", MANGLE_VALID_HOOKS, 6,
+      sizeof(struct ip6t_standard) * 5 + sizeof(struct ip6t_error),
+      { [NF_IP6_PRE_ROUTING] 	0,
+	[NF_IP6_LOCAL_IN]	sizeof(struct ip6t_standard),
+	[NF_IP6_FORWARD]	sizeof(struct ip6t_standard) * 2,
+	[NF_IP6_LOCAL_OUT] 	sizeof(struct ip6t_standard) * 3,
+	[NF_IP6_POST_ROUTING]	sizeof(struct ip6t_standard) * 4},
+      { [NF_IP6_PRE_ROUTING] 	0,
+	[NF_IP6_LOCAL_IN]	sizeof(struct ip6t_standard),
+	[NF_IP6_FORWARD]	sizeof(struct ip6t_standard) * 2,
+	[NF_IP6_LOCAL_OUT] 	sizeof(struct ip6t_standard) * 3,
+	[NF_IP6_POST_ROUTING]	sizeof(struct ip6t_standard) * 4},
       0, NULL, { } },
     {
 	    /* PRE_ROUTING */
-	    { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ip6t_entry),
+		sizeof(struct ip6t_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
+	    /* LOCAL_IN */
+            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ip6t_entry),
+		sizeof(struct ip6t_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
+	    /* FORWARD */
+            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
 		0,
 		sizeof(struct ip6t_entry),
 		sizeof(struct ip6t_standard),
@@ -56,6 +82,14 @@
 	      { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
 		-NF_ACCEPT - 1 } },
 	    /* LOCAL_OUT */
+            { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
+		0,
+		sizeof(struct ip6t_entry),
+		sizeof(struct ip6t_standard),
+		0, { 0, 0 }, { } },
+	      { { { { IP6T_ALIGN(sizeof(struct ip6t_standard_target)), "" } }, { } },
+		-NF_ACCEPT - 1 } },
+	    /* POST_ROUTING */
 	    { { { { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, { { { 0 } } }, "", "", { 0 }, { 0 }, 0, 0, 0 },
 		0,
 		sizeof(struct ip6t_entry),
@@ -79,11 +113,11 @@
 
 static struct ip6t_table packet_mangler
 = { { NULL, NULL }, "mangle", &initial_table.repl,
-    MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL };
+    MANGLE_VALID_HOOKS, RW_LOCK_UNLOCKED, NULL, THIS_MODULE };
 
 /* The work comes in here from netfilter.c. */
 static unsigned int
-ip6t_hook(unsigned int hook,
+ip6t_route_hook(unsigned int hook,
 	 struct sk_buff **pskb,
 	 const struct net_device *in,
 	 const struct net_device *out,
@@ -93,7 +127,7 @@
 }
 
 static unsigned int
-ip6t_local_out_hook(unsigned int hook,
+ip6t_local_hook(unsigned int hook,
 		   struct sk_buff **pskb,
 		   const struct net_device *in,
 		   const struct net_device *out,
@@ -142,9 +176,11 @@
 }
 
 static struct nf_hook_ops ip6t_ops[]
-= { { { NULL, NULL }, ip6t_hook, PF_INET6, NF_IP6_PRE_ROUTING, NF_IP6_PRI_MANGLE },
-    { { NULL, NULL }, ip6t_local_out_hook, PF_INET6, NF_IP6_LOCAL_OUT,
-		NF_IP6_PRI_MANGLE }
+= { { { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_PRE_ROUTING,  NF_IP6_PRI_MANGLE },
+    { { NULL, NULL }, ip6t_local_hook, PF_INET6, NF_IP6_LOCAL_IN,     NF_IP6_PRI_MANGLE },
+    { { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_FORWARD,      NF_IP6_PRI_MANGLE },
+    { { NULL, NULL }, ip6t_local_hook, PF_INET6, NF_IP6_LOCAL_OUT,    NF_IP6_PRI_MANGLE },
+    { { NULL, NULL }, ip6t_route_hook, PF_INET6, NF_IP6_POST_ROUTING, NF_IP6_PRI_MANGLE }
 };
 
 static int __init init(void)
@@ -165,8 +201,26 @@
 	if (ret < 0)
 		goto cleanup_hook0;
 
+	ret = nf_register_hook(&ip6t_ops[2]);
+	if (ret < 0)
+		goto cleanup_hook1;
+
+	ret = nf_register_hook(&ip6t_ops[3]);
+	if (ret < 0)
+		goto cleanup_hook2;
+
+	ret = nf_register_hook(&ip6t_ops[4]);
+	if (ret < 0)
+		goto cleanup_hook3;
+
 	return ret;
 
+ cleanup_hook3:
+        nf_unregister_hook(&ip6t_ops[3]);
+ cleanup_hook2:
+	nf_unregister_hook(&ip6t_ops[2]);
+ cleanup_hook1:
+	nf_unregister_hook(&ip6t_ops[1]);
  cleanup_hook0:
 	nf_unregister_hook(&ip6t_ops[0]);
  cleanup_table:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/proc.c linux/net/ipv6/proc.c
--- linux.orig/net/ipv6/proc.c	Mon Jul 10 05:30:41 2000
+++ linux/net/ipv6/proc.c	Mon Feb  4 16:47:32 2002
@@ -7,7 +7,7 @@
  *		PROC file system.  This is very similar to the IPv4 version,
  *		except it reports the sockets in the INET6 address family.
  *
- * Version:	$Id: proc.c,v 1.15 2000/07/07 22:29:42 davem Exp $
+ * Version:	$Id: proc.c,v 1.15.2.1 2002/01/24 15:46:07 davem Exp $
  *
  * Authors:	David S. Miller (davem@caip.rutgers.edu)
  *
@@ -37,7 +37,7 @@
 	return res;
 }
 
-int afinet6_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+int afinet6_get_info(char *buffer, char **start, off_t offset, int length)
 {
 	int len = 0;
 	len += sprintf(buffer+len, "TCP6: inuse %d\n",
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- linux.orig/net/ipv6/tcp_ipv6.c	Tue Jan 22 17:51:24 2002
+++ linux/net/ipv6/tcp_ipv6.c	Mon Jan 14 16:24:07 2002
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.142 2001/11/06 22:21:08 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.142.2.1 2001/12/21 05:06:08 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -1172,6 +1172,7 @@
 
 	tcp_parse_options(skb, &tp, 0);
 
+	tp.tstamp_ok = tp.saw_tstamp;
 	tcp_openreq_init(req, &tp, skb);
 
 	req->class = &or_ipv6;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/Config.in linux/net/irda/Config.in
--- linux.orig/net/irda/Config.in	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/Config.in	Thu Jan 10 18:12:41 2002
@@ -14,13 +14,10 @@
       source net/irda/irnet/Config.in
       source net/irda/ircomm/Config.in
       bool '  Ultra (connectionless) protocol' CONFIG_IRDA_ULTRA
-      bool '  IrDA protocol options' CONFIG_IRDA_OPTIONS
-      if [ "$CONFIG_IRDA_OPTIONS" != "n" ]; then
-	 comment '  IrDA options'
-	 bool '    Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
-	 bool '    Fast RRs' CONFIG_IRDA_FAST_RR
-	 bool '    Debug information' CONFIG_IRDA_DEBUG
-      fi
+      comment 'IrDA options'
+      bool '  Cache last LSAP' CONFIG_IRDA_CACHE_LAST_LSAP
+      bool '  Fast RRs (low latency)' CONFIG_IRDA_FAST_RR
+      bool '  Debug information' CONFIG_IRDA_DEBUG
    fi
 
    if [ "$CONFIG_IRDA" != "n" ]; then
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/ircomm/ircomm_core.c linux/net/irda/ircomm/ircomm_core.c
--- linux.orig/net/irda/ircomm/ircomm_core.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/ircomm/ircomm_core.c	Thu Jan 10 18:12:41 2002
@@ -490,18 +490,34 @@
 { 	
 	struct ircomm_cb *self;
 	unsigned long flags;
-	int i=0;
 	
 	save_flags(flags);
 	cli();
 
 	len = 0;
 
-	len += sprintf(buf+len, "Instance %d:\n", i++);
-
 	self = (struct ircomm_cb *) hashbin_get_first(ircomm);
 	while (self != NULL) {
 		ASSERT(self->magic == IRCOMM_MAGIC, return len;);
+
+		if(self->line < 0x10)
+			len += sprintf(buf+len, "ircomm%d", self->line);
+		else
+			len += sprintf(buf+len, "irlpt%d", self->line - 0x10);
+		len += sprintf(buf+len, " state: %s, ",
+			       ircomm_state[ self->state]);
+		len += sprintf(buf+len, 
+			       "slsap_sel: %#02x, dlsap_sel: %#02x, mode:",
+			       self->slsap_sel, self->dlsap_sel); 
+		if(self->service_type & IRCOMM_3_WIRE_RAW)
+			len += sprintf(buf+len, " 3-wire-raw");
+		if(self->service_type & IRCOMM_3_WIRE)
+			len += sprintf(buf+len, " 3-wire");
+		if(self->service_type & IRCOMM_9_WIRE)
+			len += sprintf(buf+len, " 9-wire");
+		if(self->service_type & IRCOMM_CENTRONICS)
+			len += sprintf(buf+len, " Centronics");
+		len += sprintf(buf+len, "\n");
 
 		self = (struct ircomm_cb *) hashbin_get_next(ircomm);
  	} 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/ircomm/ircomm_lmp.c linux/net/irda/ircomm/ircomm_lmp.c
--- linux.orig/net/irda/ircomm/ircomm_lmp.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/ircomm/ircomm_lmp.c	Thu Jan 10 18:12:41 2002
@@ -103,12 +103,30 @@
  *    
  *
  */
-int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *skb)
+int ircomm_lmp_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
 {
+	struct sk_buff *skb;
 	int ret;
 
 	IRDA_DEBUG(0, __FUNCTION__"()\n");
 	
+	/* Any userdata supplied? */
+	if (userdata == NULL) {
+		skb = dev_alloc_skb(64);
+		if (!skb)
+			return -ENOMEM;
+
+		/* Reserve space for MUX and LAP header */
+		skb_reserve(skb, LMP_MAX_HEADER);
+	} else {
+		skb = userdata;
+		/*  
+		 *  Check that the client has reserved enough space for 
+		 *  headers
+		 */
+		ASSERT(skb_headroom(skb) >= LMP_MAX_HEADER, return -1;);
+	}
+
 	ret = irlmp_connect_response(self->lsap, skb);
 
 	return 0;
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irda_device.c linux/net/irda/irda_device.c
--- linux.orig/net/irda/irda_device.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irda_device.c	Thu Jan 10 18:12:41 2002
@@ -81,6 +81,7 @@
 	"TV_REMOTE",
 };
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *task_state[] = {
 	"IRDA_TASK_INIT",
 	"IRDA_TASK_DONE", 
@@ -92,6 +93,7 @@
 	"IRDA_TASK_CHILD_WAIT",
 	"IRDA_TASK_CHILD_DONE",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 static void irda_task_timer_expired(void *data);
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/iriap.c linux/net/irda/iriap.c
--- linux.orig/net/irda/iriap.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/iriap.c	Thu Jan 10 18:12:41 2002
@@ -41,6 +41,7 @@
 #include <net/irda/iriap_event.h>
 #include <net/irda/iriap.h>
 
+#ifdef CONFIG_IRDA_DEBUG
 /* FIXME: This one should go in irlmp.c */
 static const char *ias_charset_types[] = {
 	"CS_ASCII",
@@ -55,6 +56,7 @@
 	"CS_ISO_8859_9",
 	"CS_UNICODE"
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 static hashbin_t *iriap = NULL;
 static __u32 service_handle; 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irlan/irlan_common.c linux/net/irda/irlan/irlan_common.c
--- linux.orig/net/irda/irlan/irlan_common.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irlan/irlan_common.c	Thu Jan 10 18:12:41 2002
@@ -317,8 +317,15 @@
 
 	del_timer(&self->watchdog_timer);
 
-	irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
-	irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, skb);
+	/* If you want to pass the skb to *both* state machines, you will
+	 * need to skb_clone() it, so that you don't free it twice.
+	 * As the state machines don't need it, git rid of it here...
+	 * Jean II */
+	if (skb)
+		dev_kfree_skb(skb);
+
+	irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
+	irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL);
 
 	if (self->provider.access_type == ACCESS_PEER) {
 		/* 
@@ -421,6 +428,13 @@
 		break;
 	}
 	
+	/* If you want to pass the skb to *both* state machines, you will
+	 * need to skb_clone() it, so that you don't free it twice.
+	 * As the state machines don't need it, git rid of it here...
+	 * Jean II */
+	if (userdata)
+		dev_kfree_skb(userdata);
+
 	irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL);
 	irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL);
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irlan/irlan_eth.c linux/net/irda/irlan/irlan_eth.c
--- linux.orig/net/irda/irlan/irlan_eth.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irlan/irlan_eth.c	Thu Jan 10 18:12:41 2002
@@ -61,7 +61,16 @@
 	dev->hard_start_xmit    = irlan_eth_xmit; 
 	dev->get_stats	        = irlan_eth_get_stats;
 	dev->set_multicast_list = irlan_eth_set_multicast_list;
-	dev->features          |= NETIF_F_DYNALLOC;
+
+	/* NETIF_F_DYNALLOC feature was set by irlan_eth_init() and would
+	 * cause the unregister_netdev() to do asynch completion _and_
+	 * kfree self->dev afterwards. Which is really bad because the
+	 * netdevice was not allocated separately but is embedded in
+	 * our control block and therefore gets freed with *self.
+	 * The only reason why this would have been enabled is to hide
+	 * some netdev refcount issues. If unregister_netdev() blocks
+	 * forever, tell us about it... */
+	//dev->features          |= NETIF_F_DYNALLOC;
 
 	ether_setup(dev);
 	
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irlap.c linux/net/irda/irlap.c
--- linux.orig/net/irda/irlap.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irlap.c	Thu Jan 10 18:12:41 2002
@@ -59,6 +59,7 @@
 extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb);
 static void __irlap_close(struct irlap_cb *self);
 
+#ifdef CONFIG_IRDA_DEBUG
 static char *lap_reasons[] = {
 	"ERROR, NOT USED",
 	"LAP_DISC_INDICATION",
@@ -69,6 +70,7 @@
 	"LAP_PRIMARY_CONFLICT",
 	"ERROR, NOT USED",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 #ifdef CONFIG_PROC_FS
 int irlap_proc_read(char *, char **, off_t, int);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irlap_event.c linux/net/irda/irlap_event.c
--- linux.orig/net/irda/irlap_event.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irlap_event.c	Thu Jan 10 18:12:41 2002
@@ -77,6 +77,7 @@
 static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, 
 				   struct sk_buff *, struct irlap_info *);
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *irlap_event[] = {
 	"DISCOVERY_REQUEST",
 	"CONNECT_REQUEST",
@@ -117,6 +118,7 @@
 	"BACKOFF_TIMER_EXPIRED",
 	"MEDIA_BUSY_TIMER_EXPIRED",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 const char *irlap_state[] = {
 	"LAP_NDM",
@@ -312,7 +314,6 @@
 {
 	discovery_t *discovery_rsp;
 	int ret = 0;
-	int i;
 
 	ASSERT(self != NULL, return -1;);
 	ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -478,6 +479,8 @@
 		break;
 #ifdef CONFIG_IRDA_ULTRA
 	case SEND_UI_FRAME:
+	{   
+		int i;
 		/* Only allowed to repeat an operation twice */
 		for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) {
 			skb = skb_dequeue(&self->txq_ultra);
@@ -492,6 +495,7 @@
 			irda_device_set_media_busy(self->netdev, TRUE);
 		}
 		break;
+	}
 	case RECV_UI_FRAME:
 		/* Only accept broadcast frames in NDM mode */
 		if (info->caddr != CBROADCAST) {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irlmp_event.c linux/net/irda/irlmp_event.c
--- linux.orig/net/irda/irlmp_event.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irlmp_event.c	Thu Jan 10 18:12:41 2002
@@ -49,6 +49,7 @@
 	"LSAP_SETUP_PEND",
 };
 
+#ifdef CONFIG_IRDA_DEBUG
 static const char *irlmp_event[] = {
 	"LM_CONNECT_REQUEST",
  	"LM_CONNECT_CONFIRM",
@@ -75,6 +76,7 @@
  	"LM_LAP_DISCOVERY_CONFIRM",
 	"LM_LAP_IDLE_TIMEOUT",
 };
+#endif	/* CONFIG_IRDA_DEBUG */
 
 /* LAP Connection control proto declarations */
 static void irlmp_state_standby  (struct lap_cb *, IRLMP_EVENT, 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/irda/irsyms.c linux/net/irda/irsyms.c
--- linux.orig/net/irda/irsyms.c	Tue Jan 22 17:51:24 2002
+++ linux/net/irda/irsyms.c	Thu Jan 10 18:12:41 2002
@@ -198,7 +198,7 @@
 	return 0;
 }
 
-static void __exit irda_cleanup(void)
+void __exit irda_cleanup(void)
 {
 #ifdef CONFIG_SYSCTL
 	irda_sysctl_unregister();
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/netlink/af_netlink.c linux/net/netlink/af_netlink.c
--- linux.orig/net/netlink/af_netlink.c	Sat Jun 30 02:38:26 2001
+++ linux/net/netlink/af_netlink.c	Wed Jan 23 20:15:38 2002
@@ -36,7 +36,7 @@
 #include <asm/uaccess.h>
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
-#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 #include <linux/proc_fs.h>
 #include <linux/smp_lock.h>
 #include <net/sock.h>
@@ -47,8 +47,6 @@
 #if defined(CONFIG_NETLINK_DEV) || defined(CONFIG_NETLINK_DEV_MODULE)
 #define NL_EMULATE_DEV
 #endif
-
-#define BUG_TRAP(x) if (!(x)) { printk("Assertion (" #x ") failed at " __FILE__ "(%d):" __FUNCTION__ "\n", __LINE__); }
 
 struct netlink_opt
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/netrom/af_netrom.c linux/net/netrom/af_netrom.c
--- linux.orig/net/netrom/af_netrom.c	Mon Sep 10 14:58:35 2001
+++ linux/net/netrom/af_netrom.c	Wed Feb  6 20:47:54 2002
@@ -31,6 +31,7 @@
  *	NET/ROM 007	Jonathan(G4KLX)	New timer architecture.
  *					Impmented Idle timer.
  *			Arnaldo C. Melo s/suser/capable/, micro cleanups
+ *			Jeroen (PE1RXQ)	Use sock_orphan() on release.
  */
 
 #include <linux/config.h>
@@ -572,9 +573,8 @@
 			sk->state                = TCP_CLOSE;
 			sk->shutdown            |= SEND_SHUTDOWN;
 			sk->state_change(sk);
-			sk->dead                 = 1;
+			sock_orphan(sk);
 			sk->destroy              = 1;
-			sk->socket               = NULL;
 			break;
 
 		default:
@@ -838,17 +838,38 @@
 	frametype          = skb->data[19] & 0x0F;
 	flags              = skb->data[19] & 0xF0;
 
+	switch (frametype) {
+	case NR_PROTOEXT:
 #ifdef CONFIG_INET
-	/*
-	 * Check for an incoming IP over NET/ROM frame.
-	 */
-	if (frametype == NR_PROTOEXT && circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
-		skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
-		skb->h.raw = skb->data;
+		/*
+		 * Check for an incoming IP over NET/ROM frame.
+		 */
+		if (circuit_index == NR_PROTO_IP && circuit_id == NR_PROTO_IP) {
+			skb_pull(skb, NR_NETWORK_LEN + NR_TRANSPORT_LEN);
+			skb->h.raw = skb->data;
 
-		return nr_rx_ip(skb, dev);
-	}
+			return nr_rx_ip(skb, dev);
+		}
 #endif
+		return 0;
+
+	case NR_CONNREQ:
+	case NR_CONNACK:
+	case NR_DISCREQ:
+	case NR_DISCACK:
+	case NR_INFO:
+	case NR_INFOACK:
+		/*
+		 * These frame types we understand.
+		 */
+		break;
+
+	default:
+		/*
+		 * Everything else is ignored.
+		 */
+		return 0;
+	}
 
 	/*
 	 * Find an existing socket connection, based on circuit ID, if it's
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/netsyms.c linux/net/netsyms.c
--- linux.orig/net/netsyms.c	Fri Dec 21 17:42:06 2001
+++ linux/net/netsyms.c	Thu Feb  7 16:52:23 2002
@@ -292,6 +292,8 @@
 EXPORT_SYMBOL(ndisc_mc_map);
 EXPORT_SYMBOL(register_inet6addr_notifier);
 EXPORT_SYMBOL(unregister_inet6addr_notifier);
+#include <net/ip6_route.h>
+EXPORT_SYMBOL(ip6_route_output);
 #endif
 #if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_KHTTPD) || defined (CONFIG_KHTTPD_MODULE)
 /* inet functions common to v4 and v6 */
@@ -519,6 +521,10 @@
 EXPORT_SYMBOL(hippi_type_trans);
 #endif
 
+#ifdef CONFIG_NET_FASTROUTE
+EXPORT_SYMBOL(netdev_fastroute);
+#endif
+
 #ifdef CONFIG_SYSCTL
 EXPORT_SYMBOL(sysctl_wmem_max);
 EXPORT_SYMBOL(sysctl_rmem_max);
@@ -571,6 +577,10 @@
 EXPORT_SYMBOL(nf_setsockopt);
 EXPORT_SYMBOL(nf_getsockopt);
 EXPORT_SYMBOL(ip_ct_attach);
+#ifdef CONFIG_INET
+#include <linux/netfilter_ipv4.h>
+EXPORT_SYMBOL(ip_route_me_harder);
+#endif
 #endif
 
 EXPORT_SYMBOL(register_gifconf);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/rose/af_rose.c linux/net/rose/af_rose.c
--- linux.orig/net/rose/af_rose.c	Mon Sep 10 14:58:35 2001
+++ linux/net/rose/af_rose.c	Wed Feb  6 20:47:54 2002
@@ -22,6 +22,10 @@
  *					Added use count to neighbour.
  *                      Tomi(OH2BNS)    Fixed rose_getname().
  *                      Arnaldo C. Melo s/suser/capable/ + micro cleanups
+ *                      Joroen (PE1RXQ) Use sock_orphan() on release.
+ *
+ *  ROSE 0.63	Jean-Paul(F6FBB) Fixed wrong length of L3 packets
+ *					Added CLEAR_REQUEST facilities
  */
 
 #include <linux/config.h>
@@ -650,16 +654,16 @@
 			sk->state                = TCP_CLOSE;
 			sk->shutdown            |= SEND_SHUTDOWN;
 			sk->state_change(sk);
-			sk->dead                 = 1;
+			sock_orphan(sk);
 			sk->destroy              = 1;
 			break;
 
 		default:
+			sk->socket = NULL;
 			break;
 	}
 
 	sock->sk = NULL;	
-	sk->socket = NULL;	/* Not used, but we should do this. **/
 
 	return 0;
 }
@@ -1429,7 +1433,7 @@
 
 static struct net_device *dev_rose;
 
-static const char banner[] = KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.62 for AX25.037 Linux 2.4\n";
+static const char banner[] = KERN_INFO "F6FBB/G4KLX ROSE for Linux. Version 0.63 for AX25.037 Linux 2.4\n";
 
 static int __init rose_proto_init(void)
 {
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/rose/rose_link.c linux/net/rose/rose_link.c
--- linux.orig/net/rose/rose_link.c	Fri Dec 29 22:44:46 2000
+++ linux/net/rose/rose_link.c	Wed Feb  6 20:47:54 2002
@@ -264,15 +264,21 @@
 	struct sk_buff *skb;
 	unsigned char *dptr;
 	int len;
+	struct net_device *first;
+	int faclen = 0;
 
 	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 3;
 
-	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
+	first = rose_dev_first();
+	if (first)
+		faclen = 6 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN;
+	
+	if ((skb = alloc_skb(len + faclen, GFP_ATOMIC)) == NULL)
 		return;
 
 	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN);
 
-	dptr = skb_put(skb, ROSE_MIN_LEN + 3);
+	dptr = skb_put(skb, ROSE_MIN_LEN + 3 + faclen);
 
 	*dptr++ = AX25_P_ROSE;
 	*dptr++ = ((lci >> 8) & 0x0F) | ROSE_GFI;
@@ -280,6 +286,21 @@
 	*dptr++ = ROSE_CLEAR_REQUEST;
 	*dptr++ = cause;
 	*dptr++ = diagnostic;
+
+	if (first) {	
+		*dptr++ = 0x00;		/* Address length */
+		*dptr++ = 4 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN; /* Facilities length */
+		*dptr++ = 0;
+		*dptr++ = FAC_NATIONAL;
+		*dptr++ = FAC_NATIONAL_FAIL_CALL;
+		*dptr++ = AX25_ADDR_LEN;
+		memcpy(dptr, &rose_callsign, AX25_ADDR_LEN);
+		dptr += AX25_ADDR_LEN;
+		*dptr++ = FAC_NATIONAL_FAIL_ADD;
+		*dptr++ = ROSE_ADDR_LEN + 1;
+		*dptr++ = ROSE_ADDR_LEN * 2;
+		memcpy(dptr, first->dev_addr, ROSE_ADDR_LEN);
+	}
 
 	if (!rose_send_frame(skb, neigh))
 		kfree_skb(skb);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/rose/rose_subr.c linux/net/rose/rose_subr.c
--- linux.orig/net/rose/rose_subr.c	Sat Jun 30 02:38:26 2001
+++ linux/net/rose/rose_subr.c	Wed Feb  6 20:47:54 2002
@@ -114,9 +114,10 @@
 	unsigned char  lci1, lci2;
 	char buffer[100];
 	int len, faclen = 0;
+	int ax25_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1;
 
-	len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
-
+	len = ax25_header_len + ROSE_MIN_LEN;
+	
 	switch (frametype) {
 		case ROSE_CALL_REQUEST:
 			len   += 1 + ROSE_ADDR_LEN + ROSE_ADDR_LEN;
@@ -124,10 +125,27 @@
 			len   += faclen;
 			break;
 		case ROSE_CALL_ACCEPTED:
-		case ROSE_CLEAR_REQUEST:
 		case ROSE_RESET_REQUEST:
 			len   += 2;
 			break;
+		case ROSE_CLEAR_REQUEST:
+			len   += 3;
+			/* facilities */
+			faclen = 3 + 2 + AX25_ADDR_LEN + 3 + ROSE_ADDR_LEN;
+			dptr = buffer;
+			*dptr++ = faclen-1;	/* Facilities length */
+			*dptr++ = 0;
+			*dptr++ = FAC_NATIONAL;
+			*dptr++ = FAC_NATIONAL_FAIL_CALL;
+			*dptr++ = AX25_ADDR_LEN;
+			memcpy(dptr, &rose_callsign, AX25_ADDR_LEN);
+			dptr += AX25_ADDR_LEN;
+			*dptr++ = FAC_NATIONAL_FAIL_ADD;
+			*dptr++ = ROSE_ADDR_LEN + 1;
+			*dptr++ = ROSE_ADDR_LEN * 2;
+			memcpy(dptr, &sk->protinfo.rose->source_addr, ROSE_ADDR_LEN);
+			len   += faclen;
+			break;
 	}
 
 	if ((skb = alloc_skb(len, GFP_ATOMIC)) == NULL)
@@ -136,9 +154,9 @@
 	/*
 	 *	Space for AX.25 header and PID.
 	 */
-	skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1);
+	skb_reserve(skb, ax25_header_len);
 	
-	dptr = skb_put(skb, skb_tailroom(skb));
+	dptr = skb_put(skb, len - ax25_header_len);
 
 	lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F;
 	lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF;
@@ -172,6 +190,9 @@
 			*dptr++ = frametype;
 			*dptr++ = sk->protinfo.rose->cause;
 			*dptr++ = sk->protinfo.rose->diagnostic;
+			*dptr++ = 0x00;		/* Address length */
+			memcpy(dptr, buffer, faclen);
+			dptr   += faclen;
 			break;
 
 		case ROSE_RESET_REQUEST:
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/sunrpc/sched.c linux/net/sunrpc/sched.c
--- linux.orig/net/sunrpc/sched.c	Tue Jan 22 17:51:24 2002
+++ linux/net/sunrpc/sched.c	Mon Jan 14 16:24:07 2002
@@ -21,6 +21,7 @@
 #include <linux/spinlock.h>
 
 #include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
 
 #ifdef RPC_DEBUG
 #define RPCDBG_FACILITY		RPCDBG_SCHED
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/unix/af_unix.c linux/net/unix/af_unix.c
--- linux.orig/net/unix/af_unix.c	Tue Jan 22 17:51:24 2002
+++ linux/net/unix/af_unix.c	Wed Jan 23 20:24:26 2002
@@ -8,7 +8,7 @@
  *		as published by the Free Software Foundation; either version
  *		2 of the License, or (at your option) any later version.
  *
- * Version:	$Id: af_unix.c,v 1.126 2001/11/13 05:06:28 davem Exp $
+ * Version:	$Id: af_unix.c,v 1.126.2.4 2002/01/14 07:07:41 davem Exp $
  *
  * Fixes:
  *		Linus Torvalds	:	Assorted bug cures.
@@ -101,13 +101,14 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <net/sock.h>
-#include <net/tcp.h>
+#include <linux/tcp.h>
 #include <net/af_unix.h>
 #include <linux/proc_fs.h>
 #include <net/scm.h>
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/smp_lock.h>
+#include <linux/rtnetlink.h>
 
 #include <asm/checksum.h>
 
@@ -483,7 +484,7 @@
 	sk->protinfo.af_unix.dentry=NULL;
 	sk->protinfo.af_unix.mnt=NULL;
 	sk->protinfo.af_unix.lock = RW_LOCK_UNLOCKED;
-	atomic_set(&sk->protinfo.af_unix.inflight, 0);
+	atomic_set(&sk->protinfo.af_unix.inflight, sock ? 0 : -1);
 	init_MUTEX(&sk->protinfo.af_unix.readsem);/* single task reading lock */
 	init_waitqueue_head(&sk->protinfo.af_unix.peer_wait);
 	sk->protinfo.af_unix.list=NULL;
@@ -990,7 +991,12 @@
 	unix_state_wunlock(sk);
 
 	/* take ten and and send info to listening sock */
-	skb_queue_tail(&other->receive_queue,skb);
+	spin_lock(&other->receive_queue.lock);
+	__skb_queue_tail(&other->receive_queue,skb);
+	/* Undo artificially decreased inflight after embrion
+	 * is installed to listening socket. */
+	atomic_inc(&newsk->protinfo.af_unix.inflight);
+	spin_unlock(&other->receive_queue.lock);
 	unix_state_runlock(other);
 	other->data_ready(other, 0);
 	sock_put(other);
@@ -1053,8 +1059,12 @@
 	 */
 
 	skb = skb_recv_datagram(sk, 0, flags&O_NONBLOCK, &err);
-	if (!skb)
+	if (!skb) {
+		/* This means receive shutdown. */
+		if (err == 0)
+			err = -EINVAL;
 		goto out;
+	}
 
 	tsk = skb->sk;
 	skb_free_datagram(sk, skb);
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/net/unix/garbage.c linux/net/unix/garbage.c
--- linux.orig/net/unix/garbage.c	Sat Jun 30 02:38:26 2001
+++ linux/net/unix/garbage.c	Wed Jan 23 20:24:26 2002
@@ -76,9 +76,9 @@
 #include <linux/netdevice.h>
 #include <linux/file.h>
 #include <linux/proc_fs.h>
+#include <linux/tcp.h>
 
 #include <net/sock.h>
-#include <net/tcp.h>
 #include <net/af_unix.h>
 #include <net/scm.h>
 
@@ -205,12 +205,21 @@
 
 	forall_unix_sockets(i, s)
 	{
+		int open_count = 0;
+
 		/*
 		 *	If all instances of the descriptor are not
 		 *	in flight we are in use.
+		 *
+		 *	Special case: when socket s is embrion, it may be
+		 *	hashed but still not in queue of listening socket.
+		 *	In this case (see unix_create1()) we set artificial
+		 *	negative inflight counter to close race window.
+		 *	It is trick of course and dirty one.
 		 */
-		if(s->socket && s->socket->file &&
-		   file_count(s->socket->file) > atomic_read(&s->protinfo.af_unix.inflight))
+		if(s->socket && s->socket->file)
+			open_count = file_count(s->socket->file);
+		if (open_count > atomic_read(&s->protinfo.af_unix.inflight))
 			maybe_unmark_and_push(s);
 	}
 
diff -Naur -X /home/marcelo/lib/dontdiff linux.orig/scripts/mkspec linux/scripts/mkspec
--- linux.orig/scripts/mkspec	Tue Sep 18 06:04:05 2001
+++ linux/scripts/mkspec	Wed Feb  6 20:47:54 2002
@@ -8,7 +8,7 @@
 #
 echo "Name: kernel"
 echo "Summary: The Linux Kernel"
-echo "Version: "$VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION | sed -e "s/-//"
+echo "Version: "$VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION | sed -e "s/-//g"
 # we need to determine the NEXT version number so that uname and
 # rpm -q will agree
 echo "Release: `. scripts/mkversion`"
@@ -17,7 +17,7 @@
 echo "Vendor: The Linux Community"
 echo "URL: http://www.kernel.org"
 echo -n "Source: kernel-$VERSION.$PATCHLEVEL.$SUBLEVEL"
-echo "$EXTRAVERSION.tar.gz" | sed -e "s/-//"
+echo "$EXTRAVERSION.tar.gz" | sed -e "s/-//g"
 echo "BuildRoot: /var/tmp/%{name}-%{PACKAGE_VERSION}-root"
 echo "%define __spec_install_post /usr/lib/rpm/brp-compress || :"
 echo ""
