+- add patches.drivers/usb-2.5
authorOlaf Hering <olh@suse.de>
Tue, 9 Dec 2003 16:39:31 +0000 (16:39 +0000)
committerOlaf Hering <olh@suse.de>
Tue, 9 Dec 2003 16:39:31 +0000 (16:39 +0000)
+      patches.drivers/usb-2.5_kobject.debug
+      patches.drivers/usb-2.5_module_sig-depend
+  dump the usb and sysfs tree in our repo

suse-commit: 77e3783d8341477adc25d6323240e8b8b4fa45d6

248 files changed:
CREDITS
Documentation/devices.txt
Documentation/i2c/porting-clients [new file with mode: 0644]
Documentation/i2c/sysfs-interface
Documentation/i2c/writing-clients
Documentation/usb/w9968cf.txt [new file with mode: 0644]
MAINTAINERS
arch/i386/Kconfig
arch/mips/.gdbinit [new file with mode: 0644]
arch/ppc/8xx_io/cs4218_tdm.c
arch/um/drivers/hostaudio_kern.c
drivers/base/bus.c
drivers/base/class.c
drivers/base/core.c
drivers/base/driver.c
drivers/base/sys.c
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/fjkeyinf.c [new file with mode: 0644]
drivers/char/mem.c
drivers/char/misc.c
drivers/char/sysrq.c
drivers/char/vc_screen.c
drivers/i2c/algos/i2c-algo-bit.c
drivers/i2c/busses/Kconfig
drivers/i2c/busses/i2c-elektor.c
drivers/i2c/busses/i2c-ite.c
drivers/i2c/busses/i2c-piix4.c
drivers/i2c/busses/i2c-savage4.c
drivers/i2c/busses/i2c-viapro.c
drivers/i2c/chips/Kconfig
drivers/i2c/chips/Makefile
drivers/i2c/chips/it87.c
drivers/i2c/chips/lm75.c
drivers/i2c/chips/lm78.c
drivers/i2c/chips/lm83.c [new file with mode: 0644]
drivers/i2c/chips/lm85.c
drivers/i2c/chips/sensors_vid.h [new file with mode: 0644]
drivers/i2c/chips/smbus-arp.c [new file with mode: 0644]
drivers/i2c/chips/via686a.c
drivers/i2c/chips/w83781d.c
drivers/media/video/saa7134/saa7134-core.c
drivers/media/video/tvmixer.c
drivers/pci/hotplug/Kconfig
drivers/pci/hotplug/Makefile
drivers/pci/hotplug/amdshpc.h [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_core.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_ctrl.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_ddi.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_ddi.h [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_dsb.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_enb.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_int.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_led.c [new file with mode: 0644]
drivers/pci/hotplug/amdshpc_pci.c [new file with mode: 0644]
drivers/pci/hotplug/dummyphp.c [new file with mode: 0644]
drivers/usb/Makefile
drivers/usb/class/audio.c
drivers/usb/class/audio.h
drivers/usb/class/cdc-acm.c
drivers/usb/class/usb-midi.c
drivers/usb/class/usb-midi.h
drivers/usb/class/usblp.c
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/usb.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/serial.c [new file with mode: 0644]
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-q.c
drivers/usb/host/ohci.h
drivers/usb/image/scanner.c
drivers/usb/image/scanner.h
drivers/usb/media/Kconfig
drivers/usb/media/Makefile
drivers/usb/media/w9968cf.c [new file with mode: 0644]
drivers/usb/media/w9968cf.h [new file with mode: 0644]
drivers/usb/media/w9968cf_decoder.h [new file with mode: 0644]
drivers/usb/media/w9968cf_externaldef.h [new file with mode: 0644]
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/legousbtower.c [new file with mode: 0644]
drivers/usb/net/usbnet.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/ftdi_sio.h
drivers/usb/serial/pl2303.c
drivers/usb/serial/pl2303.h
drivers/usb/serial/usb-serial.c
drivers/usb/storage/debug.c
drivers/usb/storage/debug.h
drivers/usb/storage/protocol.c
drivers/usb/storage/protocol.h
drivers/usb/storage/raw_bulk.c
drivers/usb/storage/sddr09.c
drivers/usb/storage/transport.c
drivers/usb/storage/usb.c
drivers/video/aty/aty128fb.c
drivers/video/cirrusfb.c
drivers/video/cyber2000fb.c
drivers/video/fbmem.c
drivers/video/i810/i810_main.c
drivers/video/igafb.c
drivers/video/imsttfb.c
drivers/video/matrox/matroxfb_crtc2.c
drivers/video/neofb.c
drivers/video/radeonfb.c
drivers/video/riva/fbdev.c
drivers/video/sis/sis_main.c
drivers/video/sstfb.c
drivers/video/tdfxfb.c
drivers/video/tgafb.c
drivers/video/tridentfb.c
fs/sysfs/dir.c
include/linux/device.h
include/linux/fb.h
include/linux/i2c-id.h
include/linux/miscdevice.h
include/linux/module.h
include/linux/pci.h
include/linux/sound.h
include/linux/usb_ch9.h
include/linux/videodev.h
include/sound/hwdep.h
include/sound/opl3.h
include/sound/pcm.h
include/sound/rawmidi.h
init/Kconfig
kernel/Makefile
kernel/module-sig.c [new file with mode: 0644]
kernel/module-sig.h [new file with mode: 0644]
kernel/module.c
lib/Kconfig
lib/kobject.c
scripts/lxdialog/textbox.c
security/Kconfig
security/Makefile
security/rsa/Kconfig [new file with mode: 0644]
security/rsa/Makefile [new file with mode: 0644]
security/rsa/foo.h [new file with mode: 0644]
security/rsa/gnupg_cipher_rsa-verify.c [new file with mode: 0644]
security/rsa/gnupg_cipher_rsa-verify.h [new file with mode: 0644]
security/rsa/gnupg_mpi_config.h [new file with mode: 0644]
security/rsa/gnupg_mpi_g10defs.h [new file with mode: 0644]
security/rsa/gnupg_mpi_g10m.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpi-asm-defs.h [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-add1.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-lshift.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-mul1.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-mul2.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-mul3.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-rshift.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_mpih-sub1.c [new file with mode: 0644]
security/rsa/gnupg_mpi_generic_udiv-w-sdiv.c [new file with mode: 0644]
security/rsa/gnupg_mpi_longlong.h [new file with mode: 0644]
security/rsa/gnupg_mpi_memory.h [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-add.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-bit.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-cmp.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-div.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-gcd.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-inline.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-inline.h [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-internal.h [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-inv.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-mpow.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-mul.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-pow.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi-scan.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpi.h [new file with mode: 0644]
security/rsa/gnupg_mpi_mpicoder.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpih-cmp.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpih-div.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpih-mul.c [new file with mode: 0644]
security/rsa/gnupg_mpi_mpiutil.c [new file with mode: 0644]
security/rsa/gnupg_mpi_types.h [new file with mode: 0644]
security/rsa/mpi.c [new file with mode: 0644]
security/rsa/mpi_generic.c [new file with mode: 0644]
security/rsa/rsa.c [new file with mode: 0644]
security/rsa/rsa.h [new file with mode: 0644]
security/rsa/rsa_key.c [new file with mode: 0644]
security/rsa/userspace/Makefile [new file with mode: 0644]
security/rsa/userspace/extract_pkey.c [new file with mode: 0644]
security/rsa/userspace/mod.c [new file with mode: 0644]
security/rsa/userspace/mod_elf.c [new file with mode: 0644]
security/rsa/userspace/sign [new file with mode: 0644]
sound/core/hwdep.c
sound/core/pcm.c
sound/core/rawmidi.c
sound/core/sound_oss.c
sound/drivers/opl3/opl3_lib.c
sound/oss/ad1889.c
sound/oss/ali5455.c
sound/oss/btaudio.c
sound/oss/cmpci.c
sound/oss/cs4281/cs4281m.c
sound/oss/cs46xx.c
sound/oss/dev_table.c
sound/oss/emu10k1/main.c
sound/oss/es1370.c
sound/oss/es1371.c
sound/oss/esssolo1.c
sound/oss/forte.c
sound/oss/i810_audio.c
sound/oss/maestro.c
sound/oss/maestro3.c
sound/oss/msnd_pinnacle.c
sound/oss/rme96xx.c
sound/oss/sonicvibes.c
sound/oss/soundcard.c
sound/oss/trident.c
sound/oss/via82cxxx_audio.c
sound/oss/wavfront.c
sound/oss/ymfpci.c
sound/pci/ali5451/ali5451.c
sound/pci/als4000.c
sound/pci/azt3328.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/emumpu401.c
sound/pci/emu10k1/emupcm.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1724.c
sound/pci/intel8x0.c
sound/pci/korg1212/korg1212.c
sound/pci/maestro3.c
sound/pci/nm256/nm256.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/rme9652.c
sound/pci/sonicvibes.c
sound/pci/trident/trident.c
sound/pci/trident/trident_main.c
sound/pci/via82xx.c
sound/pci/vx222/vx222.c
sound/pci/ymfpci/ymfpci.c
sound/pci/ymfpci/ymfpci_main.c
sound/sound_core.c
sound/usb/usbaudio.h
sound/usb/usbmidi.c

diff --git a/CREDITS b/CREDITS
index 0281477..3c1022d 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -2669,6 +2669,13 @@ S: Kasarmikatu 11 A4
 S: 70110 Kuopio
 S: Finland
 
+N: Luca Risolia
+E: luca_ing@libero.it
+D: V4L driver for W996[87]CF JPEG USB Dual Mode Camera Chip
+S: Via Libertà 41/a
+S: Osio Sotto, 24046, Bergamo
+S: Italy
+
 N: William E. Roadcap
 E: roadcapw@cfw.com
 W: http://www.cfw.com/~roadcapw
@@ -3569,4 +3576,4 @@ S: France
 # alphabetically. Leonard used to be very proud of being the 
 # last entry, and he'll get positively pissed if he can't even
 # be second-to-last.  (and this file really _is_ supposed to be
-# in alphabetic order) 
+# in alphabetic order)
index ab76dac..bd5cee3 100644 (file)
@@ -2165,16 +2165,27 @@ Your cooperation is appreciated.
                  0 = /dev/usb/lp0      First USB printer
                    ...
                 15 = /dev/usb/lp15     16th USB printer
-                16 = /dev/usb/mouse0   First USB mouse
-                   ...
-                31 = /dev/usb/mouse15  16th USB mouse
-                32 = /dev/usb/ez0      First USB firmware loader
-                   ...
-                47 = /dev/usb/ez15     16th USB firmware loader
+                32 = /dev/usb/mdc800   MDC800 USB camera
                 48 = /dev/usb/scanner0 First USB scanner
                    ...
                 63 = /dev/usb/scanner15 16th USB scanner
                 64 = /dev/usb/rio500   Diamond Rio 500
+                96 = /dev/usb/hiddev0  1st USB HID device
+                   ...
+               111 = /dev/usb/hiddev15 16th USB HID device
+               112 = /dev/usb/auer0    1st auerswald ISDN device
+                   ...
+               127 = /dev/usb/auer15   16th auerswald ISDN device
+               128 = /dev/usb/brlvgr0  First Braille Voyager device
+                   ...
+               131 = /dev/usb/brlvgr3  Fourth Braille Voyager device
+               144 = /dev/usb/lcd      USB LCD device
+               160 = /dev/usb/legousbtower0    1st USB Legotower device
+                   ...
+               175 = /dev/usb/legousbtower15   16th USB Legotower device
+               240 = /dev/usb/dabusb0  First daubusb device
+                   ...
+               243 = /dev/usb/dabusb3  Fourth dabusb device
 
 181 char       Conrad Electronic parallel port radio clocks
                  0 = /dev/pcfclock0    First Conrad radio clock
diff --git a/Documentation/i2c/porting-clients b/Documentation/i2c/porting-clients
new file mode 100644 (file)
index 0000000..fab37c4
--- /dev/null
@@ -0,0 +1,121 @@
+Revision 3, 2003-10-04
+Jean Delvare <khali@linux-fr.org>
+Greg KH <greg@kroah.com>
+
+This is a guide on how to convert I2C chip drivers from Linux 2.4 to
+Linux 2.6. I have been using existing drivers (lm75, lm78) as examples.
+Then I converted a driver myself (lm83) and updated this document.
+
+There are two sets of points below. The first set concerns technical
+changes. The second set concerns coding policy. Both are mandatory.
+
+Although reading this guide will help you porting drivers, I suggest
+you keep an eye on an already ported driver while porting your own
+driver. This will help you a lot understanding what this guide
+exactly means. Choose the chip driver that is the more similar to
+yours for best results.
+
+Technical changes:
+
+* [Includes] Get rid of "version.h". Replace <linux/i2c-proc.h> with
+  <linux/i2c-sensor.h>. Includes typically look like that:
+  #include <linux/module.h>
+  #include <linux/init.h>
+  #include <linux/slab.h>
+  #include <linux/i2c.h>
+  #include <linux/i2c-sensor.h>
+  #include <linux/i2c-vid.h>     /* if you need VRM support */
+  #include <asm/io.h>            /* if you have I/O operations */
+  Some extra headers may be required for a given driver.
+
+* [Addresses] SENSORS_I2C_END becomes I2C_CLIENT_END, SENSORS_ISA_END
+  becomes I2C_CLIENT_ISA_END.
+
+* [Client data] Get rid of sysctl_id. Try using standard names for
+  register values (for example, temp_os becomes temp_max). You're
+  still relatively free here, but you *have* to follow the standard
+  names for sysfs files (see the Sysctl section below).
+
+* [Function prototypes] The detect functions loses its flags
+  parameter. Sysctl (e.g. lm75_temp) and miscellaneous (e.g.
+  swap_bytes) functions are off the list of prototypes. This
+  usually leaves five prototypes:
+  static int lm75_attach_adapter(struct i2c_adapter *adapter);
+  static int lm75_detect(struct i2c_adapter *adapter, int address,
+      int kind);
+  static void lm75_init_client(struct i2c_client *client);
+  static int lm75_detach_client(struct i2c_client *client);
+  static void lm75_update_client(struct i2c_client *client);
+
+* [Sysctl] All sysctl stuff is of course gone (defines, ctl_table
+  and functions). Instead, right after the static id definition
+  line, you have to define show and set functions for each sysfs
+  file. Only define set for writable values. Take a look at an
+  existing 2.6 driver for details (lm78 for example). Don't forget
+  to define the attributes for each file (this is that step that
+  links callback functions). Use the file names specified in
+  Documentation/i2c/sysfs-interface for the individual files. Also
+  convert the units these files read and write to the specified ones.
+  If you need to add a new type of file, please discuss it on the
+  sensors mailing list <sensors@stimpy.netroedge.com> by providing a
+  patch to the Documentation/i2c/sysfs-interface file.
+
+* [Attach] For I2C drivers, the attach function should make sure
+  that the adapter's class has I2C_ADAP_CLASS_SMBUS, using the
+  following construct:
+  if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
+          return 0;
+  ISA-only drivers of course don't need this.
+
+* [Detect] As mentioned earlier, the flags parameter is gone.
+  The type_name and client_name strings are replaced by a single
+  name string, which will be filled with a lowercase, short string
+  (typically the driver name, e.g. "lm75"). The errorN labels are
+  reduced to the number needed. If that number is 2 (i2c-only
+  drivers), it is advised that the labels are named exit and
+  exit_free. For i2c+isa drivers, labels should be named ERROR0,
+  ERROR1 and ERROR2. Don't forget to properly set err before
+  jumping to error labels. By the way, labels should be
+  left-aligned.
+  Use memset to fill the client and data area with 0x00.
+  Use i2c_set_clientdata to set the client data (as opposed to
+  a direct access to client->data).
+  Use strlcpy instead of strcpy to copy the client name.
+  Replace the sysctl directory registration by calls to
+  device_create_file. Move the driver initialization before any
+  sysfs file creation.
+
+* [Detach] Get rid of data, remove the call to
+  i2c_deregister_entry.
+
+* [Update] Don't access client->data directly, use
+  i2c_get_clientdata(client) instead.
+
+* [Interface] Init function should not print anything. Make sure
+  there is a MODULE_LICENSE() line.
+
+Coding policy:
+
+* [Copyright] Use (C), not (c), for copyright.
+
+* [Debug/log] Get rid of #ifdef DEBUG/#endif constructs whenever you
+  can. Calls to printk/pr_debug for debugging purposes are replaced
+  by calls to dev_dbg. Here is an example on how to call it (taken
+  from lm75_detect):
+  dev_dbg(&adapter->dev,
+          "lm75_detect called for an ISA bus adapter?!?\n");
+  Replace other printk calls with the dev_info, dev_err or dev_warn
+  function, as appropriate.
+
+* [Constants] Constants defines (registers, conversions, initial
+  values) should be aligned. This greatly improves readability.
+  Same goes for variables declarations. Alignments are achieved by the
+  means of tabs, not spaces. Remember that tabs are set to 8 in the
+  Linux kernel code.
+
+* [Structure definition] The name field should be standardized. All
+  lowercase and as simple as the driver name itself (e.g. "lm75").
+
+* [Layout] Avoid extra empty lines between comments and what they
+  comment. Respect the coding style (see Documentation/CodingStyle),
+  in particular when it comes to placing curly braces.
index d601296..49e9dc8 100644 (file)
@@ -68,9 +68,7 @@ curr_max[1-n] Current max value
                Fixed point XXXXX, divide by 1000 to get Amps.
                Read/Write.
 
-curr_min[1-n]  Current min or hysteresis value.
-               Preferably a hysteresis value, reported as a absolute
-               current, NOT a delta from the max value.
+curr_min[1-n]  Current min value.
                Fixed point XXXXX, divide by 1000 to get Amps.
                Read/Write.
 
@@ -144,25 +142,38 @@ sensor[1-3]       Sensor type selection.
                Integers 1,2,3, or thermistor Beta value (3435)
                Read/Write.
 
-temp_max[1-3]  Temperature max value.
+temp_max[1-4]  Temperature max value.
                Fixed point value in form XXXXX and should be divided by
                1000 to get degrees Celsius.
                Read/Write value.
 
-temp_min[1-3]  Temperature min or hysteresis value.
+temp_min[1-3]  Temperature min value.
                Fixed point value in form XXXXX and should be divided by
-               1000 to get degrees Celsius.  This is preferably a
-               hysteresis value, reported as a absolute temperature,
-               NOT a delta from the max value.
+               1000 to get degrees Celsius.
                Read/Write value.
 
-temp_input[1-3] Temperature input value.
+temp_hyst[1-3] Temperature hysteresis value.
+               Fixed point value in form XXXXX and should be divided by
+               1000 to get degrees Celsius.  Must be reported as an
+               absolute temperature, NOT a delta from the max value.
+               Read/Write value.
+
+temp_input[1-4] Temperature input value.
+               Fixed point value in form XXXXX and should be divided by
+               1000 to get degrees Celsius.
                Read only value.
 
+temp_crit      Temperature critical value, typically greater than all
+               temp_max values.
+               Fixed point value in form XXXXX and should be divided by
+               1000 to get degrees Celsius.
+               Common to all temperature channels.
+               Read/Write value.
+
                If there are multiple temperature sensors, temp_*1 is
                generally the sensor inside the chip itself, generally
-               reported as "motherboard temperature".  temp_*2 and
-               temp_*3 are generally sensors external to the chip
+               reported as "motherboard temperature".  temp_*2 to
+               temp_*4 are generally sensors external to the chip
                itself, for example the thermal diode inside the CPU or
                a thermistor nearby.
 
index 8b310ea..a454212 100644 (file)
@@ -24,16 +24,14 @@ all clients from it. Remember, a driver structure contains general access
 routines, a client structure specific information like the actual I2C
 address.
 
-  struct i2c_driver foo_driver
-  {  
-    /* name           */  "Foo version 2.3 and later driver",
-    /* id             */  I2C_DRIVERID_FOO,
-    /* flags          */  I2C_DF_NOTIFY,
-    /* attach_adapter */  &foo_attach_adapter,
-    /* detach_client  */  &foo_detach_client,
-    /* command        */  &foo_command,   /* May be NULL */
-    /* inc_use        */  &foo_inc_use,   /* May be NULL */
-    /* dec_use        */  &foo_dec_use    /* May be NULL */
+  static struct i2c_driver foo_driver = {
+    .owner          = THIS_MODULE,
+    .name           = "Foo version 2.3 driver",
+    .id             = I2C_DRIVERID_FOO, /* usually from i2c-id.h */
+    .flags          = I2C_DF_NOTIFY,
+    .attach_adapter = &foo_attach_adapter,
+    .detach_client  = &foo_detach_client,
+    .command        = &foo_command /* may be NULL */
   }
  
 The name can be chosen freely, and may be upto 40 characters long. Please
@@ -50,43 +48,8 @@ This is almost always what you want.
 All other fields are for call-back functions which will be explained 
 below.
 
-
-Module usage count
-==================
-
-If your driver can also be compiled as a module, there are moments at 
-which the module can not be removed from memory. For example, when you
-are doing a lengthy transaction, or when you create a /proc directory,
-and some process has entered that directory (this last case is the
-main reason why these call-backs were introduced).
-
-To increase or decrease the module usage count, you can use the
-MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module
-which needs to get its usage count changed; that is why each driver
-module has to implement its own callback.
-
-  void foo_inc_use (struct i2c_client *client)
-  {
-  #ifdef MODULE
-    MOD_INC_USE_COUNT;
-  #endif
-  }
-
-  void foo_dec_use (struct i2c_client *client)
-  {
-  #ifdef MODULE
-    MOD_DEC_USE_COUNT;
-  #endif
-  }
-
-Do not call these call-back functions directly; instead, use one of the
-following functions defined in i2c.h:
-  void i2c_inc_use_client(struct i2c_client *);
-  void i2c_dec_use_client(struct i2c_client *);
-
-You should *not* increase the module count just because a device is
-detected and a client created. This would make it impossible to remove
-an adapter driver! 
+There use to be two additional fields in this structure, inc_use et dec_use,
+for module usage count, but these fields were obsoleted and removed.
 
 
 Extra client data
diff --git a/Documentation/usb/w9968cf.txt b/Documentation/usb/w9968cf.txt
new file mode 100644 (file)
index 0000000..498e7be
--- /dev/null
@@ -0,0 +1,463 @@
+
+         W996[87]CF JPEG USB Dual Mode Camera Chip driver for Linux 2.6
+         ==============================================================
+
+                               - Documentation -
+
+
+Index
+=====
+1. Copyright
+2. License
+3. Overview
+4. Supported devices
+5. Kernel configuration and third-part module compilation
+6. Module loading
+7. Module paramaters
+8. Credits
+
+
+1. Copyright
+============
+Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>
+
+
+2. License
+==========
+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.
+
+
+3. Overview
+===========
+This driver supports the video streaming capabilities of the devices mounting
+Winbond W9967CF and Winbond W9968CF JPEG USB Dual Mode Camera Chips, when they
+are being commanded by USB.
+
+The driver relies on the Video4Linux, USB and I2C core modules of the Linux
+kernel, version 2.6.0 or greater, and is not compatible in any way with
+previous versions. It has been designed to run properly on SMP systems
+as well. At the moment, an additional module, "ovcamchip", is mandatory; it
+provides support for some OmniVision CMOS sensors connected to the W996[87]CF
+chips.
+
+The driver is split into two modules: the basic one, "w9968cf", is needed for
+the supported devices to work; the second one, "w9968cf-vpp", is an optional
+module, which provides some useful video post-processing functions like video
+decoding, up-scaling and colour conversions. These routines can't be included
+into official kernels for performance purposes. Once the driver is installed,
+every time an application tries to open a recognized device, "w9968cf" checks
+the presence of the "w9968cf-vpp" module and loads it automatically by default.
+
+Up to 32 cameras can be handled at the same time. They can be connected and
+disconnected from the host many times without turning off the computer, if
+your system supports the hotplug facility.
+
+To change the default settings for each camera, many paramaters can be passed
+through command line when the module is loaded into memory.
+
+The latest and full featured version of the W996[87]CF driver can be found at:
+http://go.lamarinapunto.com/
+
+The "ovcamchip" module is part of the OV511 driver, version 2.25, which can be
+downloaded from internet:
+http://alpha.dyndns.org/ov511/
+To know how to patch, compile and load it, read the paragraphs below.
+
+
+4. Supported devices
+====================
+At the moment, known W996[87]CF based devices are:
+- Aroma Digi Pen ADG-5000 Refurbished
+- AVerTV USB
+- Creative Labs Video Blaster WebCam Go
+- Creative Labs Video Blaster WebCam Go Plus
+- Die Lebon LDC-D35A Digital Kamera
+- Ezonics EZ-802 EZMega Cam
+- OPCOM Digi Pen VGA Dual Mode Pen Camera
+
+If you know any other W996[87]CF based cameras, please contact me.
+
+The list above does NOT imply that all those devices work with this driver: up
+until now only webcams that have a CMOS sensor supported by the "ovcamchip"
+module work. 
+For a list of supported CMOS sensors, please visit the module author homepage:
+http://alpha.dyndns.org/ov511/
+
+Possible external microcontrollers of those webcams are not supported: this
+means that still images can't be downloaded from the device memory.
+
+Furthermore, it's worth to note that I was only able to run tests on my
+"Creative Labs Video Blaster WebCam Go". Donations of other models, for
+additional testing and full support, would be much appreciated.
+
+
+5. Kernel configuration and third-part module compilation
+=========================================================
+As noted above, kernel 2.6.0 is the minimum for this driver; for it to work
+properly, the driver needs kernel support for Video4Linux, USB and I2C, and a
+third-part module for the CMOS sensor.
+
+The following options of the kernel configuration file must be enabled and
+corresponding modules must be compiled:
+
+       # Multimedia devices
+       #
+       CONFIG_VIDEO_DEV=m
+
+       # I2C support
+       #
+       CONFIG_I2C=m
+
+The I2C core module can be compiled statically in the kernel as well.
+
+       # USB support
+       #
+       CONFIG_USB=m
+
+In addition, depending on the hardware being used, just one of the modules
+below is necessary:
+
+       # USB Host Controller Drivers
+       #
+       CONFIG_USB_EHCI_HCD=m
+       CONFIG_USB_UHCI_HCD=m
+       CONFIG_USB_OHCI_HCD=m
+
+Also, make sure "Enforce bandwidth allocation" is NOT enabled.
+
+       # USB Multimedia devices
+       #
+       CONFIG_USB_W9968CF=m
+
+The last module we need is "ovcamchip.o". To obtain it, you have to download
+the OV511 driver, version 2.25 - don't use other versions - which is available
+at http://alpha.dyndns.org/ov511/ . Then you have to download the latest 
+version of the full featured W996[87]CF driver, which contains a patch for the
+"ovcamchip" module; it is available at http://go.lamarinapunto.com .
+Once you have obtained the packages, decompress, patch and compile the 
+"ovcamchip" module. In other words:
+
+       [user@localhost home]$ tar xvzf w9968cf-x.x.tar.gz
+       [user@localhost home]$ tar xvjf ov511-2.25.tar.bz2
+       [user@localhost home]$ cd ov511-2.25
+       [user@localhost ov511-2.25]$ patch -p1 <                              \
+                                    /path/to/w9968cf-x.x/ov511-2.25.patch
+       [user@localhost ov511-2.25]$ make
+
+It's worth to note that the full featured version of the W996[87]CF driver
+can also be installed overwriting the one in the kernel; in this case, read the
+documentation included in the package.
+
+If everything went well, the W996[87]CF driver can be immediatly used (see next
+paragraph).
+
+
+6. Module loading
+=================
+To use the driver, it is necessary to load the "w9968cf" module into memory
+after every other module required.
+
+For example, loading can be done this way, as root:
+
+       [root@localhost home]# modprobe usbcore
+       [root@localhost home]# modprobe i2c-core
+       [root@localhost ov511-x.xx]# insmod ./ovcamchip.ko
+       [root@localhost home]# modprobe w9968cf
+
+At this point the devices should be recognized: "dmesg" can be used to analyze
+kernel messages:
+
+       [user@localhost home]$ dmesg
+
+There are a lot of parameters the module can use to change the default
+settings for each device. To list every possible parameter with a brief
+explanation about them and which syntax to use, it is recommended to run the
+"modinfo" command:
+
+       [root@locahost home]# modinfo w9968cf
+
+
+7. Module paramaters
+====================
+
+Module paramaters are listed below:
+-------------------------------------------------------------------------------
+Name:           vppmod_load
+Type:           int
+Syntax:         <0|1>
+Description:    Automatic 'w9968cf-vpp' module loading: 0 disabled, 1 enabled.
+                If enabled, every time an application attempts to open a
+                camera, 'insmod' searches for the video post-processing module
+                in the system and loads it automatically (if present).
+                The 'w9968cf-vpp' module adds extra image manipulation 
+                capabilities to the 'w9968cf' module,like software up-scaling,
+                colour conversions and video decoding.
+Default:        1
+-------------------------------------------------------------------------------
+Name:           simcams 
+Type:           int 
+Syntax:         <n> 
+Description:    Number of cameras allowed to stream simultaneously.
+                n may vary from 0 to 32.
+Default:        32
+-------------------------------------------------------------------------------
+Name:           video_nr
+Type:           int array (min = 0, max = 32)
+Syntax:         <-1|n[,...]> 
+Description:    Specify V4L minor mode number.
+                -1 = use next available
+                 n = use minor number n
+                You can specify 32 cameras this way.
+                For example:
+                video_nr=-1,2,-1 would assign minor number 2 to the second
+                recognized camera and use auto for the first one and for every
+                other camera.
+Default:        -1
+-------------------------------------------------------------------------------
+Name:           packet_size
+Type:           int array (min = 0, max = 32)
+Syntax:         <n[,...]> 
+Description:    Specify the maximum data payload size in bytes for alternate
+                settings, for each device. n is scaled between 63 and 1023.
+Default:        1023
+-------------------------------------------------------------------------------
+Name:           max_buffers
+Type:           int array (min = 0, max = 32)
+Syntax:         <n[,...]>
+Description:    Only for advanced users.
+                Specify the maximum number of video frame buffers to allocate
+                for each device, from 2 to 32.
+Default:        2
+-------------------------------------------------------------------------------
+Name:           double_buffer
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    Hardware double buffering: 0 disabled, 1 enabled.
+                It should be enabled if you want smooth video output: if you
+                obtain out of sync. video, disable it at all, or try to
+                decrease the 'clockdiv' module paramater value.
+Default:        1 for every device.
+-------------------------------------------------------------------------------
+Name:           clamping
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    Video data clamping: 0 disabled, 1 enabled.
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           filter_type
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1|2[,...]> 
+Description:    Video filter type.
+                0 none, 1 (1-2-1) 3-tap filter, 2 (2-3-6-3-2) 5-tap filter.
+                The filter is used to reduce noise and aliasing artifacts
+                produced by the CCD or CMOS sensor.
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           largeview
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    Large view: 0 disabled, 1 enabled.
+Default:        1 for every device.
+-------------------------------------------------------------------------------
+Name:           upscaling
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    Software scaling (for non-compressed video only):
+                0 disabled, 1 enabled.
+                Disable it if you have a slow CPU or you don't have enough
+                memory.
+Default:        0 for every device.
+Note:           If 'w9968cf-vpp' is not loaded, this paramater is set to 0.
+-------------------------------------------------------------------------------
+Name:           decompression
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1|2[,...]>
+Description:    Software video decompression:
+                0 = disables decompression
+                    (doesn't allow formats needing decompression).
+                1 = forces decompression
+                    (allows formats needing decompression only).
+                2 = allows any permitted formats.
+                Formats supporting (de)compressed video are YUV422P and
+                YUV420P/YUV420 in any resolutions where width and height are
+                multiples of 16.
+Default:        2 for every device.
+Note:           If 'w9968cf-vpp' is not loaded, forcing decompression is not
+                allowed; in this case this paramater is set to 2.
+-------------------------------------------------------------------------------
+Name:           force_palette
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|9|10|13|15|8|7|1|6|3|4|5[,...]>
+Description:    Force picture palette.
+                In order:
+                 0 = Off - allows any of the following formats:
+                 9 = UYVY    16 bpp - Original video, compression disabled
+                10 = YUV420  12 bpp - Original video, compression enabled
+                13 = YUV422P 16 bpp - Original video, compression enabled
+                15 = YUV420P 12 bpp - Original video, compression enabled
+                 8 = YUVY    16 bpp - Software conversion from UYVY
+                 7 = YUV422  16 bpp - Software conversion from UYVY
+                 1 = GREY     8 bpp - Software conversion from UYVY
+                 6 = RGB555  16 bpp - Software conversion from UYVY
+                 3 = RGB565  16 bpp - Software conversion from UYVY
+                 4 = RGB24   24 bpp - Software conversion from UYVY
+                 5 = RGB32   32 bpp - Software conversion from UYVY
+                When not 0, this paramater will override 'decompression'.
+Default:        0 for every device. Initial palette is 9 (UYVY).
+Note:           If 'w9968cf-vpp' is not loaded, this paramater is set to 9.
+-------------------------------------------------------------------------------
+Name:           force_rgb
+Type:           int array (min = 0, max = 32)
+Syntax:         <0|1[,...]>
+Description:    Read RGB video data instead of BGR:
+                1 = use RGB component ordering.
+                0 = use BGR component ordering.
+                This parameter has effect when using RGBX palettes only.
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           autobright
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]>
+Description:    CMOS sensor automatically changes brightness:
+                0 = no, 1 = yes
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           autoexp
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]>
+Description:    CMOS sensor automatically changes exposure:
+                0 = no, 1 = yes
+Default:        1 for every device.
+-------------------------------------------------------------------------------
+Name:           lightfreq
+Type:           long array (min = 0, max = 32)
+Syntax:         <50|60[,...]>
+Description:    Light frequency in Hz:
+                50 for European and Asian lighting, 60 for American lighting.
+Default:        50 for every device.
+-------------------------------------------------------------------------------
+Name:           bandingfilter
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    Banding filter to reduce effects of fluorescent 
+                lighting:
+                0 disabled, 1 enabled.
+                This filter tries to reduce the pattern of horizontal
+                light/dark bands caused by some (usually fluorescent) lighting.
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           clockdiv
+Type:           long array (min = 0, max = 32)
+Syntax:         <-1|n[,...]>
+Description:    Force pixel clock divisor to a specific value (for experts):
+                n may vary from 0 to 127.
+                -1 for automatic value.
+                See also the 'double_buffer' module paramater.
+Default:        -1 for every device.
+-------------------------------------------------------------------------------
+Name:           backlight
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]>
+Description:    Objects are lit from behind:
+                0 = no, 1 = yes
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           mirror
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]>
+Description:    Reverse image horizontally:
+                0 = no, 1 = yes
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           sensor_mono
+Type:           long array (min = 0, max = 32)
+Syntax:         <0|1[,...]> 
+Description:    The CMOS sensor is monochrome:
+                0 = no, 1 = yes
+Default:        0 for every device.
+-------------------------------------------------------------------------------
+Name:           brightness
+Type:           long array (min = 0, max = 32)
+Syntax:         <n[,...]>
+Description:    Set picture brightness (0-65535).
+                This parameter has no effect if 'autobright' is enabled.
+Default:        31000 for every device.
+-------------------------------------------------------------------------------
+Name:           hue
+Type:           long array (min = 0, max = 32)
+Syntax:         <n[,...]>
+Description:    Set picture hue (0-65535).
+Default:        32768 for every device.
+-------------------------------------------------------------------------------
+Name:           colour
+Type:           long array (min = 0, max = 32)
+Syntax:         <n[,...]>
+Description:    Set picture saturation (0-65535).
+Default:        32768 for every device.
+-------------------------------------------------------------------------------
+Name:           contrast
+Type:           long array (min = 0, max = 32)
+Syntax:         <n[,...]> 
+Description:    Set picture contrast (0-65535).
+Default:        50000 for every device.
+-------------------------------------------------------------------------------
+Name:           whiteness
+Type:           long array (min = 0, max = 32)
+Syntax:         <n[,...]> 
+Description:    Set picture whiteness (0-65535).
+Default:        32768 for every device.
+-------------------------------------------------------------------------------
+Name:           debug
+Type:           int
+Syntax:         <n> 
+Description:    Debugging information level, from 0 to 6:
+                0 = none (be cautious)
+                1 = critical errors
+                2 = significant informations
+                3 = configuration or general messages
+                4 = warnings
+                5 = called functions
+                6 = function internals
+                Level 5 and 6 are useful for testing only, when just one
+                device is used.
+Default:        2
+-------------------------------------------------------------------------------
+Name:           specific_debug
+Type:           int
+Syntax:         <0|1>
+Description:    Enable or disable specific debugging messages:
+                0 = print messages concerning every level <= 'debug' level.
+                1 = print messages concerning the level indicated by 'debug'.
+Default:        0
+-------------------------------------------------------------------------------
+
+
+8. Credits
+==========
+The development would not have proceed much further without having looked at
+the source code of other drivers and without the help of several persons; in
+particular:
+
+- the I2C interface to kernel and high-level CMOS sensor control routines have
+  been taken from the OV511 driver by Mark McClelland;
+
+- memory management code has been copied from the bttv driver by Ralph Metzler,
+  Marcus Metzler and Gerd Knorr;
+
+- the low-level I2C read function has been written by Frédéric Jouault, who
+  also gave me commented logs about sniffed USB traffic taken from another
+  driver for another system;
+
+- the low-level I2C fast write function has been written by Piotr Czerczak;
index 6e6d7eb..3029f74 100644 (file)
@@ -2218,6 +2218,13 @@ M:       dbrownell@users.sourceforge.net
 L:     linux-usb-devel@lists.sourceforge.net
 S:     Maintained
 
+USB W996[87]CF DRIVER
+P:     Luca Risolia
+M:     luca_ing@libero.it
+L:     linux-usb-devel@lists.sourceforge.net
+W:     http://go.lamarinapunto.com
+S:     Maintained
+
 USER-MODE LINUX
 P:     Jeff Dike
 M:     jdike@karaya.com
index f3b3269..19d5e1b 100644 (file)
@@ -1203,6 +1203,13 @@ config DEBUG_INFO
          Say Y here only if you plan to use gdb to debug the kernel.
          If you don't debug the kernel, you can say N.
          
+config DEBUG_DEV_PRINTK
+       bool "Debug dev_printk"
+       depends on DEBUG_KERNEL
+       help
+         If you say Y here, all dev_printk() parameters will be checked before
+         dev_printk() is called.  This is useful when debugging new drivers.
+
 config DEBUG_SPINLOCK_SLEEP
        bool "Sleep-inside-spinlock checking"
        help
diff --git a/arch/mips/.gdbinit b/arch/mips/.gdbinit
new file mode 100644 (file)
index 0000000..8bdcdae
--- /dev/null
@@ -0,0 +1,7 @@
+echo Setting up the environment for debugging vmlinux...\n
+echo set remotedebug 0 \n
+set remotedebug 0
+echo cd arch/mips/kernel \n
+cd arch/mips/kernel
+echo target remote /dev/ttyS0 \n
+target remote /dev/ttyS0
index da6790b..2878b76 100644 (file)
@@ -1731,7 +1731,7 @@ static struct file_operations mixer_fops =
 
 static void __init mixer_init(void)
 {
-       mixer_unit = register_sound_mixer(&mixer_fops, -1);
+       mixer_unit = register_sound_mixer(&mixer_fops, -1, NULL);
        if (mixer_unit < 0)
                return;
 
@@ -2320,7 +2320,7 @@ static struct file_operations sq_fops =
 
 static void __init sq_init(void)
 {
-       sq_unit = register_sound_dsp(&sq_fops, -1);
+       sq_unit = register_sound_dsp(&sq_fops, -1, NULL);
        if (sq_unit < 0)
                return;
 
@@ -2453,7 +2453,7 @@ static struct file_operations state_fops =
 
 static void __init state_init(void)
 {
-       state_unit = register_sound_special(&state_fops, SND_DEV_STATUS);
+       state_unit = register_sound_special(&state_fops, SND_DEV_STATUS, NULL);
        if (state_unit < 0)
                return;
        state.busy = 0;
index 8df2d6a..9bc7b7f 100644 (file)
@@ -292,13 +292,13 @@ static int __init hostaudio_init_module(void)
         printk(KERN_INFO "UML Audio Relay (host dsp = %s, host mixer = %s)\n",
               dsp, mixer);
 
-       module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1);
+       module_data.dev_audio = register_sound_dsp(&hostaudio_fops, -1, NULL);
         if(module_data.dev_audio < 0){
                 printk(KERN_ERR "hostaudio: couldn't register DSP device!\n");
                 return -ENODEV;
         }
 
-       module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1);
+       module_data.dev_mixer = register_sound_mixer(&hostmixer_fops, -1, NULL);
         if(module_data.dev_mixer < 0){
                 printk(KERN_ERR "hostmixer: couldn't register mixer "
                       "device!\n");
index 28982a1..c7fb63f 100644 (file)
@@ -8,7 +8,11 @@
  *
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/device.h>
 #include <linux/module.h>
index 4858ba6..9fe32df 100644 (file)
  *
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/device.h>
 #include <linux/module.h>
index 5493382..db0d884 100644 (file)
@@ -8,7 +8,11 @@
  *
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/device.h>
 #include <linux/err.h>
index 7fd9d5c..880221d 100644 (file)
@@ -8,7 +8,11 @@
  *
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/device.h>
 #include <linux/module.h>
index 5aa2391..d8597a7 100644 (file)
  * add themselves as children of the system bus.
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/sysdev.h>
 #include <linux/err.h>
index 2a5fa62..6c82ecc 100644 (file)
@@ -963,6 +963,13 @@ config SCx200_GPIO
 
          If compiled as a module, it will be called scx200_gpio.
 
+config FJKEYINFO
+       tristate "Fujitsu Lifebook Application Key Support"
+       help
+         Provide support for Fujitsu Lifebook application keys.
+
+         If compiled as a module, it will be called fjkeyinf.
+
 config RAW_DRIVER
        tristate "RAW driver (/dev/raw/rawN)"
        help
index ca8aa47..b72137e 100644 (file)
@@ -72,6 +72,7 @@ obj-$(CONFIG_DZ) += dz.o
 obj-$(CONFIG_NWBUTTON) += nwbutton.o
 obj-$(CONFIG_NWFLASH) += nwflash.o
 obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+obj-$(CONFIG_FJKEYINFO) += fjkeyinf.o
 
 obj-$(CONFIG_WATCHDOG) += watchdog/
 obj-$(CONFIG_MWAVE) += mwave/
diff --git a/drivers/char/fjkeyinf.c b/drivers/char/fjkeyinf.c
new file mode 100644 (file)
index 0000000..efb131a
--- /dev/null
@@ -0,0 +1,590 @@
+/*
+    SMBus client for the Fujitsu Siemens Lifebook C-6535 Application Panel
+    
+    Copyright (C) 2001 Jochen Eisinger <jochen.eisinger@gmx.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/miscdevice.h>
+
+/* Pulled what was needed from the userspace apanel.h file here */
+/* Make sure they match what the userspace tools were build against */
+
+/* Describes to which subsystem the ioctl applies. 'P' for proprietary? */
+#define APANEL_IOCTL_GROUP      'P'
+
+
+
+/*
+ * Use one ioctl for all devices
+ * This has the advantage that one interface is defined
+ * for all devices. The caps can tell what is possible on
+ * a certain device (with some defaults for each device
+ * of course which the programmer doesn't have to check
+ * explicitly like on/off for led and read for buttons).
+ */
+
+struct apanelcmd {
+       int device;           /* Device to operate command on (APANEL_DEV_GEN_*) */
+       int cmd;              /* Command to execute (APANEL_CMD_*) */
+       int data;             /* Data for command */
+};
+
+
+struct fj_device {
+       __u8    type;
+       __u8    access_method;
+       __u8    chip_type;
+       __u8    number;
+} __attribute__((packed));
+
+#define DEVICE_APPLICATION_BUTTONS     1
+#define DEVICE_CD_BUTTONS              2
+#define DEVICE_LCD                     3
+#define DEVICE_LED_1                   4
+#define DEVICE_LED_2                   6
+#define DEVICE_APPLICATION_3_BUTTONS   7
+
+
+#define IOCTL_APANEL_CMD        _IOR(APANEL_IOCTL_GROUP,202,struct apanelcmd)
+
+
+/* Devices */
+#define APANEL_DEV_LED          1
+#define APANEL_DEV_LCD          2
+#define APANEL_DEV_CDBTN        3
+#define APANEL_DEV_APPBTN       4
+
+/*
+ * Commands
+ */
+
+/* Device capabilities */
+#define APANEL_CAP_ONOFF        1       /* Turn on/off */
+#define APANEL_CAP_GET          2       /* General get command */
+#define APANEL_CAP_SET          4       /* General set command */
+#define APANEL_CAP_BLINK        8       /* Blinking */
+#define APANEL_CAP_DUTY         0x10    /* Duty cycle of blinking */
+#define APANEL_CAP_RESET        0x20    /* Reset device */
+#define APANEL_CAP_MAX          0x40    /* Get highest possible value for */
+                                        /* set/get */
+#define APANEL_CAP_MIN          0x80    /* Get lowest possible value */
+#define APANEL_CMD_ONOFF        2
+#define APANEL_ONOFF_ON         1       /* Or is APANEL_ON nicer? */
+#define APANEL_ONOFF_OFF        0
+
+#define APANEL_CMD_SET          3
+#define APANEL_CMD_GET          4
+
+#define APANEL_CMD_BLINK        5       /* data=Blink frequency *0.01Hz or so */
+#define APANEL_CMD_DUTY         6       /* data=percentage high */
+
+#define APANEL_CMD_RESET        7       /* If this is useful at all */
+
+#define APANEL_CMD_MAX          8
+#define APANEL_CMD_MIN          9
+
+/*
+ * Button masks
+ */
+/* Masks for application buttons */
+#define APANEL_APPBTN_A         1
+#define APANEL_APPBTN_B         2
+#define APANEL_APPBTN_INTERNET  4
+#define APANEL_APPBTN_EMAIL     8
+
+/* Masks for cd buttons */
+#define APANEL_CDBTN_STOP       1
+#define APANEL_CDBTN_EJECT      2
+#define APANEL_CDBTN_PLAY       4
+#define APANEL_CDBTN_PAUSE      8
+#define APANEL_CDBTN_BACK       0x10
+#define APANEL_CDBTN_FORW       0x20
+
+
+
+/* print lots of useless debug infos */
+#define DEBUG
+
+#define MY_NAME        "fjkeyinf"
+
+#define dbg(format, arg...)                                    \
+       do {                                                    \
+               if (debug)                                      \
+                       printk(KERN_DEBUG "%s: " format,        \
+                               MY_NAME , ## arg);              \
+       } while (0)
+
+static int debug = 1;
+
+/*
+ * this is the internal driver version number. There is no direct relationship
+ * between the hardware, the apanel protocol, or the driver versions
+ * However, this version number should be increased for every change
+ */
+/* current driver version */
+#define FJKEYINF_VERSION_MAJOR 0
+#define FJKEYINF_VERSION_MINOR 4
+
+/*
+ * This is the apanel version this driver implements. This _must_ be the
+ * same as your apanel.h file
+ */
+/* protocol version */
+#define FJKEYINF_APANEL_MAJOR  1
+#define FJKEYINF_APANEL_MINOR  0
+
+/*
+ * Some real leet m4cr0s
+ */
+#define STRINGIFY1(x)  #x
+#define STRINGIFY(x)   STRINGIFY1(x)
+
+#define PASTE1(x,y)    x##y
+#define PASTE(x,y)     PASTE1(x,y)
+
+/*
+ * This is the version of the driver as a string
+ */
+#define FJKEYINF_VERSION_STRING        PASTE("v",                              \
+                               PASTE(STRINGIFY(FJKEYINF_VERSION_MAJOR),\
+                               PASTE(".",STRINGIFY(FJKEYINF_VERSION_MINOR))))
+
+/*
+ * This is the version of the protocol as a string
+ */
+#define FJKEYINF_APANEL_VERSION_STRING PASTE("v",                      \
+                                       PASTE(STRINGIFY(FJKEYINF_APANEL_MAJOR),\
+                                       PASTE(".",                      \
+                                       STRINGIFY(FJKEYINF_APANEL_MINOR))))
+
+/* 
+ * every i2c device has to register to the i2c sub-system. Therefor it needs
+ * a driver id. We should be I2C_DRIVERID_FJKEYINF. However, if this isn't
+ * defined in i2c-id.h, we'll use a generic id.
+ */
+#ifndef I2C_DRIVERID_FJKEYINF
+#define I2C_DRIVERID_FJKEYINF  0xF000
+#endif
+
+/*
+ * Yes, it's unbelievable, but me crappy driver has an official devices
+ * entry. It's register as a misc-device (char-major-10) minor 216. The
+ * location should be /dev/fujitsu/apanel... after all, it seems to be
+ * a quite c00l driver ;>
+ */
+#define FJKEYINF_CHAR_MINOR    216
+
+/*
+ * Modules can store nice extra infos...
+ */
+MODULE_AUTHOR("Jochen Eisinger <jochen.eisinger@gmx.net>");
+MODULE_DESCRIPTION("Application Panel driver for Lifebook C-series");
+MODULE_LICENSE("GPL");
+
+/*
+ * So, let's start the hackin'
+ *
+ * we first define the interface for the i2c driver, cuz the misc device
+ * part uses the struct fjkeyinf_client. also note that this struct is
+ * static where it would be better to dynamically allocate one... but what
+ * fsck, more than one apanel your crappy laptop won't have...
+ */
+
+/* definitions for i2c (smbus) interface */
+
+/* forward declaration of the interface procedures */
+
+/* detach removes the smbus driver from the system */
+       static int fjkeyinf_detach(struct i2c_client *client);
+/* attach tries to attach to a given smbus */
+       static int fjkeyinf_attach(struct i2c_adapter *adap);
+
+/* this structur defines the interface to the i2c sub-system */
+static struct i2c_driver fjkeyinf_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "fujitsu_panel" /* FJKEYINF_VERSION_STRING */,
+       .id             = I2C_DRIVERID_FJKEYINF,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = &fjkeyinf_attach,
+       .detach_client  = &fjkeyinf_detach,
+       .command        = NULL,
+};
+
+
+/* Addresses to scan. afaik the device is at id 0x18. so we only scan this */
+static unsigned short normal_i2c[] = {0x18,I2C_CLIENT_END};
+static unsigned short normal_i2c_range[] = {I2C_CLIENT_END};
+
+/* 
+ * generate some stupid additional structures. we don't really care about
+ * this
+ */
+I2C_CLIENT_INSMOD;
+
+
+/*
+ * If we've found the device, we have to provide an i2c_client structure
+ * to the i2c subsystem with which this devices can be accessed. as I stated
+ * above, there's max. 1 device, so I use _one static_ entry
+ */
+static struct i2c_client fjkeyinf_client = 
+{
+       .id =           -1,
+       .flags =        0,
+       .addr =         0,
+       .adapter =      NULL,
+       .driver =       &fjkeyinf_driver,
+       .name =         "fjkeyinf",
+};
+
+
+/*
+ * luckily, the c-series laptops have a configuration block in there BIOS.
+ * so we can use it to detect the presence of the apanel device. There's
+ * also a checksum somewhere, but we don't care about it.
+ *
+ * Note the first 8 characters. that's where this strange driver name comes
+ * from.
+ *
+ * The configuration block can be found at 0x000FFA30
+ *
+ * There should also be an access method 3, but I don't know much about it.
+ * Basically there should also be an LCD device. but my notebook hasn't one
+ * so I don't know what the configuration block looks like
+ *
+ * type 1 is LED type 4 ist BUTTONS, probably 2 is LCD.
+ */
+static const unsigned char fjkeyinf_info[16] = {
+       'F', 'J', 'K', 'E', 'Y', 'I', 'N', 'F',
+       /* device 0 */  /* type         */      1,
+                       /* access method*/      1,
+                       /* enabled      */      1,
+                       /* smbus device */      0x30,
+       /* device 1 */  /* type         */      4,
+                       /* access method*/      1,
+                       /* enabled      */      1,
+                       /* smbus device */      0x30};
+
+#define FJKEYINF_INFO_ADDR     0x000FFA30
+#define FJKEYINF_INFO_ADDR     0x000F6f20      /* Address for the P2120 */
+
+/*
+ * the following functions implement the ioctls. Note however, that not
+ * much is implemented yet.
+ */
+
+/* turn a device on or off */
+
+int fjkeyinf_onoff(int dev, int state)
+{
+
+       switch (dev) {
+
+               case APANEL_DEV_LED:
+
+                       if (state) {
+                               dbg("turning LED on...\n");
+
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x10, 0x8080);
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x0f, 0x100);
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x11, 1);
+
+                       } else {
+                               
+                               dbg("turning LED off...\n");
+
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x10, 0);
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x0f, 0x100);
+                               i2c_smbus_write_word_data(&fjkeyinf_client,
+                                              0x11, 0);
+
+
+                       }
+
+                       return state;
+
+               default:
+
+                       printk(KERN_NOTICE
+                               "fjkeyinf: ONOFF called for invalid device"
+                               " (dev %d, state %d)\n", dev, state);
+
+                       return -EINVAL;
+
+       }
+
+}
+
+/* gets the current value from a device */
+int fjkeyinf_get(int dev)
+{
+       switch (dev) {
+               case APANEL_DEV_LED:
+                       return ((i2c_smbus_read_word_data
+                                       (&fjkeyinf_client, 0x00) >> 7) & 1);
+
+               case APANEL_DEV_APPBTN:
+                       {
+                               int state;
+
+                               state = i2c_smbus_read_word_data
+                                       (&fjkeyinf_client, 0x00);
+
+                               state = (state >> 8) & 0x0f;
+
+                               state ^= 0x0f;
+
+                               return (((state & 1) ? APANEL_APPBTN_EMAIL : 0)
+                                       + ((state & 2) ? APANEL_APPBTN_INTERNET : 0)
+                                       + ((state & 4) ? APANEL_APPBTN_A : 0)
+                                       + ((state & 8) ? APANEL_APPBTN_B : 0));
+                       }
+
+               default:
+                       printk(KERN_NOTICE "fjkeyinf: GET called for invalid"
+                                       " device (dev %d)\n", dev);
+                       return -EINVAL;
+       }
+}
+
+
+
+/* 
+ * file operations for /dev/fujitsu/apanel
+ * 
+ * That what the name says: file operation. pretty basic stuff follows
+ */
+                       
+/*
+ * if somebody opens us, we just realize and let him pass
+ */
+static int fjkeyinf_open (struct inode *inode, struct file *filp)
+{
+       dbg("open called for /dev/fujitsu/apanel\n");
+       return 0;
+}
+
+/* same with close */
+static int fjkeyinf_close (struct inode *inode, struct file *filp)
+{
+       dbg("close called for /dev/fujitsu/apanel\n");
+       return 0;
+}
+
+/* all commands are passed using the ioctl interface. so we have to decide
+ * here, what we have to do */
+int fjkeyinf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+               unsigned long args)
+{
+       struct apanelcmd *acmd;
+
+       dbg("ioctl (cmd: %u, args: 0x%08lx)\n", cmd, args);
+
+       /* Let's see, what they want from us... */
+       
+       switch (cmd) {
+               case IOCTL_APANEL_CMD:
+                       /* the actual command is passed in a apanelcmd
+                        * structure */
+
+                       if (!(args)) {
+
+                               printk(KERN_NOTICE "fjkeyinf: invalid apanel command "
+                                               "(NULL pointer)\n");
+                               return -EINVAL;
+
+                       }
+
+                       acmd = (struct apanelcmd *)args;
+
+                       /* although not all commands are implemented, we
+                        * understand all... */
+
+                       dbg("apanel command %d\n", acmd->cmd);
+
+
+                       switch (acmd->cmd) {
+                               case APANEL_CMD_ONOFF:
+                                       return fjkeyinf_onoff(acmd->device,
+                                                       acmd->data);
+
+                               case APANEL_CMD_GET:
+                                       return fjkeyinf_get(acmd->device);
+
+                               default:
+                                       printk(KERN_NOTICE "fjkeyinf: unknown "
+                                               "device/command %d/%d\n",
+                                               acmd->device, acmd->cmd);
+                                       return -EINVAL;
+                       }
+
+               default:
+                       printk(KERN_NOTICE "fjkeyinf: unknown ioctl code %u\n", cmd);
+                       return -EINVAL;
+       }
+}
+
+/* now we tell the misc_device what nice functions we've implemented */
+static struct file_operations fjkeyinf_fops = {
+       .owner =        THIS_MODULE,
+       .ioctl =        fjkeyinf_ioctl,
+       .open =         fjkeyinf_open,
+       .release =      fjkeyinf_close,
+};
+
+/* misc dev entry. We need this to register to the misc dev driver */
+static struct miscdevice fjkeyinf_dev = {
+       FJKEYINF_CHAR_MINOR,
+       "apanel",
+       &fjkeyinf_fops
+};
+
+
+/* Now comes the i2c driver stuff. This is pretty straight forward, just as they
+ * described it in their docu.
+ */
+
+/* basically this function should probe the i2c client, but we know that it has
+ * to be the one we're looking for - and I have no idea how I should probe for
+ * it, so we just register... */
+static int fjkeyinf_probe(struct i2c_adapter *adap, int addr, int kind)
+{
+       int result;
+        fjkeyinf_client.adapter = adap;
+        fjkeyinf_client.addr = addr;
+
+       dbg("%s\n", __FUNCTION__);
+       if ((result = misc_register(&fjkeyinf_dev)) < 0) {
+               printk(KERN_NOTICE "fjkeyinf: could not register misc device (%d)\n",
+                               result);
+               return result;
+       }
+
+        i2c_attach_client(&fjkeyinf_client);
+
+       printk(KERN_INFO "fjkeyinf: Application Panel Driver "
+                       /*FJKEYINF_VERSION_STRING*/ "\n");
+       printk(KERN_INFO "fjkeyinf: Copyright (c) 2001 by "
+               "Jochen Eisinger <jochen.eisinger@gmx.net>\n");
+
+       
+       dbg("driver loaded at address 0x%02x...\n", addr);
+       
+       return 0;
+}
+
+
+/* this function is invoked, when we should release our resource... */
+static int fjkeyinf_detach(struct i2c_client *client)
+{
+       dbg("driver detached...\n");
+
+       misc_deregister(&fjkeyinf_dev);
+               
+       i2c_detach_client(client);
+       
+       return 0;
+}
+
+/* this function is invoked for every i2c adapter, that has a device at the
+ * address we've specified */
+static int fjkeyinf_attach(struct i2c_adapter *adap)
+{
+       dbg("%s\n", __FUNCTION__);
+       return i2c_probe(adap, &addr_data, fjkeyinf_probe);
+}
+
+
+
+/* startup */
+static int __init fjkeyinf_init(void)
+{
+       unsigned char *fujitsu_bios = __va(FJKEYINF_INFO_ADDR);
+//     int ctr;
+       struct fj_device        *dev;
+       
+       if (__pa(high_memory) < (FJKEYINF_INFO_ADDR - 16)) {
+               dbg("Fujitsu BIOS not found...\n");
+               return -ENODEV;
+       }
+
+       dbg("Configuration block [%c%c%c%c%c%c%c%c] "
+               "(%d, %d, %d, 0x%02x) (%d, %d, %d, 0x%02x)\n",
+               fujitsu_bios[0],
+               fujitsu_bios[1],
+               fujitsu_bios[2],
+               fujitsu_bios[3],
+               fujitsu_bios[4],
+               fujitsu_bios[5],
+               fujitsu_bios[6],
+               fujitsu_bios[7],
+               (int)fujitsu_bios[8],
+               (int)fujitsu_bios[9],
+               (int)fujitsu_bios[10],
+               (int)fujitsu_bios[11],
+               (int)fujitsu_bios[12],
+               (int)fujitsu_bios[13],
+               (int)fujitsu_bios[14],
+               (int)fujitsu_bios[15]);
+
+       dev = (struct fj_device *)&fujitsu_bios[8];
+       while (1) {
+               if (dev->type == 0)
+                       break;
+               dbg("type = %d, access_method = %d, chip_type = %d, number = %d\n",
+                   dev->type, dev->access_method, dev->chip_type, dev->number);
+               ++dev;
+       }
+       
+//     for (ctr=0 ; ctr<16 ; ctr++)
+//             if (fujitsu_bios[ctr] != fjkeyinf_info[ctr]) {
+//                     dbg("device not found...\n");
+//                     return -ENODEV;
+//             }
+
+       dbg("device found...\n");
+       
+       i2c_add_driver(&fjkeyinf_driver);
+       return 0;
+}
+
+static void __exit fjkeyinf_exit(void)
+{
+       i2c_del_driver(&fjkeyinf_driver);
+}
+
+module_init(fjkeyinf_init);
+module_exit(fjkeyinf_exit);
+
index ef9608d..e64b538 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/smp_lock.h>
 #include <linux/devfs_fs_kernel.h>
 #include <linux/ptrace.h>
+#include <linux/device.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -657,7 +658,7 @@ static struct file_operations memory_fops = {
        .open           = memory_open,  /* just a selector for the real open */
 };
 
-static const struct {
+static const struct mem_dev {
        unsigned int            minor;
        char                    *name;
        umode_t                 mode;
@@ -676,6 +677,23 @@ static const struct {
        {11,"kmsg",    S_IRUGO | S_IWUSR,           &kmsg_fops},
 };
 
+static void release_mem_dev(struct class_device *class_dev)
+{
+       kfree(class_dev);
+}
+
+static struct class mem_class = {
+       .name           = "mem",
+       .release        = &release_mem_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct mem_dev *mem_dev = class_get_devdata(class_dev);
+       return print_dev_t(buf, MKDEV(MEM_MAJOR, mem_dev->minor));
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
 static int __init chr_dev_init(void)
 {
        int i;
@@ -683,7 +701,20 @@ static int __init chr_dev_init(void)
        if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
                printk("unable to get major %d for memory devs\n", MEM_MAJOR);
 
+       class_register(&mem_class);
        for (i = 0; i < ARRAY_SIZE(devlist); i++) {
+               struct class_device *class_dev;
+
+               class_dev = kmalloc(sizeof(*class_dev), GFP_KERNEL);
+               if (class_dev) {
+                       memset(class_dev, 0x00, sizeof(*class_dev));
+                       class_dev->class = &mem_class;
+                       strncpy(class_dev->class_id, devlist[i].name, BUS_ID_SIZE);
+                       class_set_devdata(class_dev, (void *)&devlist[i]);
+                       if (!class_device_register(class_dev));
+                               class_device_create_file(class_dev, &class_device_attr_dev);
+               }
+
                devfs_mk_cdev(MKDEV(MEM_MAJOR, devlist[i].minor),
                                S_IFCHR | devlist[i].mode, devlist[i].name);
        }
index a250243..811b6b7 100644 (file)
@@ -47,7 +47,7 @@
 #include <linux/devfs_fs_kernel.h>
 #include <linux/stat.h>
 #include <linux/init.h>
-
+#include <linux/device.h>
 #include <linux/tty.h>
 #include <linux/kmod.h>
 
@@ -180,6 +180,91 @@ fail:
        return err;
 }
 
+/* Misc class implementation */
+
+/* 
+ * TODO for 2.7:
+ *  - add a struct class_device to struct miscdevice and make all usages of
+ *    them dynamic.  This will let us get rid of struct misc_dev below.
+ */
+struct misc_dev {
+       struct list_head node;
+       dev_t dev;
+       struct class_device class_dev;
+};
+#define to_misc_dev(d) container_of(d, struct misc_dev, class_dev)
+
+static LIST_HEAD(misc_dev_list);
+static spinlock_t misc_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static void release_misc_dev(struct class_device *class_dev)
+{
+       struct misc_dev *misc_dev = to_misc_dev(class_dev);
+       kfree(misc_dev);
+}
+
+static struct class misc_class = {
+       .name           = "misc",
+       .release        = &release_misc_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct misc_dev *misc_dev = to_misc_dev(class_dev);
+       return print_dev_t(buf, misc_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static void misc_add_class_device(struct miscdevice *misc)
+{
+       struct misc_dev *misc_dev = NULL;
+       int retval;
+
+       misc_dev = kmalloc(sizeof(*misc_dev), GFP_KERNEL);
+       if (!misc_dev)
+               return;
+       memset(misc_dev, 0x00, sizeof(*misc_dev));
+
+       misc_dev->dev = MKDEV(MISC_MAJOR, misc->minor);
+       misc_dev->class_dev.dev = misc->dev;
+       misc_dev->class_dev.class = &misc_class;
+       snprintf(misc_dev->class_dev.class_id, BUS_ID_SIZE, "%s", misc->name);
+       retval = class_device_register(&misc_dev->class_dev);
+       if (retval)
+               goto error;
+       class_device_create_file(&misc_dev->class_dev, &class_device_attr_dev);
+       spin_lock(&misc_dev_list_lock);
+       list_add(&misc_dev->node, &misc_dev_list);
+       spin_unlock(&misc_dev_list_lock);
+       return;
+error:
+       kfree(misc_dev);
+}
+
+static void misc_remove_class_device(struct miscdevice *misc)
+{
+       struct misc_dev *misc_dev = NULL;
+       struct list_head *tmp;
+       int found = 0;
+
+       spin_lock(&misc_dev_list_lock);
+       list_for_each(tmp, &misc_dev_list) {
+               misc_dev = list_entry(tmp, struct misc_dev, node);
+               if ((MINOR(misc_dev->dev) == misc->minor)) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               list_del(&misc_dev->node);
+               spin_unlock(&misc_dev_list_lock);
+               class_device_unregister(&misc_dev->class_dev);
+       } else {
+               spin_unlock(&misc_dev_list_lock);
+       }
+}
+
+
 static struct file_operations misc_fops = {
        .owner          = THIS_MODULE,
        .open           = misc_open,
@@ -236,6 +321,7 @@ int misc_register(struct miscdevice * misc)
 
        devfs_mk_cdev(MKDEV(MISC_MAJOR, misc->minor),
                        S_IFCHR|S_IRUSR|S_IWUSR|S_IRGRP, misc->devfs_name);
+       misc_add_class_device(misc);
 
        /*
         * Add it to the front, so that later devices can "override"
@@ -265,6 +351,7 @@ int misc_deregister(struct miscdevice * misc)
 
        down(&misc_sem);
        list_del(&misc->list);
+       misc_remove_class_device(misc);
        devfs_remove(misc->devfs_name);
        if (i < DYNAMIC_MINORS && i>0) {
                misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
@@ -285,6 +372,7 @@ static int __init misc_init(void)
        if (ent)
                ent->proc_fops = &misc_proc_fops;
 #endif
+       class_register(&misc_class);
 #ifdef CONFIG_MVME16x
        rtc_MK48T08_init();
 #endif
index bd1e497..743346b 100644 (file)
@@ -266,7 +266,7 @@ static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = {
 /* v */        NULL, /* May be assigned at init time by SMP VOYAGER */
 /* w */        NULL,
 /* x */        NULL,
-/* y */        NULL,
+/* y */        &sysrq_mountro_op,      /* stupid fujitsu laptop, can't hit the U key */
 /* z */        NULL
 };
 
index 15c28a5..17be689 100644 (file)
@@ -36,6 +36,7 @@
 #include <linux/kbd_kern.h>
 #include <linux/console.h>
 #include <linux/smp_lock.h>
+#include <linux/device.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
@@ -469,6 +470,85 @@ static struct file_operations vcs_fops = {
        .open           = vcs_open,
 };
 
+/* vc class implementation */
+
+struct vc_dev {
+       struct list_head node;
+       dev_t dev;
+       struct class_device class_dev;
+};
+#define to_vc_dev(d) container_of(d, struct vc_dev, class_dev)
+
+static LIST_HEAD(vc_dev_list);
+static spinlock_t vc_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static void release_vc_dev(struct class_device *class_dev)
+{
+       struct vc_dev *vc_dev = to_vc_dev(class_dev);
+       kfree(vc_dev);
+}
+
+static struct class vc_class = {
+       .name           = "vc",
+       .release        = &release_vc_dev,
+};
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct vc_dev *vc_dev = to_vc_dev(class_dev);
+       return print_dev_t(buf, vc_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static int vc_add_class_device(dev_t dev, char *name, int minor)
+{
+       struct vc_dev *vc_dev = NULL;
+       int retval;
+
+       vc_dev = kmalloc(sizeof(*vc_dev), GFP_KERNEL);
+       if (!vc_dev)
+               return -ENOMEM;
+       memset(vc_dev, 0x00, sizeof(*vc_dev));
+
+       vc_dev->dev = dev;
+       vc_dev->class_dev.class = &vc_class;
+       snprintf(vc_dev->class_dev.class_id, BUS_ID_SIZE, name, minor);
+       retval = class_device_register(&vc_dev->class_dev);
+       if (retval)
+               goto error;
+       class_device_create_file(&vc_dev->class_dev, &class_device_attr_dev);
+       spin_lock(&vc_dev_list_lock);
+       list_add(&vc_dev->node, &vc_dev_list);
+       spin_unlock(&vc_dev_list_lock);
+       return 0;
+error:
+       kfree(vc_dev);
+       return retval;
+}
+
+static void vc_remove_class_device(int minor)
+{
+       struct vc_dev *vc_dev = NULL;
+       struct list_head *tmp;
+       int found = 0;
+
+       spin_lock(&vc_dev_list_lock);
+       list_for_each(tmp, &vc_dev_list) {
+               vc_dev = list_entry(tmp, struct vc_dev, node);
+               if (MINOR(vc_dev->dev) == minor) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               list_del(&vc_dev->node);
+               spin_unlock(&vc_dev_list_lock);
+               class_device_unregister(&vc_dev->class_dev);
+       } else {
+               spin_unlock(&vc_dev_list_lock);
+       }
+}
+
 void vcs_make_devfs(struct tty_struct *tty)
 {
        devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 1),
@@ -477,19 +557,26 @@ void vcs_make_devfs(struct tty_struct *tty)
        devfs_mk_cdev(MKDEV(VCS_MAJOR, tty->index + 129),
                        S_IFCHR|S_IRUSR|S_IWUSR,
                        "vcc/a%u", tty->index + 1);
+       vc_add_class_device(MKDEV(VCS_MAJOR, tty->index + 1), "vcs%u", tty->index + 1);
+       vc_add_class_device(MKDEV(VCS_MAJOR, tty->index + 129), "vcsa%u", tty->index + 1);
 }
 void vcs_remove_devfs(struct tty_struct *tty)
 {
        devfs_remove("vcc/%u", tty->index + 1);
        devfs_remove("vcc/a%u", tty->index + 1);
+       vc_remove_class_device(tty->index + 1);
+       vc_remove_class_device(tty->index + 129);
 }
 
 int __init vcs_init(void)
 {
        if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))
                panic("unable to get major %d for vcs device", VCS_MAJOR);
+       class_register(&vc_class);
 
        devfs_mk_cdev(MKDEV(VCS_MAJOR, 0), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/0");
        devfs_mk_cdev(MKDEV(VCS_MAJOR, 128), S_IFCHR|S_IRUSR|S_IWUSR, "vcc/a0");
+       vc_add_class_device(MKDEV(VCS_MAJOR, 0), "vcs", 0);
+       vc_add_class_device(MKDEV(VCS_MAJOR, 128), "vcsa", 128);
        return 0;
 }
index f8330b3..f5d98d2 100644 (file)
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               */
 /* ------------------------------------------------------------------------- */
 
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
-   Frodo Looijaard <frodol@dds.nl> */
-
-/* $Id: i2c-algo-bit.c,v 1.44 2003/01/21 08:08:16 kmalkki Exp $ */
+/* With some changes from Frodo Looijaard <frodol@dds.nl>, Kyösti Mälkki
+   <kmalkki@cc.hut.fi> and Jean Delvare <khali@linux-fr.org> */
 
 /* #define DEBUG 1 */
 
@@ -87,8 +85,10 @@ static inline int sclhi(struct i2c_algo_bit_data *adap)
        setscl(adap,1);
 
        /* Not all adapters have scl sense line... */
-       if (adap->getscl == NULL )
+       if (adap->getscl == NULL ) {
+               udelay(adap->udelay);
                return 0;
+       }
 
        start=jiffies;
        while (! getscl(adap) ) {       
@@ -222,68 +222,72 @@ static int i2c_inb(struct i2c_adapter *i2c_adap)
  */
 static int test_bus(struct i2c_algo_bit_data *adap, char* name) {
        int scl,sda;
+
+       if (adap->getscl==NULL)
+               printk(KERN_INFO "i2c-algo-bit.o: Testing SDA only, "
+                       "SCL is not readable.\n");
+
        sda=getsda(adap);
-       if (adap->getscl==NULL) {
-               printk(KERN_WARNING "i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n");
-               return 0;               
-       }
-       scl=getscl(adap);
-       printk(KERN_INFO "i2c-algo-bit.o: Adapter: %s scl: %d  sda: %d -- testing...\n",
-              name,getscl(adap),getsda(adap));
+       scl=(adap->getscl==NULL?1:getscl(adap));
+       printk(KERN_DEBUG "i2c-algo-bit.o: (0) scl=%d, sda=%d\n",scl,sda);
        if (!scl || !sda ) {
-               printk(KERN_INFO " i2c-algo-bit.o: %s seems to be busy.\n",name);
+               printk(KERN_WARNING "i2c-algo-bit.o: %s seems to be busy.\n", name);
                goto bailout;
        }
+
        sdalo(adap);
-       printk(KERN_DEBUG "i2c-algo-bit.o:1 scl: %d  sda: %d \n",getscl(adap),
-              getsda(adap));
-       if ( 0 != getsda(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck high!\n",name);
-               sdahi(adap);
+       sda=getsda(adap);
+       scl=(adap->getscl==NULL?1:getscl(adap));
+       printk(KERN_DEBUG "i2c-algo-bit.o: (1) scl=%d, sda=%d\n",scl,sda);
+       if ( 0 != sda ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck high!\n");
                goto bailout;
        }
-       if ( 0 == getscl(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n",
-                       name);
+       if ( 0 == scl ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+                       "while pulling SDA low!\n");
                goto bailout;
        }               
+
        sdahi(adap);
-       printk(KERN_DEBUG "i2c-algo-bit.o:2 scl: %d  sda: %d \n",getscl(adap),
-              getsda(adap));
-       if ( 0 == getsda(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SDA stuck low!\n",name);
-               sdahi(adap);
+       sda=getsda(adap);
+       scl=(adap->getscl==NULL?1:getscl(adap));
+       printk(KERN_DEBUG "i2c-algo-bit.o: (2) scl=%d, sda=%d\n",scl,sda);
+       if ( 0 == sda ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SDA stuck low!\n");
                goto bailout;
        }
-       if ( 0 == getscl(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n",
-                      name);
-       goto bailout;
+       if ( 0 == scl ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SCL unexpected low "
+                       "while pulling SDA high!\n");
+               goto bailout;
        }
+
        scllo(adap);
-       printk(KERN_DEBUG "i2c-algo-bit.o:3 scl: %d  sda: %d \n",getscl(adap),
-              getsda(adap));
-       if ( 0 != getscl(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck high!\n",name);
-               sclhi(adap);
+       sda=getsda(adap);
+       scl=(adap->getscl==NULL?0:getscl(adap));
+       printk(KERN_DEBUG "i2c-algo-bit.o: (3) scl=%d, sda=%d\n",scl,sda);
+       if ( 0 != scl ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck high!\n");
                goto bailout;
        }
-       if ( 0 == getsda(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n",
-                       name);
+       if ( 0 == sda ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+                       "while pulling SCL low!\n");
                goto bailout;
        }
+       
        sclhi(adap);
-       printk(KERN_DEBUG "i2c-algo-bit.o:4 scl: %d  sda: %d \n",getscl(adap),
-              getsda(adap));
-       if ( 0 == getscl(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SCL stuck low!\n",name);
-               sclhi(adap);
+       sda=getsda(adap);
+       scl=(adap->getscl==NULL?1:getscl(adap));
+       printk(KERN_DEBUG "i2c-algo-bit.o: (4) scl=%d, sda=%d\n",scl,sda);
+       if ( 0 == scl ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SCL stuck low!\n");
                goto bailout;
        }
-       if ( 0 == getsda(adap) ) {
-               printk(KERN_WARNING "i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n",
-                       name);
+       if ( 0 == sda ) {
+               printk(KERN_WARNING "i2c-algo-bit.o: SDA unexpected low "
+                       "while pulling SCL high!\n");
                goto bailout;
        }
        printk(KERN_INFO "i2c-algo-bit.o: %s passed test.\n",name);
index 40ded8d..5037884 100644 (file)
@@ -161,11 +161,12 @@ config I2C_PIIX4
        help
          If you say yes to this option, support will be included for the Intel
          PIIX4 family of mainboard I2C interfaces.  Specifically, the following
-         versions of the chipset is supported:
+         versions of the chipset are supported:
            Intel PIIX4
            Intel 440MX
            Serverworks OSB4
            Serverworks CSB5
+           Serverworks CSB6
            SMSC Victory66
 
          This driver can also be built as a module.  If so, the module
@@ -309,6 +310,7 @@ config I2C_VIAPRO
          8233
          8233A
          8235
+         8237
 
          This driver can also be built as a module.  If so, the module
          will be called i2c-viapro.
index 2a8b03b..c1693a0 100644 (file)
@@ -119,12 +119,16 @@ static void pcf_isa_waitforpin(void) {
        int timeout = 2;
 
        if (irq > 0) {
-               cli();
+               /* This cli() stuff has to get cleaned up */
+               #if defined(CONFIG_SMP)
+               WARN_ON(1);
+               #endif
+               /* cli(); */
                if (pcf_pending == 0) {
                        interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ );
                } else
                        pcf_pending = 0;
-               sti();
+               /* sti(); */
        } else {
                udelay(100);
        }
index 36fcce3..a439a51 100644 (file)
@@ -45,7 +45,6 @@
 #include <linux/i2c.h>
 #include <linux/i2c-algo-ite.h>
 #include <linux/i2c-adap-ite.h>
-#include "../i2c-ite.h"
 
 #define DEFAULT_BASE  0x14014030
 #define ITE_IIC_IO_SIZE        0x40
index c46252b..58d7f9a 100644 (file)
@@ -22,7 +22,7 @@
 /*
    Supports:
        Intel PIIX4, 440MX
-       Serverworks OSB4, CSB5
+       Serverworks OSB4, CSB5, CSB6
        SMSC Victory66
 
    Note: we assume there can only be one device, with one SMBus interface.
@@ -423,6 +423,13 @@ static struct pci_device_id piix4_ids[] = {
                .driver_data =  0,
        },
        {
+               .vendor =       PCI_VENDOR_ID_SERVERWORKS,
+               .device =       PCI_DEVICE_ID_SERVERWORKS_CSB6,
+               .subvendor =    PCI_ANY_ID,
+               .subdevice =    PCI_ANY_ID,
+               .driver_data =  0,
+       },
+       {
                .vendor =       PCI_VENDOR_ID_INTEL,
                .device =       PCI_DEVICE_ID_INTEL_82443MX_3,
                .subvendor =    PCI_ANY_ID,
index 06167b8..5d1b5cc 100644 (file)
@@ -1,13 +1,11 @@
 /*
     i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
               monitoring
-    Copyright (c) 1998, 1999  Frodo Looijaard <frodol@dds.nl>,
-    Philip Edelbrock <phil@netroedge.com>,
-    Ralph Metzler <rjkm@thp.uni-koeln.de>, and
+    Copyright (C) 1998-2003  The LM Sensors Team
+    Alexander Wold <awold@bigfoot.com>
     Mark D. Studebaker <mdsxyz123@yahoo.com>
     
-    Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
-    Simon Vogl
+    Based on i2c-voodoo3.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
@@ -195,9 +193,7 @@ static void __exit i2c_savage4_exit(void)
        pci_unregister_driver(&savage4_driver);
 }
 
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
-               "Philip Edelbrock <phil@netroedge.com>, "
-               "Ralph Metzler <rjkm@thp.uni-koeln.de>, "
+MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
                "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
 MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
 MODULE_LICENSE("GPL");
index c030fdb..760ff57 100644 (file)
@@ -29,6 +29,7 @@
        8233
        8233A (0x3147 and 0x3177)
        8235
+       8237
    Note: we assume there can only be one device, with one SMBus interface.
 */
 
@@ -436,6 +437,13 @@ static struct pci_device_id vt596_ids[] = {
        },
        {
                .vendor         = PCI_VENDOR_ID_VIA,
+               .device         = PCI_DEVICE_ID_VIA_8237,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+               .driver_data    = SMBBA3
+       },
+       {
+               .vendor         = PCI_VENDOR_ID_VIA,
                .device         = PCI_DEVICE_ID_VIA_8231_4,
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
index b992f4a..8108afd 100644 (file)
@@ -68,6 +68,17 @@ config SENSORS_LM78
          This driver can also be built as a module.  If so, the module
          will be called lm78.
 
+config SENSORS_LM83
+       tristate "National Semiconductor LM83"
+       depends on I2C && EXPERIMENTAL
+       select I2C_SENSOR
+       help
+         If you say yes here you get support for National Semiconductor
+         LM83 sensor chips.
+
+         This driver can also be built as a module.  If so, the module
+         will be called lm83.
+
 config SENSORS_LM85
        tristate "National Semiconductor LM85 and compatibles"
        depends on I2C && EXPERIMENTAL
@@ -99,7 +110,12 @@ config SENSORS_W83781D
          of sensor chips: the W83781D, W83782D, W83783S and W83682HF,
          and the similar Asus AS99127F.
          
-         This driver can also be built as a module.  If so, the module
-         will be called w83781d.
+
+
+config SENSORS_SMBUS_ARP
+       tristate "SMBus ARP 2.0 support"
+       depends on I2C && EXPERIMENTAL
+       select I2C_SENSOR
+         
 
 endmenu
index e4896ed..d2ab036 100644 (file)
@@ -10,5 +10,10 @@ obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o
 obj-$(CONFIG_SENSORS_IT87)     += it87.o
 obj-$(CONFIG_SENSORS_LM75)     += lm75.o
 obj-$(CONFIG_SENSORS_LM78)     += lm78.o
+obj-$(CONFIG_SENSORS_LM83)     += lm83.o
 obj-$(CONFIG_SENSORS_LM85)     += lm85.o
 obj-$(CONFIG_SENSORS_VIA686A)  += via686a.o
+
+
+
+obj-$(CONFIG_SENSORS_SMBUS_ARP)        += smbus-arp.o
index 49137e5..52f9afe 100644 (file)
@@ -343,7 +343,6 @@ static ssize_t show_temp(struct device *dev, char *buf, int nr)
        it87_update_client(client);
        return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])*100 );
 }
-/* more like overshoot temperature */
 static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -351,7 +350,6 @@ static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
        it87_update_client(client);
        return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])*100);
 }
-/* more like hysteresis temperature */
 static ssize_t show_temp_min(struct device *dev, char *buf, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -414,7 +412,6 @@ show_temp_offset(1);
 show_temp_offset(2);
 show_temp_offset(3);
 
-/* more like overshoot temperature */
 static ssize_t show_sensor(struct device *dev, char *buf, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
@@ -563,15 +560,15 @@ show_fan_offset(1);
 show_fan_offset(2);
 show_fan_offset(3);
 
-/* Alarm */
-static ssize_t show_alarm(struct device *dev, char *buf)
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf)
 {
        struct i2c_client *client = to_i2c_client(dev);
        struct it87_data *data = i2c_get_clientdata(client);
        it87_update_client(client);
        return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
 }
-static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO | S_IWUSR, show_alarms, NULL);
 
 /* This function is called when:
      * it87_driver is inserted (when this module is loaded), for each
@@ -751,7 +748,7 @@ int it87_detect(struct i2c_adapter *adapter, int address, int kind)
        device_create_file(&new_client->dev, &dev_attr_fan_div1);
        device_create_file(&new_client->dev, &dev_attr_fan_div2);
        device_create_file(&new_client->dev, &dev_attr_fan_div3);
-       device_create_file(&new_client->dev, &dev_attr_alarm);
+       device_create_file(&new_client->dev, &dev_attr_alarms);
 
        return 0;
 
index ac8d688..8d6d196 100644 (file)
@@ -116,7 +116,7 @@ set(temp_max, LM75_REG_TEMP_OS);
 set(temp_hyst, LM75_REG_TEMP_HYST);
 
 static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
-static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
+static DEVICE_ATTR(temp_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst);
 static DEVICE_ATTR(temp_input, S_IRUGO, show_temp_input, NULL);
 
 static int lm75_attach_adapter(struct i2c_adapter *adapter)
@@ -209,7 +209,7 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
        
        /* Register sysfs hooks */
        device_create_file(&new_client->dev, &dev_attr_temp_max);
-       device_create_file(&new_client->dev, &dev_attr_temp_min);
+       device_create_file(&new_client->dev, &dev_attr_temp_hyst);
        device_create_file(&new_client->dev, &dev_attr_temp_input);
 
        return 0;
index 7e8f830..5c0b67a 100644 (file)
@@ -367,7 +367,7 @@ static ssize_t set_temp_hyst(struct device *dev, const char *buf, size_t count)
 static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL)
 static DEVICE_ATTR(temp_max, S_IRUGO | S_IWUSR,
                show_temp_over, set_temp_over)
-static DEVICE_ATTR(temp_min, S_IRUGO | S_IWUSR,
+static DEVICE_ATTR(temp_hyst, S_IRUGO | S_IWUSR,
                show_temp_hyst, set_temp_hyst)
 
 /* 3 Fans */
@@ -674,8 +674,8 @@ int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
        device_create_file(&new_client->dev, &dev_attr_in_min6);
        device_create_file(&new_client->dev, &dev_attr_in_max6);
        device_create_file(&new_client->dev, &dev_attr_temp_input);
-       device_create_file(&new_client->dev, &dev_attr_temp_min);
        device_create_file(&new_client->dev, &dev_attr_temp_max);
+       device_create_file(&new_client->dev, &dev_attr_temp_hyst);
        device_create_file(&new_client->dev, &dev_attr_fan_input1);
        device_create_file(&new_client->dev, &dev_attr_fan_min1);
        device_create_file(&new_client->dev, &dev_attr_fan_div1);
diff --git a/drivers/i2c/chips/lm83.c b/drivers/i2c/chips/lm83.c
new file mode 100644 (file)
index 0000000..b54d35a
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * lm83.c - Part of lm_sensors, Linux kernel modules for hardware
+ *          monitoring
+ * Copyright (C) 2003  Jean Delvare <khali@linux-fr.org>
+ *
+ * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
+ * a sensor chip made by National Semiconductor. It reports up to four
+ * temperatures (its own plus up to three external ones) with a 1 deg
+ * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained
+ * from National's website at:
+ *   http://www.national.com/pf/LM/LM83.html
+ * Since the datasheet omits to give the chip stepping code, I give it
+ * here: 0x03 (at register 0xff).
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+
+/*
+ * Addresses to scan
+ * Address is selected using 2 three-level pins, resulting in 9 possible
+ * addresses.
+ */
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b,
+    0x4c, 0x4e, I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/*
+ * Insmod parameters
+ */
+
+SENSORS_INSMOD_1(lm83);
+
+/*
+ * The LM83 registers
+ * Manufacturer ID is 0x01 for National Semiconductor.
+ */
+
+#define LM83_REG_R_MAN_ID              0xFE
+#define LM83_REG_R_CHIP_ID             0xFF
+#define LM83_REG_R_CONFIG              0x03
+#define LM83_REG_W_CONFIG              0x09
+#define LM83_REG_R_STATUS1             0x02
+#define LM83_REG_R_STATUS2             0x35
+#define LM83_REG_R_LOCAL_TEMP          0x00
+#define LM83_REG_R_LOCAL_HIGH          0x05
+#define LM83_REG_W_LOCAL_HIGH          0x0B
+#define LM83_REG_R_REMOTE1_TEMP                0x30
+#define LM83_REG_R_REMOTE1_HIGH                0x38
+#define LM83_REG_W_REMOTE1_HIGH                0x50
+#define LM83_REG_R_REMOTE2_TEMP                0x01
+#define LM83_REG_R_REMOTE2_HIGH                0x07
+#define LM83_REG_W_REMOTE2_HIGH                0x0D
+#define LM83_REG_R_REMOTE3_TEMP                0x31
+#define LM83_REG_R_REMOTE3_HIGH                0x3A
+#define LM83_REG_W_REMOTE3_HIGH                0x52
+#define LM83_REG_R_TCRIT               0x42
+#define LM83_REG_W_TCRIT               0x5A
+
+/*
+ * Conversions, initial values and various macros
+ * The LM83 uses signed 8-bit values.
+ */
+
+#define TEMP_FROM_REG(val)     ((val > 127 ? val-256 : val) * 1000)
+#define TEMP_TO_REG(val)       ((val < 0 ? val+256 : val) / 1000)
+
+#define LM83_INIT_HIGH         100
+#define LM83_INIT_CRIT         120
+
+static const u8 LM83_REG_R_TEMP[] = {
+       LM83_REG_R_LOCAL_TEMP,
+       LM83_REG_R_REMOTE1_TEMP,
+       LM83_REG_R_REMOTE2_TEMP,
+       LM83_REG_R_REMOTE3_TEMP
+};
+
+static const u8 LM83_REG_R_HIGH[] = {
+       LM83_REG_R_LOCAL_HIGH,
+       LM83_REG_R_REMOTE1_HIGH,
+       LM83_REG_R_REMOTE2_HIGH,
+       LM83_REG_R_REMOTE3_HIGH
+};
+
+static const u8 LM83_REG_W_HIGH[] = {
+       LM83_REG_W_LOCAL_HIGH,
+       LM83_REG_W_REMOTE1_HIGH,
+       LM83_REG_W_REMOTE2_HIGH,
+       LM83_REG_W_REMOTE3_HIGH
+};
+
+/*
+ * Functions declaration
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter);
+static int lm83_detect(struct i2c_adapter *adapter, int address,
+    int kind);
+static void lm83_init_client(struct i2c_client *client);
+static int lm83_detach_client(struct i2c_client *client);
+static void lm83_update_client(struct i2c_client *client);
+
+/*
+ * Driver data (common to all clients)
+ */
+static struct i2c_driver lm83_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "lm83",
+       .id             = I2C_DRIVERID_LM83,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = lm83_attach_adapter,
+       .detach_client  = lm83_detach_client,
+};
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct lm83_data
+{
+       struct semaphore update_lock;
+       char valid; /* zero until following fields are valid */
+       unsigned long last_updated; /* in jiffies */
+
+       /* registers values */
+       u8 temp_input[4];
+       u8 temp_high[4];
+       u8 temp_crit;
+       u16 alarms; /* bitvector, combined */
+};
+
+/*
+ * Internal variables
+ */
+
+static int lm83_id = 0;
+
+/*
+ * Sysfs stuff
+ */
+
+#define show_temp(suffix, value) \
+static ssize_t show_temp_##suffix(struct device *dev, char *buf) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct lm83_data *data = i2c_get_clientdata(client); \
+       lm83_update_client(client); \
+       return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \
+}
+show_temp(input1, temp_input[0]);
+show_temp(input2, temp_input[1]);
+show_temp(input3, temp_input[2]);
+show_temp(input4, temp_input[3]);
+show_temp(high1, temp_high[0]);
+show_temp(high2, temp_high[1]);
+show_temp(high3, temp_high[2]);
+show_temp(high4, temp_high[3]);
+show_temp(crit, temp_crit);
+
+#define set_temp(suffix, value, reg) \
+static ssize_t set_temp_##suffix(struct device *dev, const char *buf, \
+       size_t count) \
+{ \
+       struct i2c_client *client = to_i2c_client(dev); \
+       struct lm83_data *data = i2c_get_clientdata(client); \
+       data->value = TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \
+       i2c_smbus_write_byte_data(client, reg, data->value); \
+       return count; \
+}
+set_temp(high1, temp_high[0], LM83_REG_W_LOCAL_HIGH);
+set_temp(high2, temp_high[1], LM83_REG_W_REMOTE1_HIGH);
+set_temp(high3, temp_high[2], LM83_REG_W_REMOTE2_HIGH);
+set_temp(high4, temp_high[3], LM83_REG_W_REMOTE3_HIGH);
+set_temp(crit, temp_crit, LM83_REG_W_TCRIT);
+
+static ssize_t show_alarms(struct device *dev, char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lm83_data *data = i2c_get_clientdata(client);
+       lm83_update_client(client);
+       return sprintf(buf, "%d\n", data->alarms);
+}
+
+static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input1, NULL);
+static DEVICE_ATTR(temp_input2, S_IRUGO, show_temp_input2, NULL);
+static DEVICE_ATTR(temp_input3, S_IRUGO, show_temp_input3, NULL);
+static DEVICE_ATTR(temp_input4, S_IRUGO, show_temp_input4, NULL);
+static DEVICE_ATTR(temp_max1, S_IWUSR | S_IRUGO, show_temp_high1,
+    set_temp_high1);
+static DEVICE_ATTR(temp_max2, S_IWUSR | S_IRUGO, show_temp_high2,
+    set_temp_high2);
+static DEVICE_ATTR(temp_max3, S_IWUSR | S_IRUGO, show_temp_high3,
+    set_temp_high3);
+static DEVICE_ATTR(temp_max4, S_IWUSR | S_IRUGO, show_temp_high4,
+    set_temp_high4);
+static DEVICE_ATTR(temp_crit, S_IWUSR | S_IRUGO, show_temp_crit,
+    set_temp_crit);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+/*
+ * Real code
+ */
+
+static int lm83_attach_adapter(struct i2c_adapter *adapter)
+{
+       if (!(adapter->class & I2C_ADAP_CLASS_SMBUS))
+               return 0;
+       return i2c_detect(adapter, &addr_data, lm83_detect);
+}
+
+/*
+ * The following function does more than just detection. If detection
+ * succeeds, it also registers the new chip.
+ */
+static int lm83_detect(struct i2c_adapter *adapter, int address,
+    int kind)
+{
+       struct i2c_client *new_client;
+       struct lm83_data *data;
+       int err = 0;
+       const char *name = "";
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+               goto exit;
+
+       if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+           sizeof(struct lm83_data), GFP_KERNEL))) {
+               err = -ENOMEM;
+               goto exit;
+       }
+       memset(new_client, 0x00, sizeof(struct i2c_client) +
+           sizeof(struct lm83_data));
+
+       /* The LM83-specific data is placed right after the common I2C
+        * client data. */
+       data = (struct lm83_data *) (new_client + 1);
+       i2c_set_clientdata(new_client, data);
+       new_client->addr = address;
+       new_client->adapter = adapter;
+       new_client->driver = &lm83_driver;
+       new_client->flags = 0;
+
+       /* Now we do the detection and identification. A negative kind
+        * means that the driver was loaded with no force parameter
+        * (default), so we must both detect and identify the chip
+        * (actually there is only one possible kind of chip for now, LM83).
+        * A zero kind means that the driver was loaded with the force
+        * parameter, the detection step shall be skipped. A positive kind
+        * means that the driver was loaded with the force parameter and a
+        * given kind of chip is requested, so both the detection and the
+        * identification steps are skipped. */
+       if (kind < 0) { /* detection */
+               if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1)
+                   & 0xA8) != 0x00) ||
+                   ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2)
+                   & 0x48) != 0x00) ||
+                   ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG)
+                   & 0x41) != 0x00)) {
+                       dev_dbg(&client->dev,
+                           "LM83 detection failed at 0x%02x.\n", address);
+                       goto exit_free;
+               }
+       }
+
+       if (kind <= 0) { /* identification */
+               u8 man_id, chip_id;
+
+               man_id = i2c_smbus_read_byte_data(new_client,
+                   LM83_REG_R_MAN_ID);
+               chip_id = i2c_smbus_read_byte_data(new_client,
+                   LM83_REG_R_CHIP_ID);
+               if (man_id == 0x01) { /* National Semiconductor */
+                       if (chip_id == 0x03) {
+                               kind = lm83;
+                               name = "lm83";
+                       }
+               }
+
+               if (kind <= 0) { /* identification failed */
+                       dev_info(&adapter->dev,
+                           "Unsupported chip (man_id=0x%02X, "
+                           "chip_id=0x%02X).\n", man_id, chip_id);
+                       goto exit_free;
+               }
+       }
+
+       /* We can fill in the remaining client fields */
+       strlcpy(new_client->name, name, I2C_NAME_SIZE);
+       new_client->id = lm83_id++;
+       data->valid = 0;
+       init_MUTEX(&data->update_lock);
+
+       /* Tell the I2C layer a new client has arrived */
+       if ((err = i2c_attach_client(new_client)))
+               goto exit_free;
+
+       /* Initialize the LM83 chip */
+       lm83_init_client(new_client);
+
+       /* Register sysfs hooks */
+       device_create_file(&new_client->dev, &dev_attr_temp_input1);
+       device_create_file(&new_client->dev, &dev_attr_temp_input2);
+       device_create_file(&new_client->dev, &dev_attr_temp_input3);
+       device_create_file(&new_client->dev, &dev_attr_temp_input4);
+       device_create_file(&new_client->dev, &dev_attr_temp_max1);
+       device_create_file(&new_client->dev, &dev_attr_temp_max2);
+       device_create_file(&new_client->dev, &dev_attr_temp_max3);
+       device_create_file(&new_client->dev, &dev_attr_temp_max4);
+       device_create_file(&new_client->dev, &dev_attr_temp_crit);
+       device_create_file(&new_client->dev, &dev_attr_alarms);
+
+       return 0;
+
+exit_free:
+       kfree(new_client);
+exit:
+       return err;
+}
+
+static void lm83_init_client(struct i2c_client *client)
+{
+       int nr;
+
+       for (nr = 0; nr < 4; nr++)
+               i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr],
+                   TEMP_TO_REG(LM83_INIT_HIGH));
+       i2c_smbus_write_byte_data(client, LM83_REG_W_TCRIT,
+           TEMP_TO_REG(LM83_INIT_CRIT));
+}
+
+static int lm83_detach_client(struct i2c_client *client)
+{
+       int err;
+
+       if ((err = i2c_detach_client(client))) {
+               dev_err(&client->dev,
+                   "Client deregistration failed, client not detached.\n");
+               return err;
+       }
+
+       kfree(client);
+       return 0;
+}
+
+static void lm83_update_client(struct i2c_client *client)
+{
+       struct lm83_data *data = i2c_get_clientdata(client);
+
+       down(&data->update_lock);
+
+       if ((jiffies - data->last_updated > HZ * 2) ||
+           (jiffies < data->last_updated) ||
+           !data->valid) {
+               int nr;
+               dev_dbg(&client->dev, "Updating lm83 data.\n");
+               for (nr = 0; nr < 4 ; nr++) {
+                       data->temp_input[nr] =
+                           i2c_smbus_read_byte_data(client,
+                           LM83_REG_R_TEMP[nr]);
+                       data->temp_high[nr] =
+                           i2c_smbus_read_byte_data(client,
+                           LM83_REG_R_HIGH[nr]);
+               }
+               data->temp_crit =
+                   i2c_smbus_read_byte_data(client, LM83_REG_R_TCRIT);
+               data->alarms =
+                   i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1)
+                   + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2)
+                   << 8);
+               data->last_updated = jiffies;
+               data->valid = 1;
+       }
+
+       up(&data->update_lock);
+}
+
+static int __init sensors_lm83_init(void)
+{
+       return i2c_add_driver(&lm83_driver);
+}
+
+static void __exit sensors_lm83_exit(void)
+{
+       i2c_del_driver(&lm83_driver);
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("LM83 driver");
+MODULE_LICENSE("GPL");
+
+module_init(sensors_lm83_init);
+module_exit(sensors_lm83_exit);
index 8133819..209adf0 100644 (file)
@@ -433,14 +433,11 @@ static ssize_t set_fan_min(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->fan_min[nr] = FAN_TO_REG(val);
-       lm85_write_value(client, LM85_REG_FAN_MIN(nr), data->fan_min[nr]);
-       up(&data->update_lock);
+       val = FAN_TO_REG(val);
+       lm85_write_value(client, LM85_REG_FAN_MIN(nr), val);
        return count;
 }
 
@@ -527,14 +524,11 @@ static ssize_t set_pwm(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->pwm[nr] = PWM_TO_REG(val);
-       lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]);
-       up(&data->update_lock);
+       val = PWM_TO_REG(val);
+       lm85_write_value(client, LM85_REG_PWM(nr), val);
        return count;
 }
 static ssize_t show_pwm_enable(struct device *dev, char *buf, int nr)
@@ -592,14 +586,11 @@ static ssize_t set_in_min(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->in_min[nr] = INS_TO_REG(nr, val);
-       lm85_write_value(client, LM85_REG_IN_MIN(nr), data->in_min[nr]);
-       up(&data->update_lock);
+       val = INS_TO_REG(nr, val);
+       lm85_write_value(client, LM85_REG_IN_MIN(nr), val);
        return count;
 }
 static ssize_t show_in_max(struct device *dev, char *buf, int nr)
@@ -614,14 +605,11 @@ static ssize_t set_in_max(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->in_max[nr] = INS_TO_REG(nr, val);
-       lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]);
-       up(&data->update_lock);
+       val = INS_TO_REG(nr, val);
+       lm85_write_value(client, LM85_REG_IN_MAX(nr), val);
        return count;
 }
 #define show_in_reg(offset)                                            \
@@ -681,14 +669,11 @@ static ssize_t set_temp_min(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->temp_min[nr] = TEMP_TO_REG(val);
-       lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]);
-       up(&data->update_lock);
+       val = TEMP_TO_REG(val);
+       lm85_write_value(client, LM85_REG_TEMP_MIN(nr), val);
        return count;
 }
 static ssize_t show_temp_max(struct device *dev, char *buf, int nr)
@@ -703,14 +688,11 @@ static ssize_t set_temp_max(struct device *dev, const char *buf,
                size_t count, int nr)
 {
        struct i2c_client *client = to_i2c_client(dev);
-       struct lm85_data *data = i2c_get_clientdata(client);
        int     val;
 
-       down(&data->update_lock);
        val = simple_strtol(buf, NULL, 10);
-       data->temp_max[nr] = TEMP_TO_REG(val);
-       lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]);
-       up(&data->update_lock);
+       val = TEMP_TO_REG(val);
+       lm85_write_value(client, LM85_REG_TEMP_MAX(nr), val);
        return count;
 }
 #define show_temp_reg(offset)                                          \
@@ -953,8 +935,12 @@ int lm85_detach_client(struct i2c_client *client)
 
 int lm85_read_value(struct i2c_client *client, u8 reg)
 {
+       struct lm85_data *data = i2c_get_clientdata(client);
        int res;
 
+       /* serialize access to the hardware */
+       down(&data->update_lock);
+
        /* What size location is it? */
        switch( reg ) {
        case LM85_REG_FAN(0) :  /* Read WORD data */
@@ -978,14 +964,19 @@ int lm85_read_value(struct i2c_client *client, u8 reg)
                res = i2c_smbus_read_byte_data(client, reg);
                break ;
        }
+       up(&data->update_lock);
 
        return res ;
 }
 
 int lm85_write_value(struct i2c_client *client, u8 reg, int value)
 {
+       struct lm85_data *data = i2c_get_clientdata(client);
        int res ;
 
+       /* serialize access to the hardware */
+       down(&data->update_lock);
+
        switch( reg ) {
        case LM85_REG_FAN(0) :  /* Write WORD data */
        case LM85_REG_FAN(1) :
@@ -1007,6 +998,7 @@ int lm85_write_value(struct i2c_client *client, u8 reg, int value)
                res = i2c_smbus_write_byte_data(client, reg, value);
                break ;
        }
+       up(&data->update_lock);
 
        return res ;
 }
@@ -1068,8 +1060,6 @@ void lm85_update_client(struct i2c_client *client)
        struct lm85_data *data = i2c_get_clientdata(client);
        int i;
 
-       down(&data->update_lock);
-
        if ( !data->valid ||
             (jiffies - data->last_reading > LM85_DATA_INTERVAL ) ) {
                /* Things that change quickly */
@@ -1207,8 +1197,6 @@ void lm85_update_client(struct i2c_client *client)
        };  /* last_config */
 
        data->valid = 1;
-
-       up(&data->update_lock);
 }
 
 
diff --git a/drivers/i2c/chips/sensors_vid.h b/drivers/i2c/chips/sensors_vid.h
new file mode 100644 (file)
index 0000000..d40e343
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    vrm.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
+    With assistance from Trent Piepho <xyzzy@speakeasy.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.
+
+    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.
+*/
+
+/*
+    This file contains common code for decoding VID pins.
+    This file is #included in various chip drivers in this directory.
+    As the user is unlikely to load more than one driver which
+    includes this code we don't worry about the wasted space.
+    Reference: VRM x.y DC-DC Converter Design Guidelines,
+    available at http://developer.intel.com
+*/
+
+/*
+    Legal val values 00 - 1F.
+    vrm is the Intel VRM document version.
+    Note: vrm version is scaled by 10 and the return value is scaled by 1000
+    to avoid floating point in the kernel.
+*/
+
+#define DEFAULT_VRM    82
+
+static inline int vid_from_reg(int val, int vrm)
+{
+       switch(vrm) {
+
+       case 91:                /* VRM 9.1 */
+       case 90:                /* VRM 9.0 */
+               return(val == 0x1f ? 0 :
+                                      1850 - val * 25);
+
+       case 85:                /* VRM 8.5 */
+               return((val & 0x10  ? 25 : 0) +
+                      ((val & 0x0f) > 0x04 ? 2050 : 1250) -
+                      ((val & 0x0f) * 50));
+
+       case 84:                /* VRM 8.4 */
+               val &= 0x0f;
+                               /* fall through */
+       default:                /* VRM 8.2 */
+               return(val == 0x1f ? 0 :
+                      val & 0x10  ? 5100 - (val) * 100 :
+                                    2050 - (val) * 50);
+       }
+}
diff --git a/drivers/i2c/chips/smbus-arp.c b/drivers/i2c/chips/smbus-arp.c
new file mode 100644 (file)
index 0000000..db9b25c
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+    smbus-arp.c - Part of lm_sensors, Linux kernel modules for hardware
+               monitoring
+    Copyright (c) 2002  Mark D. Studebaker <mdsxyz123@yahoo.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.
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/i2c-sensor.h>
+#include <linux/init.h>
+
+#define DEBUG 1
+
+/* Addresses to scan */
+#define        ARP_ADDRESS     0x61
+static unsigned short normal_i2c[] = { ARP_ADDRESS, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };
+static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END };
+static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END };
+
+/* Insmod parameters */
+SENSORS_INSMOD_1(arp);
+
+/* ARP Commands */
+#define        ARP_PREPARE             0x01
+#define        ARP_RESET_DEV           0x02
+#define        ARP_GET_UDID_GEN        0x03
+#define        ARP_ASSIGN_ADDR         0x04
+
+/* UDID Fields */
+#define ARP_CAPAB      0
+#define ARP_VER                1
+#define ARP_VEND       2
+#define ARP_DEV                4
+#define ARP_INT                6
+#define ARP_SUBVEND    8
+#define ARP_SUBDEV     10
+#define ARP_SPECID     12
+
+#define UDID_LENGTH    0x11
+
+static u8 reserved[] =
+/* As defined by SMBus Spec. Appendix C */
+                       {0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x28,
+                        0x37, ARP_ADDRESS,
+/* As defined by SMBus Spec. Sect. 5.2 */
+                       0x01, 0x02, 0x03, 0x04, 0x05,
+                       0x06, 0x07, 0x78, 0x79, 0x7a, 0x7b,
+                       0x7c, 0x7d, 0x7e, 0x7f,
+/* Common PC addresses (bad idea) */
+                       0x2d, 0x48, 0x49, /* sensors */
+                       0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* eeproms */
+                       0x69, /* clock chips */
+/* Must end in 0 which is also reserved */
+                       0x00};
+
+#define SMBUS_ADDRESS_SIZE     0x80
+#define ARP_FREE               0       
+#define ARP_RESERVED           1
+#define ARP_BUSY               2
+
+#define ARP_MAX_DEVICES                8
+struct arp_device {
+       int status;
+       u8 udid[UDID_LENGTH];
+       u8 dev_cap;
+       u8 dev_ver;
+       u16 dev_vid;
+       u16 dev_did;
+       u16 dev_int;
+       u16 dev_svid;
+       u16 dev_sdid;
+       u32 dev_vsid;
+       u8 saddr;
+};
+
+/* Each client has this additional data */
+struct arp_data {
+       struct semaphore update_lock;
+       char valid;                     /* !=0 if following fields are valid */
+       unsigned long last_updated;     /* In jiffies */
+
+       u8 address_pool[SMBUS_ADDRESS_SIZE];
+       struct arp_device dev[ARP_MAX_DEVICES];
+};
+
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter);
+static int smbusarp_detect(struct i2c_adapter *adapter, int address, int kind);
+static int smbusarp_detach_client(struct i2c_client *client);
+
+static int smbusarp_init_client(struct i2c_client *client);
+
+static struct i2c_driver smbusarp_driver = {
+       .owner          = THIS_MODULE,
+       .name           = "SMBus_ARP",
+       .id             = I2C_DRIVERID_ARP,
+       .flags          = I2C_DF_NOTIFY,
+       .attach_adapter = smbusarp_attach_adapter,
+       .detach_client  = smbusarp_detach_client,
+};
+
+#if 0
+/* -- SENSORS SYSCTL START -- */
+#define ARP_SYSCTL1    1000
+#define ARP_SYSCTL2    1001
+#define ARP_SYSCTL3    1002
+#define ARP_SYSCTL4    1003
+#define ARP_SYSCTL5    1004
+#define ARP_SYSCTL6    1005
+#define ARP_SYSCTL7    1006
+#define ARP_SYSCTL8    1007
+
+/* -- SENSORS SYSCTL END -- */
+static ctl_table smbusarp_dir_table_template[] = {
+       {ARP_SYSCTL1, "0", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL2, "1", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL3, "2", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL4, "3", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL5, "4", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL6, "5", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL7, "6", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {ARP_SYSCTL8, "7", NULL, 0, 0644, NULL, &i2c_proc_real,
+        &i2c_sysctl_real, NULL, &smbusarp_contents},
+       {0}
+};
+#endif
+
+static int smbusarp_id = 0;
+
+static int smbusarp_attach_adapter(struct i2c_adapter *adapter)
+{
+       return i2c_detect(adapter, &addr_data, smbusarp_detect);
+}
+
+/* This function is called by i2c_detect */
+static int smbusarp_detect(struct i2c_adapter *adapter, int address, int kind)
+{
+       struct i2c_client *new_client;
+       struct arp_data *data;
+       int err = 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA))
+               return(0);
+
+       if (!(new_client = kmalloc(sizeof(struct i2c_client) +
+                                  sizeof(struct arp_data),
+                                  GFP_KERNEL))) {
+               return(-ENOMEM);
+       }
+       memset(new_client, 0x00, sizeof(struct i2c_client) +
+                                sizeof(struct arp_data));
+
+       data = (struct arp_data *) (new_client + 1);
+       new_client->addr = address;
+       i2c_set_clientdata(new_client, data);
+       new_client->adapter = adapter;
+       new_client->driver = &smbusarp_driver;
+       new_client->flags = I2C_CLIENT_PEC;
+
+       strcpy(new_client->name, "arp");
+
+       new_client->id = smbusarp_id++;
+       data->valid = 0;
+       init_MUTEX(&data->update_lock);
+
+       if ((err = i2c_attach_client(new_client)))
+               goto error;
+
+       smbusarp_init_client(new_client);
+       return 0;
+
+error:
+       kfree(new_client);
+       return err;
+}
+
+static int smbusarp_detach_client(struct i2c_client *client)
+{
+       int err;
+
+       err = i2c_detach_client(client);
+       if (err) {
+               dev_err(&client->dev, "Client deregistration failed, client not detached.\n");
+               return err;
+       }
+
+       kfree(client);
+
+       return 0;
+}
+
+
+static u8 choose_addr(u8 * pool)
+{
+       int i;
+
+       for (i = 0; i < 0x7f; i++) {
+               if (pool[i] == ARP_FREE)
+                       return (u8)i;
+       }
+       return 0xff;
+}
+
+static int smbusarp_init_client(struct i2c_client *client)
+{
+       int ret = -1;
+       struct arp_data *data = i2c_get_clientdata(client);
+       struct list_head *item;
+       u8 blk[I2C_SMBUS_BLOCK_MAX];
+       u8 *r;
+       u8 addr;
+       int i;
+       int found = 0;
+       int newdev = 0;
+       
+       for(i = 0; i < ARP_MAX_DEVICES; i++)
+               data->dev[i].status = ARP_FREE;
+
+       for(i = 0; i < SMBUS_ADDRESS_SIZE; i++)
+               data->address_pool[i] = ARP_FREE;
+
+       r = reserved;
+       do {
+               data->address_pool[*r] = ARP_RESERVED;
+       } while(*r++);
+
+       list_for_each(item, &client->adapter->clients) {
+               struct i2c_client *c = list_entry(item, struct i2c_client, list);
+               data->address_pool[c->addr] = ARP_BUSY;
+       }
+
+       ret = i2c_smbus_write_byte(client, ARP_PREPARE);
+       if (ret < 0) {
+               dev_dbg(client->dev, "No ARP response on adapter 0x%X\n", client->adapter->id);
+               return(-1);     /* Packet wasn't acked */
+       }
+       while(1) {
+               ret = i2c_smbus_read_block_data(client, ARP_GET_UDID_GEN, blk);
+               if(ret != UDID_LENGTH) {
+                       dev_dbg(&client->dev, "No/Bad UDID response %d on adapter 0x%X\n", ret, client->adapter->id);
+                       if(found)
+                               return found;
+                       else
+                               return -1;      /* Bad response */
+               }
+               dev_dbg(&client->dev, "Good UDID response on adapter 0x%X\n", client->adapter->id);
+               dev_dbg(&client->dev, "Cap. 0x%02x  Rev. 0x%02x  Vend. 0x%02x%02x  Dev. 0x%02x%02x\n", blk[0], blk[1], blk[2], blk[3], blk[4], blk[5]);
+               dev_dbg(&client->dev, "Int. 0x%02x%02x  Subvend. 0x%02x%02x  Subdev. 0x%02x%02x  Spec. 0x%02x%02x%02x%02x\n", blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15]);
+
+               /* clean up this... */
+               found++;
+               do {
+                       if (data->dev[newdev].status == ARP_FREE)
+                               break;
+               } while(++newdev < ARP_MAX_DEVICES);
+               if (newdev == ARP_MAX_DEVICES) {
+                       printk(KERN_WARNING "smbus-arp.o: No more slots available\n");
+                       return -1;
+               }
+
+               /* check device slave addr */           
+               addr = blk[16];
+               if(addr != 0xFF) {
+                       addr >>= 1;
+                       if(blk[0] & 0xC0) {
+                               if(data->address_pool[addr] == ARP_FREE) {
+                                       dev_dbg(&client->dev, "Requested free Non-fixed Address 0x%02x\n", addr);
+                               } else {
+                                       dev_dbg(&client->dev, "Requested busy Non-fixed Address 0x%02x\n", addr);
+                                       addr = choose_addr(data->address_pool);
+                                       if (addr == 0xff) {
+                                               dev_warn(&client->dev, "Address pool exhausted\n");
+                                               return -1;
+                                       }
+                               }
+                       } else {
+                               dev_dbg(&client->dev, "Fixed Address 0x%02x\n", addr);
+                       }
+               } else {
+                       dev_dbg(&client->dev, "No Address\n");
+                       addr = choose_addr(data->address_pool);
+                       if (addr == 0xff) {
+                               dev_warn(&client->dev, "Address pool exhausted\n");
+                               return -1;
+                       }
+               }
+               /* store things both ways */
+               for (i = 0; i < UDID_LENGTH; i++)
+                       data->dev[newdev].udid[i] = blk[i];
+               data->dev[newdev].saddr = addr;
+               data->dev[newdev].status = ARP_BUSY;
+               data->dev[newdev].dev_cap = blk[0];
+               data->dev[newdev].dev_ver = blk[1];
+               data->dev[newdev].dev_vid = (blk[2] << 8) | blk[3];
+               data->dev[newdev].dev_did = (blk[4] << 8) | blk[5];
+               data->dev[newdev].dev_int = (blk[6] << 8) | blk[7];
+               data->dev[newdev].dev_svid = (blk[8] << 8) | blk[9];
+               data->dev[newdev].dev_sdid = (blk[10] << 8) | blk[11];
+               data->dev[newdev].dev_vsid = (blk[12] << 24) | (blk[13] << 16) |
+                                            (blk[14] << 8) | blk[15] ;
+
+               blk[16] = addr << 1;
+               ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR, UDID_LENGTH, blk);
+               if (ret) {
+                       dev_dbg(&client->dev, "Bad response, address 0x%02x not assigned\n", addr);
+               } else {
+                       data->address_pool[addr] = ARP_BUSY;
+                       dev_dbg(&client->dev, "Assigned address 0x%02x\n", addr);
+               }
+                       /* retry? */
+
+       } /* while 1  */
+
+       return ret;
+}
+
+#define show(value)    \
+static ssize_t show_slot_##value(struct device *dev, char *buf, int slot)      \
+{                                                              \
+       struct i2c_client *client = to_i2c_client(dev);         \
+       struct arp_data *data = i2c_get_clientdata(client);     \
+                                                               \
+       return sprintf(buf, "%d\n", data->dev[slot].value);     \
+}
+show(saddr);
+show(dev_cap);
+show(dev_ver);
+show(dev_vid);
+
+#define X(num) \
+static ssize_t show_slot_saddr_##num(struct device *dev, char *buf)    \
+{                                                                      \
+       return show_slot_saddr(dev, buf, num);                          \
+}                                                                      \
+static DEVICE_ATTR(slot_##num, S_IWUSR | S_IRUGO, show_slot_saddr_##num, NULL);
+
+X(0);
+
+#if 0
+/* reassign address on writex */
+static void smbusarp_contents(struct i2c_client *client, int operation,
+                    int ctl_name, int *nrels_mag, long *results)
+{
+       int nr = ctl_name - ARP_SYSCTL1;
+       struct arp_data *data = i2c_get_clientdata(client);
+       int ret;
+       u8 save;
+       u8 a;
+
+       if (operation == SENSORS_PROC_REAL_INFO)
+               *nrels_mag = 0;
+       else if (operation == SENSORS_PROC_REAL_READ) {
+               if(data->dev[nr].status == ARP_BUSY) {
+                       results[0] = data->dev[nr].saddr;
+                       results[1] = data->dev[nr].dev_cap;
+                       results[2] = data->dev[nr].dev_ver;
+                       results[3] = data->dev[nr].dev_vid;
+                       results[4] = data->dev[nr].dev_did;
+                       results[5] = data->dev[nr].dev_int;
+                       results[6] = data->dev[nr].dev_svid;
+                       results[7] = data->dev[nr].dev_sdid;
+                       results[8] = data->dev[nr].dev_vsid;
+                       *nrels_mag = 9;
+               } else {
+                       *nrels_mag = 0;
+               }
+       } else if (operation == SENSORS_PROC_REAL_WRITE) {
+               a = results[0];
+               if ((*nrels_mag >= 1) &&
+                   (a < SMBUS_ADDRESS_SIZE) &&
+                   (data->dev[nr].status == ARP_BUSY) &&
+                   (data->address_pool[a] == ARP_FREE)) {
+                       save = data->dev[nr].udid[16];
+                       data->dev[nr].udid[16] = a << 1;
+                       ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR, UDID_LENGTH, data->dev[nr].udid);
+                       if (ret) {
+                               data->dev[nr].udid[16] = save;
+                               dev_dbg(&client->dev, "smbus-arp Bad response, address 0x%02x not assigned\n", a);
+                       } else {
+                               data->dev[nr].saddr = a;
+                               data->address_pool[a] = ARP_BUSY;
+                               dev_dbg(&client->dev, "smbus-arp Assigned address 0x%02x\n", a);
+                       }
+               } else {
+                       dev_warn(&client->dev, "smbus-arp Bad address 0x%02x\n", a);
+               }
+       }
+}
+#endif
+
+static int __init sm_smbusarp_init(void)
+{
+       printk(KERN_INFO "smbus-arp\n");
+
+       /* magic force invocation */
+       force_arp[0] = -1;
+       force_arp[1] = ARP_ADDRESS;
+       return i2c_add_driver(&smbusarp_driver);
+}
+
+static void __exit sm_smbusarp_exit(void)
+{
+       i2c_del_driver(&smbusarp_driver);
+}
+
+
+MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
+MODULE_DESCRIPTION("SMBUS ARP Driver");
+MODULE_LICENSE("GPL");
+
+module_init(sm_smbusarp_init);
+module_exit(sm_smbusarp_exit);
index 428c1f4..78dbba5 100644 (file)
@@ -496,21 +496,19 @@ static ssize_t show_temp(struct device *dev, char *buf, int nr) {
        via686a_update_client(client);
        return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])*100 );
 }
-/* more like overshoot temperature */
-static ssize_t show_temp_max(struct device *dev, char *buf, int nr) {
+static ssize_t show_temp_over(struct device *dev, char *buf, int nr) {
        struct i2c_client *client = to_i2c_client(dev);
        struct via686a_data *data = i2c_get_clientdata(client);
        via686a_update_client(client);
        return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])*100);
 }
-/* more like hysteresis temperature */
-static ssize_t show_temp_min(struct device *dev, char *buf, int nr) {
+static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) {
        struct i2c_client *client = to_i2c_client(dev);
        struct via686a_data *data = i2c_get_clientdata(client);
        via686a_update_client(client);
        return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])*100);
 }
-static ssize_t set_temp_max(struct device *dev, const char *buf, 
+static ssize_t set_temp_over(struct device *dev, const char *buf, 
                size_t count, int nr) {
        struct i2c_client *client = to_i2c_client(dev);
        struct via686a_data *data = i2c_get_clientdata(client);
@@ -519,7 +517,7 @@ static ssize_t set_temp_max(struct device *dev, const char *buf,
        via686a_write_value(client, VIA686A_REG_TEMP_OVER(nr), data->temp_over[nr]);
        return count;
 }
-static ssize_t set_temp_min(struct device *dev, const char *buf, 
+static ssize_t set_temp_hyst(struct device *dev, const char *buf, 
                size_t count, int nr) {
        struct i2c_client *client = to_i2c_client(dev);
        struct via686a_data *data = i2c_get_clientdata(client);
@@ -534,30 +532,30 @@ static ssize_t show_temp_##offset (struct device *dev, char *buf) \
        return show_temp(dev, buf, 0x##offset - 1);                     \
 }                                                                      \
 static ssize_t                                                         \
-show_temp_##offset##_max (struct device *dev, char *buf)               \
+show_temp_##offset##_over (struct device *dev, char *buf)              \
 {                                                                      \
-       return show_temp_max(dev, buf, 0x##offset - 1);                 \
+       return show_temp_over(dev, buf, 0x##offset - 1);                        \
 }                                                                      \
 static ssize_t                                                         \
-show_temp_##offset##_min (struct device *dev, char *buf)               \
+show_temp_##offset##_hyst (struct device *dev, char *buf)              \
 {                                                                      \
-       return show_temp_min(dev, buf, 0x##offset - 1);                 \
+       return show_temp_hyst(dev, buf, 0x##offset - 1);                        \
 }                                                                      \
-static ssize_t set_temp_##offset##_max (struct device *dev,            \
+static ssize_t set_temp_##offset##_over (struct device *dev,           \
                const char *buf, size_t count)                          \
 {                                                                      \
-       return set_temp_max(dev, buf, count, 0x##offset - 1);           \
+       return set_temp_over(dev, buf, count, 0x##offset - 1);          \
 }                                                                      \
-static ssize_t set_temp_##offset##_min (struct device *dev,            \
+static ssize_t set_temp_##offset##_hyst (struct device *dev,           \
                const char *buf, size_t count)                          \
 {                                                                      \
-       return set_temp_min(dev, buf, count, 0x##offset - 1);           \
+       return set_temp_hyst(dev, buf, count, 0x##offset - 1);          \
 }                                                                      \
 static DEVICE_ATTR(temp_input##offset, S_IRUGO, show_temp_##offset, NULL) \
 static DEVICE_ATTR(temp_max##offset, S_IRUGO | S_IWUSR,                \
-               show_temp_##offset##_max, set_temp_##offset##_max)      \
-static DEVICE_ATTR(temp_min##offset, S_IRUGO | S_IWUSR,                \
-               show_temp_##offset##_min, set_temp_##offset##_min)      
+               show_temp_##offset##_over, set_temp_##offset##_over)    \
+static DEVICE_ATTR(temp_hyst##offset, S_IRUGO | S_IWUSR,               \
+               show_temp_##offset##_hyst, set_temp_##offset##_hyst)    
 
 show_temp_offset(1);
 show_temp_offset(2);
@@ -637,14 +635,14 @@ static DEVICE_ATTR(fan_div##offset, S_IRUGO | S_IWUSR,                    \
 show_fan_offset(1);
 show_fan_offset(2);
 
-/* Alarm */
-static ssize_t show_alarm(struct device *dev, char *buf) {
+/* Alarms */
+static ssize_t show_alarms(struct device *dev, char *buf) {
        struct i2c_client *client = to_i2c_client(dev);
        struct via686a_data *data = i2c_get_clientdata(client);
        via686a_update_client(client);
        return sprintf(buf,"%d\n", ALARMS_FROM_REG(data->alarms));
 }
-static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, show_alarm, NULL);
+static DEVICE_ATTR(alarms, S_IRUGO | S_IWUSR, show_alarms, NULL);
 
 /* The driver. I choose to use type i2c_driver, as at is identical to both
    smbus_driver and isa_driver, and clients could be of either kind */
@@ -760,16 +758,16 @@ static int via686a_detect(struct i2c_adapter *adapter, int address, int kind)
        device_create_file(&new_client->dev, &dev_attr_temp_max1);
        device_create_file(&new_client->dev, &dev_attr_temp_max2);
        device_create_file(&new_client->dev, &dev_attr_temp_max3);
-       device_create_file(&new_client->dev, &dev_attr_temp_min1);
-       device_create_file(&new_client->dev, &dev_attr_temp_min2);
-       device_create_file(&new_client->dev, &dev_attr_temp_min3);
+       device_create_file(&new_client->dev, &dev_attr_temp_hyst1);
+       device_create_file(&new_client->dev, &dev_attr_temp_hyst2);
+       device_create_file(&new_client->dev, &dev_attr_temp_hyst3);
        device_create_file(&new_client->dev, &dev_attr_fan_input1);
        device_create_file(&new_client->dev, &dev_attr_fan_input2);
        device_create_file(&new_client->dev, &dev_attr_fan_min1);
        device_create_file(&new_client->dev, &dev_attr_fan_min2);
        device_create_file(&new_client->dev, &dev_attr_fan_div1);
        device_create_file(&new_client->dev, &dev_attr_fan_div2);
-       device_create_file(&new_client->dev, &dev_attr_alarm);
+       device_create_file(&new_client->dev, &dev_attr_alarms);
 
        return 0;
 
index 8be736c..addff9b 100644 (file)
@@ -309,11 +309,11 @@ struct w83781d_data {
        u8 fan[3];              /* Register value */
        u8 fan_min[3];          /* Register value */
        u8 temp;
-       u8 temp_min;            /* Register value */
        u8 temp_max;            /* Register value */
+       u8 temp_hyst;           /* Register value */
        u16 temp_add[2];        /* Register value */
        u16 temp_max_add[2];    /* Register value */
-       u16 temp_min_add[2];    /* Register value */
+       u16 temp_hyst_add[2];   /* Register value */
        u8 fan_div[3];          /* Register encoding, shifted right */
        u8 vid;                 /* Register encoding, combined */
        u32 alarms;             /* Register encoding, combined */
@@ -510,8 +510,8 @@ static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
        } \
 }
 show_temp_reg(temp);
-show_temp_reg(temp_min);
 show_temp_reg(temp_max);
+show_temp_reg(temp_hyst);
 
 #define store_temp_reg(REG, reg) \
 static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \
@@ -538,8 +538,8 @@ static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t cou
         \
        return count; \
 }
-store_temp_reg(OVER, min);
-store_temp_reg(HYST, max);
+store_temp_reg(OVER, max);
+store_temp_reg(HYST, hyst);
 
 #define sysfs_temp_offset(offset) \
 static ssize_t \
@@ -562,8 +562,8 @@ static DEVICE_ATTR(temp_##reg##offset, S_IRUGO| S_IWUSR, show_regs_temp_##reg##o
 
 #define sysfs_temp_offsets(offset) \
 sysfs_temp_offset(offset); \
-sysfs_temp_reg_offset(min, offset); \
-sysfs_temp_reg_offset(max, offset);
+sysfs_temp_reg_offset(max, offset); \
+sysfs_temp_reg_offset(hyst, offset);
 
 sysfs_temp_offsets(1);
 sysfs_temp_offsets(2);
@@ -573,7 +573,7 @@ sysfs_temp_offsets(3);
 do { \
 device_create_file(&client->dev, &dev_attr_temp_input##offset); \
 device_create_file(&client->dev, &dev_attr_temp_max##offset); \
-device_create_file(&client->dev, &dev_attr_temp_min##offset); \
+device_create_file(&client->dev, &dev_attr_temp_hyst##offset); \
 } while (0)
 
 static ssize_t
@@ -1865,15 +1865,15 @@ w83781d_update_client(struct i2c_client *client)
                }
 
                data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1));
-               data->temp_min =
-                   w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
                data->temp_max =
+                   w83781d_read_value(client, W83781D_REG_TEMP_OVER(1));
+               data->temp_hyst =
                    w83781d_read_value(client, W83781D_REG_TEMP_HYST(1));
                data->temp_add[0] =
                    w83781d_read_value(client, W83781D_REG_TEMP(2));
                data->temp_max_add[0] =
                    w83781d_read_value(client, W83781D_REG_TEMP_OVER(2));
-               data->temp_min_add[0] =
+               data->temp_hyst_add[0] =
                    w83781d_read_value(client, W83781D_REG_TEMP_HYST(2));
                if (data->type != w83783s && data->type != w83697hf) {
                        data->temp_add[1] =
@@ -1881,7 +1881,7 @@ w83781d_update_client(struct i2c_client *client)
                        data->temp_max_add[1] =
                            w83781d_read_value(client,
                                               W83781D_REG_TEMP_OVER(3));
-                       data->temp_min_add[1] =
+                       data->temp_hyst_add[1] =
                            w83781d_read_value(client,
                                               W83781D_REG_TEMP_HYST(3));
                }
index f1b1ae6..375488a 100644 (file)
@@ -973,7 +973,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
                if (oss) {
                        err = dev->oss.minor_dsp =
                                register_sound_dsp(&saa7134_dsp_fops,
-                                                  dsp_nr[saa7134_devcount]);
+                                                  dsp_nr[saa7134_devcount],
+                                                  &dev->pci->dev);
                        if (err < 0) {
                                goto fail4;
                        }
@@ -982,7 +983,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
                        
                        err = dev->oss.minor_mixer =
                                register_sound_mixer(&saa7134_mixer_fops,
-                                                    mixer_nr[saa7134_devcount]);
+                                                    mixer_nr[saa7134_devcount],
+                                                    &dev->pci->dev);
                        if (err < 0)
                                goto fail5;
                        printk(KERN_INFO "%s: registered device mixer%d\n",
index cffcdfa..8ea4938 100644 (file)
@@ -282,7 +282,7 @@ static int tvmixer_clients(struct i2c_client *client)
                return -1;
 
        /* everything is fine, register */
-       if ((minor = register_sound_mixer(&tvmixer_fops,devnr)) < 0) {
+       if ((minor = register_sound_mixer(&tvmixer_fops,devnr,&client->dev)) < 0) {
                printk(KERN_ERR "tvmixer: cannot allocate mixer device\n");
                return -1;
        }
index ae279ed..62ea6c1 100644 (file)
@@ -88,6 +88,10 @@ config HOTPLUG_PCI_ACPI
 
          When in doubt, say N.
 
+config HOTPLUG_PCI_AMD
+       tristate "AMD Standard Hot Plug Controller (SHPC) driver"
+       depends on HOTPLUG_PCI
+
 config HOTPLUG_PCI_CPCI
        bool "CompactPCI Hotplug driver"
        depends on HOTPLUG_PCI
index 8d4a680..b05e2c5 100644 (file)
@@ -7,10 +7,10 @@ obj-$(CONFIG_HOTPLUG_PCI_FAKE)                += fakephp.o
 obj-$(CONFIG_HOTPLUG_PCI_COMPAQ)       += cpqphp.o
 obj-$(CONFIG_HOTPLUG_PCI_IBM)          += ibmphp.o
 obj-$(CONFIG_HOTPLUG_PCI_ACPI)         += acpiphp.o
+obj-$(CONFIG_HOTPLUG_PCI_AMD)          += amdshpc.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)  += cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
-obj-$(CONFIG_HOTPLUG_PCI_RPA)          += rpaphp.o
-obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)    += rpadlpar_io.o
+obj-$(CONFIG_HOTPLUG_PCI_FAKE)         += fakephp.o 
 
 pci_hotplug-objs       :=      pci_hotplug_core.o
 
@@ -35,6 +35,15 @@ acpiphp-objs         :=      acpiphp_core.o  \
                                acpiphp_pci.o   \
                                acpiphp_res.o
 
+amdshpc-objs           :=      amdshpc_core.o  \
+                               amdshpc_ctrl.o  \
+                               amdshpc_pci.o   \
+                               amdshpc_ddi.o   \
+                               amdshpc_int.o   \
+                               amdshpc_led.o   \
+                               amdshpc_enb.o   \
+                               amdshpc_dsb.o
+
 rpaphp-objs            :=      rpaphp_core.o   \
                                rpaphp_pci.o    
 
diff --git a/drivers/pci/hotplug/amdshpc.h b/drivers/pci/hotplug/amdshpc.h
new file mode 100644 (file)
index 0000000..25fd4f4
--- /dev/null
@@ -0,0 +1,1265 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#ifndef _SHPC_H_
+#define _SHPC_H_
+
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <asm/semaphore.h>
+#include "pci_hotplug.h"
+
+//
+// Timeouts
+//
+#define ONE_TENTH_SEC_TIMEOUT          10              // 0.1 sec
+#define ONE_SEC_TIMEOUT                 HZ * 1          // 1 sec
+#define FIVE_SEC_TIMEOUT                HZ * 5          // 5 secs
+#define TEN_SEC_TIMEOUT                 HZ * 10         // 10 secs
+#define FIFTEEN_SEC_TIMEOUT             HZ * 15         // 15 secs
+#define QUIESCE_QUIET_TIMEOUT           HZ * 30         // 30 secs
+#define QUIESCE_TIMEOUT                 HZ * 60         // 60 secs
+#define ONE_SEC_INCREMENT               HZ * 1          // 1 sec
+
+#define SLOT_MAGIC      0x67267322
+struct slot {
+       u32 magic;
+       struct slot *next;
+       struct list_head slot_list;
+       u8 bus;
+       u8 device;
+       u8 number;
+       u8 is_a_board;
+       u8 configured;
+       u8 state;
+       u8 switch_save;
+       u8 presence_save;
+       u32 capabilities;
+       u16 reserved2;
+       struct timer_list task_event;
+       u8 hp_slot;
+       struct controller *ctrl;
+       void *p_sm_slot;
+       struct hotplug_slot *hotplug_slot;
+       void* private;
+};
+
+struct controller {
+       struct controller *next;
+       void *shpc_context;
+       u32 ctrl_int_comp;
+       void *hpc_reg;          /* cookie for our pci controller location */
+       struct pci_resource *mem_head;
+       struct pci_resource *p_mem_head;
+       struct pci_resource *io_head;
+       struct pci_resource *bus_head;
+       struct pci_dev *pci_dev;
+       struct pci_bus *pci_bus;
+       struct slot *slot;
+       u8 interrupt;
+       u8 bus;
+       u8 device;
+       u8 function;
+       u8 slot_device_offset;
+       u8 first_slot;
+       u8 add_support;
+       u16 vendor_id;
+};
+
+
+static LIST_HEAD(slot_list);
+
+#if !defined(CONFIG_HOTPLUG_PCI_AMD_MODULE)
+       #define MY_NAME "amd_shpc.o"
+#else
+       #define MY_NAME THIS_MODULE->name
+#endif
+
+//
+// Debug Facilities
+//
+#define debug 0
+#define dbg(format, arg...)                                     \
+       do {                                                    \
+               if (debug)                                      \
+                   printk (KERN_DEBUG "%s: " format "\n",      \
+                   MY_NAME , ## arg);          \
+       } while (0)
+
+#define err(format, arg...) printk(KERN_ERR "%s: " format "\n", MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
+
+#define msg_initialization_err  "Initialization failure, error=%d\n"
+#define msg_HPC_rev_error       "Unsupported revision of the PCI hot plug controller found.\n"
+#define msg_HPC_non_amd         "Non-AMD PCI hot plug controller is not supported by this driver.\n"
+#define msg_HPC_not_amd_hp     "Device is not a hot plug controller.\n"
+#define msg_HPC_not_supported   "This system is not supported by this version of amdshpc. Upgrade to a newer version of amdshpc\n"
+#define msg_unable_to_save      "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
+
+struct hrt {
+       char sig0;
+       char sig1;
+       char sig2;
+       char sig3;
+       u16 unused_IRQ;
+       u16 PCIIRQ;
+       u8 number_of_entries;
+       u8 revision;
+       u16 reserved1;
+       u32 reserved2;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug resource table registers based on the above structure layout */
+enum hrt_offsets {
+       SIG0 =                  offsetof(struct hrt, sig0),
+       SIG1 =                  offsetof(struct hrt, sig1),
+       SIG2 =                  offsetof(struct hrt, sig2),
+       SIG3 =                  offsetof(struct hrt, sig3),
+       UNUSED_IRQ =            offsetof(struct hrt, unused_IRQ),
+       PCIIRQ =                offsetof(struct hrt, PCIIRQ),
+       NUMBER_OF_ENTRIES =     offsetof(struct hrt, number_of_entries),
+       REVISION =              offsetof(struct hrt, revision),
+       HRT_RESERVED1 =         offsetof(struct hrt, reserved1),
+       HRT_RESERVED2 =         offsetof(struct hrt, reserved2),
+};
+
+struct slot_rt {
+       u8 dev_func;
+       u8 primary_bus;
+       u8 secondary_bus;
+       u8 max_bus;
+       u16 io_base;
+       u16 io_length;
+       u16 mem_base;
+       u16 mem_length;
+       u16 pre_mem_base;
+       u16 pre_mem_length;
+} __attribute__ ((packed));
+
+/* offsets to the hotplug slot resource table registers based on the above structure layout */
+enum slot_rt_offsets {
+       DEV_FUNC =      offsetof(struct slot_rt, dev_func),
+       PRIMARY_BUS =   offsetof(struct slot_rt, primary_bus),
+       SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
+       MAX_BUS =       offsetof(struct slot_rt, max_bus),
+       IO_BASE =       offsetof(struct slot_rt, io_base),
+       IO_LENGTH =     offsetof(struct slot_rt, io_length),
+       MEM_BASE =      offsetof(struct slot_rt, mem_base),
+       MEM_LENGTH =    offsetof(struct slot_rt, mem_length),
+       PRE_MEM_BASE =  offsetof(struct slot_rt, pre_mem_base),
+       PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
+};
+
+struct pci_func {
+       struct pci_func *next;
+       u8 bus;
+       u8 device;
+       u8 function;
+       u8 is_a_board;
+       u16 status;
+       u8 configured;
+       u8 switch_save;
+       u8 presence_save;
+       u32 base_length[0x06];
+       u8 base_type[0x06];
+       u16 reserved2;
+       u32 config_space[0x20];
+       struct pci_resource *mem_head;
+       struct pci_resource *p_mem_head;
+       struct pci_resource *io_head;
+       struct pci_resource *bus_head;
+       struct timer_list *p_task_event;
+       struct pci_dev* pci_dev;
+};
+
+
+#ifndef FALSE
+       #define FALSE 0
+       #define TRUE 1
+#endif
+
+#define IN
+#define OUT
+
+enum mutex_action {
+       ACQUIRE,
+       RELEASE,
+};
+
+enum hp_boolean {
+       HP_FALSE = 0,
+       HP_TRUE = 1,
+};
+
+// card power requirements
+enum hp_power_requirements {
+       POWER_LOW,              // low power requirements
+       POWER_MEDIUM,           // medium power requirements
+       POWER_HIGH,             // high power requirements
+};
+
+//
+// slot event masks
+//
+#define ATTN_BUTTON_EVENT               0x00000001
+#define ALERT_EVENT                    0x00000002
+#define BUS_REBALANCE_EVENT             0x00000004
+#define QUIESCE_EVENT                   0x00000008
+#define ATTN_LED_PROBLEM_EVENT          0x00000010
+#define ATTN_LED_REQUEST_EVENT          0x00000020
+#define SLOT_REQUEST_EVENT              0x00000040
+#define SLOT_TIMER1_EVENT               0x00000080
+#define SLOT_TIMER2_EVENT               0x00000100
+#define SLOT_TIMER3_EVENT               0x00000200
+#define SLOT_TIMER4_EVENT               0x00000400
+#define SLOT_TIMER5_EVENT               0x00000800
+#define SLOT_TIMER6_EVENT               0x00001000
+#define SLOT_TIMER7_EVENT               0x00002000
+#define SLOT_TIMER8_EVENT               0x00004000
+#define SLOT_TIMER9_EVENT               0x00008000
+#define SLOT_TIMER10_EVENT              0x00010000
+#define LED_TIMER1_EVENT                0x00020000
+#define LED_TIMER2_EVENT                0x00040000
+#define LED_TIMER3_EVENT                0x00080000
+#define LED_TIMER4_EVENT                0x00100000
+#define CMD_ACQUIRE_EVENT               0x00200000
+#define CMD_RELEASE_EVENT               0x00400000
+#define LED_CMD_ACQUIRE_EVENT           0x00800000
+#define LED_CMD_RELEASE_EVENT           0x01000000
+#define BUS_RELEASE_EVENT               0x02000000
+#define BUS_ACQUIRE_EVENT               0x04000000
+
+//
+// controller event masks
+//
+#define BUS_COMPLETE_EVENT              0x00000001
+#define SUSPEND_EVENT                   0x00000002
+#define RESUME_EVENT                    0x00000004
+#define REMOVE_EVENT                    0x00000008
+#define EXIT_REQUEST_EVENT              0x00000010
+#define CTRL_TIMER_EVENT                0x00000020
+#define CMD_COMPLETION_EVENT            0x00000040
+#define CMD_AVAILABLE_MUTEX_EVENT       0x00000080
+#define BUS_AVAILABLE_MUTEX_EVENT       0x00000100
+#define LED_CMD_AVAILABLE_MUTEX_EVENT   0x00000200
+
+
+#define PCI_TO_PCI_BRIDGE_CLASS         0x00060400
+#define SLOT_MASK                       0x28
+
+
+#define ADD_NOT_SUPPORTED               0x00000003
+#define ADAPTER_NOT_SAME                0x00000006
+#define NO_ADAPTER_PRESENT              0x00000009
+
+#define REMOVE_NOT_SUPPORTED            0x00000003
+
+
+
+// slot states
+enum hp_states {
+       SLOT_DISABLE,           // slot disable
+       SLOT_ENABLE,            // slot enable
+};
+
+// indicator values
+enum mode_frequency {
+       MODE_PCI_33,            // PCI 33Mhz
+       MODE_PCI_66,            // PCI 66Mhz
+       MODE_PCIX_66,           // PCI-X 66Mhz
+       MODE_PCIX_100,          // PCI-X 100Mhz
+       MODE_PCIX_133,          // PCI-X 133Mhz
+};
+
+enum hp_indicators {
+       INDICATOR_OFF,          // Indicator off state
+       INDICATOR_ON,           // Indicator on state
+       INDICATOR_BLINK,        // Indicator blink state
+       INDICATOR_NORMAL,       // Indicator normal state
+};
+
+struct pci_resource {
+       struct pci_resource * next;
+       u32 base;
+       u32 length;
+};
+
+struct resource_descriptor {
+       u32 base;
+       u32 limit;
+};
+
+struct irq_mapping {
+       u8 barber_pole;
+       u8 valid_INT;
+       u8 interrupt[4];
+};
+
+struct resource_lists {
+       struct pci_resource *mem_head;
+       struct pci_resource *p_mem_head;
+       struct pci_resource *io_head;
+       struct pci_resource *bus_head;
+       struct irq_mapping *irqs;
+};
+
+#define ROM_PHY_ADDR                    0x0F0000
+#define ROM_PHY_LEN                     0x00ffff
+
+#define NOT_ENOUGH_RESOURCES            0x0000000B
+#define DEVICE_TYPE_NOT_SUPPORTED       0x0000000C
+
+//
+// Prototypes
+//
+extern int  amdshpc_resource_sort_and_combine (struct pci_resource **head);
+
+//
+// State-Machine Function
+//
+typedef long ( *SLOT_STATE_FUNCTION )(
+                                    void* shpc_context,
+                                    void* slot_context);
+
+//
+// SHPC Constants
+//
+#define SHPC_MAX_NUM_SLOTS      4
+
+
+#define arraysize(p) (sizeof(p)/sizeof((p)[0]))
+
+
+//
+// SHPC Register Offsets
+//
+enum shpc_register_offset {
+       SHPC_SLOTS_AVAILABLE1_REG_OFFSET        = 0x04,
+       SHPC_SLOTS_AVAILABLE2_REG_OFFSET        = 0x08,
+       SHPC_SLOT_CONFIG_REG_OFFSET             = 0x0C,
+       SHPC_SEC_BUS_CONFIG_REG_OFFSET          = 0x10,
+       SHPC_COMMAND_REG_OFFSET                 = 0x14,
+       SHPC_STATUS_REG_OFFSET                  = 0x16,
+       SHPC_INT_LOCATOR_REG_OFFSET             = 0x18,
+       SHPC_SERR_LOCATOR_REG_OFFSET            = 0x1C,
+       SHPC_SERR_INT_REG_OFFSET                = 0x20,
+       SHPC_LOGICAL_SLOT_REG_OFFSET            = 0x24,
+};
+
+
+//
+// SHPC Slots Available Register I
+//
+union SHPC_SLOTS_AVAILABLE1_DWREG {
+       struct {
+               u32 N_33CONV    :5;     // 4:0
+               u32 reserved1   :3;     // 7:5
+               u32 N_66PCIX    :5;     // 12:8
+               u32 reserved2   :3;     // 15:13
+               u32 N_100PCIX   :5;     // 20:16
+               u32 reserved3   :3;     // 23:21
+               u32 N_133PCIX   :5;     // 28:24
+               u32 reserved4   :3;     // 31:29
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC Slots Available Register II
+//
+union SHPC_SLOTS_AVAILABLE2_DWREG {
+       struct {
+               u32 N_66CONV    :5;     // 4:0
+               u32 reserved4   :27;    // 31:5
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC Slot Configuration Register
+//
+union SHPC_SLOT_CONFIG_DWREG {
+       struct {
+               u32 NSI         :5;     // 4:0
+               u32 reserved1   :3;     // 7:5
+               u32 FDN         :5;     // 12:8
+               u32 reserved2   :3;     // 15:13
+               u32 PSN         :11;    // 26:16
+               u32 reserved3   :2;     // 28:27
+               u32 PSN_UP      :1;     // 29
+               u32 MRLSI       :1;     // 30
+               u32 ABI         :1;     // 31
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC Secondary Bus Configuration Register
+//
+union SHPC_SEC_BUS_CONFIG_DWREG {
+       struct {
+               u32 MODE        :3;     // 2:0
+               u32 reserved    :21;    // 23:3
+               u32 format      :8;     // 31:24
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC Command Register
+//
+union SHPC_COMMAND_WREG {
+       struct {
+               u16 state               : 2;    // 1:0
+               u16 power_led           : 2;    // 3:2
+               u16 attention_led       : 2;    // 5:4
+               u16 code                : 2;    // 7:6
+               u16 TGT                 : 5;    // 12:8
+               u16 reserved            : 3;    // 15:13
+       } Slot;
+       struct {
+               u16 speed_mode          : 3;    // 2:0
+               u16 code                : 5;    // 7:3
+               u16 reserved            : 8;    // 15:8
+       } Bus;
+       struct {
+               u16 code                : 8;    // 7:0
+               u16 reserved            : 8;    // 15:8
+       }x;
+       u16 AsWord;
+};
+
+
+//
+// SHPC Status Register
+//
+union SHPC_STATUS_WREG {
+       struct {
+               u16 BSY         :1;     // 0
+               u16 MRLO_ERR    :1;     // 1
+               u16 INVCMD_ERR  :1;     // 2
+               u16 INVSM_ERR   :1;     // 3
+               u16 reserved    :12;    // 15:4
+       }x;
+       u16 AsWord;
+};
+
+
+//
+// SHPC Interrupt Locator Register
+//
+union SHPC_INT_LOCATOR_DWREG {
+       struct {
+               u32 CC_IP       :1;     // 0
+               u32 SLOT_IP     :4;     // 4:1
+               u32 reserved    :27;    // 31:5
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC SERR Locator Register
+//
+union SHPC_SERR_LOCATOR_DWREG {
+       struct {
+               u32 A_SERRP     :1;     // 0
+               u32 SLOT_SERRP  :4;     // 4:1
+               u32 reserved    :27;    // 31:5
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC SERR-INT Register
+//
+union SHPC_SERR_INT_DWREG {
+       struct {
+               u32 GIM         :1;     // 0
+               u32 GSERRM      :1;     // 1
+               u32 CC_IM       :1;     // 2
+               u32 A_SERRM     :1;     // 3
+               u32 reserved1   :12;    // 15:4
+               u32 CC_STS      :1;     // 16
+               u32 ATOUT_STS   :1;     // 17
+               u32 reserved2   :14;    // 31:18
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// SHPC Logical Slot Register
+//
+union SHPC_LOGICAL_SLOT_DWREG {
+       struct {
+               u32 S_STATE     :2;     // 1:0
+               u32 PIS         :2;     // 3:2
+               u32 AIS         :2;     // 5:4
+               u32 PF          :1;     // 6
+               u32 AB          :1;     // 7
+               u32 MRLS        :1;     // 8
+               u32 M66_CAP     :1;     // 9
+               u32 PRSNT1_2    :2;     // 11:10
+               u32 PCIX_CAP    :2;     // 13:12
+               u32 reserved1   :2;     // 15:14
+               u32 CPC_STS     :1;     // 16
+               u32 IPF_STS     :1;     // 17
+               u32 ABP_STS     :1;     // 18
+               u32 MRLSC_STS   :1;     // 19
+               u32 CPF_STS     :1;     // 20
+               u32 reserved2   :3;     // 23:21
+               u32 CP_IM       :1;     // 24
+               u32 IPF_IM      :1;     // 25
+               u32 AB_IM       :1;     // 26
+               u32 MRLS_IM     :1;     // 27
+               u32 CPF_IM      :1;     // 28
+               u32 MRLS_SERRM  :1;     // 29
+               u32 CPF_SERRM   :1;     // 30
+               u32 reserved3   :1;     // 31
+       }x;
+       u32 AsDWord;
+};
+
+
+//
+// Bus Speed/Mode
+//
+enum shpc_speed_mode {
+       SHPC_BUS_CONV_33        = 0,
+       SHPC_BUS_CONV_66        = 1,
+       SHPC_BUS_PCIX_66        = 2,
+       SHPC_BUS_PCIX_100       = 3,
+       SHPC_BUS_PCIX_133       = 4,
+};
+
+
+//
+// Slot PCIX Capability
+//
+enum shpc_slot_pcix_cap {
+       SHPC_SLOT_CONV          = 0,
+       SHPC_SLOT_PCIX_66       = 1,
+       SHPC_SLOT_PCIX_133      = 3,
+};
+
+
+//
+// Slot LEDs
+//
+enum shpc_slot_led {
+       SHPC_led_NO_CHANGE      = 0,
+       SHPC_LED_ON             = 1,
+       SHPC_LED_BLINK          = 2,
+       SHPC_LED_OFF            = 3,
+};
+
+
+//
+// Slot State
+//
+enum shpc_slot_state {
+       SHPC_SLOT_NO_CHANGE     = 0,
+       SHPC_POWER_ONLY         = 1,
+       SHPC_ENABLE_SLOT        = 2,
+       SHPC_DISABLE_SLOT       = 3,
+};
+
+
+//
+// Command Code
+//
+#define SHPC_SLOT_OPERATION             0x00    // 7:6  (00xxxxxxb)
+#define SHPC_SET_BUS_SPEED_MODE         0x08    // 7:3  (01000xxxb)
+#define SHPC_POWER_ONLY_ALL_SLOTS       0x48    // 7:0  (01001000b)
+#define SHPC_ENABLE_ALL_SLOTS           0x49    // 7:0  (01001001b)
+
+
+//
+// SHPC Status
+//
+enum shpc_status {
+       SHPC_STATUS_CLEARED     = 0,
+       SHPC_STATUS_SET         = 1,
+};
+
+
+//
+// SHPC Mask
+//
+enum shpc_mask {
+       SHPC_UNMASKED   = 0,
+       SHPC_MASKED     = 1,
+};
+
+
+//
+// Slot MRL Sensor
+//
+enum shpc_slot_mrl {
+       SHPC_MRL_CLOSED = 0,
+       SHPC_MRL_OPEN   = 1,
+};
+
+
+//
+// Slot Attn Button
+//
+enum shpc_slot_attn_button {
+       SHPC_ATTN_BUTTON_RELEASED  = 0,
+       SHPC_ATTN_BUTTON_PRESSED   = 1,
+};
+
+
+//
+// Card Power Requirements
+//
+enum shpc_card_power {
+       SHPC_CARD_PRESENT_7_5W  = 0,
+       SHPC_CARD_PRESENT_15W   = 1,
+       SHPC_CARD_PRESENT_25W   = 2,
+       SHPC_SLOT_EMPTY         = 3,
+};
+
+
+// slot config structure
+union SLOT_CONFIG_INFO {
+       struct {
+               u32             lu_slots_implemented    : 5;    // [ 4:0 ]Number of slots implemented
+               u32             lu_reserved1            : 3;    // [ 7:5 ]Reserved
+               u32             lu_base_FDN             : 5;    // [ 12:8 ]First Device Number
+               u32             lu_reserved2            : 3;    // [ 15:13 ]Reserved
+               u32             lu_base_PSN             : 11;   // [ 26:16 ]Physical Slot Number
+               u32             lu_reserved3            : 2;    // [ 28:27 ]Reserved
+               u32             lu_PSN_up               : 1;    // [ 29 ]PSN Up (1=TRUE, 0=FALSE)
+               u32             lu_reserved4            : 2;    // [ 31:30 ]Reserved
+       }x;
+       u32     AsDWord;
+};
+
+
+// logical slot information
+union SLOT_STATUS_INFO {
+       struct {
+               u32     lu_slot_state           : 1;    // [ 0 ]Slot state (1=Enabled, 0=Disabled)
+               u32     lu_power_fault          : 1;    // [ 1 ]Power-Fault? (1=TRUE, 0=FALSE)
+               u32     lu_card_present         : 1;    // [ 2 ]Card Present? (1=TRUE, 0=FALSE)
+               u32     lu_card_power           : 2;    // [ 4:3 ]Card Power Requirements (low/medium/high)
+               u32     lu_card_mode_freq_cap   : 3;    // [ 7:5 ]Card Speed/mode capability
+               u32     lu_mrl_implemented      : 1;    // [ 8 ]MRL Implemented? (1=TRUE, 0=FALSE)
+               u32     lu_mrl_opened           : 1;    // [ 9 ]MRL State (if implemented: 1=TRUE, 0=FALSE)
+               u32     lu_ai_state             : 2;    // [ 11:10 ]Attn Indicator State (Blink/On/Off)
+               u32     lu_pi_state             : 2;    // [ 13:12 ]Power Indicator State (Blink/On/Off)
+               u32     lu_reserved1            : 2;    // [ 14 ]Reserved
+               u32     lu_card_pci66_capable   : 1;    // [ 15 ]Card PCI66 capability (1=TRUE, 0=FALSE)
+               u32     lu_bus_mode_freq        : 3;    // [ 18:16 ]Current Bus speed/mode
+               u32     lu_max_bus_mode_freq    : 3;    // [ 21:19 ]Maximum Bus speed/mode
+               u32     lu_reserved2            : 9;    // [ 30:22 ]Reserved
+               u32     lu_request_failed       : 1;    // [ 31 ]Request Failed? (1=TRUE, 0=FALSE)
+       }x;
+       u32     AsDWord;
+};
+
+enum return_status {
+       STATUS_UNSUCCESSFUL,
+       STATUS_SUCCESS
+};
+
+//
+// Async Request
+//
+enum shpc_async_request {
+       SHPC_ASYNC_ENABLE_SLOT,
+       SHPC_ASYNC_DISABLE_SLOT,
+       SHPC_ASYNC_SURPRISE_REMOVE,
+       SHPC_ASYNC_QUIESCE_DEVNODE,
+       SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+       SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY,
+       SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE,
+       SHPC_ASYNC_LED_LOCATE,
+       SHPC_ASYNC_LED_NORMAL
+};
+
+
+//
+// Async Request
+//
+struct async_request {
+       enum shpc_async_request type;
+       wait_queue_head_t       event;
+       unsigned long           timeout;
+       void                    *request_context;
+};
+
+
+//
+// Async Completion
+//
+struct async_completion {
+       enum shpc_async_request type;
+       unsigned long                   timeout;
+       u8                              hw_initiated;
+       u8                              done;
+       enum hp_boolean                 failed;
+       void                            *request_context;
+};
+
+// ****************************************************************************
+//
+// async_callback() @ PASSIVE_LEVEL
+//
+// Parameters
+//      driver_context - Pointer provided in hp_AddDevice()
+//      slot_id - Zero-based slot number (0..n-1).
+//      Request - Async request completed.  For example: Slot Enable/Disable, AttnLED Attn/Normal.
+//      Status - Slot status at completion
+//      request_context - Pointer provided in hp_StartAsyncRequest(), NULL for
+//              completions on hardware-initiated requests.
+//
+// Return Value
+//      For QUIESCE_DEVNODE request: #DevNodes associated with a particular slot, else 0.
+//
+// ****************************************************************************
+typedef unsigned long ( *SHPC_ASYNC_CALLBACK )( void* driver_context,
+                                               u8 slot_id,
+                                               enum shpc_async_request Request,
+                                               union SLOT_STATUS_INFO Status,
+                                               void* request_context );
+
+//
+// Slot Context
+//
+struct slot_context {
+
+       spinlock_t              slot_spinlock;
+       struct semaphore        slot_event_bits_semaphore;
+       struct semaphore        cmd_acquire_mutex;
+       struct semaphore        bus_acquire_mutex;
+       u32                     *logical_slot_addr;
+       u8                      slot_number;
+       u8                      slot_psn;
+       u32                     quiesce_requests;
+       u32                     quiesce_replies;
+       u8                      slot_enabled;
+       enum shpc_speed_mode    card_speed_mode;
+       u8                      card_pci66_capable;
+       u8                      in_bus_speed_mode_contention;
+       u8                      problem_detected;
+       u8                      slot_quiesced;
+       u8                      slot_occupied;
+       struct tasklet_struct   attn_button_dpc;
+       struct tasklet_struct   mrl_sensor_dpc;
+       struct tasklet_struct   card_presence_dpc;
+       struct tasklet_struct   isolated_power_fault_dpc;
+       struct tasklet_struct   connected_power_fault_dpc;
+       wait_queue_head_t       slot_event;
+       wait_queue_head_t       led_cmd_acquire_event;
+       wait_queue_head_t       led_cmd_release_event;
+       wait_queue_head_t       cmd_acquire_event;
+       wait_queue_head_t       cmd_release_event;
+       wait_queue_head_t       bus_acquire_event;
+       wait_queue_head_t       bus_release_event;
+       u32                     slot_event_bits;
+       void                    *slot_thread;
+       void                    *attn_led_thread;
+       SLOT_STATE_FUNCTION     slot_function;
+       SLOT_STATE_FUNCTION     attn_led_function;
+       struct async_request    slot_request;
+       struct async_completion slot_completion;
+       struct async_request    attn_led_request;
+       struct async_completion attn_led_completion;
+       void                    *shpc_context;
+       struct timer_list       slot_timer1;
+       struct timer_list       slot_timer2;
+       struct timer_list       slot_timer3;
+       struct timer_list       slot_timer4;
+       struct timer_list       slot_timer5;
+       struct timer_list       slot_timer6;
+       struct timer_list       slot_timer7;
+       struct timer_list       slot_timer8;
+       struct timer_list       slot_timer9;
+       struct timer_list       slot_timer10;
+       struct timer_list       led_timer1;
+       struct timer_list       led_timer2;
+       struct timer_list       led_timer3;
+       struct timer_list       led_timer4;
+};
+
+//
+// SHPC Context
+//
+struct shpc_context {
+       spinlock_t              shpc_spinlock;
+       struct semaphore        shpc_event_bits_semaphore;
+       void                    *mmio_base_addr;
+       struct shpc_context     *next;
+       u8                      first_slot;
+       u8                      number_of_slots;
+       u8                      slots_enabled;
+       u8                      at_power_device_d0;
+       u8                      bus_released;
+       enum shpc_speed_mode    max_speed_mode;
+       enum shpc_speed_mode    bus_speed_mode;
+       struct semaphore        cmd_available_mutex;
+       struct tasklet_struct   cmd_completion_dpc;
+       struct semaphore        bus_available_mutex;
+       wait_queue_head_t       *user_event_pointer;
+       u32                     shpc_event_bits;
+       void                    *driver_context;
+       SHPC_ASYNC_CALLBACK     async_callback;
+       u32                     shpc_instance;
+       struct slot_context     slot_context[ SHPC_MAX_NUM_SLOTS ];
+       void                    *hpc_reg;               // cookie for our pci controller location
+       struct pci_ops          *pci_ops;
+       struct pci_resource     *mem_head;
+       struct pci_resource     *p_mem_head;
+       struct pci_resource     *io_head;
+       struct pci_resource     *bus_head;
+       struct pci_dev          *pci_dev;
+       u8                      interrupt;
+       u8                      bus;
+       u8                      device;
+       u8                      function;
+       u16                     vendor_id;
+       u32                     ctrl_int_comp;
+       u8                      add_support;
+};
+
+//
+// Function Prototypes
+//
+int amdshpc_get_bus_dev (struct controller  *ctrl, u8 * bus_num, u8 * dev_num, u8 slot);
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func);
+int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func);
+int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start);
+int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO * is_hot_plug);
+struct pci_func *amdshpc_slot_create(u8 busnumber);
+
+
+void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask);
+void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask);
+
+void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask);
+void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask);
+
+void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask);
+void hp_send_slot_event(struct slot_context *slot_context, u32 mask);
+
+int hp_get_led_cmd_available_mutex_thread(void *slot_context);
+int hp_get_cmd_available_mutex_thread    (void *slot_context);
+int hp_get_bus_available_mutex_thread(void *slot_context);
+int hp_cmd_available_mutex_thread(void * slot_context);
+int hp_bus_available_mutex_thread(void * slot_context);
+int hp_led_cmd_available_mutex_thread(void * slot_context);
+
+void hp_slot_timer1_func(unsigned long data);
+void hp_slot_timer2_func(unsigned long data);
+void hp_slot_timer3_func(unsigned long data);
+void hp_slot_timer4_func(unsigned long data);
+void hp_slot_timer5_func(unsigned long data);
+void hp_slot_timer6_func(unsigned long data);
+void hp_slot_timer7_func(unsigned long data);
+void hp_slot_timer8_func(unsigned long data);
+void hp_slot_timer9_func(unsigned long data);
+void hp_slot_timer10_func(unsigned long data);
+void hp_led_timer1_func(unsigned long data);
+void hp_led_timer2_func(unsigned long data);
+void hp_led_timer3_func(unsigned long data);
+void hp_led_timer4_func(unsigned long data);
+
+irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs);
+
+u32 board_replaced(struct pci_func * func, struct controller  * ctrl);
+struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index);
+int amdshpc_save_base_addr_length(struct controller  *ctrl, struct pci_func * func);
+int amdshpc_save_used_resources (struct controller  *ctrl, struct pci_func * func);
+int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources);
+int amdshpc_save_slot_config (struct controller  *ctrl, struct pci_func * new_slot);
+int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func);
+int amdshpc_unconfigure_device(struct pci_func* func);
+
+
+
+void
+hp_attn_button_dpc(
+                         unsigned long deferred_context
+                         );
+
+void
+hp_mrl_sensor_dpc(
+                        unsigned long deferred_context
+                        );
+
+void
+hp_card_presence_dpc(
+                           unsigned long deferred_context
+                           );
+
+void
+hp_isolated_power_fault_dpc(
+                                  unsigned long deferred_context
+                                  );
+
+void
+hp_connected_power_fault_dpc(
+                           unsigned long deferred_context
+                           );
+
+void
+hp_cmd_completion_dpc(
+                    unsigned long deferred_context
+                    );
+
+int
+hp_slot_thread(
+             void* slot_context
+             );
+
+long
+hp_at_slot_disabled_wait_for_slot_request(
+                                        struct shpc_context* shpc_context,
+                                        struct slot_context* slot_context
+                                                );
+
+long
+hp_at_slot_disabled_wait_for_led_cmd_available(
+                                             struct shpc_context* shpc_context,
+                                             struct slot_context* slot_context
+                                             );
+
+long
+hp_at_slot_disabled_wait_for_led_cmd_completion(
+                                              struct shpc_context* shpc_context,
+                                              struct slot_context* slot_context
+                                              );
+
+long
+hp_at_slot_disabled_wait_for_timeout(
+                                   struct shpc_context* shpc_context,
+                                   struct slot_context* slot_context
+                                   );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_available(
+                                               struct shpc_context* shpc_context,
+                                               struct slot_context* slot_context
+                                               );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_timeout(
+                                             struct shpc_context* shpc_context,
+                                             struct slot_context* slot_context
+                                             );
+
+long
+hp_at_slot_disabled_wait_for_power_cmd_completion(
+                                                struct shpc_context* shpc_context,
+                                                struct slot_context* slot_context
+                                                );
+
+long
+hp_at_slot_disabled_wait_for_bus_available(
+                                         struct shpc_context* shpc_context,
+                                         struct slot_context* slot_context
+                                         );
+
+long
+hp_at_slot_disabled_wait_for_bus_released(
+                                        struct shpc_context* shpc_context,
+                                        struct slot_context* slot_context
+                                        );
+
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_available(
+                                                    struct shpc_context* shpc_context,
+                                                    struct slot_context* slot_context
+                                                    );
+
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_completion(
+                                                     struct shpc_context* shpc_context,
+                                                     struct slot_context* slot_context
+                                                     );
+
+long
+hp_at_slot_disabled_wait_for_enable_cmd_available(
+                                                struct shpc_context* shpc_context,
+                                                struct slot_context* slot_context
+                                                );
+
+long
+hp_at_slot_disabled_wait_for_enable_cmd_completion(
+                                                 struct shpc_context* shpc_context,
+                                                 struct slot_context* slot_context
+                                                 );
+
+long
+hp_at_slot_disabled_wait_for_enable_timeout(
+                                          struct shpc_context* shpc_context,
+                                          struct slot_context* slot_context
+                                          );
+
+long
+hp_to_slot_disabled_wait_for_led_cmd_available(
+                                             struct shpc_context* shpc_context,
+                                             struct slot_context* slot_context
+                                             );
+
+long
+hp_to_slot_disabled_wait_for_led_cmd_completion(
+                                              struct shpc_context* shpc_context,
+                                              struct slot_context* slot_context
+                                              );
+
+long
+hp_to_slot_disabled_wait_for_disable_cmd_available(
+                                                 struct shpc_context* shpc_context,
+                                                 struct slot_context* slot_context
+                                                 );
+
+long
+hp_to_slot_disabled_wait_for_disable_cmd_completion(
+                                                  struct shpc_context* shpc_context,
+                                                  struct slot_context* slot_context
+                                                  );
+
+long
+hp_to_slot_disabled_wait_for_disable_timeout(
+                                           struct shpc_context* shpc_context,
+                                           struct slot_context* slot_context
+                                           );
+
+long
+hp_to_slot_disabled_wait_for_bus_available(
+                                         struct shpc_context* shpc_context,
+                                         struct slot_context* slot_context
+                                         );
+
+long
+hp_at_slot_enabled_wait_for_slot_request(
+                                       struct shpc_context* shpc_context,
+                                       struct slot_context* slot_context
+                                       );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_bus_rebalance(
+                                                struct shpc_context* shpc_context,
+                                                struct slot_context* slot_context
+                                                );
+
+long
+hp_at_slot_enabled_wait_for_power_cmd_available(
+                                              struct shpc_context* shpc_context,
+                                              struct slot_context* slot_context
+                                              );
+
+long
+hp_at_slot_enabled_wait_for_power_cmd_completion(
+                                               struct shpc_context* shpc_context,
+                                               struct slot_context* slot_context
+                                               );
+
+long
+hp_at_slot_enabled_wait_for_led_cmd_available(
+                                            struct shpc_context* shpc_context,
+                                            struct slot_context* slot_context
+                                            );
+
+long
+hp_at_slot_enabled_wait_for_led_cmd_completion(
+                                             struct shpc_context* shpc_context,
+                                             struct slot_context* slot_context
+                                             );
+
+long
+hp_at_slot_enabled_wait_for_timeout(
+                                  struct shpc_context* shpc_context,
+                                  struct slot_context* slot_context
+                                  );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable(
+                                               struct shpc_context* shpc_context,
+                                               struct slot_context* slot_context
+                                               );
+
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet(
+                                                     struct shpc_context* shpc_context,
+                                                     struct slot_context* slot_context
+                                                     );
+
+long
+hp_to_slot_enabled_wait_for_led_cmd_available(
+                                            struct shpc_context* shpc_context,
+                                            struct slot_context* slot_context
+                                            );
+
+long
+hp_to_slot_enabled_wait_for_led_cmd_completion(
+                                             struct shpc_context* shpc_context,
+                                             struct slot_context* slot_context
+                                             );
+
+void
+hp_get_slot_configuration(
+                        struct shpc_context* shpc_context
+                        );
+
+void
+hp_enable_slot_interrupts(
+                        struct slot_context* slot_context
+                        );
+
+void
+hp_disable_slot_interrupts(
+                         struct slot_context* slot_context
+                         );
+
+void
+hp_enable_global_interrupts(
+                          struct shpc_context* shpc_context
+                          );
+
+void
+hp_disable_global_interrupts(
+                           struct shpc_context* shpc_context
+                           );
+
+enum shpc_speed_mode
+hp_get_bus_speed_mode(
+                    struct shpc_context* shpc_context
+                    );
+
+enum shpc_speed_mode
+hp_get_card_speed_mode(
+                     struct slot_context* slot_context
+                     );
+
+enum mode_frequency
+hp_translate_speed_mode(
+                      enum shpc_speed_mode shpc_speed_mode
+                      );
+
+enum hp_power_requirements
+hp_translate_card_power(
+                      enum shpc_card_power ShpcCardPower
+                              );
+
+enum hp_indicators
+hp_translate_indicator(
+                     enum shpc_slot_led ShpcIndicator
+                             );
+
+u8
+hp_flag_slot_as_enabled(
+                      struct shpc_context* shpc_context,
+                      struct slot_context* slot_context
+                      );
+
+u8
+hp_flag_slot_as_disabled(
+                       struct shpc_context* shpc_context,
+                       struct slot_context* slot_context
+                       );
+
+u8
+hp_signal_enabled_slots_to_rebalance_bus(
+                                       struct shpc_context* shpc_context
+                                       );
+
+enum shpc_speed_mode
+hp_get_max_speed_mode(
+                    struct shpc_context* shpc_context,
+                    enum shpc_speed_mode From_speed_mode
+                    );
+
+void
+hp_signal_user_event(
+                   struct shpc_context* shpc_context
+                   );
+
+void
+hp_signal_user_event_at_dpc_level(
+                                struct shpc_context* shpc_context
+                                );
+
+int
+hp_attn_led_thread(
+                 void* slot_context
+                 );
+
+long
+hp_wait_for_attn_led_request(
+                           struct shpc_context* shpc_context,
+                           struct slot_context* slot_context
+                           );
+
+long
+hp_wait_for_attn_led_blink_cmd_available(
+                                       struct shpc_context* shpc_context,
+                                       struct slot_context* slot_context
+                                       );
+
+long
+hp_wait_for_attn_led_blink_cmd_completion(
+                                        struct shpc_context* shpc_context,
+                                        struct slot_context* slot_context
+                                        );
+
+long
+hp_wait_for_attn_led_blink_timeout(
+                                 struct shpc_context* shpc_context,
+                                 struct slot_context* slot_context
+                                 );
+
+long
+hp_wait_for_attn_led_normal_cmd_available(
+                                        struct shpc_context* shpc_context,
+                                        struct slot_context* slot_context
+                                        );
+
+long
+hp_wait_for_attn_led_normal_cmd_completion(
+                                         struct shpc_context* shpc_context,
+                                         struct slot_context* slot_context
+                                         );
+
+long
+hp_wait_for_attn_led_back_to_normal_cmd_available(
+                                                struct shpc_context* shpc_context,
+                                                struct slot_context* slot_context
+                                                );
+
+long
+hp_wait_for_attn_led_back_to_normal_cmd_completion(
+                                                 struct shpc_context* shpc_context,
+                                                 struct slot_context* slot_context
+                                                 );
+
+
+
+#endif  // _SHPC_H_
diff --git a/drivers/pci/hotplug/amdshpc_core.c b/drivers/pci/hotplug/amdshpc_core.c
new file mode 100644 (file)
index 0000000..9fe7209
--- /dev/null
@@ -0,0 +1,915 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "amdshpc.h"
+#include "amdshpc_ddi.h"
+#include "pci_hotplug.h"
+#include "../../../arch/i386/pci/pci.h"
+
+/* Global variables */
+int amdshpc_debug;
+struct shpc_context *amdshpc_ctrl_list; // used for the shpc state machine
+struct controller *ctrl_list;            // used only for resource management
+struct pci_func *amdshpc_slot_list[256];
+
+static int num_slots;
+static void *amdshpc_rom_start;
+static unsigned long shpc_instance;
+
+#define DRIVER_VERSION "1.03"
+#define DRIVER_AUTHOR  "Dave Keck <david.keck@amd.com>"
+#define DRIVER_DESC    "AMD Standard Hot Plug Controller Driver"
+#define PCI_DEVICE_ID_AMD_GOLAM_7450   0x7450
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+//MODULE_PARM(debug, "i");
+//MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot         (struct hotplug_slot *slot);
+static int disable_slot                (struct hotplug_slot *slot);
+static int set_attention_status (struct hotplug_slot *slot, u8 value);
+static int hardware_test       (struct hotplug_slot *slot, u32 value);
+static int get_power_status    (struct hotplug_slot *slot, u8 *value);
+static int get_attention_status        (struct hotplug_slot *slot, u8 *value);
+static int get_latch_status    (struct hotplug_slot *slot, u8 *value);
+static int get_adapter_status  (struct hotplug_slot *slot, u8 *value);
+
+// values to be returned to the PCI Hotplug Core
+#define CORE_SLOT_DISABLED             0
+#define CORE_SLOT_ENABLED              1
+
+#define        CORE_INDICATOR_OFF              0
+#define        CORE_INDICATOR_ON               1
+#define        CORE_INDICATOR_BLINK            2
+
+#define CORE_LATCH_CLOSED              1
+#define CORE_LATCH_OPENED              0
+
+static int init_slots (        struct controller *ctrl, int num_slots );
+static void translate_slot_info (struct hotplug_slot_info *info,
+                                                                union SLOT_STATUS_INFO *query);
+
+static struct hotplug_slot_ops skel_hotplug_slot_ops = {
+       .owner =                THIS_MODULE,
+       .enable_slot =          enable_slot,
+       .disable_slot =         disable_slot,
+       .set_attention_status = set_attention_status,
+       .hardware_test =        hardware_test,
+       .get_power_status =     get_power_status,
+       .get_attention_status = get_attention_status,
+       .get_latch_status =     get_latch_status,
+       .get_adapter_status =   get_adapter_status,
+};
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static inline int slot_paranoia_check (struct slot *slot, const char *function)
+{
+       if (!slot) {
+               dbg("-->%s - slot == NULL", function);
+               return -1;
+       }
+       if (slot->magic != SLOT_MAGIC) {
+               dbg("-->%s - bad magic number for slot", function);
+               return -1;
+       }
+       if (!slot->hotplug_slot) {
+               dbg("-->%s - slot->hotplug_slot == NULL!", function);
+               return -1;
+       }
+       return 0;
+}
+
+static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
+{
+       struct slot *slot;
+
+       if (!hotplug_slot) {
+               dbg("-->%s - hotplug_slot == NULL\n", function);
+               return NULL;
+       }
+
+       slot = (struct slot *)hotplug_slot->private;
+       if (slot_paranoia_check (slot, function))
+                               return NULL;
+       return slot;
+}
+
+static int enable_slot (struct hotplug_slot *hotplug_slot)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       long status;
+       int retval = 0;
+
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       /*
+        *  enable the specified slot
+        */
+       shpc_context = ( struct shpc_context * )slot->private;
+       status = hp_StartAsyncRequest(shpc_context, slot->number,
+               SHPC_ASYNC_ENABLE_SLOT, 0, slot );
+
+       //
+       // pretend async request was completed (we're not queuing slot requests)
+       //
+       hp_QuerySlotStatus( shpc_context, slot->number, &query );
+       if( status == STATUS_SUCCESS ) {
+               query.x.lu_slot_state = SLOT_ENABLE;
+               query.x.lu_pi_state = INDICATOR_BLINK;
+               if( query.x.lu_card_present &&
+                       ( query.x.lu_mrl_implemented == HP_FALSE ||
+                       query.x.lu_mrl_opened == HP_FALSE ) &&
+                       query.x.lu_power_fault == HP_FALSE ) {
+                               query.x.lu_request_failed = HP_FALSE;
+               }
+               else {
+                       query.x.lu_request_failed = HP_TRUE;
+               }
+       }
+       else {
+               query.x.lu_request_failed = HP_TRUE;
+       }
+
+       //
+       // translate the slot info to PCI HOTPLUG CORE values
+       //
+       translate_slot_info (hotplug_slot->info, &query);
+
+       retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1;
+       return retval;
+}
+
+
+static int disable_slot (struct hotplug_slot *hotplug_slot)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       long status;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       /*
+        *  disable the specified slot
+        */
+       shpc_context = ( struct shpc_context * )slot->private;
+       status = hp_StartAsyncRequest(shpc_context, slot->number,
+               SHPC_ASYNC_DISABLE_SLOT, 0, slot );
+
+       //
+       // pretend async request was completed (we're not queuing slot requests)
+       //
+       hp_QuerySlotStatus( shpc_context, slot->number, &query );
+       if( status == STATUS_SUCCESS ) {
+               query.x.lu_slot_state = SLOT_DISABLE;
+               query.x.lu_pi_state = INDICATOR_BLINK;
+               query.x.lu_request_failed = HP_FALSE;
+       }
+       else {
+               query.x.lu_request_failed = HP_TRUE;
+       }
+
+       //
+       // translate the slot info to CORE values
+       //
+       translate_slot_info (hotplug_slot->info, &query);
+
+       retval = ( query.x.lu_request_failed == HP_TRUE ) ? 0 : -1;
+       return retval;
+}
+
+static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg (" %s - physical_slot = %s  state = %d",__FUNCTION__, hotplug_slot->name, status);
+
+       /*
+        *  turn light on/off
+        */
+       shpc_context = (struct shpc_context *)slot->private;
+
+       status = hp_StartAsyncRequest(shpc_context, slot->number,
+               ((status == CORE_INDICATOR_OFF) ? SHPC_ASYNC_LED_NORMAL : SHPC_ASYNC_LED_LOCATE), 10, slot);
+       hotplug_slot->info->attention_status  = status;
+
+       return retval;
+}
+
+static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+       /*
+        * get the current power status of the specific
+        * slot and store it in the *value location.
+        */
+       shpc_context = (struct shpc_context *)slot->private;
+       hp_QuerySlotStatus(shpc_context, slot->number, &query);
+       translate_slot_info (hotplug_slot->info, &query);
+       *value = hotplug_slot->info->power_status;
+
+       return retval;
+}
+
+static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+       /*
+        * get the current attention status of the specific
+        * slot and store it in the *value location.
+        */
+       shpc_context = (struct shpc_context *)slot->private;
+       hp_QuerySlotStatus(shpc_context, slot->number, &query);
+       translate_slot_info (hotplug_slot->info, &query);
+       *value = hotplug_slot->info->attention_status;
+
+       return retval;
+}
+
+static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+       /*
+        * get the current latch status of the specific
+        * slot and store it in the *value location.
+        */
+       shpc_context = (struct shpc_context *)slot->private;
+       hp_QuerySlotStatus(shpc_context, slot->number, &query);
+       translate_slot_info (hotplug_slot->info, &query);
+       *value = hotplug_slot->info->latch_status;
+
+       return retval;
+}
+
+static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       struct shpc_context *shpc_context;
+       union SLOT_STATUS_INFO query;
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg("%s - physical_slot = %s\n",__FUNCTION__, hotplug_slot->name);
+
+       /*
+        * get the current adapter status of the specific
+        * slot and store it in the *value location.
+        */
+       shpc_context = (struct shpc_context *)slot->private;
+       hp_QuerySlotStatus(shpc_context, slot->number, &query);
+       translate_slot_info (hotplug_slot->info, &query);
+       *value = hotplug_slot->info->adapter_status;
+
+       return retval;
+}
+
+static void translate_slot_info (struct hotplug_slot_info *info,
+                                union SLOT_STATUS_INFO *query)
+{
+       // power indicator
+       if( query->x.lu_pi_state == INDICATOR_OFF ) {
+               info->power_status = CORE_INDICATOR_OFF;
+       }
+       else if( query->x.lu_pi_state == INDICATOR_ON ) {
+               info->power_status = CORE_INDICATOR_ON;
+       }
+       else {
+               info->power_status = CORE_INDICATOR_BLINK;
+       }
+
+       // attention indicator
+       if( query->x.lu_ai_state == INDICATOR_OFF ) {
+               info->attention_status = CORE_INDICATOR_OFF;
+       }
+       else if( query->x.lu_ai_state == INDICATOR_ON ) {
+               info->attention_status = CORE_INDICATOR_ON;
+       }
+       else {
+               info->attention_status = CORE_INDICATOR_BLINK;
+       }
+
+       // retention latch
+       if( query->x.lu_mrl_implemented == HP_TRUE &&
+               query->x.lu_mrl_opened == HP_TRUE ) {
+               info->latch_status = CORE_LATCH_OPENED;
+       }
+       else {
+               info->latch_status = CORE_LATCH_CLOSED;
+       }
+
+       // adapter status
+       if( query->x.lu_slot_state == SLOT_ENABLE ) {
+               info->adapter_status = CORE_SLOT_ENABLED;
+       }
+       else {
+               info->adapter_status = CORE_SLOT_DISABLED;
+       }
+}
+
+static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+       int retval = 0;
+
+       if (slot == NULL)
+               return -ENODEV;
+
+       dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       err ("No hardware tests are defined for this driver");
+       retval = -ENODEV;
+
+       /* Or you can specify a test if you want to */
+       /* AMD driver does not have a test */
+       return retval;
+}
+
+#define SLOT_NAME_SIZE 10
+static void make_slot_name (struct slot *slot)
+{
+       unsigned long slot_psn;
+       struct shpc_context *shpc_context;
+
+       shpc_context = ( struct shpc_context * )slot->private;
+
+       //
+       // Get physical slot number
+       //
+       hp_Queryslot_psn(shpc_context, slot->number, &slot_psn);
+
+       snprintf (slot->hotplug_slot->name, SLOT_NAME_SIZE, "%d", (char)slot_psn);
+}
+
+static void release_slot(struct hotplug_slot *hotplug_slot)
+{
+       struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
+
+       if (slot == NULL)
+               return;
+
+       dbg ("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       kfree(slot->hotplug_slot->info);
+       kfree(slot->hotplug_slot->name);
+       kfree(slot->hotplug_slot);
+       kfree(slot);
+}
+
+static int init_slots (struct controller *ctrl, int num_slots)
+{
+       struct slot *slot;
+       struct hotplug_slot *hotplug_slot;
+       struct hotplug_slot_info *info;
+       char *name;
+       int retval = 0;
+       int i;
+       u8 value;
+
+       /*
+        * Create a structure for each slot, and register that slot
+        * with the pci_hotplug subsystem.
+        */
+       for (i = 0; i < num_slots; ++i) {
+               slot = kmalloc (sizeof (struct slot), GFP_KERNEL);
+               if (!slot)
+                       return -ENOMEM;
+               memset(slot, 0, sizeof(struct slot));
+
+               hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
+               if (!hotplug_slot) {
+                       kfree (slot);
+                       return -ENOMEM;
+               }
+               memset(hotplug_slot, 0, sizeof (struct hotplug_slot));
+               slot->hotplug_slot = hotplug_slot;
+
+               info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+               if (!info) {
+                       kfree (hotplug_slot);
+                       kfree (slot);
+                       return -ENOMEM;
+               }
+               memset(info, 0, sizeof (struct hotplug_slot_info));
+               hotplug_slot->info = info;
+
+               name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
+               if (!name) {
+                       kfree (info);
+                       kfree (hotplug_slot);
+                       kfree (slot);
+                       return -ENOMEM;
+               }
+               hotplug_slot->name = name;
+
+               slot->magic = SLOT_MAGIC;
+               slot->number = i;
+               slot->private = (void*) ctrl->shpc_context;
+
+               hotplug_slot->private = slot;
+               hotplug_slot->release = &release_slot;
+               make_slot_name (slot);
+               hotplug_slot->ops = &skel_hotplug_slot_ops;
+
+               /*
+                * Initilize the slot info structure with some known
+                * good values.
+                */
+               get_power_status(hotplug_slot, &value);
+               info->power_status = value;
+               get_attention_status(hotplug_slot, &value);
+               info->attention_status = value;
+               get_latch_status(hotplug_slot, &value);
+               info->latch_status = value;
+               get_adapter_status(hotplug_slot, &value);
+               info->adapter_status = value;
+
+               dbg ("registering slot %d\n", i);
+               retval = pci_hp_register (slot->hotplug_slot);
+               if (retval) {
+                       err ("pci_hp_register failed with error %d\n", retval);
+                       kfree (info);
+                       kfree (name);
+                       kfree (hotplug_slot);
+                       kfree (slot);
+                       return retval;
+               }
+
+               /* add slot to our internal list */
+               list_add (&slot->slot_list, &slot_list);
+       }
+
+       return retval;
+}
+
+static int amdshpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int retval;
+       int loop;
+       u16 vendor_id;
+       u16 device_id;
+       u32 rc;
+       long status = STATUS_SUCCESS;
+       struct controller *ctrl;
+       struct shpc_context *shpc_context;
+       union SLOT_CONFIG_INFO slot_config;
+
+       rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
+       dbg( "%s-->Vendor ID: %x\n",__FUNCTION__, vendor_id);
+       if (rc || (vendor_id != PCI_VENDOR_ID_AMD)) {
+               err(msg_HPC_non_amd);
+               return -ENODEV;
+       }
+
+       rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
+       dbg( "%s-->Device ID: %x\n",__FUNCTION__, device_id);
+       if (rc || (device_id != PCI_DEVICE_ID_AMD_GOLAM_7450)) {
+               err(msg_HPC_not_amd_hp);
+               return -ENODEV;
+       }
+
+       if (vendor_id == PCI_VENDOR_ID_AMD) {
+
+               shpc_context = (struct shpc_context *)kmalloc(sizeof(struct shpc_context), GFP_KERNEL);
+               if (!shpc_context) {
+                       err("%s : out of memory\n",__FUNCTION__);
+                       return -ENOMEM;
+               }
+               memset(shpc_context, 0, sizeof(struct shpc_context));
+
+               ctrl = (struct controller *)kmalloc(sizeof(struct controller), GFP_KERNEL);
+               if (!ctrl) {
+                       err("%s : out of memory\n", __FUNCTION__);
+                       rc =  -ENOMEM;
+                       goto err_free_shpc_context;
+               }
+               memset(ctrl, 0, sizeof(struct controller));
+
+               /* Set Vendor ID, so it can be accessed later from other functions */
+               ctrl->vendor_id = vendor_id;
+
+       } else {
+               err(msg_HPC_not_supported);
+               return -ENODEV;
+       }
+
+       ctrl->shpc_context = shpc_context;
+       ctrl->pci_dev = pdev;
+       ctrl->interrupt = pdev->irq;
+       ctrl->device = PCI_SLOT(pdev->devfn);
+       ctrl->function = PCI_FUNC(pdev->devfn);
+
+       //
+       // the AMD hotplug bus is behind a bridge
+       //
+//     ctrl->pci_ops = pdev->subordinate->ops;
+       ctrl->pci_bus = pdev->subordinate;
+       ctrl->bus = pdev->subordinate->number;
+
+       dbg( "%s-->bus = %d   device = %d   function = %d\n",__FUNCTION__, ctrl->bus, ctrl->device, ctrl->function);
+
+       info("Found PCI hot plug controller on bus %d\n", pdev->bus->number);
+       info("Checking if MMIO region available for this HP controller...\n");
+                   
+       //
+       // Get memory mapped I/O region
+       //
+       dbg( "%s-->pdev = %p\n",__FUNCTION__, pdev);
+       dbg("%s -->pci resource start %lx\n",__FUNCTION__, pci_resource_start(pdev, 0));
+       dbg("%s -->pci resource len   %lx\n",__FUNCTION__, pci_resource_len  (pdev, 0));
+       if (!request_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0), MY_NAME)) {
+               err("MMIO region not available, skipping\n");
+               rc = -ENOMEM;
+               goto err_free_ctrl;
+       }
+
+       //
+       // Get linear address to put in controller structure
+       //
+       shpc_context->mmio_base_addr = ioremap(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+       if (!shpc_context->mmio_base_addr) {
+               err("cannot remap MMIO region %lx @ %lx\n", pci_resource_len(pdev, 0), pci_resource_start(pdev, 0));
+               rc = -ENODEV;
+               goto err_free_mem_region;
+       }
+
+       dbg("%s -->shpc_context->mmio_base_addr = %p",__FUNCTION__, (unsigned long*)shpc_context->mmio_base_addr);
+
+       hp_AddDevice(shpc_context, ctrl, shpc_context->async_callback, shpc_instance++);
+
+       // Initialize controller
+       shpc_context->interrupt = pdev->irq;
+       dbg("%s -->shpc_context->interrupt = %d", __FUNCTION__,pdev->irq);
+       if (!hp_StartDevice(shpc_context)){
+               rc = -ENODEV;
+               goto err_iounmap;
+       }
+
+       //
+       // initialize this array only once
+       //
+       if (shpc_context->shpc_instance == 0 ) {
+               dbg("%s  Initialize slot lists\n",__FUNCTION__);
+               for (loop = 0; loop < 256; loop++) {
+                       amdshpc_slot_list[loop] = NULL;
+               }
+       }
+
+       if (!amdshpc_ctrl_list) {
+               amdshpc_ctrl_list = shpc_context;
+               shpc_context->next = NULL;
+       } else {
+               amdshpc_ctrl_list->next = shpc_context;
+               shpc_context->next = NULL;
+       }
+
+       if (!ctrl_list) {
+               ctrl_list = ctrl;
+               ctrl->next = NULL;
+       } else {
+               ctrl_list->next = ctrl;
+               ctrl->next = NULL;
+       }
+
+       // Map rom address so we can get the HPRT table
+       amdshpc_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
+       if (!amdshpc_rom_start) {
+               err ("Could not ioremap memory region for ROM\n");
+               retval = -EIO;;
+               iounmap(amdshpc_rom_start);
+               return retval;
+       }
+
+       //**************************************************
+       //
+       //      Save configuration headers for this and
+       //      subordinate PCI buses
+       //
+       //**************************************************
+
+       // find the physical slot number of the first hot plug slot
+       status = hp_QuerySlots(shpc_context, &slot_config);
+       // first slot on a bridged bus is always #1
+       ctrl->first_slot = 1;
+       dbg("%s  hp_QuerySlots: first_slot = %d, FDN = %d PSN_UP = %d\n",__FUNCTION__,
+                                               ctrl->first_slot, slot_config.x.lu_base_FDN, slot_config.x.lu_PSN_up);
+
+       if (rc) {
+               err(msg_initialization_err, rc);
+               goto err_iounmap;
+       }
+
+       if (!status) {
+               err(msg_initialization_err, (int)status);
+               goto err_iounmap;
+       }
+
+       // Store PCI Config Space for all devices on this bus
+       rc = amdshpc_save_config(ctrl, ctrl->bus, &slot_config);
+       if (rc) {
+               err("%s: unable to save PCI configuration data, error %d",__FUNCTION__, rc);
+               goto err_iounmap;
+       }
+
+       //
+       // Get IO, memory, and IRQ resources for new PCI devices
+       //
+       rc = amdshpc_find_available_resources(ctrl, amdshpc_rom_start);
+       if (rc) {
+               dbg("%s -->amdshpc_find_available_resources = 0x%x\n",__FUNCTION__, rc);
+               err("unable to locate PCI configuration resources for hot plug.\n");
+               goto err_iounmap;
+       }
+       
+       //
+       // set global variable num_slots
+       //
+       num_slots = shpc_context->number_of_slots;
+
+       dbg("%s   about to call init_slots()",__FUNCTION__);
+       rc = init_slots(ctrl, num_slots);
+       if (rc){
+               goto err_iounmap;
+       }
+
+       return 0;
+
+err_iounmap:
+       iounmap((void *)shpc_context->mmio_base_addr);
+err_free_mem_region:
+       release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+err_free_shpc_context:
+       kfree(shpc_context);
+err_free_ctrl:
+       kfree(ctrl);
+       return rc;
+}
+
+static void cleanup_slots (void)
+{
+       struct list_head *tmp;
+       struct list_head *next;
+       struct slot *slot;
+
+       /*
+        * Unregister all of our slots with the pci_hotplug subsystem.
+        * The memory will be freed in the release_slot() callback.
+        */
+       list_for_each_safe(tmp, next, &slot_list) {
+               slot = list_entry(tmp, struct slot, slot_list);
+               list_del(&slot->slot_list);
+               pci_hp_deregister(slot->hotplug_slot);
+       }
+
+       return;
+}
+static void unload_amdshpc(void)
+{
+       struct pci_func *next;
+       struct pci_func *TempSlot;
+       int loop;
+       struct shpc_context *shpc_context;
+       struct shpc_context *tshpc_context;
+       struct controller *ctrl;
+       struct controller *tctrl;
+       struct pci_resource *res;
+       struct pci_resource *tres;
+
+       ctrl = ctrl_list;
+
+       while (ctrl) {
+               //reclaim PCI mem
+               release_mem_region(pci_resource_start(ctrl->pci_dev, 0),
+                                  pci_resource_len(ctrl->pci_dev, 0));
+
+               res = ctrl->io_head;
+               while (res) {
+                       tres = res;
+                       res = res->next;
+                       kfree(tres);
+               }
+
+               res = ctrl->mem_head;
+               while (res) {
+                       tres = res;
+                       res = res->next;
+                       kfree(tres);
+               }
+
+               res = ctrl->p_mem_head;
+               while (res) {
+                       tres = res;
+                       res = res->next;
+                       kfree(tres);
+               }
+
+               res = ctrl->bus_head;
+               while (res) {
+                       tres = res;
+                       res = res->next;
+                       kfree(tres);
+               }
+
+               tctrl = ctrl;
+               ctrl = ctrl->next;
+               kfree(tctrl);
+       }
+
+       for (loop = 0; loop < 256; loop++) {
+               next = amdshpc_slot_list[loop];
+               while (next != NULL) {
+                       res = next->io_head;
+                       while (res) {
+                               tres = res;
+                               res = res->next;
+                               kfree(tres);
+                       }
+
+                       res = next->mem_head;
+                       while (res) {
+                               tres = res;
+                               res = res->next;
+                               kfree(tres);
+                       }
+
+                       res = next->p_mem_head;
+                       while (res) {
+                               tres = res;
+                               res = res->next;
+                               kfree(tres);
+                       }
+
+                       res = next->bus_head;
+                       while (res) {
+                               tres = res;
+                               res = res->next;
+                               kfree(tres);
+                       }
+
+                       TempSlot = next;
+                       next = next->next;
+                       kfree(TempSlot);
+               }
+       }
+
+       shpc_context = amdshpc_ctrl_list;
+
+       while(shpc_context){
+
+               dbg("%s -->shpc_context = %p",__FUNCTION__ , shpc_context);
+               dbg("%s -->kill_amdshpc() instance = %d", __FUNCTION__ ,shpc_context->shpc_instance);
+               hp_StopDevice(shpc_context);
+
+               //Free IRQ associated with hot plug device
+               free_irq(shpc_context->interrupt, shpc_context);
+
+               //Unmap the memory
+               iounmap(shpc_context->mmio_base_addr);
+
+               // free the controller memory
+               tshpc_context = shpc_context;
+               shpc_context = shpc_context->next;
+               kfree(tshpc_context);
+       }
+
+       //unmap the rom address
+       if (amdshpc_rom_start)
+               iounmap(amdshpc_rom_start);
+}
+
+
+static struct pci_device_id hpcd_pci_tbl[] = {
+       {
+       /* handle AMD Standard Hotplug controller */
+
+//    class:          ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00),
+       class:   ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
+       class_mask:     ~0,
+
+       /* AMD makes it */
+       vendor:         PCI_VENDOR_ID_AMD,
+       device:         PCI_DEVICE_ID_AMD_GOLAM_7450,
+       subvendor:      PCI_ANY_ID,
+       subdevice:      PCI_ANY_ID,
+
+       }, { /* end: all zeroes */ }
+};
+
+MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl);
+
+
+
+static struct pci_driver amdshpc_driver = {
+       name:           "pci_hotplug",
+       id_table:       hpcd_pci_tbl,
+       probe:          amdshpc_probe,
+       /* remove:      amdshpc_remove_one, */
+};
+
+
+static int __init amdshpc_init(void)
+{
+       int result;
+
+       amdshpc_debug = debug;
+       /*
+        * Do specific initialization stuff for your driver here
+        * Like initilizing your controller hardware (if any) and
+        * determining the number of slots you have in the system
+        * right now.
+        */
+
+       result = pci_module_init(&amdshpc_driver);
+       dbg("%s -->pci_module_init = %d\n",__FUNCTION__ , result);
+       if (result)
+               return result;
+
+
+       info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
+       return 0;
+}
+
+static void __exit amdshpc_exit(void)
+{
+       //
+       // Clean everything up.
+       //
+       dbg("%s -->unload_amdshpc()\n",__FUNCTION__ );
+       unload_amdshpc();
+
+       cleanup_slots();
+
+       dbg("%s -->pci_unregister_driver\n",__FUNCTION__ );
+       pci_unregister_driver(&amdshpc_driver);
+
+}
+
+module_init(amdshpc_init);
+module_exit(amdshpc_exit);
diff --git a/drivers/pci/hotplug/amdshpc_ctrl.c b/drivers/pci/hotplug/amdshpc_ctrl.c
new file mode 100644 (file)
index 0000000..52aed3b
--- /dev/null
@@ -0,0 +1,969 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_slot_thread() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+int hp_slot_thread(void* ptr)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       union SLOT_STATUS_INFO slot_status;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_slot");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context*) ptr;
+       shpc_context = (struct shpc_context*) slot_context->shpc_context;
+
+       //
+       // Insertion/Removal State Machine (loops until requested to exit)
+       //
+       do {
+               status = slot_context->slot_function( shpc_context, slot_context );
+               //
+               // Suspend?
+               //
+               if(!status) {
+                       spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+                       if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) {
+                               status = STATUS_SUCCESS;
+                       }
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+                       if(status) {
+                               dbg( "%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__,
+                                       (int)shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                               do {
+                                       interruptible_sleep_on(&slot_context->slot_event);
+                               }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) ||
+                                       (shpc_context->shpc_event_bits & REMOVE_EVENT)));
+
+                               if(shpc_context->shpc_event_bits & REMOVE_EVENT ) {
+                                       status = STATUS_UNSUCCESSFUL;
+                               }
+                               else {
+                                       dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__,
+                                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                               }
+                       }
+               }
+       } while(status);
+
+       //
+       // We're exiting, most likely due to an exit_request_event.  So, let's cleanup!
+       //
+       dbg("%s-->Slot Thread Termination: slot_id[ %d:%d ]",__FUNCTION__,
+               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Pending SW-initiated slot request?
+       //
+       if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT ) {
+               //
+               // Complete it with failure code
+               //
+               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+               slot_status.x.lu_request_failed = HP_TRUE;
+               shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       slot_context->slot_request.type,
+                       slot_status,
+                       slot_context->slot_request.request_context );
+
+               //
+               // Signal registered user EVENT
+               //
+               hp_signal_user_event( shpc_context );
+       }
+       return(status);
+}
+
+
+// ****************************************************************************
+//
+// hp_attn_led_thread() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+int
+hp_attn_led_thread(
+       void* ptr
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       union SLOT_STATUS_INFO slot_status;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_led");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context*) ptr;
+       shpc_context = (struct shpc_context*) slot_context->shpc_context;
+
+       //
+       // Attention LED State Machine (loops until requested to exit)
+       //
+       do {
+               status = slot_context->attn_led_function(shpc_context, slot_context);
+               //
+               // Suspend?
+               //
+               if(!status) {
+                       spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+                       if(shpc_context->shpc_event_bits & SUSPEND_EVENT ) {
+                               status = STATUS_SUCCESS;
+                       }
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+                       if(status) {
+                               dbg("%s-->SUSPEND: slot_id[ %d:%d ]",__FUNCTION__,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                               do {
+                                       interruptible_sleep_on(&slot_context->slot_event);
+                               }while(!((shpc_context->shpc_event_bits & RESUME_EVENT) ||
+                                       (shpc_context->shpc_event_bits & REMOVE_EVENT)));
+
+                               if(shpc_context->shpc_event_bits & REMOVE_EVENT ) {
+                                       status = STATUS_UNSUCCESSFUL;
+                               }
+                               else {
+                                       dbg("%s-->RESUME: slot_id[ %d:%d ]",__FUNCTION__,
+                                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                               }
+                       }
+               }
+       } while(status);
+
+       //
+       // We're exiting, most likely due to an exit_request_event.  So, let's cleanup!
+       //
+       dbg("%s-->LED Thread Termination: slot_id[ %d:%d ]",__FUNCTION__,
+               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Pending SW-initiated AttnLED request?
+       //
+       if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT ) {
+               //
+               // Complete it with failure code
+               //
+               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+               slot_status.x.lu_request_failed = HP_TRUE;
+               shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       slot_context->attn_led_request.type,
+                       slot_status,
+                       slot_context->attn_led_request.request_context );
+
+               //
+               // Signal registered user EVENT
+               //
+               hp_signal_user_event( shpc_context );
+       }
+       return(status);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_slot_configuration() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_get_slot_configuration(
+       struct shpc_context* shpc_context
+)
+{
+       struct slot_context* slot_context;
+       union SHPC_SLOTS_AVAILABLE1_DWREG SlotAvail1Reg;
+       union SHPC_SLOTS_AVAILABLE2_DWREG SlotAvail2Reg;
+       union SHPC_SLOT_CONFIG_DWREG SlotConfigReg;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+       enum shpc_speed_mode max_speed_mode;
+       u8 i;
+
+       //
+       // Get max number of slots available
+       //
+       SlotAvail1Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE1_REG_OFFSET);
+       SlotAvail2Reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOTS_AVAILABLE2_REG_OFFSET);
+       //
+       // Get slot configuration
+       //
+       SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+       //
+       // Get number of available slots per speed/mode
+       //
+       shpc_context->slots_enabled = 0;
+       shpc_context->number_of_slots = 0;
+       if( SlotAvail1Reg.x.N_133PCIX ) {
+               shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_133PCIX;
+               shpc_context->max_speed_mode = SHPC_BUS_PCIX_133;
+       }
+       else if( SlotAvail1Reg.x.N_100PCIX ) {
+               shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_100PCIX;
+               shpc_context->max_speed_mode = SHPC_BUS_PCIX_100;
+       }
+       else if( SlotAvail1Reg.x.N_66PCIX ) {
+               shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_66PCIX;
+               shpc_context->max_speed_mode = SHPC_BUS_PCIX_66;
+       }
+       else if( SlotAvail2Reg.x.N_66CONV ) {
+               shpc_context->number_of_slots = ( u8 )SlotAvail2Reg.x.N_66CONV;
+               shpc_context->max_speed_mode = SHPC_BUS_CONV_66;
+       }
+       else if( SlotAvail1Reg.x.N_33CONV ) {
+               shpc_context->number_of_slots = ( u8 )SlotAvail1Reg.x.N_33CONV;
+               shpc_context->max_speed_mode = SHPC_BUS_CONV_33;
+       }
+
+       if( shpc_context->number_of_slots ) {
+               //
+               // Be sure NSI field is not exceeded (this should not happen!)
+               //
+               if( shpc_context->number_of_slots > SlotConfigReg.x.NSI ) {
+                       shpc_context->number_of_slots = ( u8 )SlotConfigReg.x.NSI;
+               }
+
+               //
+               // Limit slot count to what we're prepared to support
+               //
+               if( shpc_context->number_of_slots > SHPC_MAX_NUM_SLOTS ) {
+                       shpc_context->number_of_slots = SHPC_MAX_NUM_SLOTS;
+               }
+
+               //
+               // Get current Bus speed/mode
+               //
+               shpc_context->bus_speed_mode = hp_get_bus_speed_mode( shpc_context );
+
+               //
+               // Initialize slot state based on HW disposition
+               //
+               for( i=0; i< shpc_context->number_of_slots; ++i ) {
+                       slot_context = &shpc_context->slot_context[ i ];
+
+                       //
+                       // Get Physical Slot Number (PSN-based)
+                       //
+                       if( SlotConfigReg.x.PSN_UP ) {
+                               slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN + i;
+                       }
+                       else {
+                               slot_context->slot_psn = ( u8 )SlotConfigReg.x.PSN - i;
+                       }
+
+                       //
+                       // Assign Logical Slot Number (1-based)
+                       //
+                       slot_context->slot_number = ( u8 )i+1;
+
+                       //
+                       // Get Card's speed/mode capabilities
+                       //
+                       hp_get_card_speed_mode( slot_context );
+
+                       //
+                       // Check current HW state
+                       //
+                       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+                       //
+                       // Already enabled: Card Present, MRL closed, Slot Enabled, No Power-Fault?
+                       //
+                       if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+                               ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+                               logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+                               logical_slot_reg.x.PF == SHPC_STATUS_CLEARED &&
+                               logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) {
+                               //
+                               // Treat it as a SUCCESSFUL "Slot Enabled" HW-initiated request
+                               //
+                               slot_context->slot_completion.hw_initiated = TRUE;
+                               slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+                               slot_context->slot_completion.failed = HP_FALSE;
+                               slot_context->slot_completion.request_context = NULL;
+                               slot_context->slot_completion.done = TRUE;
+
+                               //
+                               // Flag as "Slot Enabled"
+                               //
+                               ++shpc_context->slots_enabled;
+                               slot_context->slot_enabled = TRUE;
+                               slot_context->in_bus_speed_mode_contention = TRUE;
+                               if( logical_slot_reg.x.PIS == SHPC_LED_ON ) {
+                                       slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_enabled_wait_for_slot_request;
+                               }
+                               else {
+                                       slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_enabled_wait_for_led_cmd_available;
+                               }
+                       }
+                       else {
+                               //
+                               // Treat it as a SUCCESSFUL "Slot Disable" HW-initiated request
+                               //
+                               slot_context->slot_completion.hw_initiated = TRUE;
+                               slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+                               slot_context->slot_completion.failed = HP_FALSE;
+                               slot_context->slot_completion.request_context = NULL;
+                               slot_context->slot_completion.done = TRUE;
+
+                               //
+                               // Flag as "Slot Disabled"
+                               //
+                               slot_context->slot_enabled = FALSE;
+                               slot_context->in_bus_speed_mode_contention = FALSE;
+                               if( logical_slot_reg.x.S_STATE == SHPC_DISABLE_SLOT ) {
+                                       slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_at_slot_disabled_wait_for_slot_request;
+                               }
+                               else {
+                                       slot_context->slot_function = (SLOT_STATE_FUNCTION) hp_to_slot_disabled_wait_for_disable_cmd_available;
+                               }
+                       }
+
+                       //
+                       // Set Attention LED function
+                       //
+                       if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+                               //
+                               // Turn it ON
+                               //
+                               slot_context->problem_detected = TRUE;
+                               slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available;
+                       }
+                       else {
+                               //
+                               // Make sure it is turned OFF
+                               //
+                               slot_context->problem_detected = FALSE;
+                               if( logical_slot_reg.x.AIS == SHPC_LED_OFF ) {
+                                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_request;
+                               }
+                               else {
+                                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION) hp_wait_for_attn_led_back_to_normal_cmd_available;
+                               }
+                       }
+               }
+
+               //
+               // Enabled slots running at maximum speed/mode?
+               //
+               if( shpc_context->slots_enabled ) {
+                       max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode );
+
+                       //
+                       // Signal enabled slots to release the bus, then change bus speed/mode
+                       //
+                       if( shpc_context->bus_speed_mode != max_speed_mode ) {
+                               hp_signal_enabled_slots_to_rebalance_bus( shpc_context );
+                       }
+               }
+       }
+}
+
+
+// ****************************************************************************
+//
+// hp_enable_slot_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_enable_slot_interrupts(
+       struct slot_context* slot_context
+)
+{
+       struct shpc_context* shpc_context = ( struct shpc_context* )slot_context->shpc_context;
+       union SHPC_SLOT_CONFIG_DWREG SlotConfigReg;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       //
+       // Get HW implementation: Attention Button, MRL Sensor
+       //
+       SlotConfigReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+       //
+       // Attention Button: Enabled only if implemented
+       //
+       logical_slot_reg.x.AB_IM = ( SlotConfigReg.x.ABI == SHPC_STATUS_SET ) ?
+               SHPC_UNMASKED : SHPC_MASKED;
+       logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET;
+
+       //
+       // MRL Sensor: Enabled only if implemented (System Error Disabled)
+       //
+       logical_slot_reg.x.MRLS_IM = ( SlotConfigReg.x.MRLSI == SHPC_STATUS_SET ) ?
+               SHPC_UNMASKED : SHPC_MASKED;
+       logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED;
+       logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET;
+
+       //
+       // Card Presence: Enabled
+       //
+       logical_slot_reg.x.CP_IM = SHPC_UNMASKED;
+       logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET;
+
+       //
+       // Isolated Power-Fault: Enabled
+       //
+       logical_slot_reg.x.IPF_IM = SHPC_UNMASKED;
+       logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET;
+
+       //
+       // Connected Power-Fault: Enabled (System Error Disabled)
+       //
+       logical_slot_reg.x.CPF_IM = SHPC_UNMASKED;
+       logical_slot_reg.x.CPF_SERRM = SHPC_MASKED;
+       logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET;
+
+       //
+       // Update Mask and Status bits
+       //
+       writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+}
+
+
+// ****************************************************************************
+//
+// hp_disable_slot_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_disable_slot_interrupts(
+       struct slot_context* slot_context
+)
+{
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       //
+       // Get HW implementation: Attention Button, MRL Sensor
+       //
+       logical_slot_reg.AsDWord = readl(slot_context->logical_slot_addr);
+
+       //
+       // Attention Button: Disabled
+       //
+       logical_slot_reg.x.AB_IM = SHPC_MASKED;
+       logical_slot_reg.x.ABP_STS = SHPC_STATUS_SET;
+
+       //
+       // MRL Sensor: Disabled
+       //
+       logical_slot_reg.x.MRLS_IM = SHPC_MASKED;
+       logical_slot_reg.x.MRLS_SERRM = SHPC_MASKED;
+       logical_slot_reg.x.MRLSC_STS = SHPC_STATUS_SET;
+
+       //
+       // Card Presence: Disabled
+       //
+       logical_slot_reg.x.CP_IM = SHPC_MASKED;
+       logical_slot_reg.x.CPC_STS = SHPC_STATUS_SET;
+
+       //
+       // Isolated Power-Fault: Disabled
+       //
+       logical_slot_reg.x.IPF_IM = SHPC_MASKED;
+       logical_slot_reg.x.IPF_STS = SHPC_STATUS_SET;
+
+       //
+       // Connected Power-Fault: Enabled (System Error Disabled)
+       //
+       logical_slot_reg.x.CPF_IM = SHPC_MASKED;
+       logical_slot_reg.x.CPF_SERRM = SHPC_MASKED;
+       logical_slot_reg.x.CPF_STS = SHPC_STATUS_SET;
+
+       //
+       // Update Mask and Status bits
+       //
+       writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+}
+
+
+// ****************************************************************************
+//
+// hp_enable_global_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_enable_global_interrupts(
+       struct shpc_context* shpc_context
+)
+{
+       union SHPC_SERR_INT_DWREG SerrIntReg;
+
+       SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+
+       //
+       // Arbiter timeout: System Error Disabled
+       //
+       SerrIntReg.x.A_SERRM = SHPC_MASKED;
+       SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET;
+
+       //
+       // Command Completion: Enabled
+       //
+       SerrIntReg.x.CC_IM = SHPC_UNMASKED;
+       SerrIntReg.x.CC_STS = SHPC_STATUS_SET;
+
+       //
+       // Global: Interrputs Enabled, System Error Disabled
+       //
+       SerrIntReg.x.GIM = SHPC_UNMASKED;
+       SerrIntReg.x.GSERRM = SHPC_MASKED;
+
+       //
+       // Update Mask and Status bits
+       //
+       writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+}
+
+
+// ****************************************************************************
+//
+// hp_disable_global_interrupts() @ Any IRQL
+//
+// ****************************************************************************
+void
+hp_disable_global_interrupts(
+       struct shpc_context* shpc_context
+)
+{
+       union SHPC_SERR_INT_DWREG SerrIntReg;
+
+       SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+       //
+       // Arbiter timeout: System Error Disabled
+       //
+       SerrIntReg.x.A_SERRM = SHPC_MASKED;
+       SerrIntReg.x.ATOUT_STS = SHPC_STATUS_SET;
+
+       //
+       // Command Completion: Disabled
+       //
+       SerrIntReg.x.CC_IM = SHPC_MASKED;
+       SerrIntReg.x.CC_STS = SHPC_STATUS_SET;
+
+       //
+       // Global: Interrputs Disabled, System Error Disabled
+       //
+       SerrIntReg.x.GIM = SHPC_MASKED;
+       SerrIntReg.x.GSERRM = SHPC_MASKED;
+
+       //
+       // Update Mask and Status bits
+       //
+       writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_card_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_card_speed_mode(
+       struct slot_context* slot_context
+)
+{
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       //
+       // Slot powered-up?
+       //
+       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+       if(( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) &&
+               ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY ||
+               logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT )) {
+               //
+               // Get Card's maximum speed/mode
+               //
+               if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_133 ) {
+                       slot_context->card_speed_mode = SHPC_BUS_PCIX_133;
+               }
+               else if( logical_slot_reg.x.PCIX_CAP == SHPC_SLOT_PCIX_66 ) {
+                       slot_context->card_speed_mode = SHPC_BUS_PCIX_66;
+               }
+               else if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) {
+                       slot_context->card_speed_mode = SHPC_BUS_CONV_66;
+               }
+               else {
+                       slot_context->card_speed_mode = SHPC_BUS_CONV_33;
+               }
+
+               //
+               // Get Card's PCI-66 capability
+               //
+               if( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET ) {
+                       slot_context->card_pci66_capable = TRUE;
+               }
+       }
+       else {
+               //
+               // Slot is not powered-up, use PCI-33 as default
+               //
+               slot_context->card_speed_mode = SHPC_BUS_CONV_33;
+               slot_context->card_pci66_capable = FALSE;
+       }
+
+       return slot_context->card_speed_mode;
+}
+
+// ****************************************************************************
+//
+// hp_get_bus_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_bus_speed_mode(
+       struct shpc_context* shpc_context
+)
+{
+       union SHPC_SEC_BUS_CONFIG_DWREG bus_config_reg;
+       enum shpc_speed_mode bus_speed_mode;
+
+       bus_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SEC_BUS_CONFIG_REG_OFFSET);
+
+       bus_speed_mode = ( enum shpc_speed_mode )bus_config_reg.x.MODE;
+       if( bus_speed_mode > SHPC_BUS_PCIX_133 ) {
+               bus_speed_mode = SHPC_BUS_CONV_33;
+       }
+
+       return bus_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_speed_mode() @ Any IRQL
+//
+// ****************************************************************************
+enum mode_frequency
+hp_translate_speed_mode(
+       enum shpc_speed_mode shpc_speed_mode
+)
+{
+       enum mode_frequency translated_speed_mode;
+
+       switch( shpc_speed_mode ) {
+       case SHPC_BUS_PCIX_133:
+               translated_speed_mode = MODE_PCIX_133;
+               break;
+
+       case SHPC_BUS_PCIX_100:
+               translated_speed_mode = MODE_PCIX_100;
+               break;
+
+       case SHPC_BUS_PCIX_66:
+               translated_speed_mode = MODE_PCIX_66;
+               break;
+
+       case SHPC_BUS_CONV_66:
+               translated_speed_mode = MODE_PCI_66;
+               break;
+
+       case SHPC_BUS_CONV_33:
+       default:
+               translated_speed_mode = MODE_PCI_33;
+               break;
+       }
+
+       return translated_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_card_power() @ Any IRQL
+//
+// ****************************************************************************
+enum hp_power_requirements
+hp_translate_card_power(
+       enum shpc_card_power ShpcCardPower
+)
+{
+       enum hp_states TranslatedCardPower;
+
+       switch( ShpcCardPower ) {
+       case SHPC_CARD_PRESENT_25W:
+               TranslatedCardPower = POWER_HIGH;
+               break;
+
+       case SHPC_CARD_PRESENT_15W:
+               TranslatedCardPower = POWER_MEDIUM;
+               break;
+
+       case SHPC_CARD_PRESENT_7_5W:
+       default:
+               TranslatedCardPower = POWER_LOW;
+               break;
+       }
+
+       return TranslatedCardPower;
+}
+
+
+// ****************************************************************************
+//
+// hp_translate_indicator() @ Any IRQL
+//
+// ****************************************************************************
+enum hp_indicators
+hp_translate_indicator(
+       enum shpc_slot_led ShpcIndicator
+)
+{
+       enum hp_indicators TranslatedIndicator;
+
+       switch( ShpcIndicator ) {
+       case SHPC_LED_ON:
+               TranslatedIndicator = INDICATOR_ON;
+               break;
+
+       case SHPC_LED_BLINK:
+               TranslatedIndicator = INDICATOR_BLINK;
+               break;
+
+       case SHPC_LED_OFF:
+       default:
+               TranslatedIndicator =INDICATOR_OFF;
+               break;
+       }
+
+       return TranslatedIndicator;
+}
+
+
+// ****************************************************************************
+//
+// hp_flag_slot_as_enabled() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+u8
+hp_flag_slot_as_enabled(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       u8 SlotFlagged = FALSE;
+
+       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+       if( !slot_context->slot_enabled ) {
+               //
+               // Slot just coming on-line
+               //
+               SlotFlagged = TRUE;
+               ++shpc_context->slots_enabled;
+               slot_context->slot_enabled = TRUE;
+               hp_clear_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT);
+       }
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       return SlotFlagged;
+}
+
+
+// ****************************************************************************
+//
+// hp_flag_slot_as_disabled() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+u8
+hp_flag_slot_as_disabled(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       u8 SlotFlagged = FALSE;
+
+       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+       if( slot_context->slot_enabled ) {
+               if( --shpc_context->slots_enabled == 0 ) {
+                       //
+                       // This was the last enabled slot, signal waiting thread that bus is released,
+                       //
+                       shpc_context->bus_released = TRUE;
+                       hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT);
+               }
+               SlotFlagged = TRUE;
+               slot_context->slot_enabled = FALSE;
+       }
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       return SlotFlagged;
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_enabled_slots_to_rebalance_bus() @ <= DISPATCH_LEVEL
+//
+// Comments:
+//     Assumes shpc_spinlock is already held.
+//
+// ****************************************************************************
+u8
+hp_signal_enabled_slots_to_rebalance_bus(
+       struct shpc_context* shpc_context
+)
+{
+       struct slot_context* SlotArray[ SHPC_MAX_NUM_SLOTS ];
+       struct slot_context* Slot;
+       u8 i, j, n;
+
+       //
+       // Initialize array of slot pointers
+       //
+       n = shpc_context->number_of_slots;
+       for( i=0, j=0; i<n; ++i ) {
+               Slot = &shpc_context->slot_context[ i ];
+               if( Slot->slot_enabled ) {
+                       SlotArray[ j++ ] = Slot;
+               }
+       }
+       //
+       // Found slots enabled?
+       //
+       if( j ) {
+               //
+               // Bubble-sort enabled slots in order of increasing card speed/mode
+               //
+               n = j;
+               for( i=0; i<n-1; i++ ) {
+                       for( j=0; j<n-1-i; j++ ) {
+                               if( SlotArray[ j+1 ]->card_speed_mode < SlotArray[ j ]->card_speed_mode ) {
+                                       Slot = SlotArray[ j ];
+                                       SlotArray[ j ] = SlotArray[ j+1 ];
+                                       SlotArray[ j+1 ] = Slot;
+                               }
+                       }
+               }
+               //
+               // Signal enabled slots in sorted order as an attempt to re-enable slower cards first
+               //
+               hp_set_shpc_event_bit(shpc_context, BUS_REBALANCE_EVENT);
+               for( i=0; i<n; i++ ) {
+                       wake_up_interruptible( &SlotArray[ i ]->slot_event);
+               }
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+// ****************************************************************************
+//
+// hp_get_max_speed_mode() @ <= DISPATCH_LEVEL
+//
+// Comments:
+//     Assumes shpc_spinlock is already held.
+//
+// ****************************************************************************
+enum shpc_speed_mode
+hp_get_max_speed_mode(
+       struct shpc_context* shpc_context,
+       enum shpc_speed_mode From_speed_mode
+)
+{
+       struct slot_context* slot_context;
+       enum shpc_speed_mode max_speed_mode;
+       u8 i;
+
+       max_speed_mode = From_speed_mode;
+       for( i=0; i< shpc_context->number_of_slots; ++i ) {
+               slot_context = &shpc_context->slot_context[ i ];
+               if( slot_context->in_bus_speed_mode_contention &&
+                       slot_context->card_speed_mode < max_speed_mode ) {
+                       //
+                       // Can only go as fast as the slowest card
+                       //
+                       max_speed_mode = slot_context->card_speed_mode;
+               }
+       }
+
+       //
+       // Make sure all cards support conventional PCI-66 speed/mode
+       //
+       if( max_speed_mode == SHPC_BUS_CONV_66 ) {
+               for( i=0; i< shpc_context->number_of_slots; ++i ) {
+                       slot_context = &shpc_context->slot_context[ i ];
+                       if( slot_context->in_bus_speed_mode_contention &&
+                               !slot_context->card_pci66_capable ) {
+                               //
+                               // Fall back to slower common denominator
+                               //
+                               max_speed_mode = SHPC_BUS_CONV_33;
+                       }
+               }
+       }
+
+       return max_speed_mode;
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_user_event() @ <= DISPATCH_LEVEL
+//
+// ****************************************************************************
+void hp_signal_user_event(struct shpc_context* shpc_context)
+{
+       unsigned long           old_irq_flags;
+return;
+       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+       if( shpc_context->user_event_pointer ) {
+               wake_up_interruptible( shpc_context->user_event_pointer);
+       }
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+}
+
+
+// ****************************************************************************
+//
+// hp_signal_user_event_at_dpc_level() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void hp_signal_user_event_at_dpc_level(struct shpc_context* shpc_context)
+{
+return;
+       spin_lock_bh( &shpc_context->shpc_spinlock );
+       if( shpc_context->user_event_pointer ) {
+               wake_up_interruptible( shpc_context->user_event_pointer);
+       }
+       spin_unlock_bh( &shpc_context->shpc_spinlock );
+}
diff --git a/drivers/pci/hotplug/amdshpc_ddi.c b/drivers/pci/hotplug/amdshpc_ddi.c
new file mode 100644 (file)
index 0000000..21c64fe
--- /dev/null
@@ -0,0 +1,1660 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+static unsigned long async_callback (void* driver_context,
+                                        u8 slot_number,
+                                        enum shpc_async_request async_request,
+                                        union SLOT_STATUS_INFO slot_tatus,
+                                        void* request_context );
+
+// ****************************************************************************
+//
+// hp_AddDevice() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data (per hardware-instance).
+//     driver_context - Caller provided pointer to be returned upon completion.
+//     Callback - Caller provided function to be called upon completion of async requests.
+//  shpc_instance - Zero-based hardware instance.
+//
+// Return Value
+//  Status returned by any system calls made within hp_AddDevice().
+//
+// ****************************************************************************
+long
+hp_AddDevice(
+       struct shpc_context* shpc_context,
+       void* driver_context,
+       SHPC_ASYNC_CALLBACK Callback,
+       unsigned long shpc_instance
+       )
+
+{
+       struct slot_context* slot_context;
+       u8 i;
+       DECLARE_TASKLET(mrl_sensor_dpc0, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[0] );
+       DECLARE_TASKLET(attn_button_dpc0, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[0]);
+       DECLARE_TASKLET(card_presence_dpc0, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[0]);
+       DECLARE_TASKLET(isolated_power_fault_dpc0, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]);
+       DECLARE_TASKLET(connected_power_fault_dpc0, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[0]);
+
+       DECLARE_TASKLET(mrl_sensor_dpc1, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[1] );
+       DECLARE_TASKLET(attn_button_dpc1, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[1]);
+       DECLARE_TASKLET(card_presence_dpc1, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[1]);
+       DECLARE_TASKLET(isolated_power_fault_dpc1, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]);
+       DECLARE_TASKLET(connected_power_fault_dpc1, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[1]);
+
+       DECLARE_TASKLET(mrl_sensor_dpc2, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[2] );
+       DECLARE_TASKLET(attn_button_dpc2, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[2]);
+       DECLARE_TASKLET(card_presence_dpc2, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[2]);
+       DECLARE_TASKLET(isolated_power_fault_dpc2, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]);
+       DECLARE_TASKLET(connected_power_fault_dpc2, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[2]);
+
+       DECLARE_TASKLET(mrl_sensor_dpc3, hp_mrl_sensor_dpc, (unsigned long) &shpc_context->slot_context[3] );
+       DECLARE_TASKLET(attn_button_dpc3, hp_attn_button_dpc, (unsigned long) &shpc_context->slot_context[3]);
+       DECLARE_TASKLET(card_presence_dpc3, hp_card_presence_dpc, (unsigned long) &shpc_context->slot_context[3]);
+       DECLARE_TASKLET(isolated_power_fault_dpc3, hp_isolated_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]);
+       DECLARE_TASKLET(connected_power_fault_dpc3, hp_connected_power_fault_dpc, (unsigned long) &shpc_context->slot_context[3]);
+
+
+       DECLARE_TASKLET(cmd_completion_dpc, hp_cmd_completion_dpc, (unsigned long) shpc_context );
+
+       //
+       // Init common resources
+       //
+       shpc_context->cmd_completion_dpc = cmd_completion_dpc;
+       shpc_context->driver_context = driver_context;
+       shpc_context->async_callback = (SHPC_ASYNC_CALLBACK)async_callback;
+       shpc_context->shpc_instance = shpc_instance;
+       shpc_context->slots_enabled = 0;
+       shpc_context->number_of_slots = 0;
+       shpc_context->at_power_device_d0 = FALSE;
+       shpc_context->bus_released = FALSE;
+       shpc_context->user_event_pointer = NULL;
+       spin_lock_init( &shpc_context->shpc_spinlock );
+       sema_init( &shpc_context->cmd_available_mutex, 1);
+       sema_init( &shpc_context->bus_available_mutex, 1);
+       sema_init( &shpc_context->shpc_event_bits_semaphore, 1);
+
+       shpc_context->shpc_event_bits=0;        // all shpc events cleared
+
+       dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+       //
+       // Init slot resources
+       //
+       for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) {
+               slot_context = &shpc_context->slot_context[ i ];
+               slot_context->shpc_context = ( void* )shpc_context;
+               slot_context->slot_number = ( u8 )i+1;
+               slot_context->slot_enabled = FALSE;
+               slot_context->in_bus_speed_mode_contention = FALSE;
+               slot_context->problem_detected = FALSE;
+               slot_context->slot_quiesced = FALSE;
+               slot_context->slot_thread = NULL;
+               slot_context->slot_function = NULL;
+               slot_context->attn_led_thread = NULL;
+               slot_context->attn_led_function = NULL;
+
+               //
+               // Slot SpinLocks and semaphores
+               //
+               spin_lock_init( &slot_context->slot_spinlock);
+               sema_init(&slot_context->slot_event_bits_semaphore, 1);
+               sema_init(&slot_context->cmd_acquire_mutex, 1);
+               sema_init(&slot_context->bus_acquire_mutex, 1);
+
+               //
+               // Slot timers
+               //
+               init_timer(&slot_context->slot_timer1);
+               init_timer(&slot_context->slot_timer2);
+               init_timer(&slot_context->slot_timer3);
+               init_timer(&slot_context->slot_timer4);
+               init_timer(&slot_context->slot_timer5);
+               init_timer(&slot_context->slot_timer6);
+               init_timer(&slot_context->slot_timer7);
+               init_timer(&slot_context->slot_timer8);
+               init_timer(&slot_context->slot_timer9);
+               init_timer(&slot_context->slot_timer10);
+               init_timer(&slot_context->led_timer1);
+               init_timer(&slot_context->led_timer2);
+               init_timer(&slot_context->led_timer3);
+               init_timer(&slot_context->led_timer4);
+
+               //
+               // Interrupt Service
+               //
+               switch (i) {
+               case 0:
+                       slot_context->attn_button_dpc           = attn_button_dpc0;
+                       slot_context->mrl_sensor_dpc            = mrl_sensor_dpc0;
+                       slot_context->card_presence_dpc         = card_presence_dpc0;
+                       slot_context->isolated_power_fault_dpc  = isolated_power_fault_dpc0;
+                       slot_context->connected_power_fault_dpc = connected_power_fault_dpc0;
+                       break;
+               case 1:
+                       slot_context->attn_button_dpc           = attn_button_dpc1;
+                       slot_context->mrl_sensor_dpc            = mrl_sensor_dpc1;
+                       slot_context->card_presence_dpc         = card_presence_dpc1;
+                       slot_context->isolated_power_fault_dpc  = isolated_power_fault_dpc1;
+                       slot_context->connected_power_fault_dpc = connected_power_fault_dpc1;
+                       break;
+               case 2:
+                       slot_context->attn_button_dpc           = attn_button_dpc2;
+                       slot_context->mrl_sensor_dpc            = mrl_sensor_dpc2;
+                       slot_context->card_presence_dpc         = card_presence_dpc2;
+                       slot_context->isolated_power_fault_dpc  = isolated_power_fault_dpc2;
+                       slot_context->connected_power_fault_dpc = connected_power_fault_dpc2;
+                       break;
+               case 3:
+                       slot_context->attn_button_dpc           = attn_button_dpc3;
+                       slot_context->mrl_sensor_dpc            = mrl_sensor_dpc3;
+                       slot_context->card_presence_dpc         = card_presence_dpc3;
+                       slot_context->isolated_power_fault_dpc  = isolated_power_fault_dpc3;
+                       slot_context->connected_power_fault_dpc = connected_power_fault_dpc3;
+                       break;
+               }
+
+
+               //
+               // Slot Events
+               //
+               slot_context->slot_event_bits=0;        // all slot events cleared
+
+               dbg("%s -->Init slot wait queues",__FUNCTION__ );
+
+               init_waitqueue_head(&slot_context->slot_event);
+               init_waitqueue_head(&slot_context->led_cmd_acquire_event);
+               init_waitqueue_head(&slot_context->led_cmd_release_event);
+               init_waitqueue_head(&slot_context->cmd_acquire_event);
+               init_waitqueue_head(&slot_context->cmd_release_event);
+               init_waitqueue_head(&slot_context->bus_acquire_event);
+               init_waitqueue_head(&slot_context->bus_release_event);
+       }
+       return STATUS_SUCCESS;
+}
+
+
+// ****************************************************************************
+//
+// hp_StartDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_StartDevice().
+//
+//
+// ****************************************************************************
+long
+hp_StartDevice(
+       struct shpc_context* shpc_context
+       )
+{
+       struct slot_context* slot_context;
+       long status = STATUS_SUCCESS;
+       u32 *logical_slot_addr;
+       u8 i;
+       int pid;
+
+       dbg("%s -->From hp_StartDevice:  MmioBase[ %p ]",__FUNCTION__ , (unsigned long*)shpc_context->mmio_base_addr);
+
+       //
+       // Disable Global Interrupts
+       //
+       dbg("%s -->hp_disable_global_interrupts( shpc_context=%p );",__FUNCTION__ , shpc_context);
+       hp_disable_global_interrupts( shpc_context );
+
+       //
+       // Reset common resources
+       //
+       shpc_context->at_power_device_d0 = TRUE;
+       shpc_context->bus_released = FALSE;
+
+       //
+       // Reset slot resources
+       //
+       logical_slot_addr = shpc_context->mmio_base_addr + SHPC_LOGICAL_SLOT_REG_OFFSET;
+       for( i=0; i< SHPC_MAX_NUM_SLOTS; ++i ) {
+               slot_context = &shpc_context->slot_context[ i ];
+
+               //
+               // Assign Logical Slot Register Address
+               //
+               slot_context->logical_slot_addr = logical_slot_addr++;
+
+               //
+               // Disable Slot Interrupts
+               //
+               dbg("%s -->hp_disable_slot_interrupts(slot_context)=%p",__FUNCTION__ , slot_context);
+               hp_disable_slot_interrupts(slot_context);
+
+               //
+               // Reset slot flags and pointers
+               //
+               slot_context->slot_enabled = FALSE;
+               slot_context->in_bus_speed_mode_contention = FALSE;
+               slot_context->problem_detected = FALSE;
+               slot_context->slot_quiesced = FALSE;
+               slot_context->slot_thread = NULL;
+               slot_context->slot_function = NULL;
+               slot_context->attn_led_thread = NULL;
+               slot_context->attn_led_function = NULL;
+               slot_context->slot_occupied = 0;
+       }
+
+       //
+       // Get initial slot configuration: number_of_slots, slots_enabled, SlotStateFunction
+       //
+       shpc_context->slots_enabled = 0;
+       shpc_context->number_of_slots = 0;
+       hp_get_slot_configuration( shpc_context );
+       dbg("%s -->from hp_StartDevice() number_of_slots = %d", __FUNCTION__ ,shpc_context->number_of_slots);
+       if( shpc_context->number_of_slots == 0 ) {
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       //
+       //  Hook Interrupt
+       //
+       dbg("%s -->HPC interrupt = %d \n", __FUNCTION__ ,shpc_context->interrupt);
+
+       if (request_irq(shpc_context->interrupt, hp_interrupt_service, SA_SHIRQ, MY_NAME, shpc_context)) {
+               err("Can't get irq %d for the PCI hotplug controller\n", shpc_context->interrupt);
+               status = STATUS_UNSUCCESSFUL;
+               return(status);
+       }
+
+       //
+       // Set slot operation in motion
+       //
+       for( i=0; i<shpc_context->number_of_slots && status; ++i ) {
+
+               slot_context = &shpc_context->slot_context[ i ];
+
+               //
+               // Launch slot command and bus completion mutex threads
+               //
+               // get led cmd available thread
+               pid = kernel_thread(hp_get_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+               if (pid < 0) {
+                       err ("Can't start up our get_led_cmd_available_mutex thread\n");
+                       status = STATUS_UNSUCCESSFUL;
+               }
+               dbg("%s -->Our hp_get_led_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+               // get cmd available thread
+               pid = kernel_thread(hp_get_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+               if (pid < 0) {
+                       err ("Can't start up our get_cmd_available_mutex thread\n");
+                       status = STATUS_UNSUCCESSFUL;
+               }
+               dbg("%s -->Our hp_get_cmd_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+               // get bus available thread
+               pid = kernel_thread(hp_get_bus_available_mutex_thread, slot_context, CLONE_SIGHAND);
+               if (pid < 0) {
+                       err ("Can't start up our get_bus_available_mutex thread\n");
+                       status = STATUS_UNSUCCESSFUL;
+               }
+               dbg("%s \n\n\n-->Our get_bus_available_mutex thread pid = %d",__FUNCTION__ , pid);
+
+               //
+               // Launch slot thread
+               //
+               pid = kernel_thread(hp_slot_thread, slot_context, CLONE_SIGHAND);
+               if (pid < 0) {
+                       err ("Can't start up our event thread\n");
+                       status = STATUS_UNSUCCESSFUL;
+               }
+               dbg("%s -->Our slot event thread pid = %d\n",__FUNCTION__ , pid);
+
+               //
+               // Launch Attention LED Thread
+               //
+               pid = kernel_thread(hp_attn_led_thread, slot_context, CLONE_SIGHAND);
+               if (pid < 0) {
+                       err ("Can't start up our event thread\n");
+                       status = STATUS_UNSUCCESSFUL;
+               }
+               dbg("%s -->Our LED event thread pid = %d\n",__FUNCTION__ , pid);
+
+               //
+               // Enable Slot Interrupts: Attn Button, MRL Sensor, Card Presence, Power-Fault
+               //
+               if(status) {
+                       dbg("%s -->hpStartDevice() Enabling slot interrupts...",__FUNCTION__ );
+                       hp_enable_slot_interrupts( slot_context );
+               }
+       }
+
+       //
+       // Enable Global Interrupts: Command Completion
+       //
+       if(status) {
+               dbg("%s -->hpStartDevice() Enabling global interrupts...",__FUNCTION__ );
+               hp_enable_global_interrupts( shpc_context );
+       } else {
+               //
+               // Bail out, we're hosed!
+               //
+               hp_StopDevice( shpc_context );
+               status = STATUS_UNSUCCESSFUL;
+       }
+       dbg("%s -->status = %d\n",__FUNCTION__ , (u32)status);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_StopDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_StopDevice().
+//
+// Comments:
+//     The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(),
+//  after calling hp_StopDevice() for resource re-balancing or device removal.
+//
+// ****************************************************************************
+long
+hp_StopDevice(
+       struct shpc_context* shpc_context
+       )
+{
+       struct slot_context* slot_context;
+       long status = STATUS_SUCCESS;
+       unsigned long   old_irq_flags;
+       u8 i;
+
+       //
+       // Already stopped or never started ?
+       //
+       if( shpc_context->mmio_base_addr == 0 ) {
+               return STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Disable Global Interrupts
+       //
+       hp_disable_global_interrupts( shpc_context );
+
+       //
+       // Signal EXIT request to slot threads
+       //
+
+       spin_lock_irqsave(&shpc_context->shpc_spinlock, old_irq_flags);
+       hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT);
+       hp_send_event_to_all_slots(shpc_context,
+                                  RESUME_EVENT || REMOVE_EVENT || EXIT_REQUEST_EVENT);
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       for( i=0; i<SHPC_MAX_NUM_SLOTS; ++i ) {
+               slot_context = &shpc_context->slot_context[ i ];
+
+               //
+               // Disable Slot Interrupts
+               //
+               hp_disable_slot_interrupts( slot_context );
+
+               //
+               // Remove scheduled slot DPCs
+               //
+               tasklet_kill( &slot_context->attn_button_dpc );
+               tasklet_kill( &slot_context->card_presence_dpc );
+               tasklet_kill( &slot_context->isolated_power_fault_dpc );
+               tasklet_kill( &slot_context->connected_power_fault_dpc );
+
+               //
+               // Send events to kill all threads
+               //
+               //
+               // Set event bits to send to running threads
+               //
+               hp_set_shpc_event_bit(shpc_context,
+                                     (RESUME_EVENT | REMOVE_EVENT | EXIT_REQUEST_EVENT));
+
+               
+               
+               wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+               wake_up_interruptible(&slot_context->cmd_acquire_event);
+               wake_up_interruptible(&slot_context->bus_acquire_event);
+               wake_up_interruptible(&slot_context->led_cmd_release_event);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               //
+               // Reset slot pointers and flags
+               //
+               slot_context->slot_enabled = FALSE;
+               slot_context->slot_thread = NULL;
+               slot_context->slot_function = NULL;
+               slot_context->attn_led_thread = NULL;
+               slot_context->attn_led_function = NULL;
+       }
+
+       //
+       // Remove scheduled common DPC
+       //
+       tasklet_kill(&shpc_context->cmd_completion_dpc );
+
+       //
+       // Reset common resources
+       //
+       shpc_context->number_of_slots = 0;
+       shpc_context->slots_enabled = 0;
+       shpc_context->at_power_device_d0 = FALSE;
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_SuspendDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_SuspendDevice().
+//
+// Comments:
+//     hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0.
+//
+// ****************************************************************************
+long
+hp_SuspendDevice(
+       struct shpc_context* shpc_context
+       )
+{
+       long status = STATUS_SUCCESS;
+       unsigned long   old_irq_flags;
+
+       dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       if(shpc_context->mmio_base_addr &&
+               (!shpc_context->shpc_event_bits & SUSPEND_EVENT) &&
+               (!shpc_context->shpc_event_bits & REMOVE_EVENT)) {
+               hp_clear_shpc_event_bit(shpc_context, RESUME_EVENT);
+
+               hp_send_event_to_all_slots(shpc_context, SUSPEND_EVENT);
+               hp_send_event_to_all_slots(shpc_context, EXIT_REQUEST_EVENT);
+
+               shpc_context->at_power_device_d0 = FALSE;
+       }
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_ResumeDevice() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_ResumeDevice().
+//
+// Comments:
+//     hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0.
+//
+// ****************************************************************************
+long
+hp_ResumeDevice(
+       struct shpc_context* shpc_context
+       )
+{
+       long status = STATUS_SUCCESS;
+       unsigned long           old_irq_flags;
+
+       dbg("%s -->HwInstance[ %d ]", __FUNCTION__ ,shpc_context->shpc_instance );
+
+       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+       if(shpc_context->mmio_base_addr &&
+               (shpc_context->shpc_event_bits & SUSPEND_EVENT) &&
+               (!shpc_context->shpc_event_bits & REMOVE_EVENT)) {
+               hp_clear_shpc_event_bit(shpc_context, SUSPEND_EVENT);
+               hp_clear_shpc_event_bit(shpc_context, EXIT_REQUEST_EVENT);
+               hp_send_event_to_all_slots(shpc_context, RESUME_EVENT);
+               shpc_context->at_power_device_d0 = TRUE;
+       }
+
+       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_QuerySlots() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     slot_config - Caller provided storage for slots configuration info.
+//
+// Return Value
+//  Status returned by any system calls made within hp_QuerySlots().
+//
+// ****************************************************************************
+long
+hp_QuerySlots(
+       struct shpc_context* shpc_context,
+       union SLOT_CONFIG_INFO* slot_config
+       )
+{
+       long status = STATUS_SUCCESS;
+       union SHPC_SLOT_CONFIG_DWREG slot_config_reg;
+
+       dbg("%s -->HwInstance[ %d ]  Slots[ %d ]",__FUNCTION__ ,
+               shpc_context->shpc_instance, shpc_context->number_of_slots );
+
+       //
+       // Get slot configuration
+       //
+       slot_config_reg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SLOT_CONFIG_REG_OFFSET);
+
+       slot_config->AsDWord = 0;
+       slot_config->x.lu_slots_implemented = slot_config_reg.x.NSI;
+       slot_config->x.lu_base_PSN = slot_config_reg.x.PSN;
+       slot_config->x.lu_PSN_up = slot_config_reg.x.PSN_UP;
+       slot_config->x.lu_base_FDN = slot_config_reg.x.FDN;
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_QuerySlotStatus()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     slot_id - Zero-based slot number (0..n-1).
+//     Query - Pointer to Slot Status Structure
+//
+// Return Value
+//  Status returned by any system calls made within hp_QuerySlotStatus().
+//
+// ****************************************************************************
+long
+hp_QuerySlotStatus(
+       struct shpc_context* shpc_context,
+       u8 slot_id,
+       union SLOT_STATUS_INFO* Query
+       )
+{
+       struct slot_context* slot_context;
+       long status = STATUS_SUCCESS;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_id );
+
+       //
+       // Valid slot_id?
+       //
+       if( slot_id >= shpc_context->number_of_slots ) {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       else {
+               //
+               // Which slot?
+               //
+               slot_context = &shpc_context->slot_context[ slot_id ];
+
+               //
+               // Get Max Speed/Mode from common context
+               //
+               Query->x.lu_max_bus_mode_freq = hp_translate_speed_mode( shpc_context->max_speed_mode );
+
+               //
+               // Get Bus Speed/Mode from HW
+               //
+               Query->x.lu_bus_mode_freq = hp_translate_speed_mode( hp_get_bus_speed_mode( shpc_context ));
+
+               //
+               // Get Card Speed/Mode from HW
+               //
+               Query->x.lu_card_mode_freq_cap = hp_translate_speed_mode( hp_get_card_speed_mode( slot_context ));
+
+               //
+               // Get current slot info from HW
+               //
+               logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+               //
+               // Card Present?
+               //
+               Query->x.lu_card_present = ( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) ?
+                       HP_TRUE : HP_FALSE;
+
+               //
+               // Get Card PCI-66 capability
+               //
+               Query->x.lu_card_pci66_capable = (( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) &&
+                       ( logical_slot_reg.x.S_STATE == SHPC_POWER_ONLY || logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) &&
+                       ( logical_slot_reg.x.M66_CAP == SHPC_STATUS_SET )) ?
+                       HP_TRUE : HP_FALSE;
+
+               //
+               // Power-Fault?
+               //
+               Query->x.lu_power_fault = ( logical_slot_reg.x.PF == SHPC_STATUS_SET ) ?
+                       HP_TRUE : HP_FALSE;
+
+               //
+               //  Card Power Requirements
+               //
+               Query->x.lu_card_power = hp_translate_card_power( logical_slot_reg.x.PRSNT1_2 );
+
+               //
+               //  Attention Indicator
+               //
+               Query->x.lu_ai_state = hp_translate_indicator( logical_slot_reg.x.AIS );
+
+               //
+               //  Power Indicator
+               //
+               Query->x.lu_pi_state = hp_translate_indicator( logical_slot_reg.x.PIS );
+
+               //
+               // MRL Implemented?
+               //
+               Query->x.lu_mrl_implemented = ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED ) ?
+                       HP_TRUE : HP_FALSE;
+
+               //
+               // MRL Opened?
+               //
+               Query->x.lu_mrl_opened = (( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) &&
+                       ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) ? HP_TRUE : HP_FALSE;
+
+               //
+               // Slot State: Card Present, MRL closed, No Power-Fault, Enabled?
+               //
+               if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+                       ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+                       logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+                       logical_slot_reg.x.PF == SHPC_STATUS_CLEARED &&
+                       logical_slot_reg.x.S_STATE == SHPC_ENABLE_SLOT ) {
+                       Query->x.lu_slot_state = SLOT_ENABLE;
+               }
+               else {
+                       Query->x.lu_slot_state = SLOT_DISABLE;
+               }
+
+               //
+               // OK, it's all there!
+               //
+               Query->x.lu_reserved1 = 0;
+               Query->x.lu_reserved2 = 0;
+               Query->x.lu_request_failed = HP_FALSE;
+       }
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_StartAsyncRequest()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     slot_id - Zero-based slot number (0..n-1).
+//     Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal").
+//     timeout - For AttnLED "Attn" requests (in seconds)
+//     request_context - Caller provided pointer to be returned upon completion.
+//
+// Return Value
+//     STATUS_SUCCESS if the request is accepted.  The Callback() is later invoked with a completion status.
+//  STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress),
+//
+// Comment:
+//     For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware
+//     completes (Blink) execution.  When the timeout period expires, the AttnLED is brought back to
+//  its "Normal" (On/Off) state, and the Callback() is invoked once again.
+//
+// ****************************************************************************
+long
+hp_StartAsyncRequest(
+       struct shpc_context* shpc_context,
+       u8 slot_id,
+       enum shpc_async_request request,
+       u32 timeout,
+       void* request_context
+       )
+{
+       unsigned long           old_irq_flags;
+       struct slot_context* slot_context;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]  Request[ %d ]",__FUNCTION__ ,
+               shpc_context->shpc_instance, slot_id, request );
+
+       //
+       // Valid slot_id?
+       //
+       if( slot_id >= shpc_context->number_of_slots ) {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       else {
+               slot_context = &shpc_context->slot_context[ slot_id ];
+
+               switch( request ) {
+               case SHPC_ASYNC_ENABLE_SLOT:
+                       dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__);
+               case SHPC_ASYNC_DISABLE_SLOT:
+                       dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__);
+                       //
+                       // Slot Request Pending?
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       down_interruptible(&slot_context->slot_event_bits_semaphore);
+                       down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+                       if((slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+                               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) {
+                               status = STATUS_UNSUCCESSFUL;
+                               up(&slot_context->slot_event_bits_semaphore);
+                               up(&shpc_context->shpc_event_bits_semaphore);
+                       }
+                       else {
+                               up(&slot_context->slot_event_bits_semaphore);
+                               up(&shpc_context->shpc_event_bits_semaphore);
+                               slot_context->slot_request.type = request;
+                               slot_context->slot_request.request_context = request_context;
+                               hp_send_slot_event(slot_context, SLOT_REQUEST_EVENT);
+                       }
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       break;
+
+               case SHPC_ASYNC_LED_LOCATE:
+                       dbg("%s SHPC_ASYNC_LED_LOCATE",__FUNCTION__);
+               case SHPC_ASYNC_LED_NORMAL:
+                       dbg("%s SHPC_ASYNC_LED_NORMAL",__FUNCTION__);
+                       //
+                       // AttnLED Request Pending?
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       down_interruptible(&slot_context->slot_event_bits_semaphore);
+                       down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+                       if((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+                               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)) {
+                               dbg("%s  LED--STATUS_UNSUCCESSFUL  slot_event_bits = %08X", __FUNCTION__ ,slot_context->slot_event_bits);
+                               status = STATUS_UNSUCCESSFUL;
+                               up(&slot_context->slot_event_bits_semaphore);
+                               up(&shpc_context->shpc_event_bits_semaphore);
+                       }
+                       else {
+                               up(&slot_context->slot_event_bits_semaphore);
+                               up(&shpc_context->shpc_event_bits_semaphore);
+                               slot_context->attn_led_request.type = request;
+                               slot_context->attn_led_request.timeout = timeout;
+                               slot_context->attn_led_request.request_context = request_context;
+                               hp_send_slot_event(slot_context, ATTN_LED_REQUEST_EVENT);
+                       }
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       break;
+
+               case SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY:
+                       dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY",__FUNCTION__);
+                       //
+                       // HP library notification: DevNode is quiesced
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       ++slot_context->quiesce_replies;
+                       if( slot_context->quiesce_requests &&
+                               slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+                               slot_context->slot_quiesced = TRUE;
+                               hp_send_slot_event(slot_context, QUIESCE_EVENT);
+                       }
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       break;
+
+               case SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE:
+                       dbg("%s SHPC_ASYNC_CANCEL_QUIESCE_DEVNODE",__FUNCTION__);
+                       //
+                       // HP library notification: could not quiesce DevNode
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       slot_context->slot_quiesced = FALSE;
+                       hp_send_slot_event(slot_context, QUIESCE_EVENT);
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                       //
+                       // Abort bus-rebalancing
+                       //
+                       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+                       shpc_context->bus_released = FALSE;
+                       hp_send_event_to_all_slots(shpc_context, BUS_COMPLETE_EVENT);
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+                       break;
+
+               default:
+                       status = STATUS_UNSUCCESSFUL;
+                       break;
+               }
+       }
+
+       return status;
+}
+
+// ****************************************************************************
+//
+// hp_Queryslot_psn() 
+//
+// Parameters
+//          shpc_context - Caller provided storage for SHPC context data.
+//          SlotID - Zero-based slot number (0..n-1).
+//          slot_psn - Pointer to Physical Slot Number
+//
+// Return Value
+//  STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID.
+//
+// ****************************************************************************
+long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn)
+{
+       struct slot_context *slot_context;
+       long status = STATUS_SUCCESS;
+       dbg("%s slot_ID[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_ID);
+       //
+       // Valid SlotID?
+       //
+       if( slot_ID >= shpc_context->number_of_slots || slot_psn == NULL ) {
+                               status = STATUS_UNSUCCESSFUL;
+       }
+       else {
+               //
+               // Which slot?
+               //
+               slot_context = &shpc_context->slot_context[ slot_ID ];
+               //
+               // Get slot PSN
+               //
+               *slot_psn = slot_context->slot_psn;
+       }
+       return status;
+}
+
+// ****************************************************************************
+//
+// hp_slot_timers1-10func(): Function passed to timer to send event
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_slot_timer1_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer2_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer3_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer4_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer5_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer6_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer7_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer8_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER8_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer9_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER9_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_slot_timer10_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, SLOT_TIMER10_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+// ****************************************************************************
+//
+// hp_led_timers1-4_func(): Function passed to timer to send event
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_led_timer1_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, LED_TIMER1_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer2_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer3_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, LED_TIMER3_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+void hp_led_timer4_func(unsigned long data){
+
+       struct slot_context *slot_context;
+       slot_context = (struct slot_context*) data;
+
+       dbg("%s", __FUNCTION__);
+       hp_set_slot_event_bit(slot_context, LED_TIMER4_EVENT);
+
+       wake_up_interruptible(&slot_context->slot_event);
+}
+
+// ****************************************************************************
+//
+// hp_clear_slot_event_bit():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_clear_slot_event_bit(struct slot_context * slot_context, u32 mask)
+{
+//     dbg("%s -->slot bits %08X   MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask);
+
+       down_interruptible(&slot_context->slot_event_bits_semaphore);
+       // cleareventbit
+       slot_context->slot_event_bits &= ~mask;
+       up(&slot_context->slot_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_set_slot_event_bit():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_set_slot_event_bit(struct slot_context * slot_context, u32 mask)
+{
+//     dbg("%s -->slot bits %08X   MASK=%08X",__FUNCTION__ ,slot_context->slot_event_bits, mask);
+
+       down_interruptible(&slot_context->slot_event_bits_semaphore);
+       // cleareventbit
+       slot_context->slot_event_bits |= mask;
+       up(&slot_context->slot_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_clear_shpc_event_bit():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_clear_shpc_event_bit(struct shpc_context * shpc_context, u32 mask)
+{
+       down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+       // cleareventbit
+       shpc_context->shpc_event_bits &= ~mask;
+       up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_set_shpc_event_bit():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_set_shpc_event_bit(struct shpc_context * shpc_context, u32 mask)
+{
+       down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+       // set event bit
+       shpc_context->shpc_event_bits |= mask;
+       up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_send_event_to_all_slots():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_send_event_to_all_slots(struct shpc_context *shpc_context, u32 mask)
+{
+       u8 i;
+       struct slot_context * slot_context;
+
+       down_interruptible(&shpc_context->shpc_event_bits_semaphore);
+       // set event bit
+       shpc_context->shpc_event_bits |= mask;
+       // send event to each slot thread
+       for( i=0; i<shpc_context->number_of_slots; ++i ) {
+               slot_context = &shpc_context->slot_context[ i ];
+               wake_up_interruptible(&slot_context->slot_event);
+       }
+       up(&shpc_context->shpc_event_bits_semaphore);
+}
+
+// ****************************************************************************
+//
+// hp_send_slot_event():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+void hp_send_slot_event(struct slot_context * slot_context, u32 mask)
+{
+       // set event bit
+       hp_set_slot_event_bit(slot_context, mask);
+       wake_up_interruptible( &slot_context->slot_event);
+}
+
+
+// ****************************************************************************
+//
+// hp_get_led_cmd_available_mutex_thread():  run as a thread per each slot
+//
+// Parameters
+//     slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_get_led_cmd_available_mutex_thread(void *ptr)
+{
+       long status = STATUS_SUCCESS;
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       int pid;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_getledcmd_av_mutex");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+       do {
+               interruptible_sleep_on(&slot_context->led_cmd_acquire_event);
+               if (slot_context->slot_event_bits & LED_CMD_ACQUIRE_EVENT){
+                       hp_clear_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+                       pid = kernel_thread(hp_led_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+                       if (pid < 0) {
+                               err ("Can't start up our hp_led_cmd_available_mutex_thread\n");
+                               status = STATUS_UNSUCCESSFUL;
+                               break;
+                       }
+               } else {
+                       dbg("%s terminating return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1);
+                       return 0;
+               }
+       } while (1);
+       return(status);
+}
+
+// ****************************************************************************
+//
+// hp_led_cmd_available_mutex_thread():  run as a thread per each request for cmd
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_led_cmd_available_mutex_thread(void *ptr)
+{
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_ledcmd_av_mutex");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+       //
+       // acquire the main mutex for all slots exclusion
+       //
+       down_interruptible(&shpc_context->cmd_available_mutex);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->cmd_available_mutex);
+               return 0;
+       }
+
+       //
+       // now tell our slot thread that it has the mutex
+       //
+       hp_set_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT);
+       wake_up_interruptible(&slot_context->slot_event);
+
+       //
+       // wait for our slot thread to release the mutex
+       //
+       interruptible_sleep_on(&slot_context->led_cmd_release_event);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->cmd_available_mutex);
+               return 0;
+       }
+       hp_clear_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+
+       hp_clear_shpc_event_bit(shpc_context, LED_CMD_AVAILABLE_MUTEX_EVENT);
+       up(&shpc_context->cmd_available_mutex);
+       dbg("%s cmd_available_mutex RELEASED",__FUNCTION__);
+       return(0);
+}
+
+// ****************************************************************************
+//
+// hp_get_cmd_available_mutex_thread():  run as a thread per each slot
+//
+// Parameters
+//     slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_get_cmd_available_mutex_thread(void *ptr)
+{
+       long status = STATUS_SUCCESS;
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       int pid;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_getcmd_av_mutex");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+       do {
+               interruptible_sleep_on(&slot_context->cmd_acquire_event);
+               if ((slot_context->slot_event_bits & CMD_ACQUIRE_EVENT) || 
+                   (slot_context->slot_event_bits & CMD_RELEASE_EVENT)){
+                       hp_clear_slot_event_bit(slot_context,CMD_ACQUIRE_EVENT);
+                       pid = kernel_thread(hp_cmd_available_mutex_thread, slot_context, CLONE_SIGHAND);
+                       if (pid < 0) {
+                               err ("Can't start up our hp_get_cmd_available_mutex_thread\n");
+                               status = STATUS_UNSUCCESSFUL;
+                               break;
+                       }
+               } else {
+                       dbg("%s terminating return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1);
+                       return 0;
+               }
+       } while (1);
+       return(status);
+}
+
+// ****************************************************************************
+//
+// hp_cmd_available_mutex_thread():  run as a thread per each request for cmd
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_cmd_available_mutex_thread(void *ptr)
+{
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_cmd_av_mutex");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+       //
+       // acquire the main mutex for all slots exclusion
+       //
+       down_interruptible(&shpc_context->cmd_available_mutex);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->cmd_available_mutex);
+               return 0;
+       }
+
+       //
+       // now tell our slot thread that it has the mutex
+       //
+       hp_set_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT);
+       wake_up_interruptible(&slot_context->slot_event);
+
+       //
+       // wait for our slot thread to release the mutex
+       //
+       interruptible_sleep_on(&slot_context->cmd_release_event);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->cmd_available_mutex);
+               return 0;
+       }
+       hp_clear_slot_event_bit(slot_context,CMD_RELEASE_EVENT);
+       hp_clear_shpc_event_bit(shpc_context, CMD_AVAILABLE_MUTEX_EVENT);
+       up(&shpc_context->cmd_available_mutex);
+       return(0);
+}
+
+// ****************************************************************************
+//
+// hp_get_bus_available_mutex_thread():  run as a thread per each slot
+//
+// Parameters
+//     slot_context - Caller provided storage for slot context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_get_bus_available_mutex_thread(void *ptr)
+{
+       long status = STATUS_SUCCESS;
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       int pid;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_getbus_av_mutex");
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+       do {
+               interruptible_sleep_on(&slot_context->bus_acquire_event);
+               if (slot_context->slot_event_bits & BUS_ACQUIRE_EVENT) {
+                       hp_clear_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+                       pid = kernel_thread(hp_bus_available_mutex_thread, slot_context, CLONE_SIGHAND);
+                       if (pid < 0) {
+                               err ("Can't start up our hp_get_bus_available_mutex_thread\n");
+                               status = STATUS_UNSUCCESSFUL;
+                               break;
+                       }
+               } else {
+                       dbg("%s terminating return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1);
+                       return 0;
+               }
+       } while (1);
+       return(status);
+}
+
+// ****************************************************************************
+//
+// hp_bus_available_mutex_thread():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+int hp_bus_available_mutex_thread(void *ptr)
+{
+       struct shpc_context* shpc_context;
+       struct slot_context* slot_context;
+       
+       lock_kernel ();
+       daemonize ("amdshpc_bus_av_mutex");
+       //  New name
+       unlock_kernel ();
+
+       slot_context = (struct slot_context* ) ptr;
+       shpc_context = (struct shpc_context* ) slot_context->shpc_context;
+
+       //
+       // acquire the main mutex for all slots exclusion
+       //
+       down_interruptible(&shpc_context->bus_available_mutex);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->bus_available_mutex);
+               return 0;
+       }
+
+       //
+       // now tell our slot thread that it has the mutex
+       //
+       hp_set_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT);
+       wake_up_interruptible(&slot_context->slot_event);
+
+       //
+       // wait for our slot thread to release the mutex
+       //
+       interruptible_sleep_on(&slot_context->bus_release_event);
+       if ((shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)||
+               (shpc_context->shpc_event_bits & RESUME_EVENT)||
+               (shpc_context->shpc_event_bits & REMOVE_EVENT)){
+               dbg("%s return 0  slot_id[ %d:%d ]",__FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1);
+               up(&shpc_context->bus_available_mutex);
+               return 0;
+       }
+       hp_clear_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+
+       hp_clear_shpc_event_bit(shpc_context, BUS_AVAILABLE_MUTEX_EVENT);
+       up(&shpc_context->bus_available_mutex);
+       return(0);
+}
+
+// ****************************************************************************
+//
+// call_back_routine():
+//
+// Parameters
+//     slot_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     void
+//
+// ****************************************************************************
+static unsigned long async_callback (void* driver_context,
+                                    u8 slot_id,
+                                    enum shpc_async_request async_request,
+                                    union SLOT_STATUS_INFO slot_status,
+                                    void* request_context )
+{
+       u8 phys_slot_num;
+       long rc=0;
+       struct pci_func *slot_func;
+       struct controller *ctrl;
+       struct shpc_context *shpc_context;
+       u8 bus=0;
+       u8 device=0;
+       u8 function=0;
+       unsigned long devices_still_quiescing = 0;
+
+       dbg("%s slot_id = %d",__FUNCTION__, slot_id);
+
+       ctrl = ((struct controller*) driver_context);
+       if (ctrl == NULL){
+               return -ENODEV;
+       }
+
+       shpc_context = (struct shpc_context* ) ctrl->shpc_context;
+       phys_slot_num = shpc_context->slot_context[slot_id].slot_psn;
+       
+       bus             = ctrl->bus;
+       device  = slot_id + 1;
+       
+       dbg("%s - physical_slot = %d  instance = %d",__FUNCTION__, phys_slot_num, shpc_context->shpc_instance);
+
+       switch( async_request ) {
+       case SHPC_ASYNC_ENABLE_SLOT:
+               dbg("%s SHPC_ASYNC_ENABLE_SLOT",__FUNCTION__);
+               dbg("%s slot occupied = %d",__FUNCTION__,shpc_context->slot_context[slot_id].slot_occupied);
+               if (shpc_context->slot_context[slot_id].slot_occupied == 1) {
+                       return 0;
+               }
+               //
+               // Force pci-bus re-enumeration (probe), to load drivers on behalf on enabled device(s) on this slot.
+               //
+               dbg("%s   In callback routine processing enable slot",__FUNCTION__ );
+
+               dbg("%s   CALLING amdshpc_slot_find  bus, dev, fn = %d, %d, %d\n",__FUNCTION__ ,
+                       bus, device, function);
+               slot_func = amdshpc_slot_find(bus, device, function);
+               dbg("%s  slot_func = %p ",__FUNCTION__ , slot_func);
+               if (!slot_func) {
+                       dbg("%s --> slot_func not found",__FUNCTION__ );
+                       return -ENODEV;
+               }
+
+               slot_func->bus = bus;
+               slot_func->device = device;
+               slot_func->function = function;
+               slot_func->configured = 0;
+               dbg("%s   CALLING amdshpc_process_SI(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func);
+               rc = amdshpc_process_SI(ctrl, slot_func);
+               if (!rc ) {
+                       shpc_context->slot_context[slot_id].slot_occupied = 1;
+               }
+               dbg("%s   amdshpc_process_SI returned  rc=%d",__FUNCTION__ , (int)rc);
+               break;
+
+       case SHPC_ASYNC_SURPRISE_REMOVE:
+               dbg("%s SHPC_ASYNC_SURPRISE_REMOVE",__FUNCTION__);
+               //
+               // Something went wrong with the slot (eg, power-fault), and loaded drivers must be removed.
+               //
+       case SHPC_ASYNC_QUIESCE_DEVNODE:
+               dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE",__FUNCTION__);
+               //
+               // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot.
+               // After device drivers are removed, it's OK to show messages to that effect.
+               //
+               // If device quiecing will complete at a later time (from a separate thread),
+               // then set "devices_still_quiescing" accordingly, and upon quiecing-completion,
+               // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request.
+               //
+       case SHPC_ASYNC_QUIESCE_DEVNODE_QUIET:
+               dbg("%s SHPC_ASYNC_QUIESCE_DEVNODE_QUIET",__FUNCTION__);
+               //
+               // Friendly opportunity to quiesce (remove) drivers, prior to disabling the slot.
+               // After device drivers are removed, don't show messages to that effect.
+               //
+               // If device quiecing will complete at a later time (from a separate thread),
+               // then set "devices_still_quiescing" accordingly, and upon quiecing-completion,
+               // call hp_StartAsyncRequest() with a "SHPC_ASYNC_QUIESCE_DEVNODE_NOTIFY" request.
+               //
+               dbg("%s   Processing disable slot",__FUNCTION__ );
+               
+               dbg("%s   CALLING amdshpc_slot_find  bus, dev, fn = %d, %d, %d\n",__FUNCTION__ ,
+                       bus, device, function);
+
+               slot_func = amdshpc_slot_find(bus, device, function);
+               dbg("%s  slot_func = %p ",__FUNCTION__ , slot_func);
+               if (!slot_func) {
+                       dbg("%s --> slot_func not found",__FUNCTION__ );
+                       return -ENODEV;
+               }
+
+               dbg("%s   CALLING amdshpc_process_SS(ctrl=%p slot_func=%p)\n",__FUNCTION__ , ctrl, slot_func);
+               rc = amdshpc_process_SS(ctrl, slot_func);
+               if (!rc ) {
+                       shpc_context->slot_context[slot_id].slot_occupied = 0;
+               }
+               dbg("%s   amdshpc_process_SS returned  rc=%d",__FUNCTION__ , (int)rc);
+
+               break;
+
+       case SHPC_ASYNC_DISABLE_SLOT:
+               dbg("%s SHPC_ASYNC_DISABLE_SLOT",__FUNCTION__);
+               //
+               // Just a notification, may be used to update some interested GUI application.
+               //
+               break;
+
+       default:
+               break;
+       }
+       return devices_still_quiescing;
+}
+
diff --git a/drivers/pci/hotplug/amdshpc_ddi.h b/drivers/pci/hotplug/amdshpc_ddi.h
new file mode 100644 (file)
index 0000000..3377dcb
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+* Copyright (C) 2002,2003 Advanced Micro Devices, Inc.
+* YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+* AND CONDITIONS OF THE GNU GENERAL PUBLIC
+* LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+* INCLUDED WITH THIS FILE AND POSTED AT
+* http://www.gnu.org/licenses/gpl.html
+*
+*
+* This driver is to be used as a skeleton driver to be show how to interface
+* with the pci hotplug core easily.
+*
+* Send feedback to <david.keck@amd.com>
+*
+*/
+
+
+#ifndef _SHPC_DDI_H_
+#define _SHPC_DDI_H_
+
+#include "amdshpc.h"
+//
+// SHPC Constants
+//
+#define        SHPC_MAX_NUM_SLOTS              4
+
+
+// ****************************************************************************
+//
+// hp_AddDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data (per hardware-instance).
+//     driver_context - Caller provided pointer to be returned upon completion.
+//     Callback - Caller provided function to be called upon completion of async requests.
+//  shpc_instance - Zero-based hardware instance.
+//
+// Return Value
+//  Status returned by any system calls made within hp_AddDevice().
+//
+// ****************************************************************************
+long
+       hp_AddDevice(
+                       struct shpc_context *shpc_context,
+                       void* driver_context,
+                       SHPC_ASYNC_CALLBACK Callback,
+                       unsigned long shpc_instance
+                       );
+
+
+// ****************************************************************************
+//
+// hp_StartDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//  mmio_base_addr - from u.Memory member of CmResourceTypeMemory
+//     IntVector - from u.Interrupt.Vector member of CmResourceTypeInterrupt
+//     IntMode - from Flags member of CmResourceTypeInterrupt
+//     IntShared - from ShareDisposition member of CmResourceTypeInterrupt
+//     IntAffinity - from u.Interrupt.Affinity member of CmResourceTypeInterrupt
+//
+// Return Value
+//  Status returned by any system calls made within hp_StartDevice().
+//
+// Comments:
+//     The caller is responsible for mapping mmio_base_addr, via MmMapIoSpace(),
+//     before calling hp_StartDevice().
+//
+// ****************************************************************************
+long
+       hp_StartDevice(
+                     struct shpc_context* shpc_context
+                     );
+
+
+// ****************************************************************************
+//
+// hp_StopDevice() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_StopDevice().
+//
+// Comments:
+//     The caller is responsible for unmapping mmio_base_addr, via MmUnmapIoSpace(),
+//  after calling hp_StopDevice() for resource re-balancing or device removal.
+//
+// ****************************************************************************
+long hp_StopDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_SuspendDevice()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_SuspendDevice().
+//
+// Comments:
+//     hp_SuspendDevice() must be called before transitioning away from PowerDeviceD0.
+//
+// ****************************************************************************
+long hp_SuspendDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_ResumeDevice() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//  Status returned by any system calls made within hp_ResumeDevice().
+//
+// Comments:
+//     hp_SuspendDevice() must be called after transitioning back to PowerDeviceD0.
+//
+// ****************************************************************************
+long hp_ResumeDevice(struct shpc_context *shpc_context);
+
+// ****************************************************************************
+//
+// hp_QuerySlots() 
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     SlotConfig - Caller provided storage for slots configuration info.
+//
+// Return Value
+//  Status returned by any system calls made within hp_QuerySlots().
+//
+// ****************************************************************************
+long hp_QuerySlots(struct shpc_context *shpc_context, union SLOT_CONFIG_INFO* SlotConfig);
+
+
+// ****************************************************************************
+//
+// hp_QuerySlotStatus()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     slot_id - Zero-based slot number (0..n-1).
+//     Query - Pointer to Slot Status Structure
+//
+// Return Value
+//  Status returned by any system calls made within hp_QuerySlotStatus().
+//
+// ****************************************************************************
+long hp_QuerySlotStatus(struct shpc_context *shpc_context, u8 slot_id, union SLOT_STATUS_INFO* Query);
+
+// ****************************************************************************
+//
+// hp_Queryslot_psn()
+//
+// Parameters
+//          shpc_context - Caller provided storage for SHPC context data.
+//          SlotID - Zero-based slot number (0..n-1).
+//          slot_psn - Pointer to Physical Slot Number
+//
+// Return Value
+//  STATUS_SUCCESS, or STATUS_UNSUCCESSFUL for invalid SlotID.
+//
+// ****************************************************************************
+long hp_Queryslot_psn(struct shpc_context *shpc_context, unsigned char slot_ID, unsigned long *slot_psn);
+
+// ****************************************************************************
+//
+// hp_StartAsyncRequest()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     slot_id - Zero-based slot number (0..n-1).
+//     Request - Async request: Slot "Enable/Disable", AttnLED "Attn/Normal").
+//     timeout - For AttnLED "Attn" requests (in seconds)
+//     request_context - Caller provided pointer to be returned upon completion.
+//
+// Return Value
+//     STATUS_SUCCESS if the request is accepted.  The Callback() is later invoked with a completion status.
+//  STATUS_UNSUCCESSFUL if the request is rejected (invalid parameters, or similar request in progress),
+//
+// Comment:
+//     For AttnLED "Attn" requests, the completion Callback() function is invoked as soon as the hardware
+//     completes (Blink) execution.  When the timeout period expires, the AttnLED is brought back to
+//  its "Normal" (On/Off) state, and the Callback() is invoked once again.
+//
+// ****************************************************************************
+long hp_StartAsyncRequest(
+                        struct shpc_context *shpc_context,
+                        u8 slot_id,     
+                        enum shpc_async_request Request,
+                        u32 timeout,                            
+                        void* request_context                           
+                        );
+
+
+// ****************************************************************************
+//
+// hp_RegisterUserEvent()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//     user_event_pointer - Pointer to caller's provided EVENT object.
+//
+// Return Value
+//     STATUS_SUCCESS if the request is accepted.
+//  STATUS_UNSUCCESSFUL if the request is rejected (EVENT already registered).
+//
+// ****************************************************************************
+long hp_RegisterUserEvent(
+                        struct shpc_context *shpc_context,
+                        wait_queue_head_t *user_event_pointer
+                        );
+
+
+// ****************************************************************************
+//
+// hp_UnRegisterUserEvent()
+//
+// Parameters
+//     shpc_context - Caller provided storage for SHPC context data.
+//
+// Return Value
+//     STATUS_SUCCESS if the request is accepted.
+//  STATUS_UNSUCCESSFUL if the request is rejected (EVENT not previously registered).
+//
+// ****************************************************************************
+long hp_UnRegisterUserEvent(struct shpc_context *shpc_context);
+
+#endif // _SHPC_DDI_H_
+
diff --git a/drivers/pci/hotplug/amdshpc_dsb.c b/drivers/pci/hotplug/amdshpc_dsb.c
new file mode 100644 (file)
index 0000000..b25a8d0
--- /dev/null
@@ -0,0 +1,1502 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ * 
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_slot_request()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_slot_request(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       unsigned long  DevNodes;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Slot Enabled: complete pending slot request
+       //
+       if( slot_context->slot_completion.done ) {
+               dbg("%s -->ENABLE_DONE: slot_id[ %d:%d ]  card_speed_mode[ %d+%d ]  bus_speed_mode[ %d ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       slot_context->card_speed_mode, slot_context->card_pci66_capable,
+                       shpc_context->bus_speed_mode );
+               //
+               // Call Completion Callback()
+               //
+               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+               slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+               shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       slot_context->slot_completion.type,
+                       slot_status,
+                       slot_context->slot_completion.request_context );
+
+               //
+               // Signal registered user EVENT
+               //
+               hp_signal_user_event( shpc_context );
+
+               //
+               // Clear completion flag
+               //
+               slot_context->slot_completion.done = FALSE;
+       }
+
+       //
+       // Clear Button EVENT before waiting
+       //
+       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+       hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+       //
+       // Wait for slot request
+       //
+       shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0;
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+               (slot_context->slot_event_bits & BUS_REBALANCE_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Notify unrequested removal
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+               slot_context->slot_completion.failed = HP_FALSE;
+               slot_context->slot_completion.request_context = NULL;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // bus_rebalance_event
+       //
+       else if(slot_context->slot_event_bits & BUS_REBALANCE_EVENT) {
+               //
+               // Clear Quiesced EVENT before invoking Callback()
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = 0;
+               slot_context->quiesce_replies = 0;
+               slot_context->slot_quiesced = FALSE;
+               hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Call Completion Callback() to quiesce DevNode(s)
+               //
+               slot_status.AsDWord = 0;
+               DevNodes = shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+                       slot_status,
+                       ( void* )(unsigned long)slot_context->slot_psn );
+
+               //
+               // Update request count
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = DevNodes;
+               if( slot_context->quiesce_requests == 0 ||
+                       slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+                       slot_context->slot_quiesced = TRUE;
+                       hp_send_slot_event(slot_context, QUIESCE_EVENT);
+               }
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Wait for DevNode quiescing
+               //
+               dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_bus_rebalance;
+       }
+       //
+       // attn_button_event
+       //
+       else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+               //
+               // Set completion info for HW-initiated request
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+               slot_context->slot_completion.request_context = NULL;
+
+               //
+               // Grab Command MUTEX to blink Power LED
+               //
+               dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available;
+       }
+       //
+       // SlotRequestEvent
+       //
+       else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) {
+               //
+               // Set completion info for SW-initiated request
+               //
+               slot_context->slot_completion.hw_initiated = FALSE;
+               slot_context->slot_completion.type = slot_context->slot_request.type;
+               slot_context->slot_completion.request_context = slot_context->slot_request.request_context;
+
+               //
+               // Request to disable slot?
+               //
+               if( slot_context->slot_request.type == SHPC_ASYNC_DISABLE_SLOT ) {
+                       //
+                       // Grab Command MUTEX to blink Power LED
+                       //
+                       dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_available;
+               }
+               else {
+                       //
+                       // Slot already enabled, just complete the request
+                       //
+                       dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+               }
+
+               //
+               // Allow next SW-initiated slot request while processing this one
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_bus_rebalance()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_bus_rebalance(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+       slot_context->slot_timer1.data = (unsigned long)slot_context;
+       slot_context->slot_timer1.function = hp_slot_timer1_func;
+       slot_context->slot_timer1.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+       add_timer(&slot_context->slot_timer1);
+
+       //
+       // Wait for Quiescing EVENT
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer1);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Notify unrequested removal
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+               slot_context->slot_completion.failed = HP_FALSE;
+               slot_context->slot_completion.request_context = NULL;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // quiesce_event, timeout
+       //
+       else if((slot_context->slot_event_bits & QUIESCE_EVENT) || (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT) || slot_context->slot_quiesced ) {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Grab Command MUTEX to set slot at power-only
+                       //
+                       if((slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+                               dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       }
+                       else {
+                               dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       }
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_available;
+               }
+               else {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Cancel bus re-balancing and treat it as a "Slot Enabled" request
+                       //
+                       slot_context->slot_completion.hw_initiated = TRUE;
+                       slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+                       slot_context->slot_completion.request_context = NULL;
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       dbg("%s -->BUS_REBALANCE: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_power_cmd_available() 
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_power_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       struct task_struct;
+       union SHPC_COMMAND_WREG command_reg;
+       unsigned long   old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Notify unrequested removal
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+               slot_context->slot_completion.failed = HP_FALSE;
+               slot_context->slot_completion.request_context = NULL;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Set slot to "Disable" and blink Power LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_BLINK;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_DISABLE_SLOT;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_power_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_power_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_power_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+       slot_context->slot_timer2.data = (unsigned long)slot_context;
+       slot_context->slot_timer2.function = hp_slot_timer2_func;
+       slot_context->slot_timer2.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer2);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer2);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Notify unrequested removal
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_SURPRISE_REMOVE;
+               slot_context->slot_completion.failed = HP_FALSE;
+               slot_context->slot_completion.request_context = NULL;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+               else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                               (slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Flag this slot as DISABLED
+                       //
+                       hp_flag_slot_as_disabled( shpc_context, slot_context );
+
+                       //
+                       // Call Completion Callback(): slot disabled
+                       //
+                       hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+                       slot_status.x.lu_request_failed = HP_FALSE;
+                       shpc_context->async_callback(
+                               shpc_context->driver_context,
+                               slot_context->slot_number - 1,
+                               SHPC_ASYNC_DISABLE_SLOT,
+                               slot_status,
+                               NULL );
+
+                       //
+                       // Signal registered user EVENT
+                       //
+                       hp_signal_user_event( shpc_context );
+
+                       //
+                       // Treat it as an on-going ENABLE request
+                       //
+                       slot_context->slot_completion.hw_initiated = TRUE;
+                       slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+                       slot_context->slot_completion.request_context = NULL;
+
+                       //
+                       // Grab Command MUTEX to power-on the slot
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+               }
+               else {
+                       //
+                       // Treat it as a HW-initiated DISABLE request
+                       //
+                       slot_context->slot_completion.hw_initiated = TRUE;
+                       slot_context->slot_completion.type = SHPC_ASYNC_DISABLE_SLOT;
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.request_context = NULL;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_led_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Blink Power LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_BLINK;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_led_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status =STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_led_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       union SHPC_STATUS_WREG status_reg;
+       unsigned long  DevNodes;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+       slot_context->slot_timer3.data = (unsigned long)slot_context;
+       slot_context->slot_timer3.function = hp_slot_timer3_func;
+       slot_context->slot_timer3.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer3);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                (slot_context->slot_event_bits & ALERT_EVENT) ||
+                (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) ||
+                (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer3);
+       }
+       dbg("%s -->slot bits %08X   shpc bits  %08X",__FUNCTION__ ,
+               slot_context->slot_event_bits,shpc_context->shpc_event_bits);
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+       else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Allow cancellation of operation?
+                       //
+                       if( slot_context->slot_completion.hw_initiated ) {
+                               //
+                               // Wait for 5 sec timeout
+                               //
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_timeout;
+                       }
+                       else {
+                               //
+                               // Clear Quiesced EVENT before invoking Callback()
+                               //
+                               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                               slot_context->quiesce_requests = 0;
+                               slot_context->quiesce_replies = 0;
+                               slot_context->slot_quiesced = FALSE;
+                               hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+                               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                               //
+                               // Call Completion Callback() to quiesce DevNode(s)
+                               //
+                               slot_status.AsDWord = 0;
+                               DevNodes = shpc_context->async_callback(
+                                       shpc_context->driver_context,
+                                       slot_context->slot_number - 1,
+                                       SHPC_ASYNC_QUIESCE_DEVNODE,
+                                       slot_status,
+                                       ( void* )(unsigned long)slot_context->slot_psn );
+
+                               //
+                               // Update request count
+                               //
+                               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                               slot_context->quiesce_requests = DevNodes;
+                               if( slot_context->quiesce_requests == 0 ||
+                                       slot_context->quiesce_replies >= slot_context->quiesce_requests ) {
+                                       slot_context->slot_quiesced = TRUE;
+                                       hp_send_slot_event(slot_context, QUIESCE_EVENT);
+                               }
+                               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                               //
+                               // Wait for DevNode quiescing
+                               //
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable;
+                       }
+               }
+               else {
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       unsigned long  DevNodes;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Clear Button EVENT before waiting
+       //
+       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+       down_interruptible(&slot_context->slot_event_bits_semaphore);
+       slot_context->slot_event_bits &= ~ATTN_BUTTON_EVENT;
+       up(&slot_context->slot_event_bits_semaphore);
+       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+       slot_context->slot_timer7.data = (unsigned long)slot_context;
+       slot_context->slot_timer7.function = hp_slot_timer7_func;
+       slot_context->slot_timer7.expires = jiffies + FIVE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer7);
+
+       //
+       // Wait for 5 sec timeout
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer7);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // attn_button_event
+       //
+       else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+               //
+               // Cancel request, grab Command MUTEX to Power LED back ON
+               //
+               dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+       }
+       //
+       // timeout
+       //
+       else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) {
+               //
+               // Clear Quiesced EVENT before invoking Callback()
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = 0;
+               slot_context->quiesce_replies = 0;
+               slot_context->slot_quiesced = FALSE;
+               hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Call Completion Callback() to quiesce DevNode(s)
+               //
+               slot_status.AsDWord = 0;
+               DevNodes = shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       SHPC_ASYNC_QUIESCE_DEVNODE,
+                       slot_status,
+                       ( void* )(unsigned long)slot_context->slot_psn );
+
+               //
+               // Update request count
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = DevNodes;
+               if( slot_context->quiesce_requests == 0 ||
+                       slot_context->quiesce_replies == slot_context->quiesce_requests ) {
+                       slot_context->slot_quiesced = TRUE;
+                       hp_send_slot_event(slot_context, QUIESCE_EVENT);
+               }
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Wait for DevNode quiescing
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_slot_disable()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       unsigned long  DevNodes;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+       slot_context->slot_timer4.data = (unsigned long)slot_context;
+       slot_context->slot_timer4.function = hp_slot_timer4_func;
+       slot_context->slot_timer4.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+       add_timer(&slot_context->slot_timer4);
+
+       //
+       // Wait for Quiescing EVENT
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+       //
+       // delete the timer because we got an event other than the timer
+       //
+               del_timer_sync(&slot_context->slot_timer4);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // quiesce_event
+       //
+       else if(slot_context->slot_event_bits & QUIESCE_EVENT) {
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               if( slot_context->slot_quiesced ) {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Complete succesful DISABLE request
+                       //
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       //
+                       dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+               else {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Cancel request, grab Command MUTEX to turn Power LED back ON
+                       //
+                       slot_context->slot_completion.hw_initiated = TRUE;
+                       slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+                       slot_context->slot_completion.request_context = NULL;
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+               }
+       }
+       //
+       // timeout
+       //
+       else if(slot_context->slot_event_bits & SLOT_TIMER4_EVENT) {
+               //
+               // Clear Quiesced EVENT before invoking Callback()
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = 0;
+               slot_context->quiesce_replies = 0;
+               slot_context->slot_quiesced = FALSE;
+               hp_clear_slot_event_bit(slot_context, QUIESCE_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Call Completion Callback() to quiesce DevNode(s)
+               //
+               slot_status.AsDWord = 0;
+               DevNodes = shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       SHPC_ASYNC_QUIESCE_DEVNODE_QUIET,
+                       slot_status,
+                       ( void* )(unsigned long)slot_context->slot_psn );
+
+               //
+               // Update request count
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->quiesce_requests = DevNodes;
+               if( slot_context->quiesce_requests == 0 ||
+                       slot_context->quiesce_replies == slot_context->quiesce_requests ) {
+                       slot_context->slot_quiesced = TRUE;
+                       hp_send_slot_event(slot_context, QUIESCE_EVENT);
+               }
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Wait for DevNode quiescing
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet()
+//
+// ****************************************************************************
+long
+hp_at_slot_enabled_wait_for_stop_on_slot_disable_quiet(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+       slot_context->slot_timer5.data = (unsigned long)slot_context;
+       slot_context->slot_timer5.function = hp_slot_timer5_func;
+       slot_context->slot_timer5.expires = jiffies + QUIESCE_QUIET_TIMEOUT;
+       add_timer(&slot_context->slot_timer5);
+
+       //
+       // Wait for Quiescing EVENT
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer5);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // quiesce_event, timeout
+       //
+       else if((slot_context->slot_event_bits & QUIESCE_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               if((slot_context->slot_event_bits & SLOT_TIMER5_EVENT) || slot_context->slot_quiesced ) {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Complete succesful DISABLE request
+                       //
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       //
+                       if(slot_context->slot_event_bits & SLOT_TIMER5_EVENT) {
+                               dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Quiesce timeout",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       }
+                       else {
+                               dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Slot Quiesced",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       }
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+               else {
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+                       //
+                       // Cancel request, grab Command MUTEX to turn Power LED back ON
+                       //
+                       dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_enabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_enabled_wait_for_led_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Turn Power LED back ON
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_ON;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_enabled_wait_for_led_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_enabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_enabled_wait_for_led_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+)
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , (int)shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+       slot_context->slot_timer6.data = (unsigned long)slot_context;
+       slot_context->slot_timer6.function = hp_slot_timer6_func;
+       slot_context->slot_timer6.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer6);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+       //
+       // delete the timer because we got an event other than the timer
+       //
+               del_timer_sync(&slot_context->slot_timer6);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+               else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                               (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Wait for next request
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+               }
+               else {
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
diff --git a/drivers/pci/hotplug/amdshpc_enb.c b/drivers/pci/hotplug/amdshpc_enb.c
new file mode 100644 (file)
index 0000000..f952cf2
--- /dev/null
@@ -0,0 +1,2112 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_slot_request()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_slot_request(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Slot Disabled: complete pending slot request
+       //
+       if( slot_context->slot_completion.done ) {
+               dbg("%s -->DISABLE_DONE: slot_id[ %d:%d ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+               //
+               // Call Completion Callback()
+               //
+               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+               slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+               shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       slot_context->slot_completion.type,
+                       slot_status,
+                       slot_context->slot_completion.request_context );
+
+               //
+               // Signal registered user EVENT
+               //
+               hp_signal_user_event( shpc_context );
+
+               //
+               // Clear completion flag
+               //
+               slot_context->slot_completion.done = FALSE;
+       }
+
+       //
+       // Clear Button EVENT before waiting
+       //
+       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+       hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+       //
+       // Wait for slot request
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // attn_button_event
+       //
+       if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+               //
+               // Set completion info for HW-initiated request
+               //
+               slot_context->slot_completion.hw_initiated = TRUE;
+               slot_context->slot_completion.type = SHPC_ASYNC_ENABLE_SLOT;
+               slot_context->slot_completion.request_context = NULL;
+
+               //
+               // Get current HW disposition
+               //
+               logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+               //
+               // Card present, MRL closed, and no Power-Fault?
+               //
+               if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+                       ( logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+                       logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+                       logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Clear Alert EVENT and Attention LED
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       hp_clear_slot_event_bit(slot_context, ALERT_EVENT);
+                       slot_context->problem_detected = FALSE;
+                       hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                       //
+                       // Grab Command MUTEX to blink Power LED
+                       //
+                       dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available;
+               }
+               //
+               // Alert: MRL Opened, Power-Fault?
+               //
+               else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+                       //
+                       // Update Attention LED
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       slot_context->problem_detected = TRUE;
+                       hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+                       dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1,
+                               logical_slot_reg.AsDWord & 0x3F );
+               }
+       }
+       //
+       // SlotRequestEvent
+       //
+       else if(slot_context->slot_event_bits & SLOT_REQUEST_EVENT) {
+               //
+               // Set completion info for SW-initiated request
+               //
+               slot_context->slot_completion.hw_initiated = FALSE;
+               slot_context->slot_completion.type = slot_context->slot_request.type;
+               slot_context->slot_completion.request_context = slot_context->slot_request.request_context;
+
+               //
+               // Request to enable slot?
+               //
+               if( slot_context->slot_request.type == SHPC_ASYNC_ENABLE_SLOT ) {
+                       //
+                       // Update alert events based on current HW disposition
+                       //
+                       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+                       //
+                       // Card present, MRL closed, and no Power-Fault?
+                       //
+                       if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY &&
+                               (logical_slot_reg.x.MRLS_IM == SHPC_MASKED ||
+                               logical_slot_reg.x.MRLS == SHPC_MRL_CLOSED ) &&
+                               logical_slot_reg.x.PF == SHPC_STATUS_CLEARED ) {
+                               //
+                               // Clear Alert EVENT and Attention LED
+                               //
+                               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                               slot_context->problem_detected = FALSE;
+                               hp_clear_slot_event_bit(slot_context, ALERT_EVENT);
+                               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+                               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                               //
+                               // Grab Command MUTEX to blink Power LED
+                               //
+                               dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_available;
+                       }
+                       //
+                       // Alert: MRL Opened, Power-Fault?
+                       //
+                       else if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+                               //
+                               // Update Attention LED
+                               //
+                               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                               slot_context->problem_detected = TRUE;
+                               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+                               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                               //
+                               // Fail on-going request
+                               //
+                               slot_context->slot_completion.failed = HP_TRUE;
+                               slot_context->slot_completion.done = TRUE;
+                               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                                       logical_slot_reg.AsDWord & 0x3F );
+                       }
+               }
+               else {
+                       //
+                       // Slot already disabled, just complete the request
+                       //
+                       dbg("%s -->DISABLE_REQ: slot_id[ %d:%d ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_completion.failed = HP_FALSE;
+                       slot_context->slot_completion.done = TRUE;
+               }
+
+               //
+               // Allow next SW-initiated slot request while processing this one
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               hp_clear_slot_event_bit(slot_context, SLOT_REQUEST_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_led_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_REQUEST_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Wait for next request
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       (readl( slot_context->logical_slot_addr ) & 0x3F ));
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Blink Power LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_BLINK;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord ,shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_led_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_led_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long   old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg( "%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER1_EVENT);
+       slot_context->slot_timer1.data = (unsigned long)slot_context;
+       slot_context->slot_timer1.function = hp_slot_timer1_func;
+       slot_context->slot_timer1.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer1);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER1_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer1);
+       }
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to make sure Power LED is OFF
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+       else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER1_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Allow cancellation of operation?
+                       //
+                       if( slot_context->slot_completion.hw_initiated ) {
+                               //
+                               // Wait for 5 sec timeout
+                               //
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_timeout;
+                       }
+                       else {
+                               //
+                               // Grab Command MUTEX to power-on the slot
+                               //
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+                       }
+               }
+               else {
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to make sure Power LED is OFF
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__ ,shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Clear Button EVENT before waiting
+       //
+       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+       hp_clear_slot_event_bit(slot_context, ATTN_BUTTON_EVENT);
+       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER2_EVENT);
+       slot_context->slot_timer2.data = (unsigned long)slot_context;
+       slot_context->slot_timer2.function = hp_slot_timer2_func;
+       slot_context->slot_timer2.expires = jiffies + FIVE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer2);
+
+       //
+       // Wait for 5 sec timeout
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_BUTTON_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER2_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER2_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer2);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to turn OFF Power LED
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+       }
+       //
+       // attn_button_event
+       //
+       else if(slot_context->slot_event_bits & ATTN_BUTTON_EVENT) {
+               //
+               // Cancel request, grab Command MUTEX to turn OFF Power LED
+               //
+               dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: Attn Button",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+       }
+       //
+       // timeout
+       //
+       else if(slot_context->slot_event_bits & SLOT_TIMER2_EVENT) {
+               //
+               // Grab Command MUTEX to set slot at Power-Only state
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_available;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to turn OFF Power LED
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Power-on the slot
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_POWER_ONLY;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_timeout;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER3_EVENT);
+       slot_context->slot_timer3.data = (unsigned long)slot_context;
+       slot_context->slot_timer3.function = hp_slot_timer3_func;
+       slot_context->slot_timer3.expires = jiffies + ONE_TENTH_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer3);
+
+       //
+       // Wait for 100ms completion pre-amble on RevB-Errata (while holding MUTEX)
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER3_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER3_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer3);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // timeout
+       //
+       else if(slot_context->slot_event_bits & SLOT_TIMER3_EVENT) {
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_power_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_power_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_power_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER4_EVENT);
+       slot_context->slot_timer4.data = (unsigned long)slot_context;
+       slot_context->slot_timer4.function = hp_slot_timer4_func;
+       slot_context->slot_timer4.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer4);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER4_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer4);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+       else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER4_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Grab Bus MUTEX to validate speed/mode
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_available;
+               }
+               else {
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_bus_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_bus_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       enum shpc_speed_mode max_speed_mode, bus_speed_mode;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Bus Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->bus_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // bus_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Get current HW speed/mode
+               //
+               bus_speed_mode = hp_get_bus_speed_mode( shpc_context );
+               max_speed_mode = hp_get_card_speed_mode( slot_context );
+               if( max_speed_mode > shpc_context->max_speed_mode ) {
+                       //
+                       // Can only go as fast as the controller allows
+                       //
+                       max_speed_mode = shpc_context->max_speed_mode;
+               }
+
+               //
+               // Grab global spinlock to check current speed/mode settings
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Other slots in contetion for bus speed/mode changes?
+               //
+               slot_context->in_bus_speed_mode_contention = FALSE;
+               max_speed_mode = hp_get_max_speed_mode( shpc_context, max_speed_mode );
+
+               //
+               // Make this card can handle PCI-66 speed/mode
+               //
+               if( max_speed_mode == SHPC_BUS_CONV_66 && !slot_context->card_pci66_capable ) {
+                       //
+                       // Fall back to slower common denominator
+                       //
+                       max_speed_mode = SHPC_BUS_CONV_33;
+               }
+
+               //
+               // Bus running at incompatible speed/mode?
+               //
+               if( bus_speed_mode != max_speed_mode ) {
+                       //
+                       // Other slots already enabled?
+                       //
+                       if( hp_signal_enabled_slots_to_rebalance_bus( shpc_context )) {
+                               //
+                               // Wait for enabled slots to release the bus, then change bus speed/mode
+                               //
+                               shpc_context->bus_speed_mode = max_speed_mode;
+                               shpc_context->bus_released = FALSE;
+                               hp_clear_shpc_event_bit(shpc_context, BUS_COMPLETE_EVENT);
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_bus_released;
+                       }
+                       else {
+                               //
+                               // Change bus speed/mode to enable this slot
+                               //
+                               shpc_context->bus_speed_mode = max_speed_mode;
+                               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available;
+                       }
+               }
+               else {
+                       //
+                       // Enable slot at current bus speed/mode
+                       //
+                       shpc_context->bus_speed_mode = bus_speed_mode;
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available;
+               }
+
+               //
+               // Flag this slot in contention for bus speed/mode validation
+               //
+               slot_context->in_bus_speed_mode_contention = TRUE;
+
+               //
+               // Release global spinlock since we're done checking speed/mode
+               //
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               dbg("%s -->ENABLE_IN_PROGRESS: slot_id[ %d:%d ]  card_speed_mode[ %d+%d ]  bus_speed_mode[ %d=>%d ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       slot_context->card_speed_mode, slot_context->card_pci66_capable,
+                       bus_speed_mode, shpc_context->bus_speed_mode );
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_bus_released() 
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_bus_released(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Bus Release EVENT while holding MUTEX
+       //
+       shpc_context->shpc_event_bits = 0;slot_context->slot_event_bits = 0;
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // bus_release_event
+       //
+       else if(shpc_context->shpc_event_bits & BUS_RELEASE_EVENT) {
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               if( shpc_context->bus_released ) {
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+                       //
+                       // Grab Command MUTEX to set Bus speed/mode
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_available;
+               }
+               else {
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+                       //
+                       // Release Bus MUTEX
+                       //
+                       hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+                       wake_up_interruptible(&slot_context->bus_release_event);
+
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->ENABLE_REQ: slot_id[ %d:%d ] Cancelled: BUSY DevNode",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               status =STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_speed_mode_cmd_available()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus, Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Set Bus speed/mode
+               //
+               command_reg.Bus.code = SHPC_SET_BUS_SPEED_MODE;
+               command_reg.Bus.speed_mode = shpc_context->bus_speed_mode;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_speed_mode_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus, Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               status =STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_speed_mode_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_speed_mode_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER5_EVENT);
+       slot_context->slot_timer5.data = (unsigned long)slot_context;
+       slot_context->slot_timer5.function = hp_slot_timer5_func;
+       slot_context->slot_timer5.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer5);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER5_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer5);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+       else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER5_EVENT)) {
+
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Grab Command MUTEX to enable slot
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_available;
+               }
+               else {
+                       //
+                       // Release Bus MUTEX
+                       //
+                       hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+                       wake_up_interruptible(&slot_context->bus_release_event);
+
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]",__FUNCTION__ ,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               status =STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_cmd_available() 
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+        wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus, Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]",__FUNCTION__ ,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_available_mutex
+       //
+       else if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Enable the slot
+               //
+               dbg("%s  ENABLING SLOT...",__FUNCTION__ );
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_ON;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_ENABLE_SLOT;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding Bus,Command MUTEX)
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus, Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]",__FUNCTION__ , shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER6_EVENT);
+       slot_context->slot_timer6.data = (unsigned long)slot_context;
+       slot_context->slot_timer6.function = hp_slot_timer6_func;
+       slot_context->slot_timer6.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer6);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER6_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer6);
+       }
+
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]", __FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // cmd_completion_event, timeout
+       //
+       else if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+                       (slot_context->slot_event_bits & SLOT_TIMER6_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.MRLO_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVSM_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Wait for settling time
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_enable_timeout;
+               }
+               else {
+                       //
+                       // Release Bus MUTEX
+                       //
+                       hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+                       wake_up_interruptible(&slot_context->bus_release_event);
+
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->slot_completion.failed = HP_TRUE;
+                       slot_context->slot_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to disable slot
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Bus MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->bus_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       up( &shpc_context->cmd_available_mutex);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_at_slot_disabled_wait_for_enable_timeout()
+//
+// ****************************************************************************
+long
+hp_at_slot_disabled_wait_for_enable_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER7_EVENT);
+       slot_context->slot_timer7.data = (unsigned long)slot_context;
+       slot_context->slot_timer7.function = hp_slot_timer7_func;
+       slot_context->slot_timer7.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer7);
+
+       //
+       // Wait for timeout
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ALERT_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER7_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER7_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer7);
+       }
+       //
+       // Alert: MRL Opened, Card Removed, Power-Fault?
+       //
+       if(slot_context->slot_event_bits & ALERT_EVENT) {
+
+               //
+               // Update attn_led_problem_event LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               slot_context->problem_detected = TRUE;
+               hp_send_slot_event(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+               //
+               // Fail on-going request
+               //
+               slot_context->slot_completion.failed = HP_TRUE;
+               slot_context->slot_completion.done = TRUE;
+
+               //
+               // Grab Command MUTEX to disable slot
+               //
+               dbg("%s -->ALERT: slot_id[ %d:%d ]  LSR_13:0[ %X ]", __FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1,
+                       readl( slot_context->logical_slot_addr ) & 0x3F );
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_available;
+       }
+       //
+       // timeout
+       //
+       else if(slot_context->slot_event_bits & SLOT_TIMER7_EVENT) {
+               //
+               // Flag this slot as ENABLED
+               //
+               hp_flag_slot_as_enabled( shpc_context, slot_context );
+
+               //
+               // Complete succesful ENABLE request
+               //
+               slot_context->slot_completion.failed = HP_FALSE;
+               slot_context->slot_completion.done = TRUE;
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_enabled_wait_for_slot_request;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status =STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Bus MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->bus_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_led_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_led_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Turn OFF Power LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_OFF;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for Power LED command to complete
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_led_cmd_completion;
+       }
+       else {                          // exit_request_event
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_led_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_led_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER8_EVENT);
+       slot_context->slot_timer8.data = (unsigned long)slot_context;
+       slot_context->slot_timer8.function = hp_slot_timer8_func;
+       slot_context->slot_timer8.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer8);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER8_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer8);
+       }
+
+       if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+          (slot_context->slot_event_bits & SLOT_TIMER8_EVENT)) {
+               //
+               // Wait for next request
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+
+       }else{                          // exit_request_event
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       //
+       // Release command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_disable_cmd_available()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if(shpc_context->shpc_event_bits & CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Disable slot and turn OFF Power LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_LED_OFF;
+               command_reg.Slot.attention_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_DISABLE_SLOT;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete
+               //
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_cmd_completion;
+       }
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_disable_cmd_completion()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER9_EVENT);
+       slot_context->slot_timer9.data = (unsigned long)slot_context;
+       slot_context->slot_timer9.function = hp_slot_timer9_func;
+       slot_context->slot_timer9.expires = jiffies + FIFTEEN_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer9);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & SLOT_TIMER9_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer9);
+       }
+
+       if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+          (slot_context->slot_event_bits & SLOT_TIMER9_EVENT)) {
+               //
+               // Flag this slot as DISABLED (if enabled)
+               //
+               if( hp_flag_slot_as_disabled( shpc_context, slot_context )) {
+                       //
+                       // Wait for settling time
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_disable_timeout;
+               }
+               else {
+                       //
+                       // Wait for next request
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+               }
+       }
+       else {                          // exit_request_event
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->cmd_release_event);
+
+       return status;
+}
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_DisableTimeout()
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_disable_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, SLOT_TIMER10_EVENT);
+       slot_context->slot_timer10.data = (unsigned long)slot_context;
+       slot_context->slot_timer10.function = hp_slot_timer10_func;
+       slot_context->slot_timer10.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->slot_timer10);
+
+       //
+       // Wait for timeout
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & SLOT_TIMER10_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & SLOT_TIMER10_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->slot_timer10);
+       }
+
+       //
+       // timeout
+       //
+       if(slot_context->slot_event_bits & SLOT_TIMER10_EVENT) {
+               slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_to_slot_disabled_wait_for_bus_available;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_to_slot_disabled_wait_for_bus_available() 
+//
+// ****************************************************************************
+long
+hp_to_slot_disabled_wait_for_bus_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long           old_irq_flags;
+       long status = STATUS_SUCCESS;
+       enum shpc_speed_mode max_speed_mode;
+
+       dbg("%s -->slot_id[ %d:%d ]",  __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Bus Available MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, BUS_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->bus_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if(shpc_context->shpc_event_bits & BUS_AVAILABLE_MUTEX_EVENT) {
+                       //
+                       // Grab global spinlock to check current speed/mode settings
+                       //
+                       spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+
+                       //
+                       // Flag this slot out of contetion for bus speed/mode changes
+                       //
+                       slot_context->in_bus_speed_mode_contention = FALSE;
+
+                       //
+                       // Enabled slots running at maximum speed/mode?
+                       //
+                       if( shpc_context->slots_enabled ) {
+                               max_speed_mode = hp_get_max_speed_mode( shpc_context, shpc_context->max_speed_mode );
+
+                               //
+                               // Signal enabled slots to release the bus, then change bus speed/mode
+                               //
+                               if( shpc_context->bus_speed_mode != max_speed_mode ) {
+                                       hp_signal_enabled_slots_to_rebalance_bus( shpc_context );
+                               }
+                       }
+
+                       //
+                       // Release global spinlock since we're done checking speed/mode
+                       //
+                       spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+                       //
+                       // Wait for next request on this slot
+                       //
+                       slot_context->slot_function = (SLOT_STATE_FUNCTION)hp_at_slot_disabled_wait_for_slot_request;
+       }
+       else {                          // exit_request_event
+               status = STATUS_UNSUCCESSFUL;
+       }
+
+       //
+       // Release Bus MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, BUS_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->bus_release_event);
+
+       return status;
+}
+
diff --git a/drivers/pci/hotplug/amdshpc_int.c b/drivers/pci/hotplug/amdshpc_int.c
new file mode 100644 (file)
index 0000000..de61e6f
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_interrupt_service()
+//
+// ****************************************************************************
+irqreturn_t hp_interrupt_service(int IRQ, void *v, struct pt_regs *regs)
+{
+       struct shpc_context *shpc_context = v;
+       struct slot_context *slot_context;
+       union SHPC_SERR_INT_DWREG       SerrIntReg;
+       union SHPC_INT_LOCATOR_DWREG    IntLocatorReg, SlotIndex;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+       u8 IsShpcInterrupt = FALSE;
+       u8 i;
+
+       //
+       // Device at PowerDeviceD0?
+       //
+       if( !shpc_context->at_power_device_d0 ) {
+               return IRQ_HANDLED;
+       }
+
+       //
+       // Read Interrupt Locator Register ( Pending Interrupts )
+       //
+       IntLocatorReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_INT_LOCATOR_REG_OFFSET);
+
+       //
+       // Read SERR-INT Register ( Global Mask, Command Completion )
+       //
+       SerrIntReg.AsDWord = readl(shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+
+       //
+       // Global Interrupts Disabled?
+       //
+//     if( SerrIntReg.x.GIM == SHPC_MASKED ) {
+//             return FALSE;
+//     }
+
+       //
+       // Command Completion?
+       //
+       if( IntLocatorReg.x.CC_IP ) {
+               if(( SerrIntReg.x.CC_STS == SHPC_STATUS_SET ) &&
+                       ( SerrIntReg.x.CC_IM == SHPC_UNMASKED )) {
+                       //
+                       // Schedule Dpc
+                       //
+                       IsShpcInterrupt = TRUE;
+                       tasklet_schedule(&shpc_context->cmd_completion_dpc);
+
+                       //
+                       // Clear Interrput (Write-back 1 to STS bits)
+                       //
+                       writel(SerrIntReg.AsDWord, shpc_context->mmio_base_addr + SHPC_SERR_INT_REG_OFFSET);
+               }
+       }
+
+       //
+       // Slot Interrupts?
+       //
+       if( IntLocatorReg.x.SLOT_IP ) {
+               //
+               // Walk a "1" thru each bit position (one bit per slot)
+               //
+               for( i=0, SlotIndex.x.SLOT_IP = 1; i< SHPC_MAX_NUM_SLOTS; ++i, SlotIndex.x.SLOT_IP <<= 1 ) {
+                       slot_context = &shpc_context->slot_context[ i ];
+
+                       //
+                       // Interrupt from this slot?
+                       //
+                       if( IntLocatorReg.x.SLOT_IP & SlotIndex.x.SLOT_IP ) {
+                               //
+                               //  Read Logical Slot Register
+                               //
+                               logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+
+                               //
+                               // Attention Button?
+                               //
+                               if(( logical_slot_reg.x.ABP_STS == SHPC_STATUS_SET ) &&
+                                       ( logical_slot_reg.x.AB_IM == SHPC_UNMASKED )) {
+                                       //
+                                       // Schedule Dpc
+                                       //
+                                       IsShpcInterrupt = TRUE;
+                                       tasklet_schedule(&slot_context->attn_button_dpc);
+                               }
+
+                               //
+                               // MRL Sensor?
+                               //
+                               if(( logical_slot_reg.x.MRLSC_STS == SHPC_STATUS_SET ) &&
+                                       ( logical_slot_reg.x.MRLS_IM == SHPC_UNMASKED )) {
+                                       //
+                                       // Schedule Dpc
+                                       //
+                                       IsShpcInterrupt = TRUE;
+                                       tasklet_schedule(&slot_context->mrl_sensor_dpc);
+                               }
+
+                               //
+                               // Card Presence Change?
+                               //
+                               if(( logical_slot_reg.x.CPC_STS == SHPC_STATUS_SET ) &&
+                                       ( logical_slot_reg.x.CP_IM == SHPC_UNMASKED )) {
+                                       //
+                                       // Schedule Dpc
+                                       //
+                                       IsShpcInterrupt = TRUE;
+                                       tasklet_schedule(&slot_context->card_presence_dpc);
+                               }
+
+                               //
+                               // Isolated Power Fault?
+                               //
+                               if(( logical_slot_reg.x.IPF_STS == SHPC_STATUS_SET ) &&
+                                       ( logical_slot_reg.x.IPF_IM == SHPC_UNMASKED )) {
+                                       //
+                                       // Schedule Dpc
+                                       //
+                                       IsShpcInterrupt = TRUE;
+                                       tasklet_schedule(&slot_context->isolated_power_fault_dpc);
+                               }
+
+                               //
+                               // Connected Power Fault?
+                               //
+                               if(( logical_slot_reg.x.CPF_STS == SHPC_STATUS_SET ) &&
+                                       ( logical_slot_reg.x.CPF_IM == SHPC_UNMASKED )) {
+                                       //
+                                       // Schedule Dpc
+                                       //
+                                       IsShpcInterrupt = TRUE;
+                                       tasklet_schedule(&slot_context->connected_power_fault_dpc);
+                               }
+
+                               //
+                               // Clear Interrputs for this slot (Write-back 1 to STS bits)
+                               //
+                               writel(logical_slot_reg.AsDWord, slot_context->logical_slot_addr);
+                       }
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+
+// ****************************************************************************
+//
+// hp_attn_button_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_attn_button_dpc(
+       unsigned long deferred_context  // struct slot_context*
+       )
+{
+       struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+       struct shpc_context* shpc_context = slot_context->shpc_context;
+
+       dbg("%s ->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, (slot_context->slot_number-1) );
+       //
+       // Notification Event: Attention Button pressed
+       //
+       spin_lock( &slot_context->slot_spinlock );
+       hp_send_slot_event(slot_context, ATTN_BUTTON_EVENT);
+       spin_unlock( &slot_context->slot_spinlock );
+}
+
+
+// ****************************************************************************
+//
+// hp_card_presence_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_card_presence_dpc(
+       unsigned long deferred_context  // struct slot_context*
+       )
+{
+       struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+       struct shpc_context* shpc_context = slot_context->shpc_context;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Signal registered user EVENT
+       //
+       hp_signal_user_event_at_dpc_level( shpc_context );
+
+       //
+       // Card Removed?
+       //
+       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+       if( logical_slot_reg.x.PRSNT1_2 == SHPC_SLOT_EMPTY ) {
+               //
+               // Signal Alert EVENT
+               //
+               spin_lock( &slot_context->slot_spinlock );
+               hp_send_slot_event(slot_context, ALERT_EVENT);
+               spin_unlock( &slot_context->slot_spinlock );
+       }
+}
+
+
+// ****************************************************************************
+//
+// hp_mrl_sensor_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_mrl_sensor_dpc(
+       unsigned long deferred_context  // struct slot_context*
+       )
+{
+       struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+       struct shpc_context* shpc_context = slot_context->shpc_context;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Signal registered user EVENT
+       //
+       hp_signal_user_event_at_dpc_level( shpc_context );
+
+       //
+       // MRL Sensor opened?
+       //
+       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+       if( logical_slot_reg.x.MRLS == SHPC_MRL_OPEN ) {
+               //
+               // Card Present?
+               //
+               if( logical_slot_reg.x.PRSNT1_2 != SHPC_SLOT_EMPTY ) {
+                       //
+                       // Signal Alert EVENT
+                       //
+                       spin_lock( &slot_context->slot_spinlock );
+                       hp_send_slot_event(slot_context, ALERT_EVENT);
+                       spin_unlock( &slot_context->slot_spinlock );
+               }
+       }
+       else {
+               //
+               // Power Fault detected whith MRL closed?
+               // Note: Golem A0 may not generate power-fault interrupt
+               if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+                       //
+                       // Signal Alert EVENT
+                       //
+                       spin_lock( &slot_context->slot_spinlock );
+                       hp_send_slot_event(slot_context, ALERT_EVENT);
+                       spin_unlock( &slot_context->slot_spinlock );
+               }
+       }
+}
+
+// ****************************************************************************
+//
+// isolated_power_fault_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_isolated_power_fault_dpc(
+       unsigned long deferred_context  // struct slot_context*
+       )
+{
+       struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+       struct shpc_context* shpc_context = slot_context->shpc_context;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Signal registered user EVENT
+       //
+       hp_signal_user_event_at_dpc_level( shpc_context );
+
+       //
+       // Power Fault detected?
+       //
+       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+       if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+               //
+               // Signal Alert EVENT
+               //
+               spin_lock( &slot_context->slot_spinlock );
+               hp_send_slot_event(slot_context, ALERT_EVENT);
+               spin_unlock( &slot_context->slot_spinlock );
+       }
+}
+
+
+// ****************************************************************************
+//
+// connected_power_fault_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_connected_power_fault_dpc(
+       unsigned long deferred_context  // struct slot_context*
+       )
+{
+       struct slot_context* slot_context = ( struct slot_context* )deferred_context;
+       struct shpc_context* shpc_context = slot_context->shpc_context;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Signal registered user EVENT
+       //
+       hp_signal_user_event_at_dpc_level( shpc_context );
+
+       //
+       // Power Fault detected?
+       //
+       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+       if( logical_slot_reg.x.PF == SHPC_STATUS_SET ) {
+               //
+               // Signal Alert EVENT
+               //
+               spin_lock( &slot_context->slot_spinlock );
+               hp_send_slot_event(slot_context, ALERT_EVENT);
+               spin_unlock( &slot_context->slot_spinlock );
+       }
+}
+
+
+// ****************************************************************************
+//
+// hp_cmd_completion_dpc() @ DISPATCH_LEVEL
+//
+// ****************************************************************************
+void
+hp_cmd_completion_dpc(
+       unsigned long deferred_context  // struct shpc_context*
+       )
+{
+       struct shpc_context* shpc_context = ( struct shpc_context* )deferred_context;
+
+       dbg("%s -->HwInstance[ %d ]", __FUNCTION__, shpc_context->shpc_instance );
+
+       //
+       // Notification Event: Command Completion
+       //
+       spin_lock( &shpc_context->shpc_spinlock );
+       hp_send_event_to_all_slots(shpc_context, CMD_COMPLETION_EVENT);
+       spin_unlock( &shpc_context->shpc_spinlock );
+}
diff --git a/drivers/pci/hotplug/amdshpc_led.c b/drivers/pci/hotplug/amdshpc_led.c
new file mode 100644 (file)
index 0000000..aee8583
--- /dev/null
@@ -0,0 +1,742 @@
+/*
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <david.keck@amd.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include "amdshpc_ddi.h"
+#include "amdshpc.h"
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_request() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_request(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       union SHPC_LOGICAL_SLOT_DWREG logical_slot_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // LED "Normal": complete pending request
+       //
+       if( slot_context->attn_led_completion.done ) {
+               //
+               // Call Completion Callback()
+               //
+               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+               slot_status.x.lu_request_failed = slot_context->slot_completion.failed;
+               shpc_context->async_callback(
+                       shpc_context->driver_context,
+                       slot_context->slot_number - 1,
+                       slot_context->attn_led_completion.type,
+                       slot_status,
+                       slot_context->attn_led_completion.request_context );
+               //
+               // Signal registered user EVENT
+               //
+               hp_signal_user_event( shpc_context );
+               //
+               // Clear completion flag
+               //
+               slot_context->attn_led_completion.done = FALSE;
+       }
+       //
+       // Wait for slot request
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+               (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       // AttnLEDRequestEvent
+       if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) {
+               //
+               // Set completion info for SW-initiated request
+               //
+               slot_context->attn_led_completion.hw_initiated = FALSE;
+               slot_context->attn_led_completion.type = slot_context->attn_led_request.type;
+               slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout;
+               slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context;
+
+               //
+               // Request to locate slot?
+               //
+               if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_LOCATE ) {
+                       dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                       //
+                       // Grab Command MUTEX to blink Attn LED
+                       //
+                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_available;
+               }
+               else {
+                       dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                       logical_slot_reg.AsDWord = readl( slot_context->logical_slot_addr );
+                       if( logical_slot_reg.x.AIS == SHPC_LED_ON || logical_slot_reg.x.AIS == SHPC_LED_OFF ) {
+                               //
+                               // Already "Normal", just complete the request
+                               //
+                               slot_context->attn_led_completion.failed = HP_FALSE;
+                               slot_context->attn_led_completion.done = TRUE;
+                       }
+                       //
+                       // While waitimg on a request here, the Attn LED should already be On/Off, but...
+                       //
+                       else {
+                               //
+                               // Grab Command MUTEX to set Attn LED to "Normal" (On/Off) state
+                               //
+                               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+                       }
+               }
+
+               //
+               // Allow next SW-initiated request while processing this one
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+       }
+       // attn_led_problem_event: Detected, Resolved
+       else if (slot_context->slot_event_bits & ATTN_LED_PROBLEM_EVENT){
+               //
+               // Set completion info for HW-initiated request
+               //
+               slot_context->attn_led_completion.hw_initiated = TRUE;
+               slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL;
+               slot_context->attn_led_completion.timeout = 0;
+               slot_context->attn_led_completion.request_context = NULL;
+
+               //
+               // Grab Command MUTEX to update Attention LED (On/Off)
+               //
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+       }
+       else {  // exit_request_event
+               status = STATUS_UNSUCCESSFUL;
+               dbg("%s -->EXIT_REQUEST: slot_id[ %d:%d ]", __FUNCTION__,
+                       shpc_context->shpc_instance, slot_context->slot_number-1 );
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       //down_interruptible(&slot_context->cmd_acquire_mutex);
+       hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       // cmd_available_mutex
+       if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Blink Attention LED
+               //
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.attention_led = SHPC_LED_BLINK;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_cmd_completion;
+       }
+       // exit_request_event
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->led_cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, LED_TIMER1_EVENT);
+       slot_context->led_timer1.data = (unsigned long)slot_context;
+       slot_context->led_timer1.function = hp_led_timer1_func;
+       slot_context->led_timer1.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->led_timer1);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & LED_TIMER1_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & LED_TIMER1_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->led_timer1);
+       }
+
+       if(shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) {
+               // cmd_completion_event, timeout
+               if ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) || (slot_context->slot_event_bits & LED_TIMER1_EVENT)) {
+                       //
+                       // Command completed OK?
+                       //
+                       status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+                       if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                               status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                               //
+                               // Call Completion Callback()
+                               //
+                               hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+                               slot_status.x.lu_request_failed = HP_FALSE;
+                               shpc_context->async_callback(
+                                       shpc_context->driver_context,
+                                       slot_context->slot_number - 1,
+                                       SHPC_ASYNC_LED_LOCATE,
+                                       slot_status,
+                                       slot_context->attn_led_completion.request_context );
+
+                               //
+                               // Signal registered user EVENT
+                               //
+                               hp_signal_user_event( shpc_context );
+
+                               //
+                               // Wait for specified timeout (in seconds)
+                               //
+                               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_blink_timeout;
+                       }
+                       else {
+                               //
+                               // Fail on-going request
+                               //
+                               slot_context->attn_led_completion.failed = HP_TRUE;
+                               slot_context->attn_led_completion.done = TRUE;
+
+                               //
+                               // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off)
+                               //
+                               dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]", __FUNCTION__,
+                                       shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available;
+                       }
+
+               // exit_request_event
+               }
+               else {
+                       status = STATUS_UNSUCCESSFUL;
+               }
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+       return status;
+}
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_blink_timeout() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_blink_timeout(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SLOT_STATUS_INFO slot_status;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+       slot_context->led_timer2.data = (unsigned long)slot_context;
+       slot_context->led_timer2.function = hp_led_timer2_func;
+       slot_context->led_timer2.expires = jiffies + (ONE_SEC_INCREMENT * slot_context->attn_led_completion.timeout);
+       add_timer(&slot_context->led_timer2);
+
+       //
+       // Wait for specified timeout ( in seconds )
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) ||
+               (slot_context->slot_event_bits & LED_TIMER2_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & LED_TIMER2_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               hp_clear_slot_event_bit(slot_context, LED_TIMER2_EVENT);
+               del_timer_sync(&slot_context->led_timer2);
+       }
+
+       // AttnLEDRequestEvent
+       if(slot_context->slot_event_bits & ATTN_LED_REQUEST_EVENT) {
+               //
+               // Set completion info for SW-initiated request
+               //
+               slot_context->attn_led_completion.hw_initiated = FALSE;
+               slot_context->attn_led_completion.type = slot_context->attn_led_request.type;
+               slot_context->attn_led_completion.timeout = slot_context->attn_led_request.timeout;
+               slot_context->attn_led_completion.request_context = slot_context->attn_led_request.request_context;
+
+               //
+               // Request to cancel locate?
+               //
+               if( slot_context->attn_led_request.type == SHPC_ASYNC_LED_NORMAL ) {
+                       dbg("%s -->LED_NORMAL_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                       //
+                       // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state
+                       //
+                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION)&hp_wait_for_attn_led_normal_cmd_available;
+
+                       //
+                       // Allow next SW-initiated request while processing this one
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+               }
+               //
+               // Already located (Attn LED blinking), just re-start timeout
+               //
+               else {
+                       dbg("%s -->LED_LOCATE_REQ: slot_id[ %d:%d ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+                       //
+                       // Allow next SW-initiated request before invoking callback, since next
+                       // request may be sent in the context of this thread.
+                       //
+                       spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+                       hp_clear_slot_event_bit(slot_context, ATTN_LED_REQUEST_EVENT);
+                       spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+
+                       //
+                       // Call Completion Callback()
+                       //
+                       hp_QuerySlotStatus(     shpc_context, slot_context->slot_number - 1, &slot_status );
+                       slot_status.x.lu_request_failed = HP_FALSE;
+                       shpc_context->async_callback(
+                               shpc_context->driver_context,
+                               slot_context->slot_number - 1,
+                               SHPC_ASYNC_LED_LOCATE,
+                               slot_status,
+                               slot_context->attn_led_completion.request_context );
+
+                       //
+                       // Signal registered user EVENT
+                       //
+                       hp_signal_user_event( shpc_context );
+               }
+       }
+       // timeout
+       else if (slot_context->slot_event_bits & LED_TIMER2_EVENT) {
+               //
+               // Set completion info for HW-initiated request
+               //
+               slot_context->attn_led_completion.hw_initiated = TRUE;
+               slot_context->attn_led_completion.type = SHPC_ASYNC_LED_NORMAL;
+               slot_context->attn_led_completion.timeout = 0;
+               slot_context->attn_led_completion.request_context = NULL;
+
+               //
+               // Grab Command MUTEX to set Attn LED at "Normal" (On/Off) state
+               //
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_available;
+
+       }
+       // exit_request_event
+       else {
+       status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_normal_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_normal_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       //down_interruptible(&slot_context->cmd_acquire_mutex);
+       hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // cmd_available_mutex
+       //
+       if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Update Attention LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               command_reg.Slot.attention_led = slot_context->problem_detected ?
+                       SHPC_LED_ON : SHPC_LED_OFF;
+               hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_normal_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->led_cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_normal_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_normal_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+       union SHPC_STATUS_WREG status_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, LED_TIMER3_EVENT);
+       slot_context->led_timer3.data = (unsigned long)slot_context;
+       slot_context->led_timer3.function = hp_led_timer3_func;
+       slot_context->led_timer3.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->led_timer3);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & LED_TIMER3_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & LED_TIMER3_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->led_timer3);
+       }
+
+       //
+       // cmd_completion_event, timeout
+       //
+       if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+          (slot_context->slot_event_bits & LED_TIMER3_EVENT)) {
+               //
+               // Command completed OK?
+               //
+               status_reg.AsWord = readw(shpc_context->mmio_base_addr + SHPC_STATUS_REG_OFFSET);
+
+               if( status_reg.x.BSY == SHPC_STATUS_CLEARED &&
+                       status_reg.x.INVCMD_ERR == SHPC_STATUS_CLEARED ) {
+                       //
+                       // Complete succesful ENABLE request
+                       //
+                       slot_context->attn_led_completion.failed = HP_FALSE;
+                       slot_context->attn_led_completion.done = TRUE;
+                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request;
+               }
+               else {
+                       //
+                       // Fail on-going request
+                       //
+                       slot_context->attn_led_completion.failed = HP_TRUE;
+                       slot_context->attn_led_completion.done = TRUE;
+
+                       //
+                       // Grab Command MUTEX to make sure Attn LED gets back to "Normal" (On/Off)
+                       //
+                       dbg("%s -->CMD_ERROR: slot_id[ %d:%d ]  Cmd[ %X ]", __FUNCTION__,
+                               shpc_context->shpc_instance, slot_context->slot_number-1, status_reg.AsWord );
+                       slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_available;
+               }
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_back_to_normal_cmd_available() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_back_to_normal_cmd_available(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       unsigned long old_irq_flags;
+       long status = STATUS_SUCCESS;
+       union SHPC_COMMAND_WREG command_reg;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Wait for Command Available MUTEX
+       //
+       //down_interruptible(&slot_context->cmd_acquire_mutex);
+       hp_set_slot_event_bit(slot_context, LED_CMD_ACQUIRE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_acquire_event);
+
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       //
+       // cmd_available_mutex
+       //
+       if(shpc_context->shpc_event_bits & LED_CMD_AVAILABLE_MUTEX_EVENT) {
+               //
+               // Clear Completion EVENT before issuing next command
+               //
+               spin_lock_irqsave( &shpc_context->shpc_spinlock, old_irq_flags );
+               hp_clear_shpc_event_bit(shpc_context, CMD_COMPLETION_EVENT);
+               spin_unlock_irqrestore( &shpc_context->shpc_spinlock, old_irq_flags );
+
+               //
+               // Update Attention LED
+               //
+               spin_lock_irqsave( &slot_context->slot_spinlock, old_irq_flags );
+               command_reg.Slot.attention_led = slot_context->problem_detected ?
+                       SHPC_LED_ON : SHPC_LED_OFF;
+               hp_clear_slot_event_bit(slot_context, ATTN_LED_PROBLEM_EVENT);
+               spin_unlock_irqrestore( &slot_context->slot_spinlock, old_irq_flags );
+               command_reg.Slot.code = SHPC_SLOT_OPERATION;
+               command_reg.Slot.power_led = SHPC_led_NO_CHANGE;
+               command_reg.Slot.state = SHPC_SLOT_NO_CHANGE;
+               command_reg.Slot.TGT = slot_context->slot_number;
+               writew(command_reg.AsWord, shpc_context->mmio_base_addr + SHPC_COMMAND_REG_OFFSET);
+
+               //
+               // Wait for command to complete (while holding MUTEX)
+               //
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_back_to_normal_cmd_completion;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               //
+               // Release Command MUTEX
+               //
+               hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+               wake_up_interruptible(&slot_context->led_cmd_release_event);
+               status = STATUS_UNSUCCESSFUL;
+       }
+       return status;
+}
+
+
+// ****************************************************************************
+//
+// hp_wait_for_attn_led_back_to_normal_cmd_completion() @ PASSIVE_LEVEL
+//
+// ****************************************************************************
+long
+hp_wait_for_attn_led_back_to_normal_cmd_completion(
+       struct shpc_context* shpc_context,
+       struct slot_context* slot_context
+       )
+{
+       long status = STATUS_SUCCESS;
+
+       dbg("%s -->slot_id[ %d:%d ]", __FUNCTION__, shpc_context->shpc_instance, slot_context->slot_number-1 );
+
+       //
+       // Setup our timer
+       //
+       hp_clear_slot_event_bit(slot_context, LED_TIMER4_EVENT);
+       slot_context->led_timer4.data = (unsigned long)slot_context;
+       slot_context->led_timer4.function = hp_led_timer4_func;
+       slot_context->led_timer4.expires = jiffies + ONE_SEC_TIMEOUT;
+       add_timer(&slot_context->led_timer4);
+
+       //
+       // Wait for Command Completion EVENT while holding MUTEX
+       //
+       wait_event_interruptible(slot_context->slot_event,
+               ((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+               (slot_context->slot_event_bits & LED_TIMER4_EVENT) ||
+               (shpc_context->shpc_event_bits & EXIT_REQUEST_EVENT)));
+
+       if (!(slot_context->slot_event_bits & LED_TIMER4_EVENT)) {
+               //
+               // delete the timer because we got an event other than the timer
+               //
+               del_timer_sync(&slot_context->led_timer4);
+       }
+
+       //
+       // cmd_completion_event, timeout
+       //
+       if((shpc_context->shpc_event_bits & CMD_COMPLETION_EVENT) ||
+          (slot_context->slot_event_bits & LED_TIMER4_EVENT)) {
+               slot_context->attn_led_function = (SLOT_STATE_FUNCTION)hp_wait_for_attn_led_request;
+       }
+       //
+       // exit_request_event
+       //
+       else {
+               status = STATUS_UNSUCCESSFUL;
+       }
+       //
+       // Release Command MUTEX
+       //
+       hp_set_slot_event_bit(slot_context, LED_CMD_RELEASE_EVENT);
+       wake_up_interruptible(&slot_context->led_cmd_release_event);
+
+       return status;
+}
diff --git a/drivers/pci/hotplug/amdshpc_pci.c b/drivers/pci/hotplug/amdshpc_pci.c
new file mode 100644 (file)
index 0000000..e8560fc
--- /dev/null
@@ -0,0 +1,3513 @@
+/* 
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002-2003 Advanced Micro Devices
+ *
+ * YOUR USE OF THIS CODE IS SUBJECT TO THE TERMS
+ * AND CONDITIONS OF THE GNU GENERAL PUBLIC
+ * LICENSE FOUND IN THE "GPL.TXT" FILE THAT IS
+ * INCLUDED WITH THIS FILE AND POSTED AT
+ * http://www.gnu.org/licenses/gpl.html
+ *
+ * Send feedback to <greg@kroah.com> <david.keck@amd.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include "amdshpc.h"
+#include "../pci.h"
+#include "../../../arch/i386/pci/pci.h"
+
+u8 amdshpc_nic_irq;
+u8 amdshpc_disk_irq;
+
+static u16 unused_IRQ;
+
+extern struct controller *amdshpc_ctrl_list;   /* = NULL */
+extern struct pci_func *amdshpc_slot_list[256];
+
+static int bridge_slot_remove(struct pci_func *bridge);
+static int is_bridge(struct pci_func * func);
+static int update_slot_info (struct controller  *ctrl, struct slot *slot);
+static int slot_remove(struct pci_func * old_slot);
+static u32 configure_new_device(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);
+static int configure_new_function(struct controller * ctrl, struct pci_func *func,u8 behind_bridge, struct resource_lists *resources);
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func);
+
+static u16 unused_IRQ;
+
+/**
+ * board_added - Called after a board has been added to the system.
+ *
+ * Turns power on for the board
+ * Configures board
+ *
+ */
+static u32 board_added(struct pci_func * func, struct controller * ctrl)
+{
+       int index;
+       u32 temp_register = 0xFFFFFFFF;
+       u32 rc = 0;
+       struct pci_func *new_slot = NULL;
+       struct resource_lists res_lists;
+
+       dbg("%s: func->device, slot_offset = %d, %d \n",__FUNCTION__,
+               func->device, ctrl->slot_device_offset);
+
+       // Get vendor/device ID u32
+       rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_VENDOR_ID, &temp_register);
+       dbg("%s: pci_bus_read_config_dword returns %d\n",__FUNCTION__, rc);
+       dbg("%s: temp_register is %x\n",__FUNCTION__, temp_register);
+
+       if (rc != 0) {
+               // Something's wrong here
+               temp_register = 0xFFFFFFFF;
+               dbg("%s: temp register set to %x by error\n",__FUNCTION__, temp_register);
+       }
+       // Preset return code.  It will be changed later if things go okay.
+       rc = NO_ADAPTER_PRESENT;
+
+       // All F's is an empty slot or an invalid board
+       if (temp_register != 0xFFFFFFFF) {        // Check for a board in the slot
+               res_lists.io_head       = ctrl->io_head;
+               res_lists.mem_head      = ctrl->mem_head;
+               res_lists.p_mem_head = ctrl->p_mem_head;
+               res_lists.bus_head      = ctrl->bus_head;
+               res_lists.irqs = NULL;
+
+               rc = configure_new_device(ctrl, func, 0, &res_lists);
+
+               dbg("%s: back from configure_new_device\n",__FUNCTION__);
+               ctrl->io_head   = res_lists.io_head;
+               ctrl->mem_head  = res_lists.mem_head;
+               ctrl->p_mem_head = res_lists.p_mem_head;
+               ctrl->bus_head  = res_lists.bus_head;
+
+               amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+               amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+               amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+               amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+
+               if (rc) {
+                       // Something went wrong; disable slot
+//                     TO_DO_amd_disable_slot();
+                       return(rc);
+               } else {
+                       amdshpc_save_slot_config(ctrl, func);
+               }
+
+
+               func->status = 0;
+               func->switch_save = 0x10;
+               func->is_a_board = 0x01;
+
+               //next, we will instantiate the linux pci_dev structures (with appropriate driver notification, if already present)
+               dbg("%s: configure linux pci_dev structure\n",__FUNCTION__);
+               index = 0;
+               do {
+                       new_slot = amdshpc_slot_find(ctrl->bus, func->device, index++);
+                       if (new_slot && !new_slot->pci_dev) {
+                               amdshpc_configure_device(ctrl, new_slot);
+                       }
+               } while (new_slot);
+       } else {
+               // Something went wrong; disable slot
+//             TO_DO_amd_disable_slot();
+               return(rc);
+       }
+       return 0;
+}
+
+
+/**
+ * remove_board - Returns resources
+ */
+static u32 remove_board(struct pci_func * func, u32 replace_flag, struct controller  * ctrl)
+{
+       int index;
+       u8 skip = 0;
+       u8 device;
+       u8 hp_slot;
+       u32 rc;
+       struct resource_lists res_lists;
+       struct pci_func *temp_func;
+
+       if (func == NULL)
+               return(1);
+
+       if (amdshpc_unconfigure_device(func))
+               return(1);
+
+       device = func->device;
+
+       hp_slot = func->device - ctrl->slot_device_offset;
+       dbg("In %s, hp_slot = %d\n",__FUNCTION__, hp_slot);
+
+       // When we get here, it is safe to change base Address Registers.
+       // We will attempt to save the base Address Register Lengths
+       if (replace_flag || !ctrl->add_support)
+               rc = amdshpc_save_base_addr_length(ctrl, func);
+       else if (!func->bus_head && !func->mem_head &&
+                !func->p_mem_head && !func->io_head) {
+               // Here we check to see if we've saved any of the board's
+               // resources already.  If so, we'll skip the attempt to
+               // determine what's being used.
+               index = 0;
+               temp_func = amdshpc_slot_find(func->bus, func->device, index++);
+               while (temp_func) {
+                       if (temp_func->bus_head || temp_func->mem_head
+                               || temp_func->p_mem_head || temp_func->io_head) {
+                               skip = 1;
+                               break;
+                       }
+                       temp_func = amdshpc_slot_find(temp_func->bus, temp_func->device, index++);
+               }
+
+               if (!skip)
+                       rc = amdshpc_save_used_resources(ctrl, func);
+       }
+       // Change status to shutdown
+       if (func->is_a_board)
+               func->status = 0x01;
+       func->configured = 0;
+
+//     TO_DO_amd_disable_slot(ctrl, hp_slot);
+
+       if (!replace_flag && ctrl->add_support) {
+               while (func) {
+                       res_lists.io_head = ctrl->io_head;
+                       res_lists.mem_head = ctrl->mem_head;
+                       res_lists.p_mem_head = ctrl->p_mem_head;
+                       res_lists.bus_head = ctrl->bus_head;
+
+                       amdshpc_return_board_resources(func, &res_lists);
+
+                       ctrl->io_head = res_lists.io_head;
+                       ctrl->mem_head = res_lists.mem_head;
+                       ctrl->p_mem_head = res_lists.p_mem_head;
+                       ctrl->bus_head = res_lists.bus_head;
+
+                       amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+                       amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+                       amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+                       amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+
+                       if (is_bridge(func)) {
+                               bridge_slot_remove(func);
+                       } else
+                               slot_remove(func);
+
+                       func = amdshpc_slot_find(ctrl->bus, device, 0);
+               }
+
+               // Setup slot structure with entry for empty slot
+               func = amdshpc_slot_create(ctrl->bus);
+
+               if (func == NULL) {
+                       // Out of memory
+                       return(1);
+               }
+
+               func->bus = ctrl->bus;
+               func->device = device;
+               func->function = 0;
+               func->configured = 0;
+               func->switch_save = 0x10;
+               func->is_a_board = 0;
+               func->p_task_event = NULL;
+       }
+       return 0;
+}
+
+
+/*
+ * find_slot
+ */
+static inline struct slot* find_slot (struct controller* ctrl, u8 device)
+{
+       struct slot *slot;
+
+       dbg("%s", __FUNCTION__);
+       if (!ctrl)
+               return NULL;
+
+       slot = ctrl->slot;
+
+       while (slot && (slot->device != device)) {
+               slot = slot->next;
+       }
+
+       return slot;
+}
+
+// board insertion
+int amdshpc_process_SI (struct controller *ctrl, struct pci_func *func)
+{
+       u8 device, hp_slot;
+       u16 temp_word;
+       u32 tempdword;
+       int rc;
+       struct slot* p_slot;
+       int physical_slot = 0;
+
+       dbg("%s  0", __FUNCTION__);
+       if (!ctrl)
+               return(1);
+
+       tempdword = 0;
+
+       device = func->device;
+       hp_slot = device - ctrl->slot_device_offset;
+       p_slot = find_slot(ctrl, device);
+       if (p_slot) {
+               physical_slot = p_slot->number;
+       }
+
+       if (tempdword & (0x01 << hp_slot)) {
+               dbg("%s  1", __FUNCTION__);
+               return(1);
+       }
+
+       // add board
+       slot_remove(func);
+
+       func = amdshpc_slot_create(ctrl->bus);
+       dbg("%s  2",__FUNCTION__);
+       if (func == NULL) {
+               dbg("%s 3",__FUNCTION__);
+               return(1);
+       }
+
+       func->bus = ctrl->bus;
+       func->device = device;
+       func->function = 0;
+       func->configured = 0;
+       func->is_a_board = 1;
+
+       // We have to save the presence info for these slots
+       temp_word = ctrl->ctrl_int_comp >> 16;
+       func->presence_save = (temp_word >> hp_slot) & 0x01;
+       func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02;
+
+       dbg("%s 4",__FUNCTION__);
+       if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+               dbg("%s 5",__FUNCTION__);
+               func->switch_save = 0;
+       } else {
+               dbg("%s 6",__FUNCTION__);
+               func->switch_save = 0x10;
+       }
+
+       rc = board_added(func, ctrl);
+       dbg("%s 7 rc=%d",__FUNCTION__,rc);
+       if (rc) {
+               dbg("%s 8",__FUNCTION__);
+               if (is_bridge(func)) {
+                       dbg("%s 9",__FUNCTION__);
+                       bridge_slot_remove(func);
+               } else {
+                       dbg("%s 10",__FUNCTION__);
+                       slot_remove(func);
+               }
+
+               // Setup slot structure with entry for empty slot
+               func = amdshpc_slot_create(ctrl->bus);
+
+               dbg("%s 11",__FUNCTION__);
+               if (func == NULL) {
+                       // Out of memory
+                       return(1);
+               }
+
+               func->bus = ctrl->bus;
+               func->device = device;
+               func->function = 0;
+               func->configured = 0;
+               func->is_a_board = 0;
+
+               // We have to save the presence info for these slots
+               temp_word = ctrl->ctrl_int_comp >> 16;
+               func->presence_save = (temp_word >> hp_slot) & 0x01;
+               func->presence_save |=
+               (temp_word >> (hp_slot + 7)) & 0x02;
+
+               if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) {
+                       dbg("%s 12",__FUNCTION__);
+                       func->switch_save = 0;
+               } else {
+                       dbg("%s 13",__FUNCTION__);
+                       func->switch_save = 0x10;
+               }
+       }
+
+       if (rc) {
+               dbg("%s: rc = %d\n",__FUNCTION__, rc);
+       }
+
+       if (p_slot){
+               dbg("%s 14",__FUNCTION__);
+               update_slot_info(ctrl, p_slot);
+       }
+
+       return rc;
+}
+
+// Disable Slot
+int amdshpc_process_SS (struct controller *ctrl, struct pci_func *func)
+{
+       u8 device, class_code, header_type, BCR;
+       u8 index = 0;
+       u8 replace_flag;
+       u32 rc = 0;
+       struct slot* p_slot;
+       int physical_slot=0;
+
+       dbg("%s 0",__FUNCTION__);
+       device = func->device;
+       func = amdshpc_slot_find(ctrl->bus, device, index++);
+       p_slot = find_slot(ctrl, device);
+       if (p_slot) {
+               physical_slot = p_slot->number;
+       }
+
+       // Make sure there are no video controllers here
+       while (func && !rc) {
+               dbg("%s 1..",__FUNCTION__);
+               // Check the Class Code
+               rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0B, &class_code);
+               dbg("%s 1.1 rc = %d  class_code = %02x",__FUNCTION__, rc, class_code);
+               if (rc){
+                       dbg("%s 2",__FUNCTION__);
+                       return rc;
+               }
+
+               if (class_code == PCI_BASE_CLASS_DISPLAY) {
+                       /* Display/Video adapter (not supported) */
+                       dbg("%s 3",__FUNCTION__);
+                       rc = REMOVE_NOT_SUPPORTED;
+               } else {
+                       dbg("%s 3.5",__FUNCTION__);
+                       // See if it's a bridge
+                       rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_HEADER_TYPE, &header_type);
+                       if (rc){
+                               dbg("%s 4",__FUNCTION__);
+                               return rc;
+                       }
+
+                       // If it's a bridge, check the VGA Enable bit
+                       if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+                               dbg("%s 4.5",__FUNCTION__);
+                               rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), PCI_BRIDGE_CONTROL, &BCR);
+                               if (rc){
+                                       dbg("%s 5",__FUNCTION__);
+                                       return rc;
+                               }
+
+                               dbg("%s 5.5",__FUNCTION__);
+                               // If the VGA Enable bit is set, remove isn't supported
+                               if (BCR & PCI_BRIDGE_CTL_VGA) {
+                                       dbg("%s 6",__FUNCTION__);
+                                       rc = REMOVE_NOT_SUPPORTED;
+                               }
+                       }
+               }
+
+               func = amdshpc_slot_find(ctrl->bus, device, index++);
+               dbg("%s 7",__FUNCTION__);
+       }
+
+       func = amdshpc_slot_find(ctrl->bus, device, 0);
+       dbg("%s 8",__FUNCTION__);
+       if ((func != NULL) && !rc) {
+               dbg("%s 9",__FUNCTION__);
+               //FIXME: Replace flag should be passed into process_SS
+               replace_flag = !(ctrl->add_support);
+               rc = remove_board(func, replace_flag, ctrl);
+       } else if (!rc) {
+               dbg("%s 10",__FUNCTION__);
+               rc = 1;
+       }
+
+       if (p_slot){
+               dbg("%s 11",__FUNCTION__);
+               update_slot_info(ctrl, p_slot);
+       }
+
+       dbg("%s 12",__FUNCTION__);
+       return(rc);
+}
+
+
+/*
+ * detect_HRT_floating_pointer
+ *
+ * find the Hot Plug Resource Table in the specified region of memory.
+ *
+ */
+static void *detect_HRT_floating_pointer(void *begin, void *end)
+{
+       void *fp;
+       void *endp;
+       u8 temp1, temp2, temp3, temp4;
+       int status = 0;
+
+       endp = (end - sizeof(struct hrt) + 1);
+
+       for (fp = begin; fp <= endp; fp += 16) {
+               temp1 = readb(fp + SIG0);
+               temp2 = readb(fp + SIG1);
+               temp3 = readb(fp + SIG2);
+               temp4 = readb(fp + SIG3);
+               if (temp1 == '$' &&
+                       temp2 == 'H' &&
+                       temp3 == 'R' &&
+                       temp4 == 'T') {
+                       status = 1;
+                       dbg("%s -->temp string----> %c%c%c%c  at----->  %p\n", __FUNCTION__, temp1,temp2,temp3,temp4,fp);
+                       break;
+               }
+       }
+
+       if (!status) {
+               fp = NULL;
+               dbg("%s -->Did not discover Hotplug Resource Table between start:%p  end:%p\n", __FUNCTION__, begin, end);
+               return fp;
+       }
+
+       dbg("%s -->Discovered Hotplug Resource Table at %p\n", __FUNCTION__, fp);
+       return fp;
+}
+
+/**
+ * amdshpc_slot_find - Looks for a node by bus, and device, multiple functions accessed
+ * @bus: bus to find
+ * @device: device to find
+ * @index: is 0 for first function found, 1 for the second...
+ *
+ * Returns pointer to the node if successful, %NULL otherwise.
+ */
+struct pci_func *amdshpc_slot_find(u8 bus, u8 device, u8 index) {
+       int found = -1;
+       struct pci_func *func;
+
+       func = amdshpc_slot_list[bus];
+       dbg("%s  amdshpc_slot_list[%02x] = %p", __FUNCTION__, bus, amdshpc_slot_list[bus]);
+       dbg("%s  bus, device, index  %x %d %d", __FUNCTION__, bus, device, index);
+
+       if ((func == NULL) || ((func->device == device) && (index == 0)))
+               return(func);
+
+       if (func->device == device)
+               found++;
+
+       while (func->next != NULL) {
+               func = func->next;
+
+               if (func->device == device)
+                       found++;
+
+               if (found == index)
+                       return(func);
+       }
+
+       return(NULL);
+}
+
+
+/*
+ * amdshpc_resource_sort_and_combine
+ *
+ * Sorts all of the nodes in the list in ascending order by
+ * their base addresses.  Also does garbage collection by
+ * combining adjacent nodes.
+ *
+ * returns 0 if success
+ */
+int amdshpc_resource_sort_and_combine(struct pci_resource **head)
+{
+       struct pci_resource *node1;
+       struct pci_resource *node2;
+       int out_of_order = 1;
+
+       dbg("%s: head = %p, *head = %p\n",__FUNCTION__, head, *head);
+
+       if (!(*head))
+               return(1);
+
+       dbg("%s -->*head->next = %p\n", __FUNCTION__,(*head)->next);
+
+       if (!(*head)->next)
+               return(0);      /* only one item on the list, already sorted! */
+
+       dbg("%s -->*head->base = 0x%x\n", __FUNCTION__,(*head)->base);
+       dbg("%s -->*head->next->base = 0x%x\n", __FUNCTION__,(*head)->next->base);
+       while (out_of_order) {
+               out_of_order = 0;
+
+               // Special case for swapping list head
+               if (((*head)->next) &&
+                       ((*head)->base > (*head)->next->base)) {
+                       node1 = *head;
+                       (*head) = (*head)->next;
+                       node1->next = (*head)->next;
+                       (*head)->next = node1;
+                       out_of_order++;
+               }
+
+               node1 = (*head);
+
+               while (node1->next && node1->next->next) {
+                       if (node1->next->base > node1->next->next->base) {
+                               out_of_order++;
+                               node2 = node1->next;
+                               node1->next = node1->next->next;
+                               node1 = node1->next;
+                               node2->next = node1->next;
+                               node1->next = node2;
+                       } else
+                               node1 = node1->next;
+               }
+       }  // End of out_of_order loop
+
+       node1 = *head;
+
+       while (node1 && node1->next) {
+               if ((node1->base + node1->length) == node1->next->base) {
+                       // Combine
+                       dbg("%s -->8..\n", __FUNCTION__);
+                       node1->length += node1->next->length;
+                       node2 = node1->next;
+                       node1->next = node1->next->next;
+                       kfree(node2);
+               } else
+                       node1 = node1->next;
+       }
+
+       return(0);
+}
+
+
+/*
+ * amdshpc_find_available_resources
+ *
+ * Finds available memory, IO, and IRQ resources for programming
+ * devices which may be added to the system
+ * this function is for hot plug ADD!
+ *
+ * returns 0 if success
+ */
+int amdshpc_find_available_resources (struct controller *ctrl, void *rom_start)
+{
+       u8 temp;
+       u8 populated_slot=0;
+       u8 bridged_slot;
+       u8 slot_index;
+       void *one_slot;
+       struct pci_func *func = NULL;
+       int i = 10, index;
+       u32 temp_dword, rc;
+       struct pci_resource *mem_node;
+       struct pci_resource *p_mem_node;
+       struct pci_resource *io_node;
+       struct pci_resource *bus_node;
+       void *rom_resource_table;
+       struct shpc_context *shpc_context;
+
+       slot_index=0;
+
+       shpc_context = (struct shpc_context* ) ctrl->shpc_context;
+       rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
+       dbg("%s -->rom_resource_table = %p\n", __FUNCTION__, rom_resource_table);
+
+       if (rom_resource_table == NULL) {
+               return -ENODEV;
+       }
+       // Sum all resources and setup resource maps
+       unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
+       dbg("%s -->unused_IRQ = %x\n", __FUNCTION__, unused_IRQ);
+       dbg("%s -->PCI_IRQ = %x\n", __FUNCTION__, readl(rom_resource_table + PCIIRQ));
+
+       temp = 0;
+
+       while (unused_IRQ) {
+               if (unused_IRQ & 1) {
+                       amdshpc_disk_irq = temp;
+                       break;
+               }
+               unused_IRQ = unused_IRQ >> 1;
+               temp++;
+       }
+
+       dbg("%s -->amdshpc_disk_irq= %d\n", __FUNCTION__, amdshpc_disk_irq);
+       unused_IRQ = unused_IRQ >> 1;
+       temp++;
+
+       while (unused_IRQ) {
+               if (unused_IRQ & 1) {
+                       amdshpc_nic_irq = temp;
+                       break;
+               }
+               unused_IRQ = unused_IRQ >> 1;
+               temp++;
+       }
+
+       dbg("%s -->amdshpc_nic_irq= %d\n", __FUNCTION__, amdshpc_nic_irq);
+       unused_IRQ = readl(rom_resource_table + PCIIRQ);
+
+       temp = 0;
+
+       if (!amdshpc_nic_irq) {
+               amdshpc_nic_irq = ctrl->interrupt;
+       }
+
+       if (!amdshpc_disk_irq) {
+               amdshpc_disk_irq = ctrl->interrupt;
+       }
+
+       dbg("%s -->amdshpc_disk_irq, amdshpc_nic_irq= %d, %d\n", __FUNCTION__, amdshpc_disk_irq, amdshpc_nic_irq);
+
+       one_slot = rom_resource_table + sizeof (struct hrt);
+
+       i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
+       dbg("%s -->number_of_entries = %d\n", __FUNCTION__, i);
+
+       if (!readb(one_slot + SECONDARY_BUS)) {
+               return(1);
+       }
+
+       dbg("%s -->dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n", __FUNCTION__);
+
+       while (i && readb(one_slot + SECONDARY_BUS)) {
+               u8 dev_func =           readb(one_slot + DEV_FUNC);
+               u8 primary_bus =        readb(one_slot + PRIMARY_BUS);
+               u8 secondary_bus =      readb(one_slot + SECONDARY_BUS);
+               u8 max_bus =            readb(one_slot + MAX_BUS);
+               u16 io_base =           readw(one_slot + IO_BASE);
+               u16 io_length =         readw(one_slot + IO_LENGTH);
+               u16 mem_base =          readw(one_slot + MEM_BASE);
+               u16 mem_length =        readw(one_slot + MEM_LENGTH);
+               u16 pre_mem_base =      readw(one_slot + PRE_MEM_BASE);
+               u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
+
+               dbg("%s -->%2.2x | %4.4x  | %4.4x | %4.4x   | %4.4x | %4.4x   | %4.4x |%2.2x %2.2x %2.2x\n", __FUNCTION__,
+                       dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
+                       primary_bus, secondary_bus, max_bus);
+
+               // If this entry isn't for our controller's bus, ignore it
+               if (primary_bus != ctrl->bus) {
+                       i--;
+                       one_slot += sizeof (struct slot_rt);
+                       continue;
+               }
+               
+               // find out if this entry is for an occupied slot
+               pci_bus_read_config_dword(ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
+               dbg("bus %p, pri-bus %08x, slot %d, function %d, vend ID %d, tempDW %p\n",
+                       ctrl->pci_bus, primary_bus, PCI_SLOT(dev_func), PCI_FUNC(dev_func), PCI_VENDOR_ID, &temp_dword);
+
+               dbg("%s -->temp_D_word = %08X\n", __FUNCTION__, temp_dword);
+
+               if (temp_dword != 0xFFFFFFFF) {
+                       index = 0;
+                       func = amdshpc_slot_find(primary_bus, dev_func >> 3, 0);
+                       dbg("%s -->func = %p",__FUNCTION__, (unsigned long*)func);
+                       while (func && (func->function != PCI_FUNC(dev_func))) {
+                               dbg("%s -->func = %p (bus, dev, fun) = (%d, %d, %d)\n",__FUNCTION__, func, primary_bus, dev_func >> 3, index);
+                               func = amdshpc_slot_find(primary_bus, PCI_SLOT(dev_func), index++);
+                       }
+
+                       // If we can't find a match, skip this table entry
+                       if (!func) {
+                               i--;
+                               one_slot += sizeof (struct slot_rt);
+                               continue;
+                       }
+                       // this may not work and shouldn't be used
+                       if (secondary_bus != primary_bus){
+                               bridged_slot = 1;
+                  }
+                       else{
+                               bridged_slot = 0;
+                  }
+                       shpc_context->slot_context[slot_index].slot_occupied = 1;
+               } else {
+
+                       populated_slot = 0;
+                       bridged_slot = 0;
+               }
+           slot_index++;
+           
+               // If we've got a valid IO base, use it
+
+               temp_dword = io_base + io_length;
+               dbg("%s -->temp_D_word for io base = %08x",__FUNCTION__, temp_dword);
+
+               if ((io_base) && (temp_dword < 0x10000)) {
+                       io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                       if (!io_node)
+                               return -ENOMEM;
+
+                       io_node->base = io_base;
+                       io_node->length = io_length;
+
+                       dbg("%s -->found io_node(base, length) = %x, %x\n",__FUNCTION__, io_node->base, io_node->length);
+                       dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+                       if (!populated_slot) {
+                               io_node->next = ctrl->io_head;
+                               ctrl->io_head = io_node;
+                       } else {
+                               io_node->next = func->io_head;
+                               func->io_head = io_node;
+                       }
+               }
+
+               // If we've got a valid memory base, use it
+               temp_dword = mem_base + mem_length;
+               dbg("%s -->temp_D_word for mem base = %08x",__FUNCTION__, temp_dword);
+               if ((mem_base) && (temp_dword < 0x10000)) {
+                       mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                       if (!mem_node)
+                               return -ENOMEM;
+
+                       mem_node->base = mem_base << 16;
+
+                       mem_node->length = mem_length << 16;
+
+                       dbg("%s -->found mem_node(base, length) = %08x, %08x\n",__FUNCTION__, mem_node->base, mem_node->length);
+                       dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+                       if (!populated_slot) {
+                               mem_node->next = ctrl->mem_head;
+                               ctrl->mem_head = mem_node;
+                       } else {
+                               mem_node->next = func->mem_head;
+                               func->mem_head = mem_node;
+                       }
+               }
+
+               // If we've got a valid prefetchable memory base, and
+               // the base + length isn't greater than 0xFFFF
+               temp_dword = pre_mem_base + pre_mem_length;
+               dbg("%s -->temp_D_word for pre mem base = %08x",__FUNCTION__, temp_dword);
+               if ((pre_mem_base) && (temp_dword < 0x10000)) {
+                       p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                       if (!p_mem_node)
+                               return -ENOMEM;
+
+                       p_mem_node->base = pre_mem_base << 16;
+
+                       p_mem_node->length = pre_mem_length << 16;
+                       dbg("%s -->found p_mem_node(base, length) = %08x, %08x\n",__FUNCTION__, p_mem_node->base, p_mem_node->length);
+                       dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+
+                       if (!populated_slot) {
+                               p_mem_node->next = ctrl->p_mem_head;
+                               ctrl->p_mem_head = p_mem_node;
+                       } else {
+                               p_mem_node->next = func->p_mem_head;
+                               func->p_mem_head = p_mem_node;
+                       }
+               }
+
+               // If we've got a valid bus number, use it
+               // The second condition is to ignore bus numbers on
+               // populated slots that don't have PCI-PCI bridges
+               if (secondary_bus && (secondary_bus != primary_bus)) {
+                       bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                       if (!bus_node)
+                               return -ENOMEM;
+
+                       bus_node->base = secondary_bus;
+                       bus_node->length = max_bus - secondary_bus + 1;
+                       dbg("%s -->found bus_node(base, length) = %08x, %08x\n",__FUNCTION__, bus_node->base, bus_node->length);
+                       dbg("%s -->populated slot =%d \n",__FUNCTION__, populated_slot);
+                       if (!populated_slot) {
+                               bus_node->next = ctrl->bus_head;
+                               ctrl->bus_head = bus_node;
+                       } else {
+                               bus_node->next = func->bus_head;
+                               func->bus_head = bus_node;
+                       }
+               }
+
+               i--;
+               one_slot += sizeof (struct slot_rt);
+       }
+
+       // If all of the following fail, we don't have any resources for
+       // hot plug add
+       rc = 1;
+       rc &= amdshpc_resource_sort_and_combine(&(ctrl->mem_head));
+       dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+       rc &= amdshpc_resource_sort_and_combine(&(ctrl->p_mem_head));
+       dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+       rc &= amdshpc_resource_sort_and_combine(&(ctrl->io_head));
+       dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+       rc &= amdshpc_resource_sort_and_combine(&(ctrl->bus_head));
+       dbg("%s -->rc =%d \n",__FUNCTION__, rc);
+
+       return(rc);
+}
+
+
+
+/*
+ * amdshpc_save_config
+ *
+ * Reads configuration for all slots in a PCI bus and saves info.
+ *
+ * Note:  For non-hot plug busses, the slot # saved is the device #
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_config(struct controller *ctrl, int busnumber, union SLOT_CONFIG_INFO* is_hot_plug)
+ {
+       long rc;
+       u8 class_code;
+       u8 header_type;
+       u32 ID;
+       u8 secondary_bus;
+       struct pci_func *new_slot;
+       int sub_bus;
+       int FirstSupported;
+       int LastSupported;
+       int max_functions;
+       int function;
+       u8 DevError;
+       int device = 0;
+       int cloop = 0;
+       int stop_it;
+       int index;
+
+       //  Decide which slots are supported
+       if (is_hot_plug) {
+               FirstSupported  = ctrl->first_slot;
+               LastSupported = (FirstSupported + is_hot_plug->x.lu_slots_implemented) - 1;
+       } else {
+               FirstSupported = 0;
+               LastSupported = 0x1F;
+       }
+
+       // Save PCI configuration space for all devices in supported slots
+       for (device = FirstSupported; device <= LastSupported; device++) {
+               int devfn = PCI_DEVFN(device, 0);
+
+               ID = 0xFFFFFFFF;
+               rc = pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &ID);
+               if (rc)
+                       return rc;
+
+               if (ID != 0xFFFFFFFF) {   //  device in slot
+                       rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+                       if (rc)
+                               return rc;
+
+                       rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+                       if (rc)
+                               return rc;
+
+                       // If multi-function device, set max_functions to 8
+                       if (header_type & 0x80)
+                               max_functions = 8;
+                       else
+                               max_functions = 1;
+
+                       function = 0;
+
+                       do {
+                               DevError = 0;
+
+                               if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {   // P-P Bridge
+                                       //  Recurse the subordinate bus
+                                       //  get the subordinate bus number
+                                       rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
+                                       if (rc) {
+                                               return rc;
+                                       } else {
+                                               sub_bus = (int) secondary_bus;
+
+                                               // Save secondary bus cfg spc
+                                               // with this recursive call.
+                                               rc = amdshpc_save_config(ctrl, sub_bus, 0);
+
+                                               if (rc)
+                                                       return rc;
+                                       }
+                               }
+
+                               index = 0;
+                               new_slot = amdshpc_slot_find(busnumber, device, index++);
+                               while (new_slot &&
+                                      (new_slot->function != (u8) function))
+                                       new_slot = amdshpc_slot_find(busnumber, device, index++);
+
+                               if (!new_slot) {
+                                       // Setup slot structure.
+                                       new_slot = amdshpc_slot_create(busnumber);
+
+                                       if (new_slot == NULL)
+                                               return(1);
+                               }
+
+                               new_slot->bus = (u8) busnumber;
+                               new_slot->device = (u8) device;
+                               new_slot->function = (u8) function;
+                               new_slot->is_a_board = 1;
+                               new_slot->switch_save = 0x10;
+                               // In case of unsupported board
+                               new_slot->status = DevError;
+                               new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
+                               dbg("%s EXISTING SLOT", __FUNCTION__);
+                               dbg("%s ns->bus         = %d", __FUNCTION__, new_slot->bus);
+                               dbg("%s ns->device      = %d", __FUNCTION__, new_slot->device);
+                               dbg("%s ns->function    = %d", __FUNCTION__, new_slot->function);
+                               dbg("%s ns->is_a_board  = %d", __FUNCTION__, new_slot->is_a_board);
+                               dbg("%s ns->switch_save = %02x", __FUNCTION__, new_slot->switch_save);
+                               dbg("%s ns->pci_dev     = %p", __FUNCTION__, new_slot->pci_dev);
+
+                               for (cloop = 0; cloop < 0x20; cloop++) {
+                                       rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+                                       if (rc)
+                                               return rc;
+                               }
+
+                               function++;
+                               stop_it = 0;
+
+                               //  this loop skips to the next present function
+                               //  reading in Class Code and Header type.
+                               while ((function < max_functions)&&(!stop_it)) {
+                                       rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
+                                       if (ID == 0xFFFFFFFF) {  // nothing there.
+                                               function++;
+                                       } else {  // Something there
+                                               rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
+                                               if (rc)
+                                                       return rc;
+
+                                               rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
+                                               if (rc)
+                                                       return rc;
+
+                                               stop_it++;
+                                       }
+                               }
+
+                       } while (function < max_functions);
+               }               // End of IF (device in slot?)
+               else if (is_hot_plug) {
+                       // Setup slot structure with entry for empty slot
+                       new_slot = amdshpc_slot_create(busnumber);
+
+                       if (new_slot == NULL) {
+                               return(1);
+                       }
+
+                       new_slot->bus = (u8) busnumber;
+                       new_slot->device = (u8) device;
+                       new_slot->function = 0;
+                       new_slot->is_a_board = 0;
+                       new_slot->presence_save = 0;
+                       new_slot->switch_save = 0;
+                       dbg("%s NEW SLOT", __FUNCTION__);
+                       dbg("%s ns->bus         = %d", __FUNCTION__, new_slot->bus);
+                       dbg("%s ns->device      = %d", __FUNCTION__, new_slot->function);
+                       dbg("%s ns->function    = %d", __FUNCTION__, new_slot->function);
+               }
+       }// End of FOR loop
+
+       return 0;
+}
+
+
+/*
+ * amdshpc_set_irq
+ *
+ * @bus_num: bus number of PCI device
+ * @dev_num: device number of PCI device
+ */
+/*
+int amdshpc_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
+{
+       int rc;
+       u16 temp_word;
+       struct pci_dev fakedev;
+       struct pci_bus fakebus;
+
+       fakedev.devfn = dev_num << 3;
+       fakedev.bus = &fakebus;
+       fakebus.number = bus_num;
+       dbg("%s : dev %d, bus %d, pin %d, num %d\n",__FUNCTION__,
+               dev_num, bus_num, int_pin, irq_num);
+       rc = pcibios_set_irq_routing(&fakedev, int_pin - 0x0a, irq_num);
+       dbg("%s:rc %d\n",__FUNCTION__, rc);
+       if (rc)
+               return rc;
+
+       // set the Edge Level Control Register (ELCR)
+       temp_word = inb(0x4d0);
+       temp_word |= inb(0x4d1) << 8;
+
+       temp_word |= 0x01 << irq_num;
+
+       // This should only be for x86 as it sets the Edge Level Control Register
+       outb((u8) (temp_word & 0xFF), 0x4d0);
+       outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1);
+
+       return 0;
+}
+*/
+
+/*
+ * do_pre_bridge_resource_split
+ *
+ *     Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment) {
+       struct pci_resource *prevnode = NULL;
+       struct pci_resource *node;
+       struct pci_resource *split_node;
+       u32 rc;
+       u32 temp_dword;
+       dbg("%s -->do_pre_bridge_resource_split\n",__FUNCTION__);
+
+       if (!(*head) || !(*orig_head))
+               return(NULL);
+
+       rc = amdshpc_resource_sort_and_combine(head);
+
+       if (rc)
+               return(NULL);
+
+       if ((*head)->base != (*orig_head)->base)
+               return(NULL);
+
+       if ((*head)->length == (*orig_head)->length)
+               return(NULL);
+
+
+       // If we got here, there the bridge requires some of the resource, but
+       // we may be able to split some off of the front
+
+       node = *head;
+
+       if (node->length & (alignment -1)) {
+               // this one isn't an aligned length, so we'll make a new entry
+               // and split it up.
+               split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+               if (!split_node)
+                       return(NULL);
+
+               temp_dword = (node->length | (alignment-1)) + 1 - alignment;
+
+               split_node->base = node->base;
+               split_node->length = temp_dword;
+
+               node->length -= temp_dword;
+               node->base += split_node->length;
+
+               // Put it in the list
+               *head = split_node;
+               split_node->next = node;
+       }
+
+       if (node->length < alignment) {
+               return(NULL);
+       }
+
+       // Now unlink it
+       if (*head == node) {
+               *head = node->next;
+               node->next = NULL;
+       } else {
+               prevnode = *head;
+               while (prevnode->next != node)
+                       prevnode = prevnode->next;
+
+               prevnode->next = node->next;
+               node->next = NULL;
+       }
+
+       return(node);
+}
+
+
+/*
+ * do_bridge_resource_split
+ *
+ *     Returns zero or one node of resources that aren't in use
+ *
+ */
+static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment) {
+       struct pci_resource *prevnode = NULL;
+       struct pci_resource *node;
+       u32 rc;
+       u32 temp_dword;
+
+       if (!(*head))
+               return(NULL);
+
+       rc = amdshpc_resource_sort_and_combine(head);
+
+       if (rc)
+               return(NULL);
+
+       node = *head;
+
+       while (node->next) {
+               prevnode = node;
+               node = node->next;
+               kfree(prevnode);
+       }
+
+       if (node->length < alignment) {
+               kfree(node);
+               return(NULL);
+       }
+
+       if (node->base & (alignment - 1)) {
+               // Short circuit if adjusted size is too small
+               temp_dword = (node->base | (alignment-1)) + 1;
+               if ((node->length - (temp_dword - node->base)) < alignment) {
+                       kfree(node);
+                       return(NULL);
+               }
+
+               node->length -= (temp_dword - node->base);
+               node->base = temp_dword;
+       }
+
+       if (node->length & (alignment - 1)) {
+               // There's stuff in use after this node
+               kfree(node);
+               return(NULL);
+       }
+
+       return(node);
+}
+
+
+/*
+ * sort_by_size
+ *
+ * Sorts nodes on the list by their length.
+ * Smallest first.
+ *
+ */
+static int sort_by_size(struct pci_resource **head)
+{
+       struct pci_resource *current_res;
+       struct pci_resource *next_res;
+       int out_of_order = 1;
+
+       if (!(*head))
+               return(1);
+
+       if (!((*head)->next))
+               return(0);
+
+       while (out_of_order) {
+               out_of_order = 0;
+
+               // Special case for swapping list head
+               if (((*head)->next) &&
+                       ((*head)->length > (*head)->next->length)) {
+                       out_of_order++;
+                       current_res = *head;
+                       *head = (*head)->next;
+                       current_res->next = (*head)->next;
+                       (*head)->next = current_res;
+               }
+
+               current_res = *head;
+
+               while (current_res->next && current_res->next->next) {
+                       if (current_res->next->length > current_res->next->next->length) {
+                               out_of_order++;
+                               next_res = current_res->next;
+                               current_res->next = current_res->next->next;
+                               current_res = current_res->next;
+                               next_res->next = current_res->next;
+                               current_res->next = next_res;
+                       } else
+                               current_res     = current_res->next;
+               }
+       }  // End of out_of_order loop
+
+       return(0);
+}
+
+/**
+ * amdshpc_slot_create - Creates a node and adds it to the proper bus.
+ * @busnumber - bus where new node is to be located
+ *
+ * Returns pointer to the new node or NULL if unsuccessful
+ */
+struct pci_func *amdshpc_slot_create(u8 busnumber) {
+       struct pci_func *new_slot;
+       struct pci_func *next;
+
+       dbg("%s  busnumber = %02xh",__FUNCTION__, busnumber);
+       new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL);
+
+       if (new_slot == NULL) {
+               // I'm not dead yet!
+               // You will be.
+               return(new_slot);
+       }
+
+       memset(new_slot, 0, sizeof(struct pci_func));
+
+       new_slot->next = NULL;
+       new_slot->configured = 1;
+
+       if (amdshpc_slot_list[busnumber] == NULL) {
+               amdshpc_slot_list[busnumber] = new_slot;
+               dbg("%s   created new slot in amdshpc_slot_list  amdshpc_slot_list[%02X] = %p", __FUNCTION__,
+                                                       busnumber, amdshpc_slot_list[busnumber]);
+       } else {
+               next = amdshpc_slot_list[busnumber];
+               while (next->next != NULL)
+                       next = next->next;
+               next->next = new_slot;
+       }
+       return(new_slot);
+}
+
+
+/*
+ * return_resource
+ *
+ * Puts node back in the resource list pointed to by head
+ *
+ */
+static inline void return_resource (struct pci_resource **head, struct pci_resource *node)
+{
+       dbg("%s",__FUNCTION__);
+       if (!node || !head)
+               return;
+       node->next = *head;
+       *head = node;
+}
+
+
+/*
+ * sort_by_max_size
+ *
+ * Sorts nodes on the list by their length.
+ * Largest first.
+ *
+ */
+static int sort_by_max_size(struct pci_resource **head)
+{
+       struct pci_resource *current_res;
+       struct pci_resource *next_res;
+       int out_of_order = 1;
+
+       if (!(*head))
+               return(1);
+
+       if (!((*head)->next))
+               return(0);
+
+       while (out_of_order) {
+               out_of_order = 0;
+
+               // Special case for swapping list head
+               if (((*head)->next) &&
+                       ((*head)->length < (*head)->next->length)) {
+                       out_of_order++;
+                       current_res = *head;
+                       *head = (*head)->next;
+                       current_res->next = (*head)->next;
+                       (*head)->next = current_res;
+               }
+
+               current_res = *head;
+
+               while (current_res->next && current_res->next->next) {
+                       if (current_res->next->length < current_res->next->next->length) {
+                               out_of_order++;
+                               next_res = current_res->next;
+                               current_res->next = current_res->next->next;
+                               current_res = current_res->next;
+                               next_res->next = current_res->next;
+                               current_res->next = next_res;
+                       } else
+                               current_res     = current_res->next;
+               }
+       }  // End of out_of_order loop
+
+       return(0);
+}
+
+
+/*
+ * get_max_resource
+ *
+ * Gets the largest node that is at least "size" big from the
+ * list pointed to by head.  It aligns the node on top and bottom
+ * to "size" alignment before returning it.
+ */
+static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size) {
+       struct pci_resource *max;
+       struct pci_resource *temp;
+       struct pci_resource *split_node;
+       u32 temp_dword;
+
+       if (!(*head))
+               return(NULL);
+
+       if (amdshpc_resource_sort_and_combine(head))
+               return(NULL);
+
+       if (sort_by_max_size(head))
+               return(NULL);
+
+       for (max = *head;max; max = max->next) {
+
+               // If not big enough we could probably just bail,
+               // instead we'll continue to the next.
+               if (max->length < size)
+                       continue;
+
+               if (max->base & (size - 1)) {
+                       // this one isn't base aligned properly
+                       // so we'll make a new entry and split it up
+                       temp_dword = (max->base | (size-1)) + 1;
+
+                       // Short circuit if adjusted size is too small
+                       if ((max->length - (temp_dword - max->base)) < size)
+                               continue;
+
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+
+                       split_node->base = max->base;
+                       split_node->length = temp_dword - max->base;
+                       max->base = temp_dword;
+                       max->length -= split_node->length;
+
+                       // Put it next in the list
+                       split_node->next = max->next;
+                       max->next = split_node;
+               }
+
+               if ((max->base + max->length) & (size - 1)) {
+                       // this one isn't end aligned properly at the top
+                       // so we'll make a new entry and split it up
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+                       temp_dword = ((max->base + max->length) & ~(size - 1));
+                       split_node->base = temp_dword;
+                       split_node->length = max->length + max->base
+                                                                - split_node->base;
+                       max->length -= split_node->length;
+
+                       // Put it in the list
+                       split_node->next = max->next;
+                       max->next = split_node;
+               }
+
+               // Make sure it didn't shrink too much when we aligned it
+               if (max->length < size)
+                       continue;
+
+               // Now take it out of the list
+               temp = (struct pci_resource*) *head;
+               if (temp == max) {
+                       *head = max->next;
+               } else {
+                       while (temp && temp->next != max) {
+                               temp = temp->next;
+                       }
+
+                       temp->next = max->next;
+               }
+
+               max->next = NULL;
+               return(max);
+       }
+
+       // If we get here, we couldn't find one
+       return(NULL);
+}
+
+
+/*
+ * get_io_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length that is not in the
+ * ISA aliasing window.  If it finds a node larger than "size"
+ * it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size) {
+       struct pci_resource *prevnode;
+       struct pci_resource *node;
+       struct pci_resource *split_node;
+       u32 temp_dword;
+
+       if (!(*head))
+               return(NULL);
+
+       if ( amdshpc_resource_sort_and_combine(head) )
+               return(NULL);
+
+       if ( sort_by_size(head) )
+               return(NULL);
+
+       for (node = *head; node; node = node->next) {
+               if (node->length < size)
+                       continue;
+
+               if (node->base & (size - 1)) {
+                       // this one isn't base aligned properly
+                       // so we'll make a new entry and split it up
+                       temp_dword = (node->base | (size-1)) + 1;
+
+                       // Short circuit if adjusted size is too small
+                       if ((node->length - (temp_dword - node->base)) < size)
+                               continue;
+
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+
+                       split_node->base = node->base;
+                       split_node->length = temp_dword - node->base;
+                       node->base = temp_dword;
+                       node->length -= split_node->length;
+
+                       // Put it in the list
+                       split_node->next = node->next;
+                       node->next = split_node;
+               } // End of non-aligned base
+
+               // Don't need to check if too small since we already did
+               if (node->length > size) {
+                       // this one is longer than we need
+                       // so we'll make a new entry and split it up
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+
+                       split_node->base = node->base + size;
+                       split_node->length = node->length - size;
+                       node->length = size;
+
+                       // Put it in the list
+                       split_node->next = node->next;
+                       node->next = split_node;
+               }  // End of too big on top end
+
+               // For IO make sure it's not in the ISA aliasing space
+               if (node->base & 0x300L)
+                       continue;
+
+               // If we got here, then it is the right size
+               // Now take it out of the list
+               if (*head == node) {
+                       *head = node->next;
+               } else {
+                       prevnode = *head;
+                       while (prevnode->next != node)
+                               prevnode = prevnode->next;
+
+                       prevnode->next = node->next;
+               }
+               node->next = NULL;
+               // Stop looping
+               break;
+       }
+
+       return(node);
+}
+
+
+/*
+ * get_resource
+ *
+ * this function sorts the resource list by size and then
+ * returns the first node of "size" length.  If it finds a node
+ * larger than "size" it will split it up.
+ *
+ * size must be a power of two.
+ */
+static struct pci_resource *get_resource (struct pci_resource **head, u32 size) {
+       struct pci_resource *prevnode;
+       struct pci_resource *node;
+       struct pci_resource *split_node;
+       u32 temp_dword;
+
+       if (!(*head))
+               return(NULL);
+
+       if ( amdshpc_resource_sort_and_combine(head) )
+               return(NULL);
+
+       if ( sort_by_size(head) )
+               return(NULL);
+
+       for (node = *head; node; node = node->next) {
+               dbg("%s: req_size =%x node=%p, base=%x, length=%x\n",__FUNCTION__,
+                       size, node, node->base, node->length);
+               if (node->length < size)
+                       continue;
+
+               if (node->base & (size - 1)) {
+                       dbg("%s: not aligned\n",__FUNCTION__);
+                       // this one isn't base aligned properly
+                       // so we'll make a new entry and split it up
+                       temp_dword = (node->base | (size-1)) + 1;
+
+                       // Short circuit if adjusted size is too small
+                       if ((node->length - (temp_dword - node->base)) < size)
+                               continue;
+
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+
+                       split_node->base = node->base;
+                       split_node->length = temp_dword - node->base;
+                       node->base = temp_dword;
+                       node->length -= split_node->length;
+
+                       // Put it in the list
+                       split_node->next = node->next;
+                       node->next = split_node;
+               } // End of non-aligned base
+
+               // Don't need to check if too small since we already did
+               if (node->length > size) {
+                       dbg("%s: too big\n",__FUNCTION__);
+                       // this one is longer than we need
+                       // so we'll make a new entry and split it up
+                       split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+                       if (!split_node)
+                               return(NULL);
+
+                       split_node->base = node->base + size;
+                       split_node->length = node->length - size;
+                       node->length = size;
+
+                       // Put it in the list
+                       split_node->next = node->next;
+                       node->next = split_node;
+               }  // End of too big on top end
+
+               dbg("%s: got one!!!\n",__FUNCTION__);
+               // If we got here, then it is the right size
+               // Now take it out of the list
+               if (*head == node) {
+                       *head = node->next;
+               } else {
+                       prevnode = *head;
+                       while (prevnode->next != node)
+                               prevnode = prevnode->next;
+
+                       prevnode->next = node->next;
+               }
+               node->next = NULL;
+               // Stop looping
+               break;
+       }
+       return(node);
+}
+
+/*
+ * amdshpc_return_board_resources
+ *
+ * this routine returns all resources allocated to a board to
+ * the available pool.
+ *
+ * returns 0 if success
+ */
+int amdshpc_return_board_resources(struct pci_func * func, struct resource_lists * resources)
+{
+       int rc = 1;
+       struct pci_resource *node;
+       struct pci_resource *t_node;
+       dbg("%s",__FUNCTION__);
+
+       if (!func)
+               return(1);
+
+       node = func->io_head;
+       func->io_head = NULL;
+       while (node) {
+               t_node = node->next;
+               return_resource(&(resources->io_head), node);
+               node = t_node;
+       }
+
+       node = func->mem_head;
+       func->mem_head = NULL;
+       while (node) {
+               t_node = node->next;
+               return_resource(&(resources->mem_head), node);
+               node = t_node;
+       }
+
+       node = func->p_mem_head;
+       func->p_mem_head = NULL;
+       while (node) {
+               t_node = node->next;
+               return_resource(&(resources->p_mem_head), node);
+               node = t_node;
+       }
+
+       node = func->bus_head;
+       func->bus_head = NULL;
+       while (node) {
+               t_node = node->next;
+               return_resource(&(resources->bus_head), node);
+               node = t_node;
+       }
+
+       rc |= amdshpc_resource_sort_and_combine(&(resources->mem_head));
+       rc |= amdshpc_resource_sort_and_combine(&(resources->p_mem_head));
+       rc |= amdshpc_resource_sort_and_combine(&(resources->io_head));
+       rc |= amdshpc_resource_sort_and_combine(&(resources->bus_head));
+
+       return(rc);
+}
+
+
+/*
+ * amdshpc_destroy_resource_list
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void amdshpc_destroy_resource_list (struct resource_lists * resources)
+{
+       struct pci_resource *res, *tres;
+
+       res = resources->io_head;
+       resources->io_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = resources->mem_head;
+       resources->mem_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = resources->p_mem_head;
+       resources->p_mem_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = resources->bus_head;
+       resources->bus_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+}
+
+
+/*
+ * amdshpc_destroy_board_resources
+ *
+ * Puts node back in the resource list pointed to by head
+ */
+void amdshpc_destroy_board_resources (struct pci_func * func)
+{
+       struct pci_resource *res, *tres;
+
+       res = func->io_head;
+       func->io_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = func->mem_head;
+       func->mem_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = func->p_mem_head;
+       func->p_mem_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+
+       res = func->bus_head;
+       func->bus_head = NULL;
+
+       while (res) {
+               tres = res;
+               res = res->next;
+               kfree(tres);
+       }
+}
+
+/**
+ * configure_new_device - Configures the PCI header information of one board.
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Returns 0 if success
+ *
+ */
+static u32 configure_new_device (struct controller * ctrl, struct pci_func * func,
+                                                                u8 behind_bridge, struct resource_lists * resources)
+{
+       u8 temp_byte, function, max_functions, stop_it;
+       int rc;
+       u32 ID;
+       struct pci_func *new_slot;
+       int index;
+
+       new_slot = func;
+
+       dbg("%s",__FUNCTION__);
+       // Check for Multi-function device
+       rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
+       if (rc) {
+               dbg("%s: rc = %d\n",__FUNCTION__, rc);
+               return rc;
+       }
+
+       if (temp_byte & 0x80)   // Multi-function device
+               max_functions = 8;
+       else
+               max_functions = 1;
+
+       function = 0;
+
+       do {
+               rc = configure_new_function(ctrl, new_slot, behind_bridge, resources);
+
+               if (rc) {
+                       dbg("%s -->configure_new_function failed %d\n",__FUNCTION__,rc);
+                       index = 0;
+
+                       while (new_slot) {
+                               new_slot = amdshpc_slot_find(new_slot->bus, new_slot->device, index++);
+
+                               if (new_slot)
+                                       amdshpc_return_board_resources(new_slot, resources);
+                       }
+
+                       return(rc);
+               }
+
+               function++;
+
+               stop_it = 0;
+
+               //  The following loop skips to the next present function
+               //  and creates a board structure
+
+               while ((function < max_functions) && (!stop_it)) {
+                       pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
+
+                       if (ID == 0xFFFFFFFF) {   // There's nothing there.
+                               function++;
+                       } else {  // There's something there
+                               // Setup slot structure.
+                               new_slot = amdshpc_slot_create(func->bus);
+
+                               if (new_slot == NULL) {
+                                       // Out of memory
+                                       return(1);
+                               }
+
+                               new_slot->bus = func->bus;
+                               new_slot->device = func->device;
+                               new_slot->function = function;
+                               new_slot->is_a_board = 1;
+                               new_slot->status = 0;
+
+                               stop_it++;
+                       }
+               }
+
+       } while (function < max_functions);
+       dbg("%s -->returning from configure_new_device\n",__FUNCTION__);
+
+       return 0;
+}
+
+
+/*
+  Configuration logic that involves the hotplug data structures and
+  their bookkeeping
+ */
+
+
+/**
+ * configure_new_function - Configures the PCI header information of one device
+ *
+ * @ctrl: pointer to controller structure
+ * @func: pointer to function structure
+ * @behind_bridge: 1 if this is a recursive call, 0 if not
+ * @resources: pointer to set of resource lists
+ *
+ * Calls itself recursively for bridged devices.
+ * Returns 0 if success
+ *
+ */
+static int configure_new_function (struct controller * ctrl, struct pci_func * func,
+                                                                  u8 behind_bridge, struct resource_lists * resources)
+{
+       int cloop;
+       u8 IRQ;
+       u8 temp_byte;
+       u8 device;
+       u8 class_code;
+       u16 command;
+       u16 temp_word;
+       u32 temp_dword;
+       u32 rc;
+       u32 temp_register;
+       u32 base;
+       u32 ID;
+       struct pci_resource *mem_node;
+       struct pci_resource *p_mem_node;
+       struct pci_resource *io_node;
+       struct pci_resource *bus_node;
+       struct pci_resource *hold_mem_node;
+       struct pci_resource *hold_p_mem_node;
+       struct pci_resource *hold_IO_node;
+       struct pci_resource *hold_bus_node;
+       struct irq_mapping irqs;
+       struct pci_func *new_slot;
+       struct resource_lists temp_resources;
+       int devfn = PCI_DEVFN(func->device, func->function);
+
+       dbg("%s", __FUNCTION__);
+       // Check for Bridge
+       rc = pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
+       if (rc)
+               return rc;
+
+       if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     // PCI-PCI Bridge
+               // set Primary bus
+               dbg("%s -->set Primary bus = %d\n",__FUNCTION__, func->bus);
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
+               if (rc)
+                       return rc;
+
+               // find range of busses to use
+               dbg("%s -->find ranges of buses to use\n",__FUNCTION__);
+               bus_node = get_max_resource(&resources->bus_head, 1);
+
+               // If we don't have any busses to allocate, we can't continue
+               if (!bus_node)
+                       return -ENOMEM;
+
+               // set Secondary bus
+               temp_byte = bus_node->base;
+               dbg("%s -->set Secondary bus = %d\n",__FUNCTION__, bus_node->base);
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
+               if (rc)
+                       return rc;
+
+               // set subordinate bus
+               temp_byte = bus_node->base + bus_node->length - 1;
+               dbg("%s -->set subordinate bus = %d\n",__FUNCTION__, bus_node->base + bus_node->length - 1);
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+               if (rc)
+                       return rc;
+
+               // set subordinate Latency Timer and base Latency Timer
+               temp_byte = 0x40;
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
+               if (rc)
+                       return rc;
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+               if (rc)
+                       return rc;
+
+               // set Cache Line size
+               temp_byte = 0x08;
+               rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+               if (rc)
+                       return rc;
+
+               // Setup the IO, memory, and prefetchable windows
+
+               io_node = get_max_resource(&(resources->io_head), 0x1000);
+               mem_node = get_max_resource(&(resources->mem_head), 0x100000);
+               p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000);
+               dbg("%s -->Setup the IO, memory, and prefetchable windows\n",__FUNCTION__);
+               dbg("%s -->io_node\n",__FUNCTION__);
+               dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, io_node->base, io_node->length, io_node->next);
+               dbg("%s -->mem_node\n",__FUNCTION__);
+               dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, mem_node->base, mem_node->length, mem_node->next);
+               dbg("%s -->p_mem_node\n",__FUNCTION__);
+               dbg("%s -->(base, len, next) (%x, %x, %p)\n",__FUNCTION__, p_mem_node->base, p_mem_node->length, p_mem_node->next);
+
+               // set up the IRQ info
+               if (!resources->irqs) {
+                       irqs.barber_pole = 0;
+                       irqs.interrupt[0] = 0;
+                       irqs.interrupt[1] = 0;
+                       irqs.interrupt[2] = 0;
+                       irqs.interrupt[3] = 0;
+                       irqs.valid_INT = 0;
+               } else {
+                       irqs.barber_pole = resources->irqs->barber_pole;
+                       irqs.interrupt[0] = resources->irqs->interrupt[0];
+                       irqs.interrupt[1] = resources->irqs->interrupt[1];
+                       irqs.interrupt[2] = resources->irqs->interrupt[2];
+                       irqs.interrupt[3] = resources->irqs->interrupt[3];
+                       irqs.valid_INT = resources->irqs->valid_INT;
+               }
+
+               // set up resource lists that are now aligned on top and bottom
+               // for anything behind the bridge.
+               temp_resources.bus_head = bus_node;
+               temp_resources.io_head = io_node;
+               temp_resources.mem_head = mem_node;
+               temp_resources.p_mem_head = p_mem_node;
+               temp_resources.irqs = &irqs;
+
+               // Make copies of the nodes we are going to pass down so that
+               // if there is a problem,we can just use these to free resources
+               hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+               hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+               hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+               hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+
+               if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
+                       if (hold_bus_node)
+                               kfree(hold_bus_node);
+                       if (hold_IO_node)
+                               kfree(hold_IO_node);
+                       if (hold_mem_node)
+                               kfree(hold_mem_node);
+                       if (hold_p_mem_node)
+                               kfree(hold_p_mem_node);
+
+                       return(1);
+               }
+
+               memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
+
+               bus_node->base += 1;
+               bus_node->length -= 1;
+               bus_node->next = NULL;
+
+               // If we have IO resources copy them and fill in the bridge's
+               // IO range registers
+               if (io_node) {
+                       memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
+                       io_node->next = NULL;
+
+                       // set IO base and Limit registers
+                       temp_byte = io_node->base >> 8;
+                       rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+                       temp_byte = (io_node->base + io_node->length - 1) >> 8;
+                       rc = pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+               } else {
+                       kfree(hold_IO_node);
+                       hold_IO_node = NULL;
+               }
+
+               // If we have memory resources copy them and fill in the bridge's
+               // memory range registers.  Otherwise, fill in the range
+               // registers with values that disable them.
+               if (mem_node) {
+                       memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
+                       mem_node->next = NULL;
+
+                       // set Mem base and Limit registers
+                       temp_word = mem_node->base >> 16;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+                       temp_word = (mem_node->base + mem_node->length - 1) >> 16;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+               } else {
+                       temp_word = 0xFFFF;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+                       temp_word = 0x0000;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+                       kfree(hold_mem_node);
+                       hold_mem_node = NULL;
+               }
+
+               // If we have prefetchable memory resources copy them and
+               // fill in the bridge's memory range registers.  Otherwise,
+               // fill in the range registers with values that disable them.
+               if (p_mem_node) {
+                       memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
+                       p_mem_node->next = NULL;
+
+                       // set Pre Mem base and Limit registers
+                       temp_word = p_mem_node->base >> 16;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+                       temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+               } else {
+                       temp_word = 0xFFFF;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+                       temp_word = 0x0000;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+                       kfree(hold_p_mem_node);
+                       hold_p_mem_node = NULL;
+               }
+
+               // Adjust this to compensate for extra adjustment in first loop
+               irqs.barber_pole--;
+
+               rc = 0;
+
+               // Here we actually find the devices and configure them
+               for (device = 0; (device <= 0x1F) && !rc; device++) {
+                       irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
+
+                       ID = 0xFFFFFFFF;
+                       pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x00, &ID);
+
+                       if (ID != 0xFFFFFFFF) {   //  device Present
+                               // Setup slot structure.
+                               new_slot = amdshpc_slot_create(hold_bus_node->base);
+
+                               if (new_slot == NULL) {
+                                       // Out of memory
+                                       rc = -ENOMEM;
+                                       continue;
+                               }
+
+                               new_slot->bus = hold_bus_node->base;
+                               new_slot->device = device;
+                               new_slot->function = 0;
+                               new_slot->is_a_board = 1;
+                               new_slot->status = 0;
+
+                               rc = configure_new_device(ctrl, new_slot, 1, &temp_resources);
+                               dbg("%s -->configure_new_device rc=0x%x\n",__FUNCTION__,rc);
+                       }       // End of IF (device in slot?)
+               }               // End of FOR loop
+
+               if (rc) {
+                       amdshpc_destroy_resource_list(&temp_resources);
+
+                       return_resource(&(resources->bus_head), hold_bus_node);
+                       return_resource(&(resources->io_head), hold_IO_node);
+                       return_resource(&(resources->mem_head), hold_mem_node);
+                       return_resource(&(resources->p_mem_head), hold_p_mem_node);
+                       return(rc);
+               }
+               // save the interrupt routing information
+               if (resources->irqs) {
+                       resources->irqs->interrupt[0] = irqs.interrupt[0];
+                       resources->irqs->interrupt[1] = irqs.interrupt[1];
+                       resources->irqs->interrupt[2] = irqs.interrupt[2];
+                       resources->irqs->interrupt[3] = irqs.interrupt[3];
+                       resources->irqs->valid_INT = irqs.valid_INT;
+               } else if (!behind_bridge) {
+                       // We need to hook up the interrupts here
+                       for (cloop = 0; cloop < 4; cloop++) {
+                               if (irqs.valid_INT & (0x01 << cloop)) {
+rc=0;
+//                                     rc = amdshpc_set_irq(func->bus, func->device,
+//                                                                        0x0A + cloop, irqs.interrupt[cloop]);
+                                       if (rc) {
+                                               amdshpc_destroy_resource_list (&temp_resources);
+
+                                               return_resource(&(resources-> bus_head), hold_bus_node);
+                                               return_resource(&(resources-> io_head), hold_IO_node);
+                                               return_resource(&(resources-> mem_head), hold_mem_node);
+                                               return_resource(&(resources-> p_mem_head), hold_p_mem_node);
+                                               return rc;
+                                       }
+                               }
+                       }       // end of for loop
+               }
+               // Return unused bus resources
+               // First use the temporary node to store information for the board
+               if (hold_bus_node && bus_node && temp_resources.bus_head) {
+                       hold_bus_node->length = bus_node->base - hold_bus_node->base;
+
+                       hold_bus_node->next = func->bus_head;
+                       func->bus_head = hold_bus_node;
+
+                       temp_byte = temp_resources.bus_head->base - 1;
+
+                       // set subordinate bus
+                       pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
+
+                       if (temp_resources.bus_head->length == 0) {
+                               kfree(temp_resources.bus_head);
+                               temp_resources.bus_head = NULL;
+                       } else {
+                               return_resource(&(resources->bus_head), temp_resources.bus_head);
+                       }
+               }
+
+               // If we have IO space available and there is some left,
+               // return the unused portion
+               if (hold_IO_node && temp_resources.io_head) {
+                       io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
+                                                                                                  &hold_IO_node, 0x1000);
+
+                       // Check if we were able to split something off
+                       if (io_node) {
+                               hold_IO_node->base = io_node->base + io_node->length;
+
+                               temp_byte = (hold_IO_node->base) >> 8;
+                               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_BASE, temp_byte);
+
+                               return_resource(&(resources->io_head), io_node);
+                       }
+
+                       io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
+
+                       // Check if we were able to split something off
+                       if (io_node) {
+                               // First use the temporary node to store information for the board
+                               hold_IO_node->length = io_node->base - hold_IO_node->base;
+
+                               // If we used any, add it to the board's list
+                               if (hold_IO_node->length) {
+                                       hold_IO_node->next = func->io_head;
+                                       func->io_head = hold_IO_node;
+
+                                       temp_byte = (io_node->base - 1) >> 8;
+                                       pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
+
+                                       return_resource(&(resources->io_head), io_node);
+                               } else {
+                                       // it doesn't need any IO
+                                       temp_word = 0x0000;
+                                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_IO_LIMIT, temp_word);
+
+                                       return_resource(&(resources->io_head), io_node);
+                                       kfree(hold_IO_node);
+                               }
+                       } else {
+                               // it used most of the range
+                               hold_IO_node->next = func->io_head;
+                               func->io_head = hold_IO_node;
+                       }
+               } else if (hold_IO_node) {
+                       // it used the whole range
+                       hold_IO_node->next = func->io_head;
+                       func->io_head = hold_IO_node;
+               }
+               // If we have memory space available and there is some left,
+               // return the unused portion
+               if (hold_mem_node && temp_resources.mem_head) {
+                       mem_node = do_pre_bridge_resource_split(&(temp_resources.  mem_head),
+                                                                                                       &hold_mem_node, 0x100000);
+
+                       // Check if we were able to split something off
+                       if (mem_node) {
+                               hold_mem_node->base = mem_node->base + mem_node->length;
+
+                               temp_word = (hold_mem_node->base) >> 16;
+                               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
+
+                               return_resource(&(resources->mem_head), mem_node);
+                       }
+
+                       mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000);
+
+                       // Check if we were able to split something off
+                       if (mem_node) {
+                               // First use the temporary node to store information for the board
+                               hold_mem_node->length = mem_node->base - hold_mem_node->base;
+
+                               if (hold_mem_node->length) {
+                                       hold_mem_node->next = func->mem_head;
+                                       func->mem_head = hold_mem_node;
+
+                                       // configure end address
+                                       temp_word = (mem_node->base - 1) >> 16;
+                                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+                                       // Return unused resources to the pool
+                                       return_resource(&(resources->mem_head), mem_node);
+                               } else {
+                                       // it doesn't need any Mem
+                                       temp_word = 0x0000;
+                                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
+
+                                       return_resource(&(resources->mem_head), mem_node);
+                                       kfree(hold_mem_node);
+                               }
+                       } else {
+                               // it used most of the range
+                               hold_mem_node->next = func->mem_head;
+                               func->mem_head = hold_mem_node;
+                       }
+               } else if (hold_mem_node) {
+                       // it used the whole range
+                       hold_mem_node->next = func->mem_head;
+                       func->mem_head = hold_mem_node;
+               }
+               // If we have prefetchable memory space available and there is some
+               // left at the end, return the unused portion
+               if (hold_p_mem_node && temp_resources.p_mem_head) {
+                       p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
+                                                                                                         &hold_p_mem_node, 0x100000);
+
+                       // Check if we were able to split something off
+                       if (p_mem_node) {
+                               hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
+
+                               temp_word = (hold_p_mem_node->base) >> 16;
+                               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
+
+                               return_resource(&(resources->p_mem_head), p_mem_node);
+                       }
+
+                       p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000);
+
+                       // Check if we were able to split something off
+                       if (p_mem_node) {
+                               // First use the temporary node to store information for the board
+                               hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
+
+                               // If we used any, add it to the board's list
+                               if (hold_p_mem_node->length) {
+                                       hold_p_mem_node->next = func->p_mem_head;
+                                       func->p_mem_head = hold_p_mem_node;
+
+                                       temp_word = (p_mem_node->base - 1) >> 16;
+                                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+                                       return_resource(&(resources->p_mem_head), p_mem_node);
+                               } else {
+                                       // it doesn't need any PMem
+                                       temp_word = 0x0000;
+                                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
+
+                                       return_resource(&(resources->p_mem_head), p_mem_node);
+                                       kfree(hold_p_mem_node);
+                               }
+                       } else {
+                               // it used the most of the range
+                               hold_p_mem_node->next = func->p_mem_head;
+                               func->p_mem_head = hold_p_mem_node;
+                       }
+               } else if (hold_p_mem_node) {
+                       // it used the whole range
+                       hold_p_mem_node->next = func->p_mem_head;
+                       func->p_mem_head = hold_p_mem_node;
+               }
+               // We should be configuring an IRQ and the bridge's base address
+               // registers if it needs them.  Although we have never seen such
+               // a device
+
+               // enable card
+               command = 0x0157;       // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |  PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command);
+
+               // set Bridge Control Register
+               command = 0x07;         // = PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | PCI_BRIDGE_CTL_NO_ISA
+               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+       } else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+               // Standard device
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+
+               if (class_code == PCI_BASE_CLASS_DISPLAY) {
+                       // Display (video) adapter (not supported)
+                       return(DEVICE_TYPE_NOT_SUPPORTED);
+               }
+               // Figure out IO and memory needs
+               for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+                       temp_register = 0xFFFFFFFF;
+
+                       dbg("%s -->CND: devfn=%x, offset=%d\n",__FUNCTION__, devfn, cloop);
+                       pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                       pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp_register);
+                       dbg("%s -->CND: base = 0x%x\n",__FUNCTION__, temp_register);
+
+                       if (temp_register) {      // If this register is implemented
+                               if ((temp_register & 0x03L) == 0x01) {
+                                       // Map IO
+
+                                       // set base = amount of IO space
+                                       base = temp_register & 0xFFFFFFFC;
+                                       base = ~base + 1;
+
+                                       dbg("%s -->CND:      length = 0x%x\n",__FUNCTION__, base);
+                                       io_node = get_io_resource(&(resources->io_head), base);
+                                       dbg("%s -->Got io_node start = %8.8x, length = %8.8x next (%p)\n",__FUNCTION__,
+                                               io_node->base, io_node->length, io_node->next);
+                                       dbg("%s -->func (%p) io_head (%p)\n",__FUNCTION__, func, func->io_head);
+
+                                       // allocate the resource to the board
+                                       if (io_node) {
+                                               base = io_node->base;
+
+                                               io_node->next = func->io_head;
+                                               func->io_head = io_node;
+                                       } else
+                                               return -ENOMEM;
+                               } else if ((temp_register & 0x0BL) == 0x08) {
+                                       // Map prefetchable memory
+                                       base = temp_register & 0xFFFFFFF0;
+                                       base = ~base + 1;
+
+                                       dbg("%s -->CND:      length = 0x%x\n",__FUNCTION__, base);
+                                       p_mem_node = get_resource(&(resources->p_mem_head), base);
+
+                                       // allocate the resource to the board
+                                       if (p_mem_node) {
+                                               base = p_mem_node->base;
+
+                                               p_mem_node->next = func->p_mem_head;
+                                               func->p_mem_head = p_mem_node;
+                                       } else
+                                               return -ENOMEM;
+                               } else if ((temp_register & 0x0BL) == 0x00) {
+                                       // Map memory
+                                       base = temp_register & 0xFFFFFFF0;
+                                       base = ~base + 1;
+
+                                       dbg("%s -->CND:      length = 0x%x\n",__FUNCTION__, base);
+                                       mem_node = get_resource(&(resources->mem_head), base);
+
+                                       // allocate the resource to the board
+                                       if (mem_node) {
+                                               base = mem_node->base;
+
+                                               mem_node->next = func->mem_head;
+                                               func->mem_head = mem_node;
+                                       } else
+                                               return -ENOMEM;
+                               } else if ((temp_register & 0x0BL) == 0x04) {
+                                       // Map memory
+                                       base = temp_register & 0xFFFFFFF0;
+                                       base = ~base + 1;
+
+                                       dbg("%s -->CND:      length = 0x%x\n",__FUNCTION__, base);
+                                       mem_node = get_resource(&(resources->mem_head), base);
+
+                                       // allocate the resource to the board
+                                       if (mem_node) {
+                                               base = mem_node->base;
+
+                                               mem_node->next = func->mem_head;
+                                               func->mem_head = mem_node;
+                                       } else
+                                               return -ENOMEM;
+                               } else if ((temp_register & 0x0BL) == 0x06) {
+                                       // Those bits are reserved, we can't handle this
+                                       return(1);
+                               } else {
+                                       // Requesting space below 1M
+                                       return(NOT_ENOUGH_RESOURCES);
+                               }
+
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base);
+
+                               // Check for 64-bit base
+                               if ((temp_register & 0x07L) == 0x04) {
+                                       cloop += 4;
+
+                                       // Upper 32 bits of address always zero on today's systems
+                                       // FIXME this is probably not true on Alpha and ia64???
+                                       base = 0;
+                                       pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, base);
+                               }
+                       }
+               }               // End of base register loop
+
+               // Figure out which interrupt pin this function uses
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte);
+               dbg("%s temp_byte for interrupt pin = %x", __FUNCTION__, temp_byte);
+               // If this function needs an interrupt and we are behind a bridge
+               // and the pin is tied to something that's already mapped,
+               // set this one the same
+               if (temp_byte && resources->irqs &&
+                       (resources->irqs->valid_INT &
+                        (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
+                       // We have to share with something already set up
+                       IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03];
+                       dbg("%s We're sharing the IRQ from some other device = %02x", __FUNCTION__, IRQ);
+               } else {
+                       // Program IRQ based on card type
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, 0x0B, &class_code);
+                       if (class_code == PCI_BASE_CLASS_STORAGE) {
+                               dbg("%s We're sharing the disk IRQ (maybe)", __FUNCTION__);
+                               IRQ = amdshpc_disk_irq;
+                       } else {
+                               dbg("%s We're sharing the NIC IRQ (maybe)", __FUNCTION__);
+                               IRQ = amdshpc_nic_irq;
+                       }
+               }
+
+               // IRQ Line
+               pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
+               if (!behind_bridge) {
+//                     rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
+//                     rc = amdshpc_set_irq(func->bus, func->device, temp_byte + 20, IRQ);
+                       rc = 0;
+                       if (rc)
+                               return 1;
+               } else {
+                       //TBD - this code may also belong in the other clause of this If statement
+                       resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
+                       resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
+               }
+
+               // Latency Timer
+               temp_byte = 0x40;
+               pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
+
+               // Cache Line size
+               temp_byte = 0x08;
+               pci_bus_write_config_byte(ctrl->pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
+
+               // disable ROM base Address
+               temp_dword = 0x00L;
+               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_ROM_ADDRESS, temp_dword);
+
+               // enable card
+               temp_word = 0x0157;     // = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |  PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR
+               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, temp_word);
+       }                       // End of Not-A-Bridge else
+       else {
+               // It's some strange type of PCI adapter (Cardbus?)
+               return DEVICE_TYPE_NOT_SUPPORTED;
+       }
+
+       func->configured = 1;
+
+       return 0;
+}
+
+int amdshpc_configure_device (struct controller * ctrl, struct pci_func* func)
+{
+       unsigned char bus;
+       struct pci_dev dev0;
+       struct pci_bus *child;
+       int num;
+
+       memset(&dev0, 0, sizeof(struct pci_dev));
+       dbg("%s", __FUNCTION__);
+
+       if (func->pci_dev == NULL)
+               func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+
+       //Still NULL ? Well then scan for it !
+       if (func->pci_dev == NULL) {
+               num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function));
+               if (num)
+                       pci_bus_add_devices(ctrl->pci_dev->bus);
+
+               func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
+               if (func->pci_dev == NULL) {
+                       dbg("ERROR: pci_dev still null\n");
+                       return 0;
+               }
+       }
+
+       if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
+               pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
+               child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
+               pci_do_scan_bus(child);
+
+       }
+
+       return 0;
+}
+
+
+int amdshpc_unconfigure_device(struct pci_func* func)
+{
+       int j;
+
+       dbg("%s: bus/dev/func = %x/%x/%x\n",__FUNCTION__,func->bus, func->device, func->function);
+
+       for (j=0; j<8 ; j++) {
+               struct pci_dev* temp = pci_find_slot(func->bus, (func->device << 3) | j);
+               if (temp)
+                       pci_remove_bus_device(temp);
+       }
+       return 0;
+}
+
+/*
+static int PCI_RefinedAccessConfig(struct pci_ops *ops, u8 bus, u8 device, u8 function, u8 offset, u32 *value)
+{
+       u32 vendID = 0;
+
+       dbg("%s", __FUNCTION__);
+       if (pci_read_config_dword_nodev (ops, bus, device, function, PCI_VENDOR_ID, &vendID) == -1)
+               return -1;
+       if (vendID == 0xffffffff)
+               return -1;
+       return pci_read_config_dword_nodev (ops, bus, device, function, offset, value);
+}
+
+
+//
+// WTF??? This function isn't in the code, yet a function calls it, but the
+// compiler optimizes it away?  strange.  Here as a placeholder to keep the
+// compiler happy.
+//
+static int PCI_ScanBusNonBridge (u8 bus, u8 device)
+{
+       return 0;
+}
+
+static int PCI_ScanBusForNonBridge(struct controller  *ctrl, u8 bus_num, u8 * dev_num)
+{
+       u8 tdevice;
+       u32 work;
+       u8 tbus;
+
+       dbg("%s", __FUNCTION__);
+       for (tdevice = 0; tdevice < 0x100; tdevice++) {
+               //Scan for access first
+               if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1)
+                       continue;
+               dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
+               //Yep we got one. Not a bridge ?
+               if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
+                       *dev_num = tdevice;
+                       dbg("found it !\n");
+                       return 0;
+               }
+       }
+       for (tdevice = 0; tdevice < 0x100; tdevice++) {
+               //Scan for access first
+               if (PCI_RefinedAccessConfig(ctrl->pci_ops, bus_num, tdevice >> 3, tdevice & 0x7, 0x08, &work) == -1)
+                       continue;
+               dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
+               //Yep we got one. bridge ?
+               if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+                       pci_read_config_byte_nodev (ctrl->pci_ops, tbus, tdevice, 0, PCI_SECONDARY_BUS, &tbus);
+                       dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
+                       if (PCI_ScanBusNonBridge(tbus, tdevice) == 0)
+                               return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int PCI_GetBusDevHelper(struct controller  *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
+{
+       struct irq_routing_table *PCIIRQRoutingInfoLength;
+       long len;
+       long loop;
+       u32 work;
+
+       u8 tbus, tdevice, tslot;
+
+       PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
+
+       len = (PCIIRQRoutingInfoLength->size -
+              sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+       dbg("%s  len = %d",__FUNCTION__, (int)len);
+       // Make sure I got at least one entry
+       if (len == 0) {
+               if (PCIIRQRoutingInfoLength != NULL)
+                       kfree(PCIIRQRoutingInfoLength );
+               return -1;
+       }
+
+       for (loop = 0; loop < len; ++loop) {
+               tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
+               tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn;
+               tslot = PCIIRQRoutingInfoLength->slots[loop].slot;
+               dbg("%s  tbus = %02Xh  tdevice = %02Xh  device = %02Xh function = %d  tslot = %d",__FUNCTION__,
+                                                       tbus, tdevice, tdevice >>3, tdevice & 0x7, tslot);
+               if (tslot == slot) {
+                       *bus_num = tbus;
+                       *dev_num = tdevice;
+                       pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_VENDOR_ID, &work);
+                       if (!nobridge || (work == 0xffffffff)) {
+                               if (PCIIRQRoutingInfoLength != NULL)
+                                       dbg("%s PCIIRQRoutingInfoLength != NULL  returning 0",__FUNCTION__);
+                                       kfree(PCIIRQRoutingInfoLength );
+                               return 0;
+                       }
+
+                       dbg("bus_num %d dev_num %d func_num %d\n", *bus_num, *dev_num >> 3, *dev_num & 0x7);
+                       pci_read_config_dword_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_CLASS_REVISION, &work);
+                       dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS);
+
+                       if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
+                               pci_read_config_byte_nodev (ctrl->pci_ops, *bus_num, *dev_num >> 3, *dev_num & 0x7, PCI_SECONDARY_BUS, &tbus);
+                               dbg("Scan bus for Non Bridge: bus %d\n", tbus);
+                               if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
+                                       *bus_num = tbus;
+                                       if (PCIIRQRoutingInfoLength != NULL)
+                                               kfree(PCIIRQRoutingInfoLength );
+                                       return 0;
+                               }
+                       } else {
+                               if (PCIIRQRoutingInfoLength != NULL)
+                                       kfree(PCIIRQRoutingInfoLength );
+                               return 0;
+                       }
+
+               }
+       }
+       if (PCIIRQRoutingInfoLength != NULL)
+               kfree(PCIIRQRoutingInfoLength );
+       return -1;
+}
+
+
+int amdshpc_get_bus_dev (struct controller  *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
+{
+       dbg("%s", __FUNCTION__);
+       return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0);    //plain (bridges allowed)
+}
+*/
+
+/* More PCI configuration routines; this time centered around hotplug controller */
+
+
+/*
+ * amdshpc_save_slot_config
+ *
+ * Saves configuration info for all PCI devices in a given slot
+ * including subordinate busses.
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_slot_config (struct controller  *ctrl, struct pci_func * new_slot)
+{
+       long rc;
+       u8 class_code;
+       u8 header_type;
+       u32 ID;
+       u8 secondary_bus;
+       int sub_bus;
+       int max_functions;
+       int function;
+       int cloop = 0;
+       int stop_it;
+
+       ID = 0xFFFFFFFF;
+
+       dbg("%s", __FUNCTION__);
+       pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
+
+       if (ID != 0xFFFFFFFF) {   //  device in slot
+               pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
+               pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
+
+               if (header_type & 0x80) // Multi-function device
+                       max_functions = 8;
+               else
+                       max_functions = 1;
+
+               function = 0;
+
+               do {
+                       if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     // PCI-PCI Bridge
+                               //  Recurse the subordinate bus
+                               pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
+
+                               sub_bus = (int) secondary_bus;
+
+                               // Save the config headers for the secondary bus.
+                               rc = amdshpc_save_config(ctrl, sub_bus, 0);
+
+                               if (rc)
+                                       return(rc);
+
+                       }       // End of IF
+
+                       new_slot->status = 0;
+
+                       for (cloop = 0; cloop < 0x20; cloop++) {
+                               pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
+                       }
+
+                       function++;
+
+                       stop_it = 0;
+
+                       //  this loop skips to the next present function
+                       //  reading in the Class Code and the Header type.
+
+                       while ((function < max_functions) && (!stop_it)) {
+                               pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
+
+                               if (ID == 0xFFFFFFFF) {  // nothing there.
+                                       function++;
+                               } else {  // Something there
+                                       pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
+                                       pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
+
+                                       stop_it++;
+                               }
+                       }
+
+               } while (function < max_functions);
+       }                       // End of IF (device in slot?)
+       else {
+               return(2);
+       }
+
+       return(0);
+}
+
+
+/*
+ * amdshpc_save_base_addr_length
+ *
+ * Saves the length of all base address registers for the
+ * specified slot.  this is for hot plug REPLACE
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_base_addr_length(struct controller  *ctrl, struct pci_func * func)
+{
+       u8 cloop;
+       u8 header_type;
+       u8 secondary_bus;
+       u8 type;
+       int sub_bus;
+       u32 temp_register;
+       u32 base;
+       u32 rc;
+       struct pci_func *next;
+       int index = 0;
+
+       dbg("%s", __FUNCTION__);
+       func = amdshpc_slot_find(func->bus, func->device, index++);
+
+       while (func != NULL) {
+               int devfn = PCI_DEVFN(func->device, func->function);
+
+               // Check for Bridge
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+               if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
+                       // PCI-PCI Bridge
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+                       sub_bus = (int) secondary_bus;
+
+                       next = amdshpc_slot_list[sub_bus];
+
+                       while (next != NULL) {
+                               rc = amdshpc_save_base_addr_length(ctrl, next);
+
+                               if (rc)
+                                       return(rc);
+
+                               next = next->next;
+                       }
+
+                       //FIXME: this loop is duplicated in the non-bridge case.  The two could be rolled together
+                       // Figure out IO and memory base lengths
+                       for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+                               temp_register = 0xFFFFFFFF;
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+                               if (base) {  // If this register is implemented
+                                       if (base & 0x01L) {
+                                               // IO base
+                                               // set base = amount of IO space requested
+                                               base = base & 0xFFFFFFFE;
+                                               base = (~base) + 1;
+
+                                               type = 1;
+                                       } else {
+                                               // memory base
+                                               base = base & 0xFFFFFFF0;
+                                               base = (~base) + 1;
+
+                                               type = 0;
+                                       }
+                               } else {
+                                       base = 0x0L;
+                                       type = 0;
+                               }
+
+                               // Save information in slot structure
+                               func->base_length[(cloop - 0x10) >> 2] = base;
+                               func->base_type[(cloop - 0x10) >> 2] = type;
+
+                       }       // End of base register loop
+
+
+               } else if ((header_type & 0x7F) == 0x00) {        // PCI-PCI Bridge
+                       // Figure out IO and memory base lengths
+                       for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+                               temp_register = 0xFFFFFFFF;
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+                               if (base) {  // If this register is implemented
+                                       if (base & 0x01L) {
+                                               // IO base
+                                               // base = amount of IO space requested
+                                               base = base & 0xFFFFFFFE;
+                                               base = (~base) + 1;
+
+                                               type = 1;
+                                       } else {
+                                               // memory base
+                                               // base = amount of memory space requested
+                                               base = base & 0xFFFFFFF0;
+                                               base = (~base) + 1;
+
+                                               type = 0;
+                                       }
+                               } else {
+                                       base = 0x0L;
+                                       type = 0;
+                               }
+
+                               // Save information in slot structure
+                               func->base_length[(cloop - 0x10) >> 2] = base;
+                               func->base_type[(cloop - 0x10) >> 2] = type;
+
+                       }       // End of base register loop
+
+               } else {          // Some other unknown header type
+               }
+
+               // find the next device in this slot
+               func = amdshpc_slot_find(func->bus, func->device, index++);
+       }
+
+       return(0);
+}
+
+
+/*
+ * amdshpc_save_used_resources
+ *
+ * Stores used resource information for existing boards.  this is
+ * for boards that were in the system when this driver was loaded.
+ * this function is for hot plug ADD
+ *
+ * returns 0 if success
+ */
+int amdshpc_save_used_resources (struct controller  *ctrl, struct pci_func * func)
+{
+       u8 cloop;
+       u8 header_type;
+       u8 secondary_bus;
+       u8 temp_byte;
+       u8 b_base;
+       u8 b_length;
+       u16 command;
+       u16 save_command;
+       u16 w_base;
+       u16 w_length;
+       u32 temp_register;
+       u32 save_base;
+       u32 base;
+       int index = 0;
+       struct pci_resource *mem_node;
+       struct pci_resource *p_mem_node;
+       struct pci_resource *io_node;
+       struct pci_resource *bus_node;
+
+       dbg("%s", __FUNCTION__);
+       func = amdshpc_slot_find(func->bus, func->device, index++);
+
+       while ((func != NULL) && func->is_a_board) {
+               int devfn = PCI_DEVFN(func->device, func->function);
+
+               // Save the command register
+               pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, &save_command);
+
+               // disable card
+               command = 0x00;
+               pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_COMMAND, command);
+
+               // Check for Bridge
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+               if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     // PCI-PCI Bridge
+                       // Clear Bridge Control Register
+                       command = 0x00;
+                       pci_bus_write_config_word(ctrl->pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
+
+                       bus_node =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                       if (!bus_node)
+                               return -ENOMEM;
+
+                       bus_node->base = secondary_bus;
+                       bus_node->length = temp_byte - secondary_bus + 1;
+
+                       bus_node->next = func->bus_head;
+                       func->bus_head = bus_node;
+
+                       // Save IO base and Limit registers
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_BASE, &b_base);
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_IO_LIMIT, &b_length);
+
+                       if ((b_base <= b_length) && (save_command & 0x01)) {
+                               io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                               if (!io_node)
+                                       return -ENOMEM;
+
+                               io_node->base = (b_base & 0xF0) << 8;
+                               io_node->length = (b_length - b_base + 0x10) << 8;
+
+                               io_node->next = func->io_head;
+                               func->io_head = io_node;
+                       }
+                       // Save memory base and Limit registers
+                       pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
+                       pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
+
+                       if ((w_base <= w_length) && (save_command & 0x02)) {
+                               mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                               if (!mem_node)
+                                       return -ENOMEM;
+
+                               mem_node->base = w_base << 16;
+                               mem_node->length = (w_length - w_base + 0x10) << 16;
+
+                               mem_node->next = func->mem_head;
+                               func->mem_head = mem_node;
+                       }
+                       // Save prefetchable memory base and Limit registers
+                       pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
+                       pci_bus_read_config_word(ctrl->pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
+
+                       if ((w_base <= w_length) && (save_command & 0x02)) {
+                               p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                               if (!p_mem_node)
+                                       return -ENOMEM;
+
+                               p_mem_node->base = w_base << 16;
+                               p_mem_node->length = (w_length - w_base + 0x10) << 16;
+
+                               p_mem_node->next = func->p_mem_head;
+                               func->p_mem_head = p_mem_node;
+                       }
+                       // Figure out IO and memory base lengths
+                       for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base);
+
+                               temp_register = 0xFFFFFFFF;
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+                               temp_register = base;
+
+                               if (base) {  // If this register is implemented
+                                       if (((base & 0x03L) == 0x01)
+                                           && (save_command & 0x01)) {
+                                               // IO base
+                                               // set temp_register = amount of IO space requested
+                                               temp_register = base & 0xFFFFFFFE;
+                                               temp_register = (~temp_register) + 1;
+
+                                               io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!io_node)
+                                                       return -ENOMEM;
+
+                                               io_node->base =
+                                               save_base & (~0x03L);
+                                               io_node->length = temp_register;
+
+                                               io_node->next = func->io_head;
+                                               func->io_head = io_node;
+                                       } else
+                                               if (((base & 0x0BL) == 0x08)
+                                                   && (save_command & 0x02)) {
+                                               // prefetchable memory base
+                                               temp_register = base & 0xFFFFFFF0;
+                                               temp_register = (~temp_register) + 1;
+
+                                               p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!p_mem_node)
+                                                       return -ENOMEM;
+
+                                               p_mem_node->base = save_base & (~0x0FL);
+                                               p_mem_node->length = temp_register;
+
+                                               p_mem_node->next = func->p_mem_head;
+                                               func->p_mem_head = p_mem_node;
+                                       } else
+                                               if (((base & 0x0BL) == 0x00)
+                                                   && (save_command & 0x02)) {
+                                               // prefetchable memory base
+                                               temp_register = base & 0xFFFFFFF0;
+                                               temp_register = (~temp_register) + 1;
+
+                                               mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!mem_node)
+                                                       return -ENOMEM;
+
+                                               mem_node->base = save_base & (~0x0FL);
+                                               mem_node->length = temp_register;
+
+                                               mem_node->next = func->mem_head;
+                                               func->mem_head = mem_node;
+                                       } else
+                                               return(1);
+                               }
+                       }       // End of base register loop
+               } else if ((header_type & 0x7F) == 0x00) {        // Standard header
+                       // Figure out IO and memory base lengths
+                       for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &save_base);
+
+                               temp_register = 0xFFFFFFFF;
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+                               temp_register = base;
+
+                               if (base) {       // If this register is implemented
+                                       if (((base & 0x03L) == 0x01)
+                                           && (save_command & 0x01)) {
+                                               // IO base
+                                               // set temp_register = amount of IO space requested
+                                               temp_register = base & 0xFFFFFFFE;
+                                               temp_register = (~temp_register) + 1;
+
+                                               io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!io_node)
+                                                       return -ENOMEM;
+
+                                               io_node->base = save_base & (~0x01L);
+                                               io_node->length = temp_register;
+
+                                               io_node->next = func->io_head;
+                                               func->io_head = io_node;
+                                       } else
+                                               if (((base & 0x0BL) == 0x08)
+                                                   && (save_command & 0x02)) {
+                                               // prefetchable memory base
+                                               temp_register = base & 0xFFFFFFF0;
+                                               temp_register = (~temp_register) + 1;
+
+                                               p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!p_mem_node)
+                                                       return -ENOMEM;
+
+                                               p_mem_node->base = save_base & (~0x0FL);
+                                               p_mem_node->length = temp_register;
+
+                                               p_mem_node->next = func->p_mem_head;
+                                               func->p_mem_head = p_mem_node;
+                                       } else
+                                               if (((base & 0x0BL) == 0x00)
+                                                   && (save_command & 0x02)) {
+                                               // prefetchable memory base
+                                               temp_register = base & 0xFFFFFFF0;
+                                               temp_register = (~temp_register) + 1;
+
+                                               mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
+                                               if (!mem_node)
+                                                       return -ENOMEM;
+
+                                               mem_node->base = save_base & (~0x0FL);
+                                               mem_node->length = temp_register;
+
+                                               mem_node->next = func->mem_head;
+                                               func->mem_head = mem_node;
+                                       } else
+                                               return(1);
+                               }
+                       }       // End of base register loop
+               } else {          // Some other unknown header type
+               }
+
+               // find the next device in this slot
+               func = amdshpc_slot_find(func->bus, func->device, index++);
+       }
+
+       return(0);
+}
+
+
+/*
+ * amdshpc_configure_board
+ *
+ * Copies saved configuration information to one slot.
+ * this is called recursively for bridge devices.
+ * this is for hot plug REPLACE!
+ *
+ * returns 0 if success
+ */
+int amdshpc_configure_board(struct controller  *ctrl, struct pci_func * func)
+{
+       int cloop;
+       u8 header_type;
+       u8 secondary_bus;
+       int sub_bus;
+       struct pci_func *next;
+       u32 temp;
+       u32 rc;
+       int index = 0;
+
+       dbg("%s", __FUNCTION__);
+       func = amdshpc_slot_find(func->bus, func->device, index++);
+
+       while (func != NULL) {
+               int devfn = PCI_DEVFN(func->device, func->function);
+
+               // Start at the top of config space so that the control
+               // registers are programmed last
+               for (cloop = 0x3C; cloop > 0; cloop -= 4) {
+                       pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
+               }
+
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+               // If this is a bridge device, restore subordinate devices
+               if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     // PCI-PCI Bridge
+                       pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
+
+                       sub_bus = (int) secondary_bus;
+
+                       next = amdshpc_slot_list[sub_bus];
+
+                       while (next != NULL) {
+                               rc = amdshpc_configure_board(ctrl, next);
+                               if (rc)
+                                       return rc;
+
+                               next = next->next;
+                       }
+               } else {
+                       // Check all the base Address Registers to make sure
+                       // they are the same.  If not, the board is different.
+                       for (cloop = 16; cloop < 40; cloop += 4) {
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &temp);
+                               if (temp != func->config_space[cloop >> 2]) {
+                                       dbg("Config space compare failure!!! offset = %x\n", cloop);
+                                       dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function);
+                                       dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop]);
+                                       return 1;
+                               }
+                       }
+               }
+
+               func->configured = 1;
+
+               func = amdshpc_slot_find(func->bus, func->device, index++);
+       }
+
+       return 0;
+}
+
+
+/*
+ * amdshpc_valid_replace
+ *
+ * this function checks to see if a board is the same as the
+ * one it is replacing.  this check will detect if the device's
+ * vendor or device id's are the same
+ *
+ * returns 0 if the board is the same nonzero otherwise
+ */
+int amdshpc_valid_replace(struct controller  *ctrl, struct pci_func * func)
+{
+       u8 cloop;
+       u8 header_type;
+       u8 secondary_bus;
+       u8 type;
+       u32 temp_register = 0;
+       u32 base;
+       u32 rc;
+       struct pci_func *next;
+       int index = 0;
+
+       dbg("%s", __FUNCTION__);
+       if (!func->is_a_board)
+               return(ADD_NOT_SUPPORTED);
+
+       func = amdshpc_slot_find(func->bus, func->device, index++);
+
+       while (func != NULL) {
+               int devfn = PCI_DEVFN(func->device, func->function);
+
+               pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
+
+               // No adapter present
+               if (temp_register == 0xFFFFFFFF)
+                       return(NO_ADAPTER_PRESENT);
+
+               if (temp_register != func->config_space[0])
+                       return(ADAPTER_NOT_SAME);
+
+               // Check for same revision number and class code
+               pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
+
+               // Adapter not the same
+               if (temp_register != func->config_space[0x08 >> 2])
+                       return(ADAPTER_NOT_SAME);
+
+               // Check for Bridge
+               pci_bus_read_config_byte(ctrl->pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
+
+               if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {     // PCI-PCI Bridge
+                       // In order to continue checking, we must program the
+                       // bus registers in the bridge to respond to accesses
+                       // for it's subordinate bus(es)
+
+                       temp_register = func->config_space[0x18 >> 2];
+                       pci_bus_write_config_dword(ctrl->pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
+
+                       secondary_bus = (temp_register >> 8) & 0xFF;
+
+                       next = amdshpc_slot_list[secondary_bus];
+
+                       while (next != NULL) {
+                               rc = amdshpc_valid_replace(ctrl, next);
+                               if (rc)
+                                       return(rc);
+
+                               next = next->next;
+                       }
+
+               }
+               // Check to see if it is a standard config header
+               else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
+                       // Check subsystem vendor and ID
+                       pci_bus_read_config_dword(ctrl->pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
+
+                       if (temp_register != func->config_space[0x2C >> 2]) {
+                               // If it's a SMART-2 and the register isn't filled
+                               // in, ignore the difference because
+                               // they just have an old rev of the firmware
+
+                               if (!((func->config_space[0] == 0xAE100E11)
+                                     && (temp_register == 0x00L)))
+                                       return(ADAPTER_NOT_SAME);
+                       }
+                       // Figure out IO and memory base lengths
+                       for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
+                               temp_register = 0xFFFFFFFF;
+                               pci_bus_write_config_dword(ctrl->pci_bus, devfn, cloop, temp_register);
+                               pci_bus_read_config_dword(ctrl->pci_bus, devfn, cloop, &base);
+
+                               if (base) {       // If this register is implemented
+                                       if (base & 0x01L) {
+                                               // IO base
+                                               // set base = amount of IO space requested
+                                               base = base & 0xFFFFFFFE;
+                                               base = (~base) + 1;
+
+                                               type = 1;
+                                       } else {
+                                               // memory base
+                                               base = base & 0xFFFFFFF0;
+                                               base = (~base) + 1;
+
+                                               type = 0;
+                                       }
+                               } else {
+                                       base = 0x0L;
+                                       type = 0;
+                               }
+
+                               // Check information in slot structure
+                               if (func->base_length[(cloop - 0x10) >> 2] != base)
+                                       return(ADAPTER_NOT_SAME);
+
+                               if (func->base_type[(cloop - 0x10) >> 2] != type)
+                                       return(ADAPTER_NOT_SAME);
+
+                       }       // End of base register loop
+
+               }               // End of (type 0 config space) else
+               else {
+                       // this is not a type 0 or 1 config space header so
+                       // we don't know how to do it
+                       return(DEVICE_TYPE_NOT_SUPPORTED);
+               }
+
+               // Get the next function
+               func = amdshpc_slot_find(func->bus, func->device, index++);
+       }
+
+       return(0);
+}
+
+
+
+static int update_slot_info (struct controller  *ctrl, struct slot *slot)
+{
+//     TO_DO_amd_update_slot_info();
+       dbg("%s   THIS FUNCTION IS STUBBED OUT!!!!!!!!!!!",__FUNCTION__);
+       return 0;
+       /*      struct hotplug_slot_info *info;
+       char buffer[SLOT_NAME_SIZE];
+       int result;
+
+       info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot);
+       info->power_status = get_slot_enabled(ctrl, slot);
+       info->attention_status = cpq_get_attention_status(ctrl, slot);
+       info->latch_status = cpq_get_latch_status(ctrl, slot);
+       info->adapter_status = get_presence_status(ctrl, slot);
+       result = pci_hp_change_slot_info(buffer, info);
+       kfree (info);
+       return result;
+*/
+}
+
+
+/*
+ * slot_remove - Removes a node from the linked list of slots.
+ * @old_slot: slot to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int slot_remove(struct pci_func * old_slot)
+{
+       struct pci_func *next;
+
+       dbg("%s", __FUNCTION__);
+       if (old_slot == NULL)
+               return(1);
+
+       next = amdshpc_slot_list[old_slot->bus];
+
+       if (next == NULL) {
+               return(1);
+       }
+
+       if (next == old_slot) {
+               amdshpc_slot_list[old_slot->bus] = old_slot->next;
+               amdshpc_destroy_board_resources(old_slot);
+               kfree(old_slot);
+               return(0);
+       }
+
+       while ((next->next != old_slot) && (next->next != NULL)) {
+               next = next->next;
+       }
+
+       if (next->next == old_slot) {
+               next->next = old_slot->next;
+               amdshpc_destroy_board_resources(old_slot);
+               kfree(old_slot);
+               return(0);
+       } else
+               return(2);
+}
+
+// DJZ: I don't think is_bridge will work as is.
+//FIXME
+static int is_bridge(struct pci_func * func)
+{
+       dbg("%s", __FUNCTION__);
+       // Check the header type
+       if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
+               return 1;
+       else
+               return 0;
+}
+
+
+/**
+ * bridge_slot_remove - Removes a node from the linked list of slots.
+ * @bridge: bridge to remove
+ *
+ * Returns 0 if successful, !0 otherwise.
+ */
+static int bridge_slot_remove(struct pci_func *bridge)
+{
+       u8 subordinateBus, secondaryBus;
+       u8 tempBus;
+       struct pci_func *next;
+
+       dbg("%s", __FUNCTION__);
+       if (bridge == NULL)
+               return(1);
+
+       secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
+       subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
+
+       for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
+               next = amdshpc_slot_list[tempBus];
+
+               while (!slot_remove(next)) {
+                       next = amdshpc_slot_list[tempBus];
+               }
+       }
+
+       next = amdshpc_slot_list[bridge->bus];
+
+       if (next == NULL) {
+               return(1);
+       }
+
+       if (next == bridge) {
+               amdshpc_slot_list[bridge->bus] = bridge->next;
+               kfree(bridge);
+               return(0);
+       }
+
+       while ((next->next != bridge) && (next->next != NULL)) {
+               next = next->next;
+       }
+
+       if (next->next == bridge) {
+               next->next = bridge->next;
+               kfree(bridge);
+               return(0);
+       } else
+               return(2);
+}
+
+
diff --git a/drivers/pci/hotplug/dummyphp.c b/drivers/pci/hotplug/dummyphp.c
new file mode 100644 (file)
index 0000000..df92e4f
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Dummy PCI Hot Plug Controller Driver
+ *
+ * Copyright (C) 1995,2001 Compaq Computer Corporation
+ * Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2001,2003 IBM Corp.
+ * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
+ * Copyright (C) 2002 Takayoshi Kochi (t-kouchi@cq.jp.nec.com)
+ * Copyright (C) 2002 NEC Corporation
+ * Copyright (C) 2002 Vladimir Kondratiev (vladimir.kondratiev@intel.com)
+ * Copyright (C) 2003 Rolf Eike Beer (eike-kernel@sf-tec.de)
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT.  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.
+ *
+ * Send feedback to <eike-kernel@sf-tec.de>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "pci_hotplug.h"
+#include "../pci.h"
+
+#if !defined(CONFIG_HOTPLUG_PCI_DUMMY_MODULE)
+       #define MY_NAME "dummyphp"
+#else
+       #define MY_NAME THIS_MODULE->name
+#endif
+
+#define dbg(format, arg...)                                    \
+       do {                                                    \
+               if (debug)                                      \
+                       printk(KERN_DEBUG "%s: " format,        \
+                               MY_NAME , ## arg);              \
+       } while (0)
+#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
+
+/* name size which is used for entries in pcihpfs */
+#define SLOT_NAME_SIZE 32              /* DUMMY-{BUS}:{DEV} */
+
+struct dummy_slot {
+       struct pci_bus  *bus;
+       int             devfn;
+};
+
+static int debug;
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR  "Rolf Eike Beer <eike-kernel@sf-tec.de>"
+#define DRIVER_DESC    "Dummy PCI Hot Plug Controller Driver"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+
+static int enable_slot         (struct hotplug_slot *slot);
+static int disable_slot                (struct hotplug_slot *slot);
+
+/*
+   This is a dummy driver, so we make us live as easy as possible:
+   -if there is an adapter registered in the linux PCI system, then it is present
+   -if there is an adapter present, the power is on (and vice versa!)
+   -if the power is on, the latch is closed (and vice versa)
+   -the attention LED is always off
+
+   So:
+   => latch_status = adapter_status = power_status
+ */
+
+static struct hotplug_slot_ops dummy_hotplug_slot_ops = {
+       .owner                  = THIS_MODULE,
+       .enable_slot            = enable_slot,
+       .disable_slot           = disable_slot,
+};
+
+/**
+ * enable_slot - power on and enable a slot
+ * @hotplug_slot: slot to enable
+ */
+static int
+enable_slot(struct hotplug_slot *hotplug_slot)
+{
+       struct dummy_slot *slot;
+
+       if (!hotplug_slot)
+               return -ENODEV;
+
+       dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       /* enable the specified slot */
+       slot = (struct dummy_slot *) hotplug_slot->private;
+
+       if (pci_scan_slot(slot->bus, slot->devfn)) {
+               hotplug_slot->info->power_status = 1;
+               hotplug_slot->info->adapter_status = 1;
+               hotplug_slot->info->latch_status = 1;
+               return 0;
+       }
+       return -ENODEV;
+}
+
+
+/**
+ * disable_slot - disable any adapter in this slot
+ * @hotplug_slot: slot to disable
+ */
+static int
+disable_slot(struct hotplug_slot *hotplug_slot)
+{
+       struct pci_dev* old_dev;
+       int func;
+       struct dummy_slot *slot;
+
+       if (!hotplug_slot)
+               return -ENODEV;
+
+       slot = (struct dummy_slot *) hotplug_slot->private;
+
+       dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
+
+       /* disable the specified slot */
+
+       for (func=0; func<8; func++) {
+               old_dev = pci_find_slot(slot->bus->number, slot->devfn+func);
+               if (old_dev) {
+                       printk(KERN_INFO "Slot %s removed\n", old_dev->slot_name);
+                       pci_remove_device_safe(old_dev);
+               }
+       }
+       hotplug_slot->info->power_status = 0;
+       hotplug_slot->info->adapter_status = 0;
+       hotplug_slot->info->latch_status = 0;
+
+
+       return 0;
+}
+
+
+/**
+ * dummyphp_get_power_status - look if an adapter is configured in this slot
+ * @slot: the slot to test
+ */
+static int dummyphp_get_power_status(struct dummy_slot *slot)
+{
+       struct pci_dev* old_dev;
+       int func;
+
+       for (func = 0; func < 8; func++) {
+               old_dev = pci_find_slot(slot->bus->number, slot->devfn+func);
+               if (old_dev)
+                       return 1;
+       }
+
+       return 0;
+}
+
+/**
+ * scan_pci_bus - add an entry for every slot on this bus
+ * @bus: bus to scan
+ */
+static int __init
+scan_pci_bus(const struct pci_bus *bus)
+{
+       struct dummy_slot *dslot;
+       struct hotplug_slot *hp;
+       int retval = 0;
+       unsigned int devfn;
+       struct pci_dev dev0;
+
+       memset(&dev0, 0, sizeof(dev0));
+       dev0.bus = (struct pci_bus*)bus;
+       dev0.sysdata = bus->sysdata;
+       for (devfn = 0; devfn < 0x100; devfn += 8) {
+               hp = kmalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
+               if (!hp) {
+                       return -ENOMEM;
+               }
+               memset(hp, 0, sizeof(struct hotplug_slot));
+
+               hp->info = kmalloc(sizeof(struct hotplug_slot_info), GFP_KERNEL);
+               if (!hp->info) {
+                       kfree(hp);
+                       return -ENOMEM;
+               }
+               memset(hp->info, 0, sizeof(struct hotplug_slot_info));
+
+               hp->name = kmalloc(SLOT_NAME_SIZE, GFP_KERNEL);
+               dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
+               if ( (!hp->name) || (!dslot) ) {
+                       kfree(hp->info);
+                       kfree(hp->name);
+                       kfree(dslot);
+                       kfree(hp);
+                       return -ENOMEM;
+               }
+               dslot->bus = (struct pci_bus *) bus;
+               dslot->devfn = devfn;
+
+               hp->ops = &dummy_hotplug_slot_ops;
+               hp->private = (void *) dslot;
+
+               hp->info->power_status = dummyphp_get_power_status(dslot);
+               hp->info->latch_status = hp->info->power_status;
+               hp->info->adapter_status = hp->info->power_status;
+
+               /* set fixed values so we do not need callbacks for these */
+               hp->info->attention_status = 0;
+               hp->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
+               hp->info->max_bus_speed = PCI_SPEED_UNKNOWN;
+
+               snprintf(hp->name, SLOT_NAME_SIZE, "DUMMY-%02x:%02x",
+                       dslot->bus->number,
+                       dslot->devfn / 8);
+
+               retval = pci_hp_register(hp);
+               if (retval) {
+                       err("pci_hp_register failed with error %d\n", retval);
+                       kfree(hp->info);
+                       kfree(hp->name);
+                       kfree(hp);
+                       kfree(dslot);
+                       return retval;
+               }
+
+       }
+       return 0;
+}
+
+/**
+ * scan_pci_buses - scan this bus and all child buses for slots
+ * @list: list of buses to scan
+ */
+static int __init
+pci_scan_buses(const struct list_head *list)
+{
+       int retval;
+       const struct list_head *l;
+
+       list_for_each(l,list) {
+               const struct pci_bus *b = pci_bus_b(l);
+               retval = scan_pci_bus(b);
+               if (retval)
+                       return retval;
+               retval = pci_scan_buses(&b->children);
+               if (retval)
+                       return retval;
+       }
+       return 0;
+}
+
+static void __exit
+cleanup_slots (void)
+{
+/*FIXME: look at every hotplug_slot if name begins with "DUMMY-" an kick them*/
+}
+
+
+static int __init
+dummyphp_init(void)
+{
+       info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
+
+       return pci_scan_buses(&pci_root_buses);
+}
+
+
+static void __exit
+dummyphp_exit(void)
+{
+       cleanup_slots();
+}
+
+module_init(dummyphp_init);
+module_exit(dummyphp_exit);
index 0531e2e..3d60c39 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_PWC)         += media/
 obj-$(CONFIG_USB_SE401)                += media/
 obj-$(CONFIG_USB_STV680)       += media/
 obj-$(CONFIG_USB_VICAM)                += media/
+obj-$(CONFIG_USB_W9968CF)      += media/
 
 obj-$(CONFIG_USB_CATC)         += net/
 obj-$(CONFIG_USB_KAWETH)       += net/
@@ -58,4 +59,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH)  += misc/
 obj-$(CONFIG_USB_TEST)         += misc/
 obj-$(CONFIG_USB_TIGL)         += misc/
 obj-$(CONFIG_USB_USS720)       += misc/
-
+obj-$(CONFIG_USB_LEGOTOWER)    += misc/
index 618c42d..6921a20 100644 (file)
@@ -3045,7 +3045,7 @@ static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *b
                kfree(as);
                return;
        }
-       if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) {
+       if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1, &dev->dev)) < 0) {
                printk(KERN_ERR "usbaudio: cannot register dsp\n");
                usb_free_urb(as->usbin.durb[0].urb);
                usb_free_urb(as->usbin.durb[1].urb);
@@ -3640,7 +3640,7 @@ static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *b
        ms->state = s;
        ms->iface = ctrlif;
        ms->numch = state.nrmixch;
-       if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) {
+       if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1, &s->usbdev->dev)) < 0) {
                printk(KERN_ERR "usbaudio: cannot register mixer\n");
                kfree(ms);
                return;
index bb51e46..45916eb 100644 (file)
@@ -1,9 +1,3 @@
-#define USB_DT_CS_DEVICE                0x21
-#define USB_DT_CS_CONFIG                0x22
-#define USB_DT_CS_STRING                0x23
-#define USB_DT_CS_INTERFACE             0x24
-#define USB_DT_CS_ENDPOINT              0x25
-
 #define CS_AUDIO_UNDEFINED             0x20
 #define CS_AUDIO_DEVICE                        0x21
 #define CS_AUDIO_CONFIGURATION         0x22
index 84668f3..bd21150 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * acm.c  Version 0.22
+ * cdc-acm.c
  *
  * Copyright (c) 1999 Armin Fuerst     <fuerst@in.tum.de>
  * Copyright (c) 1999 Pavel Machek     <pavel@suse.cz>
@@ -26,6 +26,7 @@
  *     v0.21 - revert to probing on device for devices with multiple configs
  *     v0.22 - probe only the control interface. if usbcore doesn't choose the
  *             config we want, sysadmin changes bConfigurationValue in sysfs.
+ *     v0.23 - use softirq for rx processing, as needed by tty layer
  */
 
 /*
@@ -44,6 +45,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
+#undef DEBUG
+
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <asm/uaccess.h>
-#undef DEBUG
 #include <linux/usb.h>
 #include <asm/byteorder.h>
 
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.21"
+#define DRIVER_VERSION "v0.23"
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
@@ -146,7 +148,8 @@ struct acm {
        struct tty_struct *tty;                         /* the corresponding tty */
        struct urb *ctrlurb, *readurb, *writeurb;       /* urbs */
        struct acm_line line;                           /* line coding (bits, stop, parity) */
-       struct work_struct work;                                        /* work queue entry for line discipline waking up */
+       struct work_struct work;                        /* work queue entry for line discipline waking up */
+       struct tasklet_struct bh;                       /* rx processing */
        unsigned int ctrlin;                            /* input control lines (DCD, DSR, RI, break, overruns) */
        unsigned int ctrlout;                           /* output control lines (DTR, RTS) */
        unsigned int writesize;                         /* max packet size for the output bulk endpoint */
@@ -184,9 +187,10 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int
 #define acm_send_break(acm, ms)                acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0)
 
 /*
- * Interrupt handler for various ACM control events
+ * Interrupt handlers for various ACM device responses
  */
 
+/* control interface reports status changes with "interrupt" transfers */
 static void acm_ctrl_irq(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = urb->context;
@@ -251,20 +255,30 @@ exit:
                     __FUNCTION__, status);
 }
 
+/* data interface returns incoming bytes, or we got unthrottled */
 static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = urb->context;
-       struct tty_struct *tty = acm->tty;
-       unsigned char *data = urb->transfer_buffer;
-       int i = 0;
 
        if (!ACM_READY(acm))
                return;
 
        if (urb->status)
-               dbg("nonzero read bulk status received: %d", urb->status);
+               dev_dbg(&acm->data->dev, "bulk rx status %d\n", urb->status);
+
+       /* calling tty_flip_buffer_push() in_irq() isn't allowed */
+       tasklet_schedule(&acm->bh);
+}
+
+static void acm_rx_tasklet(unsigned long _acm)
+{
+       struct acm *acm = (void *)_acm;
+       struct urb *urb = acm->readurb;
+       struct tty_struct *tty = acm->tty;
+       unsigned char *data = urb->transfer_buffer;
+       int i = 0;
 
-       if (!urb->status && !acm->throttle)  {
+       if (urb->actual_length > 0 && !acm->throttle)  {
                for (i = 0; i < urb->actual_length && !acm->throttle; i++) {
                        /* if we insert more than TTY_FLIPBUF_SIZE characters,
                         * we drop them. */
@@ -285,10 +299,12 @@ static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
        urb->actual_length = 0;
        urb->dev = acm->dev;
 
-       if (usb_submit_urb(urb, GFP_ATOMIC))
-               dbg("failed resubmitting read urb");
+       i = usb_submit_urb(urb, GFP_ATOMIC);
+       if (i)
+               dev_dbg(&acm->data->dev, "bulk rx resubmit %d\n", i);
 }
 
+/* data interface wrote those outgoing bytes */
 static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
 {
        struct acm *acm = (struct acm *)urb->context;
@@ -621,6 +637,8 @@ static int acm_probe (struct usb_interface *intf,
                        acm->minor = minor;
                        acm->dev = dev;
 
+                       acm->bh.func = acm_rx_tasklet;
+                       acm->bh.data = (unsigned long) acm;
                        INIT_WORK(&acm->work, acm_softint, acm);
 
                        if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
index e817e90..a8313e3 100644 (file)
@@ -1143,7 +1143,7 @@ static struct usb_mididev *allocMidiDev(
 
        memset(m, 0, sizeof(struct usb_mididev));
 
-       if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1)) < 0) {
+       if ((m->dev_midi = register_sound_midi(&usb_midi_fops, -1, &s->usbdev->dev)) < 0) {
                printk(KERN_ERR "usbmidi: cannot register midi device\n");
                kfree(m);
                return NULL;
index 1e99c99..0e6eac1 100644 (file)
 #define USB_SUBCLASS_MIDISTREAMING     3
 #endif
 
-#define USB_DT_CS_DEVICE               0x21
-#define USB_DT_CS_CONFIG               0x22
-#define USB_DT_CS_STRING               0x23
-#define USB_DT_CS_INTERFACE            0x24
-#define USB_DT_CS_ENDPOINT             0x25
-
 /* ------------------------------------------------------------------------- */
 /* Roland MIDI Devices */
 
index d74caea..0246c91 100644 (file)
@@ -706,8 +706,6 @@ static ssize_t usblp_read(struct file *file, char __user *buffer, size_t count,
                        goto done;
                }
 
-               // FIXME:  only use urb->status inside completion
-               // callbacks; this way is racey...
                add_wait_queue(&usblp->wait, &wait);
                while (1==1) {
                        if (signal_pending(current)) {
index dc12ba0..360e6de 100644 (file)
@@ -126,14 +126,20 @@ static int get_port_status(struct usb_device *dev, int port,
 static void hub_irq(struct urb *urb, struct pt_regs *regs)
 {
        struct usb_hub *hub = (struct usb_hub *)urb->context;
-       unsigned long flags;
        int status;
 
+       spin_lock(&hub_event_lock);
+       hub->urb_active = 0;
+       if (hub->urb_complete) {        /* disconnect or rmmod */
+               complete(hub->urb_complete);
+               goto done;
+       }
+
        switch (urb->status) {
        case -ENOENT:           /* synchronous unlink */
        case -ECONNRESET:       /* async unlink */
        case -ESHUTDOWN:        /* hardware going away */
-               return;
+               goto done;
 
        default:                /* presumably an error */
                /* Cause a hub reset after 10 consecutive errors */
@@ -151,18 +157,20 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
        hub->nerrors = 0;
 
        /* Something happened, let khubd figure it out */
-       spin_lock_irqsave(&hub_event_lock, flags);
        if (list_empty(&hub->event_list)) {
                list_add(&hub->event_list, &hub_event_list);
                wake_up(&khubd_wait);
        }
-       spin_unlock_irqrestore(&hub_event_lock, flags);
 
 resubmit:
        if ((status = usb_submit_urb (hub->urb, GFP_ATOMIC)) != 0
                        /* ENODEV means we raced disconnect() */
                        && status != -ENODEV)
                dev_err (&hub->intf->dev, "resubmit --> %d\n", urb->status);
+       if (status == 0)
+               hub->urb_active = 1;
+done:
+       spin_unlock(&hub_event_lock);
 }
 
 /* USB 2.0 spec Section 11.24.2.3 */
@@ -467,7 +475,8 @@ static int hub_configure(struct usb_hub *hub,
                message = "couldn't submit status urb";
                goto fail;
        }
-               
+       hub->urb_active = 1;
+
        /* Wake up khubd */
        wake_up(&khubd_wait);
 
@@ -485,6 +494,7 @@ fail:
 static void hub_disconnect(struct usb_interface *intf)
 {
        struct usb_hub *hub = usb_get_intfdata (intf);
+       DECLARE_COMPLETION(urb_complete);
        unsigned long flags;
 
        if (!hub)
@@ -492,12 +502,11 @@ static void hub_disconnect(struct usb_interface *intf)
 
        usb_set_intfdata (intf, NULL);
        spin_lock_irqsave(&hub_event_lock, flags);
+       hub->urb_complete = &urb_complete;
 
        /* Delete it and then reset it */
-       list_del(&hub->event_list);
-       INIT_LIST_HEAD(&hub->event_list);
-       list_del(&hub->hub_list);
-       INIT_LIST_HEAD(&hub->hub_list);
+       list_del_init(&hub->event_list);
+       list_del_init(&hub->hub_list);
 
        spin_unlock_irqrestore(&hub_event_lock, flags);
 
@@ -510,6 +519,8 @@ static void hub_disconnect(struct usb_interface *intf)
 
        if (hub->urb) {
                usb_unlink_urb(hub->urb);
+               if (hub->urb_active)
+                       wait_for_completion(&urb_complete);
                usb_free_urb(hub->urb);
                hub->urb = NULL;
        }
@@ -692,6 +703,9 @@ static int hub_port_status(struct usb_device *dev, int port,
        struct usb_hub *hub = usb_get_intfdata(dev->actconfig->interface[0]);
        int ret;
 
+       if (!hub)
+               return -ENODEV;
+
        ret = get_port_status(dev, port + 1, &hub->status->port);
        if (ret < 0)
                dev_err (hubdev (dev),
index 51093be..755bc2d 100644 (file)
@@ -172,6 +172,8 @@ extern void usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe);
 struct usb_hub {
        struct usb_interface    *intf;          /* the "real" device */
        struct urb              *urb;           /* for interrupt polling pipe */
+       struct completion       *urb_complete;  /* wait for urb to end */
+       unsigned int            urb_active:1;
 
        /* buffer for urb ... 1 bit each for hub and children, rounded up */
        char                    (*buffer)[(USB_MAXCHILDREN + 1 + 7) / 8];
index 3fad1ec..1d2a3f5 100644 (file)
@@ -991,6 +991,7 @@ int usb_new_device(struct usb_device *dev, struct device *parent)
        int err = -EINVAL;
        int i;
        int j;
+       int config;
 
        /*
         * Set the driver for the usb device to point to the "generic" driver.
@@ -1108,18 +1109,31 @@ int usb_new_device(struct usb_device *dev, struct device *parent)
 
        /* choose and set the configuration. that registers the interfaces
         * with the driver core, and lets usb device drivers bind to them.
+        * NOTE:  should interact with hub power budgeting.
         */
+       config = dev->config[0].desc.bConfigurationValue;
        if (dev->descriptor.bNumConfigurations != 1) {
+               for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+                       /* heuristic:  Linux is more likely to have class
+                        * drivers, so avoid vendor-specific interfaces.
+                        */
+                       if (dev->config[i].interface[0]->altsetting
+                                               ->desc.bInterfaceClass
+                                       == USB_CLASS_VENDOR_SPEC)
+                               continue;
+                       config = dev->config[i].desc.bConfigurationValue;
+                       break;
+               }
                dev_info(&dev->dev,
                        "configuration #%d chosen from %d choices\n",
-                       dev->config[0].desc.bConfigurationValue,
+                       config,
                        dev->descriptor.bNumConfigurations);
        }
-       err = usb_set_configuration(dev,
-                       dev->config[0].desc.bConfigurationValue);
+       err = usb_set_configuration(dev, config);
        if (err) {
                dev_err(&dev->dev, "can't set config #%d, error %d\n",
-                       dev->config[0].desc.bConfigurationValue, err);
+                       config, err);
+               device_del(&dev->dev);
                goto fail;
        }
 
index 21d573c..77a7eef 100644 (file)
@@ -177,6 +177,27 @@ config USB_GADGETFS_PXA2XX
        depends on USB_GADGETFS && USB_PXA2XX
        default y
 
+config USB_G_SERIAL
+       tristate "serial Gadget"
+       depends on USB_GADGET && (USB_DUMMY_HCD || USB_NET2280 || USB_PXA2XX || USB_SA1100)
+
+config USB_G_SERIAL_NET2280
+       bool
+       # for now, treat the "dummy" hcd as if it were a net2280
+       depends on USB_G_SERIAL && (USB_NET2280 || USB_DUMMY_HCD)
+       default y
+
+config USB_G_SERIAL_PXA2XX
+       bool
+       depends on USB_G_SERIAL && USB_PXA2XX
+       default y
+
+config USB_G_SERIAL_SA1100
+       bool
+       depends on USB_G_SERIAL && USB_SA1100
+       default y
+
+
 endchoice
 
 # endmenuconfig
index 6bf13ff..dca74c3 100644 (file)
@@ -8,9 +8,10 @@ obj-$(CONFIG_USB_NET2280)      += net2280.o
 #
 g_zero-objs                    := zero.o usbstring.o
 g_ether-objs                   := ether.o usbstring.o
+g_serial-objs                  := serial.o usbstring.o
 gadgetfs-objs                  := inode.o usbstring.o
  
 obj-$(CONFIG_USB_ZERO)         += g_zero.o
 obj-$(CONFIG_USB_ETH)          += g_ether.o
 obj-$(CONFIG_USB_GADGETFS)     += gadgetfs.o
-
+obj-$(CONFIG_USB_G_SERIAL)     += g_serial.o
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
new file mode 100644 (file)
index 0000000..0385a00
--- /dev/null
@@ -0,0 +1,2329 @@
+/*
+ * g_serial.c -- USB gadget serial driver
+ *
+ * $Id: gserial.c,v 1.17 2003/10/01 06:31:57 borchers Exp $
+ *
+ * Copyright 2003 (c) Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This code is based in part on the Gadget Zero driver, which
+ * is Copyright (C) 2003 by David Brownell, all rights reserved.
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (c) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ *
+ */
+
+/* Includes */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/device.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <asm/uaccess.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+
+
+/* Wait Cond */
+
+#define __wait_cond_interruptible(wq, condition, lock, flags, ret)     \
+do {                                                                   \
+       wait_queue_t __wait;                                            \
+       init_waitqueue_entry(&__wait, current);                         \
+                                                                       \
+       add_wait_queue(&wq, &__wait);                                   \
+       for (;;) {                                                      \
+               set_current_state(TASK_INTERRUPTIBLE);                  \
+               if (condition)                                          \
+                       break;                                          \
+               if (!signal_pending(current)) {                         \
+                       spin_unlock_irqrestore(lock, flags);            \
+                       schedule();                                     \
+                       spin_lock_irqsave(lock, flags);                 \
+                       continue;                                       \
+               }                                                       \
+               ret = -ERESTARTSYS;                                     \
+               break;                                                  \
+       }                                                               \
+       current->state = TASK_RUNNING;                                  \
+       remove_wait_queue(&wq, &__wait);                                \
+} while (0)
+       
+#define wait_cond_interruptible(wq, condition, lock, flags)            \
+({                                                                     \
+       int __ret = 0;                                                  \
+       if (!(condition))                                               \
+               __wait_cond_interruptible(wq, condition, lock, flags,   \
+                                               __ret);                 \
+       __ret;                                                          \
+})
+
+#define __wait_cond_interruptible_timeout(wq, condition, lock, flags,  \
+                                               timeout, ret)           \
+do {                                                                   \
+       signed long __timeout = timeout;                                \
+       wait_queue_t __wait;                                            \
+       init_waitqueue_entry(&__wait, current);                         \
+                                                                       \
+       add_wait_queue(&wq, &__wait);                                   \
+       for (;;) {                                                      \
+               set_current_state(TASK_INTERRUPTIBLE);                  \
+               if (__timeout == 0)                                     \
+                       break;                                          \
+               if (condition)                                          \
+                       break;                                          \
+               if (!signal_pending(current)) {                         \
+                       spin_unlock_irqrestore(lock, flags);            \
+                       __timeout = schedule_timeout(__timeout);        \
+                       spin_lock_irqsave(lock, flags);                 \
+                       continue;                                       \
+               }                                                       \
+               ret = -ERESTARTSYS;                                     \
+               break;                                                  \
+       }                                                               \
+       current->state = TASK_RUNNING;                                  \
+       remove_wait_queue(&wq, &__wait);                                \
+} while (0)
+       
+#define wait_cond_interruptible_timeout(wq, condition, lock, flags,    \
+                                               timeout)                \
+({                                                                     \
+       int __ret = 0;                                                  \
+       if (!(condition))                                               \
+               __wait_cond_interruptible_timeout(wq, condition, lock,  \
+                                               flags, timeout, __ret); \
+       __ret;                                                          \
+})
+
+
+/* Defines */
+
+#define GS_VERSION_STR                 "v0.1"
+#define GS_VERSION_NUM                 0x0001
+
+#define GS_LONG_NAME                   "Gadget Serial"
+#define GS_SHORT_NAME                  "g_serial"
+
+#define GS_MAJOR                       127
+#define GS_MINOR_START                 0
+
+#define GS_NUM_PORTS                   16
+
+#define GS_VENDOR_ID                   0x05F9
+#define GS_PRODUCT_ID                  0xFFFF
+
+#define GS_NUM_CONFIGS                 1
+#define GS_NO_CONFIG_ID                        0
+#define GS_BULK_CONFIG_ID              2
+
+#define GS_NUM_INTERFACES              1
+#define GS_INTERFACE_ID                        0
+#define GS_ALT_INTERFACE_ID            0
+
+#define GS_NUM_ENDPOINTS               2
+
+#define GS_MAX_DESC_LEN                        256
+
+#define GS_DEFAULT_READ_Q_SIZE         32
+#define GS_DEFAULT_WRITE_Q_SIZE                32
+
+#define GS_DEFAULT_WRITE_BUF_SIZE      8192
+#define GS_TMP_BUF_SIZE                        8192
+
+#define GS_CLOSE_TIMEOUT               15
+
+/* debug macro */
+#if G_SERIAL_DEBUG
+
+static int debug = G_SERIAL_DEBUG;
+
+#define gs_debug(format, arg...) \
+       do { if(debug) printk( KERN_DEBUG format, ## arg ); } while(0)
+#define gs_debug_level(level, format, arg...) \
+       do { if(debug>=level) printk( KERN_DEBUG format, ## arg ); } while(0)
+
+#else
+
+#define gs_debug(format, arg...) \
+       do { } while(0)
+#define gs_debug_level(level, format, arg...) \
+       do { } while(0)
+
+#endif /* G_SERIAL_DEBUG */
+
+
+/* USB Controllers */
+
+/*
+ * NetChip 2280, PCI based.
+ *
+ * This has half a dozen configurable endpoints, four with dedicated
+ * DMA channels to manage their FIFOs.  It supports high speed.
+ * Those endpoints can be arranged in any desired configuration.
+ */
+#ifdef CONFIG_USB_G_SERIAL_NET2280
+#define CHIP                           "net2280"
+#define EP0_MAXPACKET                  64
+static const char EP_OUT_NAME[] =      "ep-a";
+#define EP_OUT_NUM                     2
+static const char EP_IN_NAME[] =       "ep-b";
+#define EP_IN_NUM                      2
+#define HIGHSPEED
+#define SELFPOWER                      USB_CONFIG_ATT_SELFPOWER
+
+extern int net2280_set_fifo_mode( struct usb_gadget *gadget, int mode );
+
+static inline void hw_optimize( struct usb_gadget *gadget )
+{
+       /* we can have bigger ep-a/ep-b fifos (2KB each, 4 packets
+        * for highspeed bulk) because we're not using ep-c/ep-d.
+        */
+       net2280_set_fifo_mode (gadget, 1);
+}
+#endif
+
+
+/*
+ * PXA-2xx UDC:  widely used in second gen Linux-capable PDAs.
+ *
+ * This has fifteen fixed-function full speed endpoints, and it
+ * can support all USB transfer types.
+ *
+ * These supports three or four configurations, with fixed numbers.
+ * The hardware interprets SET_INTERFACE, net effect is that you
+ * can't use altsettings or reset the interfaces independently.
+ * So stick to a single interface.
+ */
+#ifdef CONFIG_USB_G_SERIAL_PXA2XX
+#define CHIP                           "pxa2xx"
+#define EP0_MAXPACKET                  16
+static const char EP_OUT_NAME[] =      "ep12out-bulk";
+#define EP_OUT_NUM                     12
+static const char EP_IN_NAME[] =       "ep11in-bulk";
+#define EP_IN_NUM                      11
+#define SELFPOWER                      USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g)                 do {} while (0)
+#endif
+
+
+/*
+ * SA-1100 UDC:  widely used in first gen Linux-capable PDAs.
+ *
+ * This has only two fixed function endpoints, which can only
+ * be used for bulk (or interrupt) transfers.  (Plus control.)
+ *
+ * Since it can't flush its TX fifos without disabling the UDC,
+ * the current configuration or altsettings can't change except
+ * in special situations.  So this is a case of "choose it right
+ * during enumeration" ...
+ */
+#ifdef CONFIG_USB_G_SERIAL_SA1100
+#define CHIP                           "sa1100"
+#define EP0_MAXPACKET                  8
+static const char EP_OUT_NAME[] =      "ep1out-bulk";
+#define EP_OUT_NUM                     1
+static const char EP_IN_NAME [] =      "ep2in-bulk";
+#define EP_IN_NUM                      2
+#define SELFPOWER                      USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g)                 do {} while (0)
+#endif
+
+
+/*
+ * Toshiba TC86C001 ("Goku-S") UDC
+ *
+ * This has three semi-configurable full speed bulk/interrupt endpoints.
+ */
+#ifdef CONFIG_USB_G_SERIAL_GOKU
+#define CHIP                           "goku"
+#define DRIVER_VERSION_NUM             0x0116
+#define EP0_MAXPACKET                  8
+static const char EP_OUT_NAME [] =     "ep1-bulk";
+#define EP_OUT_NUM                     1
+static const char EP_IN_NAME [] =      "ep2-bulk";
+#define EP_IN_NUM                      2
+#define SELFPOWER                      USB_CONFIG_ATT_SELFPOWER
+
+/* no hw optimizations to apply */
+#define hw_optimize(g)                 do {} while (0)
+#endif
+
+/*
+ * USB Controller Defaults
+ */
+#ifndef EP0_MAXPACKET
+#error Configure some USB peripheral controller for g_serial!
+#endif
+
+#ifndef SELFPOWER
+/* default: say we rely on bus power */
+#define SELFPOWER                      0
+/* else value must be USB_CONFIG_ATT_SELFPOWER */
+#endif
+
+#ifndef        MAX_USB_POWER
+/* any hub supports this steady state bus power consumption */
+#define MAX_USB_POWER                  100     /* mA */
+#endif
+
+#ifndef        WAKEUP
+/* default: this driver won't do remote wakeup */
+#define WAKEUP                         0
+/* else value must be USB_CONFIG_ATT_WAKEUP */
+#endif
+
+
+/* Structures */
+
+struct gs_dev;
+
+/* circular buffer */
+struct gs_buf {
+       unsigned int            buf_size;
+       char                    *buf_buf;
+       char                    *buf_get;
+       char                    *buf_put;
+};
+
+/* list of requests */
+struct gs_req_entry {
+       struct list_head        re_entry;
+       struct usb_request      *re_req;
+};
+
+/* the port structure holds info for each port, one for each minor number */
+struct gs_port {
+       struct gs_dev           *port_dev;      /* pointer to device struct */
+       struct tty_struct       *port_tty;      /* pointer to tty struct */
+       spinlock_t              port_lock;
+       int                     port_num;
+       int                     port_open_count;
+       int                     port_in_use;    /* open/close in progress */
+       wait_queue_head_t       port_write_wait;/* waiting to write */
+       struct gs_buf           *port_write_buf;
+};
+
+/* the device structure holds info for the USB device */
+struct gs_dev {
+       struct usb_gadget       *dev_gadget;    /* gadget device pointer */
+       spinlock_t              dev_lock;       /* lock for set/reset config */
+       int                     dev_config;     /* configuration number */
+       struct usb_ep           *dev_in_ep;     /* address of in endpoint */
+       struct usb_ep           *dev_out_ep;    /* address of out endpoint */
+       struct usb_request      *dev_ctrl_req;  /* control request */
+       struct list_head        dev_req_list;   /* list of write requests */
+       int                     dev_sched_port; /* round robin port scheduled */
+       struct gs_port          *dev_port[GS_NUM_PORTS]; /* the ports */
+};
+
+
+/* Functions */
+
+/* module */
+static int __init gs_module_init( void );
+static void __exit gs_module_exit( void );
+
+/* tty driver */
+static int gs_open( struct tty_struct *tty, struct file *file );
+static void gs_close( struct tty_struct *tty, struct file *file );
+static int gs_write( struct tty_struct *tty, int from_user,
+       const unsigned char *buf, int count );
+static void gs_put_char( struct tty_struct *tty, unsigned char ch );
+static void gs_flush_chars( struct tty_struct *tty );
+static int gs_write_room( struct tty_struct *tty );
+static int gs_chars_in_buffer( struct tty_struct *tty );
+static void gs_throttle( struct tty_struct * tty );
+static void gs_unthrottle( struct tty_struct * tty );
+static void gs_break( struct tty_struct *tty, int break_state );
+static int  gs_ioctl( struct tty_struct *tty, struct file *file,
+       unsigned int cmd, unsigned long arg );
+static void gs_set_termios( struct tty_struct *tty, struct termios *old );
+static int gs_read_proc( char *page, char **start, off_t off, int count,
+       int *eof, void *data );
+
+static int gs_send( struct gs_dev *dev );
+static int gs_send_packet( struct gs_dev *dev, char *packet,
+       unsigned int size );
+static int gs_recv_packet( struct gs_dev *dev, char *packet,
+       unsigned int size );
+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req );
+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req );
+
+/* gadget driver */
+static int gs_bind( struct usb_gadget *gadget );
+static void gs_unbind( struct usb_gadget *gadget );
+static int gs_setup( struct usb_gadget *gadget,
+       const struct usb_ctrlrequest *ctrl );
+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req );
+static void gs_disconnect( struct usb_gadget *gadget );
+static int gs_set_config( struct gs_dev *dev, unsigned config );
+static void gs_reset_config( struct gs_dev *dev );
+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed,
+               u8 type, unsigned int index );
+
+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len,
+       int kmalloc_flags );
+static void gs_free_req( struct usb_ep *ep, struct usb_request *req );
+
+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep, unsigned len,
+       int kmalloc_flags );
+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req );
+
+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags );
+static void gs_free_ports( struct gs_dev *dev );
+
+/* circular buffer */
+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags );
+static void gs_buf_free( struct gs_buf *gb );
+static void gs_buf_clear( struct gs_buf *gb );
+static unsigned int gs_buf_data_avail( struct gs_buf *gb );
+static unsigned int gs_buf_space_avail( struct gs_buf *gb );
+static unsigned int gs_buf_put( struct gs_buf *gb, const char *buf,
+       unsigned int count );
+static unsigned int gs_buf_get( struct gs_buf *gb, char *buf,
+       unsigned int count );
+
+
+/* Globals */
+
+static struct gs_dev *gs_device;
+
+static struct semaphore        gs_open_close_sem[GS_NUM_PORTS];
+
+static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
+static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
+
+static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
+
+static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE];
+static struct semaphore        gs_tmp_buf_sem;
+
+/* tty driver struct */
+static struct tty_operations gs_tty_ops = {
+       .open =                 gs_open,
+       .close =                gs_close,
+       .write =                gs_write,
+       .put_char =             gs_put_char,
+       .flush_chars =          gs_flush_chars,
+       .write_room =           gs_write_room,
+       .ioctl =                gs_ioctl,
+       .set_termios =          gs_set_termios,
+       .throttle =             gs_throttle,
+       .unthrottle =           gs_unthrottle,
+       .break_ctl =            gs_break,
+       .chars_in_buffer =      gs_chars_in_buffer,
+       .read_proc =            gs_read_proc,
+};
+static struct tty_driver *gs_tty_driver;
+
+/* gadget driver struct */
+static struct usb_gadget_driver gs_gadget_driver = {
+#ifdef HIGHSPEED
+       .speed =                USB_SPEED_HIGH,
+#else
+       .speed =                USB_SPEED_FULL,
+#endif
+       .function =             GS_LONG_NAME,
+       .bind =                 gs_bind,
+       .unbind =               gs_unbind,
+       .setup =                gs_setup,
+       .disconnect =           gs_disconnect,
+       .driver = {
+               .name =         GS_SHORT_NAME,
+               /* .shutdown = ... */
+               /* .suspend = ...  */
+               /* .resume = ...   */
+       },
+};
+
+
+/* USB descriptors */
+
+#define GS_MANUFACTURER_STR_ID 1
+#define GS_PRODUCT_STR_ID      2
+#define GS_SERIAL_STR_ID       3
+#define GS_CONFIG_STR_ID       4
+
+/* static strings, in iso 8859/1 */
+static struct usb_string gs_strings[] = {
+       { GS_MANUFACTURER_STR_ID, UTS_SYSNAME " " UTS_RELEASE " with " CHIP },
+       { GS_PRODUCT_STR_ID, GS_LONG_NAME },
+       { GS_SERIAL_STR_ID, "0" },
+       { GS_CONFIG_STR_ID, "Bulk" },
+       {  } /* end of list */
+};
+
+static struct usb_gadget_strings gs_string_table = {
+       .language =             0x0409, /* en-us */
+       .strings =              gs_strings,
+};
+
+static const struct usb_device_descriptor gs_device_desc = {
+       .bLength =              USB_DT_DEVICE_SIZE,
+       .bDescriptorType =      USB_DT_DEVICE,
+       .bcdUSB =               __constant_cpu_to_le16(0x0200),
+       .bDeviceClass =         USB_CLASS_VENDOR_SPEC,
+       .bMaxPacketSize0 =      EP0_MAXPACKET,
+       .idVendor =             __constant_cpu_to_le16(GS_VENDOR_ID),
+       .idProduct =            __constant_cpu_to_le16(GS_PRODUCT_ID),
+       .bcdDevice =            __constant_cpu_to_le16(GS_VERSION_NUM),
+       .iManufacturer =        GS_MANUFACTURER_STR_ID,
+       .iProduct =             GS_PRODUCT_STR_ID,
+       .iSerialNumber =        GS_SERIAL_STR_ID,
+       .bNumConfigurations =   GS_NUM_CONFIGS,
+};
+
+static const struct usb_config_descriptor gs_config_desc = {
+       .bLength =              USB_DT_CONFIG_SIZE,
+       .bDescriptorType =      USB_DT_CONFIG,
+       /* .wTotalLength set by gs_build_config_desc */
+       .bNumInterfaces =       GS_NUM_INTERFACES,
+       .bConfigurationValue =  GS_BULK_CONFIG_ID,
+       .iConfiguration =       GS_CONFIG_STR_ID,
+       .bmAttributes =         USB_CONFIG_ATT_ONE | SELFPOWER | WAKEUP,
+       .bMaxPower =            (MAX_USB_POWER + 1) / 2,
+};
+
+static const struct usb_interface_descriptor gs_interface_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bNumEndpoints =        GS_NUM_ENDPOINTS,
+       .bInterfaceClass =      USB_CLASS_VENDOR_SPEC,
+       .iInterface =           GS_CONFIG_STR_ID,
+};
+
+static const struct usb_endpoint_descriptor gs_fullspeed_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       __constant_cpu_to_le16(64),
+};
+
+static const struct usb_endpoint_descriptor gs_fullspeed_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     EP_OUT_NUM | USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       __constant_cpu_to_le16(64),
+};
+
+static const struct usb_endpoint_descriptor gs_highspeed_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     EP_IN_NUM | USB_DIR_IN,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       __constant_cpu_to_le16(512),
+};
+
+static const struct usb_endpoint_descriptor gs_highspeed_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     EP_OUT_NUM | USB_DIR_OUT,
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+       .wMaxPacketSize =       __constant_cpu_to_le16(512),
+};
+
+#ifdef HIGHSPEED
+static const struct usb_qualifier_descriptor gs_qualifier_desc = {
+       .bLength =              sizeof(struct usb_qualifier_descriptor),
+       .bDescriptorType =      USB_DT_DEVICE_QUALIFIER,
+       .bcdUSB =               __constant_cpu_to_le16 (0x0200),
+       .bDeviceClass =         USB_CLASS_VENDOR_SPEC,
+       /* assumes ep0 uses the same value for both speeds ... */
+       .bMaxPacketSize0 =      EP0_MAXPACKET,
+       .bNumConfigurations =   GS_NUM_CONFIGS,
+};
+#endif
+
+
+/* Module */
+
+MODULE_DESCRIPTION( GS_LONG_NAME );
+MODULE_AUTHOR( "Al Borchers" );
+MODULE_LICENSE( "GPL" );
+
+MODULE_PARM( debug, "i" );
+MODULE_PARM_DESC( debug, "Enable debugging, 0=off, 1=on" );
+
+MODULE_PARM( read_q_size, "i" );
+MODULE_PARM_DESC( read_q_size, "Read request queue size, default=32" );
+
+MODULE_PARM( write_q_size, "i" );
+MODULE_PARM_DESC( write_q_size, "Write request queue size, default=32" );
+
+MODULE_PARM( write_buf_size, "i" );
+MODULE_PARM_DESC( write_buf_size, "Write buffer size, default=8192" );
+
+module_init( gs_module_init );
+module_exit( gs_module_exit );
+
+/*
+*  gs_module_init
+*
+*  Register as a USB gadget driver and a tty driver.
+*/
+
+static int __init gs_module_init(void)
+{
+       int i;
+       int retval;
+
+       retval = usb_gadget_register_driver(&gs_gadget_driver);
+       if (retval) {
+               printk(KERN_ERR "gs_module_init: cannot register gadget driver, ret=%d\n", retval);
+               return retval;
+       }
+
+       gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
+       if (!gs_tty_driver)
+               return -ENOMEM;
+       gs_tty_driver->owner = THIS_MODULE;
+       gs_tty_driver->driver_name = GS_SHORT_NAME;
+       gs_tty_driver->name = "ttygs";
+       gs_tty_driver->devfs_name = "usb/ttygs/";
+       gs_tty_driver->major = GS_MAJOR;
+       gs_tty_driver->minor_start = GS_MINOR_START;
+       gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+       gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+       gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
+       gs_tty_driver->init_termios = tty_std_termios;
+       gs_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       tty_set_operations(gs_tty_driver, &gs_tty_ops);
+
+       for (i=0; i < GS_NUM_PORTS; i++)
+               sema_init(&gs_open_close_sem[i], 1);
+
+       sema_init(&gs_tmp_buf_sem, 1);
+
+       retval = tty_register_driver(gs_tty_driver);
+       if (retval) {
+               usb_gadget_unregister_driver(&gs_gadget_driver);
+               put_tty_driver(gs_tty_driver);
+               printk(KERN_ERR "gs_module_init: cannot register tty driver, ret=%d\n", retval);
+               return retval;
+       }
+
+       printk(KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, GS_VERSION_STR);
+       return 0;
+}
+
+
+/*
+* gs_module_exit
+*
+* Unregister as a tty driver and a USB gadget driver.
+*/
+
+static void __exit gs_module_exit(void)
+{
+       tty_unregister_driver(gs_tty_driver);
+       put_tty_driver(gs_tty_driver);
+       usb_gadget_unregister_driver(&gs_gadget_driver);
+
+       printk(KERN_INFO "gs_module_exit: %s %s unloaded\n", GS_LONG_NAME, GS_VERSION_STR);
+}
+
+
+/* TTY Driver */
+
+/*
+ * gs_open
+ */
+
+static int gs_open( struct tty_struct *tty, struct file *file )
+{
+
+       int port_num;
+       unsigned long flags;
+       struct gs_port *port;
+       struct gs_dev *dev;
+       struct gs_buf *buf;
+       struct semaphore *sem;
+
+
+       port_num = tty->index;
+
+       gs_debug( "gs_open: (%d,%p,%p)\n", port_num, tty, file );
+
+       tty->driver_data = NULL;
+
+       if( port_num < 0 || port_num >= GS_NUM_PORTS ) {
+               printk( KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n",
+                       port_num, tty, file );
+               return( -ENODEV );
+       }
+
+       dev = gs_device;
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_open: (%d,%p,%p) NULL device pointer\n",
+                       port_num, tty, file );
+               return( -ENODEV );
+       }
+
+       sem = &gs_open_close_sem[port_num];
+       if( down_interruptible( sem ) ) {
+               printk( KERN_ERR
+               "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n",
+                       port_num, tty, file );
+               return( -ERESTARTSYS );
+       }
+
+       spin_lock_irqsave(&dev->dev_lock, flags );
+
+       if( dev->dev_config == GS_NO_CONFIG_ID ) {
+               printk( KERN_ERR
+                       "gs_open: (%d,%p,%p) device is not connected\n",
+                       port_num, tty, file );
+               spin_unlock_irqrestore( &dev->dev_lock, flags );
+               up( sem );
+               return( -ENODEV );
+       }
+
+       port = dev->dev_port[port_num];
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n",
+                       port_num, tty, file );
+               spin_unlock_irqrestore( &dev->dev_lock, flags );
+               up( sem );
+               return( -ENODEV );
+       }
+
+       spin_lock( &port->port_lock );
+       spin_unlock( &dev->dev_lock );
+
+       if( port->port_dev == NULL ) {
+               printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n",
+                       port_num, tty, file );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return( -EIO );
+       }
+
+       if( port->port_open_count > 0 ) {
+               ++port->port_open_count;
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               gs_debug( "gs_open: (%d,%p,%p) already open\n",
+                       port_num, tty, file );
+               up( sem );
+               return( 0 );
+       }
+
+       /* mark port as in use, we can drop port lock and sleep if necessary */
+       port->port_in_use = 1;
+
+       /* allocate write buffer on first open */
+       if( port->port_write_buf == NULL ) {
+
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               buf = gs_buf_alloc( write_buf_size, GFP_KERNEL );
+               spin_lock_irqsave( &port->port_lock, flags );
+
+               /* might have been disconnected while asleep, check */
+               if( port->port_dev == NULL ) {
+                       printk( KERN_ERR
+                               "gs_open: (%d,%p,%p) port disconnected (2)\n",
+                               port_num, tty, file );
+                       port->port_in_use = 0;
+                       spin_unlock_irqrestore( &port->port_lock, flags );
+                       up( sem );
+                       return( -EIO );
+               }
+
+               if( (port->port_write_buf=buf) == NULL ) {
+                       printk( KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n",
+                               port_num, tty, file );
+                       port->port_in_use = 0;
+                       spin_unlock_irqrestore( &port->port_lock, flags );
+                       up( sem );
+                       return( -ENOMEM );
+               }
+
+       }
+
+       /* wait for carrier detect (not implemented) */
+
+       /* might have been disconnected while asleep, check */
+       if( port->port_dev == NULL ) {
+               printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n",
+                       port_num, tty, file );
+               port->port_in_use = 0;
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return( -EIO );
+       }
+
+       tty->driver_data = port;
+       port->port_tty = tty;
+       port->port_open_count = 1;
+       port->port_in_use = 0;
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+       up( sem );
+
+       gs_debug( "gs_open: (%d,%p,%p) completed\n", port_num, tty, file );
+
+       return( 0 ); 
+
+}
+
+
+/*
+ * gs_close
+ */
+
+static void gs_close( struct tty_struct *tty, struct file *file )
+{
+
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+       struct semaphore *sem;
+
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_close: NULL port pointer\n" );
+               return;
+       }
+
+       gs_debug( "gs_close: (%d,%p,%p)\n", port->port_num, tty, file );
+
+       sem = &gs_open_close_sem[port->port_num];
+       down( sem );
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_open_count == 0 ) {
+               printk( KERN_ERR
+                       "gs_close: (%d,%p,%p) port is already closed\n",
+                       port->port_num, tty, file );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return;
+       }
+
+       if( port->port_open_count > 0 ) {
+               --port->port_open_count;
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return;
+       }
+
+       /* free disconnected port on final close */
+       if( port->port_dev == NULL ) {
+               kfree( port );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return;
+       }
+
+       /* mark port as closed but in use, we can drop port lock */
+       /* and sleep if necessary */
+       port->port_in_use = 1;
+       port->port_open_count = 0;
+
+       /* wait for write buffer to drain, or */
+       /* at most GS_CLOSE_TIMEOUT seconds */
+       if( gs_buf_data_avail( port->port_write_buf ) > 0 ) {
+               wait_cond_interruptible_timeout( port->port_write_wait,
+               port->port_dev == NULL
+               || gs_buf_data_avail(port->port_write_buf) == 0,
+               &port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ );
+       }
+
+       /* free disconnected port on final close */
+       /* (might have happened during the above sleep) */
+       if( port->port_dev == NULL ) {
+               kfree( port );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               up( sem );
+               return;
+       }
+
+       gs_buf_clear( port->port_write_buf );
+
+       tty->driver_data = NULL;
+       port->port_tty = NULL;
+       port->port_in_use = 0;
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+       up( sem );
+
+       gs_debug( "gs_close: (%d,%p,%p) completed\n",
+               port->port_num, tty, file );
+
+}
+
+
+/*
+ * gs_write
+ */
+
+static int gs_write( struct tty_struct *tty, int from_user,
+       const unsigned char *buf, int count )
+{
+
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_write: NULL port pointer\n" );
+               return( -EIO );
+       }
+
+       gs_debug( "gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
+               count );
+
+       if( count == 0 )
+               return( 0 );
+
+       /* copy from user into tmp buffer, get tmp_buf semaphore */
+       if( from_user ) {
+               if( count > GS_TMP_BUF_SIZE )
+                       count = GS_TMP_BUF_SIZE;
+               down( &gs_tmp_buf_sem );
+               if( copy_from_user( gs_tmp_buf, buf, count ) != 0 ) {
+                       up( &gs_tmp_buf_sem );
+                       printk( KERN_ERR
+                       "gs_write: (%d,%p) cannot copy from user space\n",
+                               port->port_num, tty );
+                       return( -EFAULT );
+               }
+               buf = gs_tmp_buf;
+       }
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_dev == NULL ) {
+               printk( KERN_ERR "gs_write: (%d,%p) port is not connected\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               if( from_user )
+                       up( &gs_tmp_buf_sem );
+               return( -EIO );
+       }
+
+       if( port->port_open_count == 0 ) {
+               printk( KERN_ERR "gs_write: (%d,%p) port is closed\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               if( from_user )
+                       up( &gs_tmp_buf_sem );
+               return( -EBADF );
+       }
+
+       count = gs_buf_put( port->port_write_buf, buf, count );
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+
+       if( from_user )
+               up( &gs_tmp_buf_sem );
+
+       gs_send( gs_device );
+
+       gs_debug( "gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
+               count );
+
+       return( count );
+
+}
+
+
+/*
+ * gs_put_char
+ */
+
+static void gs_put_char( struct tty_struct *tty, unsigned char ch )
+{
+
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_put_char: NULL port pointer\n" );
+               return;
+       }
+
+       gs_debug( "gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2) );
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_dev == NULL ) {
+               printk( KERN_ERR "gs_put_char: (%d,%p) port is not connected\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               return;
+       }
+
+       if( port->port_open_count == 0 ) {
+               printk( KERN_ERR "gs_put_char: (%d,%p) port is closed\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               return;
+       }
+
+       gs_buf_put( port->port_write_buf, &ch, 1 );
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+
+}
+
+
+/*
+ * gs_flush_chars
+ */
+
+static void gs_flush_chars( struct tty_struct *tty )
+{
+
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_flush_chars: NULL port pointer\n" );
+               return;
+       }
+
+       gs_debug( "gs_flush_chars: (%d,%p)\n", port->port_num, tty );
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_dev == NULL ) {
+               printk( KERN_ERR
+                       "gs_flush_chars: (%d,%p) port is not connected\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               return;
+       }
+
+       if( port->port_open_count == 0 ) {
+               printk( KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n",
+                       port->port_num, tty );
+               spin_unlock_irqrestore( &port->port_lock, flags );
+               return;
+       }
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+
+       gs_send( gs_device );
+
+}
+
+
+/*
+ * gs_write_room
+ */
+
+static int gs_write_room( struct tty_struct *tty )
+{
+
+       int room = 0;
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL )
+               return( 0 );
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_dev != NULL && port->port_open_count > 0
+       && port->port_write_buf != NULL )
+               room = gs_buf_space_avail( port->port_write_buf );
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+
+       gs_debug( "gs_write_room: (%d,%p) room=%d\n",
+               port->port_num, tty, room );
+
+       return( room );
+
+}
+
+
+/*
+ * gs_chars_in_buffer
+ */
+
+static int gs_chars_in_buffer( struct tty_struct *tty )
+{
+
+       int chars = 0;
+       unsigned long flags;
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL )
+               return( 0 );
+
+       spin_lock_irqsave( &port->port_lock, flags );
+
+       if( port->port_dev != NULL && port->port_open_count > 0
+       && port->port_write_buf != NULL )
+               chars = gs_buf_data_avail( port->port_write_buf );
+
+       spin_unlock_irqrestore( &port->port_lock, flags );
+
+       gs_debug( "gs_chars_in_buffer: (%d,%p) chars=%d\n",
+               port->port_num, tty, chars );
+
+       return( chars );
+
+}
+
+
+/*
+ * gs_throttle
+ */
+
+static void gs_throttle( struct tty_struct *tty )
+{
+
+}
+
+
+/*
+ * gs_unthrottle
+ */
+
+static void gs_unthrottle( struct tty_struct *tty )
+{
+
+}
+
+
+/*
+ * gs_break
+ */
+
+static void gs_break( struct tty_struct *tty, int break_state )
+{
+
+}
+
+
+/*
+ * gs_ioctl
+ */
+
+static int  gs_ioctl( struct tty_struct *tty, struct file *file,
+       unsigned int cmd, unsigned long arg )
+{
+
+       struct gs_port *port = tty->driver_data;
+
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_ioctl: NULL port pointer\n" );
+               return( -EIO );
+       }
+
+       gs_debug( "gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
+               port->port_num, tty, file, cmd, arg );
+
+       /* handle ioctls */
+
+       /* could not handle ioctl */
+       return( -ENOIOCTLCMD );
+
+}
+
+
+/*
+ * gs_set_termios
+ */
+
+static void gs_set_termios( struct tty_struct *tty, struct termios *old )
+{
+
+}
+
+
+/*
+ * gs_read_proc
+ */
+
+static int gs_read_proc( char *page, char **start, off_t off, int count,
+       int *eof, void *data )
+{
+
+       return( 0 );
+
+}
+
+
+/*
+* gs_send
+*
+* This function finds available write requests, calls
+* gs_send_packet to fill these packets with data, and
+* continues until either there are no more write requests
+* available or no more data to send.  This function is
+* run whenever data arrives or write requests are available.
+*/
+
+static int gs_send( struct gs_dev *dev )
+{
+
+       int ret,len;
+       unsigned long flags;
+       struct usb_ep *ep;
+       struct usb_request *req;
+       struct gs_req_entry *req_entry;
+
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_send: NULL device pointer\n" );
+               return( -ENODEV );
+       }
+
+       spin_lock_irqsave( &dev->dev_lock, flags );
+
+       ep = dev->dev_in_ep;
+
+       while( !list_empty( &dev->dev_req_list ) ) {
+
+               req_entry = list_entry( dev->dev_req_list.next,
+                       struct gs_req_entry, re_entry );
+
+               req = req_entry->re_req;
+
+               len = gs_send_packet( dev, req->buf, ep->maxpacket );
+
+               if( len > 0 ) {
+gs_debug_level( 3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2) );
+                       list_del( &req_entry->re_entry );
+                       req->length = len;
+                       if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+                               printk( KERN_ERR
+                               "gs_send: cannot queue read request, ret=%d\n",
+                                       ret );
+                               break;
+                       }
+               } else {
+                       break;
+               }
+
+       }
+
+       spin_unlock_irqrestore( &dev->dev_lock, flags );
+
+       return( 0 );
+
+}
+
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned.  If there is no data to
+ * send, 0 is returned.  If there is any error a negative
+ * error number is returned.
+ *
+ * Called during USB completion routine, on interrupt time.
+ *
+ * We assume that disconnect will not happen until all completion
+ * routines have completed, so we can assume that the dev_port
+ * array does not change during the lifetime of this function.
+ */
+
+static int gs_send_packet( struct gs_dev *dev, char *packet, unsigned int size )
+{
+
+       unsigned int len;
+       struct gs_port *port;
+
+
+       /* TEMPORARY -- only port 0 is supported right now */
+       port = dev->dev_port[0];
+
+       if( port == NULL ) {
+               printk( KERN_ERR
+                       "gs_send_packet: port=%d, NULL port pointer\n",
+                       0 );
+               return( -EIO );
+       }
+
+       spin_lock( &port->port_lock );
+
+       len = gs_buf_data_avail( port->port_write_buf );
+       if( len < size )
+               size = len;
+
+       if( size == 0 ) {
+               spin_unlock( &port->port_lock );
+               return( 0 );
+       }
+
+       size = gs_buf_get( port->port_write_buf, packet, size );
+
+       wake_up_interruptible( &port->port_tty->write_wait );
+
+       spin_unlock( &port->port_lock );
+
+       return( size );
+
+}
+
+
+/*
+ * gs_recv_packet
+ *
+ * Called for each USB packet received.  Reads the packet
+ * header and stuffs the data in the appropriate tty buffer.
+ * Returns 0 if successful, or a negative error number.
+ *
+ * Called during USB completion routine, on interrupt time.
+ *
+ * We assume that disconnect will not happen until all completion
+ * routines have completed, so we can assume that the dev_port
+ * array does not change during the lifetime of this function.
+ */
+
+static int gs_recv_packet( struct gs_dev *dev, char *packet, unsigned int size )
+{
+
+       unsigned int len;
+       struct gs_port *port;
+
+
+       /* TEMPORARY -- only port 0 is supported right now */
+       port = dev->dev_port[0];
+
+       if( port == NULL ) {
+               printk( KERN_ERR "gs_recv_packet: port=%d, NULL port pointer\n",
+                       port->port_num );
+               return( -EIO );
+       }
+
+       spin_lock( &port->port_lock );
+
+       if( port->port_tty == NULL ) {
+               printk( KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n",
+                       port->port_num );
+               spin_unlock( &port->port_lock );
+               return( -EIO );
+       }
+
+       if( port->port_tty->magic != TTY_MAGIC ) {
+               printk( KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n",
+                       port->port_num );
+               spin_unlock( &port->port_lock );
+               return( -EIO );
+       }
+
+       len = (unsigned int)(TTY_FLIPBUF_SIZE - port->port_tty->flip.count);
+       if( len < size )
+               size = len;
+
+       if( size > 0 ) {
+               memcpy( port->port_tty->flip.char_buf_ptr, packet, size );
+               port->port_tty->flip.char_buf_ptr += size;
+               port->port_tty->flip.count += size;
+               tty_flip_buffer_push( port->port_tty );
+               wake_up_interruptible( &port->port_tty->read_wait );
+       }
+
+       spin_unlock( &port->port_lock );
+
+       return( 0 );
+
+}
+
+
+/*
+* gs_read_complete
+*/
+
+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req )
+{
+
+       int ret;
+       struct gs_dev *dev = ep->driver_data;
+
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_read_complete: NULL device pointer\n" );
+               return;
+       }
+
+       switch( req->status ) {
+
+       case 0:
+               /* normal completion */
+               gs_recv_packet( dev, req->buf, req->actual );
+requeue:
+               req->length = ep->maxpacket;
+               if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+                       printk( KERN_ERR
+                       "gs_read_complete: cannot queue read request, ret=%d\n",
+                               ret );
+               }
+               break;
+
+       case -ESHUTDOWN:
+               /* disconnect */
+               gs_debug( "gs_read_complete: shutdown\n" );
+               gs_free_req( ep, req );
+               break;
+
+       default:
+               /* unexpected */
+               printk( KERN_ERR
+               "gs_read_complete: unexpected status error, status=%d\n",
+                       req->status );
+               goto requeue;
+               break;
+
+       }
+
+}
+
+
+/*
+* gs_write_complete
+*/
+
+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req )
+{
+
+       struct gs_dev *dev = ep->driver_data;
+       struct gs_req_entry *gs_req = req->context;
+
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_write_complete: NULL device pointer\n" );
+               return;
+       }
+
+       switch( req->status ) {
+
+       case 0:
+               /* normal completion */
+requeue:
+               if( gs_req == NULL ) {
+                       printk( KERN_ERR
+                               "gs_write_complete: NULL request pointer\n" );
+                       return;
+               }
+
+               spin_lock( &dev->dev_lock );
+               list_add( &gs_req->re_entry, &dev->dev_req_list );
+               spin_unlock( &dev->dev_lock );
+
+               gs_send( dev );
+
+               break;
+
+       case -ESHUTDOWN:
+               /* disconnect */
+               gs_debug( "gs_write_complete: shutdown\n" );
+               gs_free_req( ep, req );
+               break;
+
+       default:
+               printk( KERN_ERR
+               "gs_write_complete: unexpected status error, status=%d\n",
+                       req->status );
+               goto requeue;
+               break;
+
+       }
+
+}
+
+
+/* Gadget Driver */
+
+/*
+ * gs_bind
+ *
+ * Called on module load.  Allocates and initializes the device
+ * structure and a control request.
+ */
+
+static int gs_bind( struct usb_gadget *gadget )
+{
+
+       int ret;
+       struct gs_dev *dev;
+
+
+       gs_device = dev = kmalloc( sizeof(struct gs_dev), GFP_KERNEL );
+       if( dev == NULL )
+               return( -ENOMEM );
+
+       set_gadget_data( gadget, dev );
+
+       memset( dev, 0, sizeof(struct gs_dev) );
+       dev->dev_gadget = gadget;
+       spin_lock_init( &dev->dev_lock );
+    INIT_LIST_HEAD( &dev->dev_req_list );
+
+       if( (ret=gs_alloc_ports( dev, GFP_KERNEL )) != 0 ) {
+               printk( KERN_ERR "gs_bind: cannot allocate ports\n" );
+               gs_unbind( gadget );
+               return( ret );
+       }
+
+       /* preallocate control response and buffer */
+       dev->dev_ctrl_req = gs_alloc_req( gadget->ep0, GS_MAX_DESC_LEN,
+               GFP_KERNEL );
+       if( dev->dev_ctrl_req == NULL ) {
+               gs_unbind( gadget );
+               return( -ENOMEM );
+       }
+       dev->dev_ctrl_req->complete = gs_setup_complete;
+
+       gadget->ep0->driver_data = dev;
+
+       printk( KERN_INFO "gs_bind: %s %s bound\n",
+               GS_LONG_NAME, GS_VERSION_STR );
+
+       return( 0 );
+
+}
+
+
+/*
+ * gs_unbind
+ *
+ * Called on module unload.  Frees the control request and device
+ * structure.
+ */
+
+static void gs_unbind( struct usb_gadget *gadget )
+{
+
+       struct gs_dev *dev = get_gadget_data( gadget );
+
+
+       gs_device = NULL;
+
+       /* read/write requests already freed, only control request remains */
+       if( dev != NULL ) {
+               if( dev->dev_ctrl_req != NULL )
+                       gs_free_req( gadget->ep0, dev->dev_ctrl_req );
+               gs_free_ports( dev );
+               kfree( dev );
+               set_gadget_data( gadget, NULL );
+       }
+
+       printk( KERN_INFO "gs_unbind: %s %s unbound\n", GS_LONG_NAME,
+               GS_VERSION_STR );
+
+}
+
+
+/*
+ * gs_setup
+ *
+ * Implements all the control endpoint functionality that's not
+ * handled in hardware or the hardware driver.
+ *
+ * Returns the size of the data sent to the host, or a negative
+ * error number.
+ */
+
+static int gs_setup( struct usb_gadget *gadget,
+       const struct usb_ctrlrequest *ctrl )
+{
+
+       int ret = -EOPNOTSUPP;
+       unsigned int sv_config;
+       struct gs_dev *dev = get_gadget_data( gadget );
+       struct usb_request *req = dev->dev_ctrl_req;
+
+
+       switch (ctrl->bRequest) {
+
+       case USB_REQ_GET_DESCRIPTOR:
+
+               if( ctrl->bRequestType != USB_DIR_IN )
+                       break;
+
+               switch (ctrl->wValue >> 8) {
+
+               case USB_DT_DEVICE:
+                       ret = min( ctrl->wLength,
+                               (u16)sizeof(struct usb_device_descriptor) );
+                       memcpy( req->buf, &gs_device_desc, ret );
+                       break;
+
+#ifdef HIGHSPEED
+               case USB_DT_DEVICE_QUALIFIER:
+                       ret = min( ctrl->wLength,
+                               (u16)sizeof(struct usb_qualifier_descriptor) );
+                       memcpy( req->buf, &gs_qualifier_desc, ret );
+                       break;
+
+               case USB_DT_OTHER_SPEED_CONFIG:
+#endif /* HIGHSPEED */
+               case USB_DT_CONFIG:
+                       ret = gs_build_config_desc( req->buf, gadget->speed,
+                               ctrl->wValue >> 8, ctrl->wValue & 0xff );
+                       if( ret >= 0 )
+                               ret = min( ctrl->wLength, (u16)ret );
+                       break;
+
+               case USB_DT_STRING:
+                       /* wIndex == language code. */
+                       ret = usb_gadget_get_string( &gs_string_table,
+                               ctrl->wValue & 0xff, req->buf );
+                       if( ret >= 0 )
+                               ret = min( ctrl->wLength, (u16)ret );
+                       break;
+               }
+               break;
+
+       case USB_REQ_SET_CONFIGURATION:
+               if( ctrl->bRequestType != 0 )
+                       break;
+               spin_lock( &dev->dev_lock );
+               ret = gs_set_config( dev, ctrl->wValue );
+               spin_unlock( &dev->dev_lock );
+               break;
+
+       case USB_REQ_GET_CONFIGURATION:
+               if( ctrl->bRequestType != USB_DIR_IN )
+                       break;
+               *(u8 *)req->buf = dev->dev_config;
+               ret = min( ctrl->wLength, (u16)1 );
+               break;
+
+       case USB_REQ_SET_INTERFACE:
+               if( ctrl->bRequestType != USB_RECIP_INTERFACE )
+                       break;
+               spin_lock( &dev->dev_lock );
+               if( dev->dev_config == GS_BULK_CONFIG_ID
+               && ctrl->wIndex == GS_INTERFACE_ID
+               && ctrl->wValue == GS_ALT_INTERFACE_ID ) {
+                       sv_config = dev->dev_config;
+                       /* since there is only one interface, setting the */
+                       /* interface is equivalent to setting the config */
+                       gs_reset_config( dev );
+                       gs_set_config( dev, sv_config );
+                       ret = 0;
+               }
+               spin_unlock( &dev->dev_lock );
+               break;
+
+       case USB_REQ_GET_INTERFACE:
+               if( ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) )
+                       break;
+               if( dev->dev_config == GS_NO_CONFIG_ID )
+                       break;
+               if( ctrl->wIndex != GS_INTERFACE_ID ) {
+                       ret = -EDOM;
+                       break;
+               }
+               *(u8 *)req->buf = GS_ALT_INTERFACE_ID;
+               ret = min( ctrl->wLength, (u16)1 );
+               break;
+
+       default:
+               printk( KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n",
+                       ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
+                       ctrl->wIndex, ctrl->wLength );
+               break;
+
+       }
+
+       /* respond with data transfer before status phase? */
+       if( ret >= 0 ) {
+               req->length = ret;
+               ret = usb_ep_queue( gadget->ep0, req, GFP_ATOMIC );
+               if( ret < 0 ) {
+                       printk( KERN_ERR
+                               "gs_setup: cannot queue response, ret=%d\n",
+                               ret );
+                       req->status = 0;
+                       gs_setup_complete( gadget->ep0, req );
+               }
+       }
+
+       /* device either stalls (ret < 0) or reports success */
+       return( ret );
+
+}
+
+
+/*
+ * gs_setup_complete
+ */
+
+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req )
+{
+       if( req->status || req->actual != req->length ) {
+               printk( KERN_ERR "gs_setup_complete: status error, status=%d, actual=%d, length=%d\n",
+                       req->status, req->actual, req->length );
+       }
+}
+
+
+/*
+ * gs_disconnect
+ *
+ * Called when the device is disconnected.  Frees the closed
+ * ports and disconnects open ports.  Open ports will be freed
+ * on close.  Then reallocates the ports for the next connection.
+ */
+
+static void gs_disconnect( struct usb_gadget *gadget )
+{
+
+       unsigned long flags;
+       struct gs_dev *dev = get_gadget_data( gadget );
+
+
+       spin_lock_irqsave( &dev->dev_lock, flags );
+
+       gs_reset_config( dev );
+
+       /* free closed ports and disconnect open ports */
+       /* (open ports will be freed when closed) */
+       gs_free_ports( dev );
+
+       /* re-allocate ports for the next connection */
+       if( gs_alloc_ports( dev, GFP_ATOMIC ) != 0 )
+               printk( KERN_ERR "gs_disconnect: cannot re-allocate ports\n" );
+
+       spin_unlock_irqrestore( &dev->dev_lock, flags );
+
+       printk( KERN_INFO "gs_disconnect: %s disconnected\n", GS_LONG_NAME );
+
+}
+
+
+/*
+ * gs_set_config
+ *
+ * Configures the device by enabling device specific
+ * optimizations, setting up the endpoints, allocating
+ * read and write requests and queuing read requests.
+ *
+ * The device lock must be held when calling this function.
+ */
+
+static int gs_set_config( struct gs_dev *dev, unsigned config )
+{
+
+       int i;
+       int ret = 0;
+       struct usb_gadget *gadget = dev->dev_gadget;
+       struct usb_ep *ep;
+       struct usb_request *req;
+       struct gs_req_entry *req_entry;
+
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_set_config: NULL device pointer\n" );
+               return( 0 );
+       }
+
+       if( config == dev->dev_config )
+               return( 0 );
+
+       gs_reset_config( dev );
+
+       if( config == GS_NO_CONFIG_ID )
+               return( 0 );
+
+       if( config != GS_BULK_CONFIG_ID )
+               return( -EINVAL );
+
+       hw_optimize( gadget );
+
+       gadget_for_each_ep( ep, gadget ) {
+
+               if( strcmp( ep->name, EP_IN_NAME ) == 0 ) {
+                       ret = usb_ep_enable( ep,
+                               gadget->speed == USB_SPEED_HIGH ?
+                               &gs_highspeed_in_desc : &gs_fullspeed_in_desc );
+                       if( ret == 0 ) {
+                               ep->driver_data = dev;
+                               dev->dev_in_ep = ep;
+                       } else {
+                               printk( KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n",
+                                       ep->name, ret );
+                               gs_reset_config( dev );
+                               return( ret );
+                       }
+               }
+
+               else if( strcmp( ep->name, EP_OUT_NAME ) == 0 ) {
+                       ret = usb_ep_enable( ep,
+                               gadget->speed == USB_SPEED_HIGH ?
+                               &gs_highspeed_out_desc :
+                               &gs_fullspeed_out_desc );
+                       if( ret == 0 ) {
+                               ep->driver_data = dev;
+                               dev->dev_out_ep = ep;
+                       } else {
+                               printk( KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n",
+                                       ep->name, ret );
+                               gs_reset_config( dev );
+                               return( ret );
+                       }
+               }
+
+       }
+
+       if( dev->dev_in_ep == NULL || dev->dev_out_ep == NULL ) {
+               gs_reset_config( dev );
+               printk( KERN_ERR "gs_set_config: cannot find endpoints\n" );
+               return( -ENODEV );
+       }
+
+       /* allocate and queue read requests */
+       ep = dev->dev_out_ep;
+       for( i=0; i<read_q_size && ret == 0; i++ ) {
+               if( (req=gs_alloc_req( ep, ep->maxpacket, GFP_ATOMIC )) ) {
+                       req->complete = gs_read_complete;
+                       if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) {
+                               printk( KERN_ERR "gs_set_config: cannot queue read request, ret=%d\n",
+                                       ret );
+                       }
+               } else {
+                       gs_reset_config( dev );
+                       printk( KERN_ERR
+                       "gs_set_config: cannot allocate read requests\n" );
+                       return( -ENOMEM );
+               }
+       }
+
+       /* allocate write requests, and put on free list */
+       ep = dev->dev_in_ep;
+       for( i=0; i<write_q_size; i++ ) {
+               if( (req_entry=gs_alloc_req_entry( ep, ep->maxpacket,
+               GFP_ATOMIC )) ) {
+                       req_entry->re_req->complete = gs_write_complete;
+                       list_add( &req_entry->re_entry, &dev->dev_req_list );
+               } else {
+                       gs_reset_config( dev );
+                       printk( KERN_ERR
+                       "gs_set_config: cannot allocate write requests\n" );
+                       return( -ENOMEM );
+               }
+       }
+
+       dev->dev_config = config;
+
+       printk( KERN_INFO "gs_set_config: %s configured for %s speed\n",
+               GS_LONG_NAME,
+               gadget->speed == USB_SPEED_HIGH ? "high" : "full" );
+
+       return( 0 );
+
+}
+
+
+/*
+ * gs_reset_config
+ *
+ * Mark the device as not configured, disable all endpoints,
+ * which forces completion of pending I/O and frees queued
+ * requests, and free the remaining write requests on the
+ * free list.
+ *
+ * The device lock must be held when calling this function.
+ */
+
+static void gs_reset_config( struct gs_dev *dev )
+{
+
+       struct gs_req_entry *req_entry;
+
+
+       if( dev == NULL ) {
+               printk( KERN_ERR "gs_reset_config: NULL device pointer\n" );
+               return;
+       }
+
+       if( dev->dev_config == GS_NO_CONFIG_ID )
+               return;
+
+       dev->dev_config = GS_NO_CONFIG_ID;
+
+       /* free write requests on the free list */
+       while( !list_empty( &dev->dev_req_list ) ) {
+               req_entry = list_entry( dev->dev_req_list.next,
+                       struct gs_req_entry, re_entry );
+               list_del( &req_entry->re_entry );
+               gs_free_req_entry( dev->dev_in_ep, req_entry );
+       }
+
+       /* disable endpoints, forcing completion of pending i/o; */
+       /* completion handlers free their requests in this case */
+       if( dev->dev_in_ep ) {
+               usb_ep_disable( dev->dev_in_ep );
+               dev->dev_in_ep = NULL;
+       }
+       if( dev->dev_out_ep ) {
+               usb_ep_disable( dev->dev_out_ep );
+               dev->dev_out_ep = NULL;
+       }
+
+}
+
+
+/*
+ * gs_build_config_desc
+ *
+ * Builds a config descriptor in the given buffer and returns the
+ * length, or a negative error number.
+ */
+
+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed,
+               u8 type, unsigned int index )
+{
+
+       int high_speed;
+       int len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE
+                               + GS_NUM_ENDPOINTS * USB_DT_ENDPOINT_SIZE;
+
+
+       /* only one config */
+       if( index != 0 )
+               return( -EINVAL );
+
+       memcpy( buf, &gs_config_desc, USB_DT_CONFIG_SIZE );
+       ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+       ((struct usb_config_descriptor *)buf)->wTotalLength =
+               __constant_cpu_to_le16( len );
+       buf += USB_DT_CONFIG_SIZE;
+
+       memcpy( buf, &gs_interface_desc, USB_DT_INTERFACE_SIZE );
+       buf += USB_DT_INTERFACE_SIZE;
+
+       /* other speed switches high and full speed */
+       high_speed = (speed == USB_SPEED_HIGH);
+       if( type == USB_DT_OTHER_SPEED_CONFIG )
+               high_speed = !high_speed;
+
+       memcpy( buf,
+               high_speed ? &gs_highspeed_in_desc : &gs_fullspeed_in_desc,
+               USB_DT_ENDPOINT_SIZE );
+       buf += USB_DT_ENDPOINT_SIZE;
+       memcpy( buf,
+               high_speed ? &gs_highspeed_out_desc : &gs_fullspeed_out_desc,
+               USB_DT_ENDPOINT_SIZE );
+
+       return( len );
+
+}
+
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer.  Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+
+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len,
+       int kmalloc_flags )
+{
+
+       struct usb_request *req;
+
+
+       if( ep == NULL )
+               return( NULL );
+
+       req = usb_ep_alloc_request( ep, kmalloc_flags );
+
+       if( req != NULL ) {
+               req->length = len;
+               req->buf = usb_ep_alloc_buffer( ep, len, &req->dma,
+                       kmalloc_flags );
+               if( req->buf == NULL ) {
+                       usb_ep_free_request( ep, req );
+                       return( NULL );
+               }
+       }
+
+       return( req );
+
+}
+
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+
+static void gs_free_req( struct usb_ep *ep, struct usb_request *req )
+{
+       if( ep != NULL && req != NULL ) {
+               if( req->buf != NULL )
+                       usb_ep_free_buffer( ep, req->buf, req->dma,
+                               req->length );
+               usb_ep_free_request( ep, req );
+       }
+}
+
+
+/*
+ * gs_alloc_req_entry
+ *
+ * Allocates a request and its buffer, using the given
+ * endpoint, buffer len, and kmalloc flags.
+ */
+
+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep,
+       unsigned len, int kmalloc_flags )
+{
+
+       struct gs_req_entry     *req;
+
+
+       req = kmalloc( sizeof(struct gs_req_entry), kmalloc_flags );
+       if( req == NULL )
+               return( NULL );
+
+       req->re_req = gs_alloc_req( ep, len, kmalloc_flags );
+       if( req->re_req == NULL ) {
+               kfree( req );
+               return( NULL );
+       }
+
+       req->re_req->context = req;
+
+       return( req );
+
+}
+
+
+/*
+ * gs_free_req_entry
+ *
+ * Frees a request and its buffer.
+ */
+
+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req )
+{
+       if( ep != NULL && req != NULL ) {
+               if( req->re_req != NULL )
+                       gs_free_req( ep, req->re_req );
+               kfree( req );
+       }
+}
+
+
+/*
+ * gs_alloc_ports
+ *
+ * Allocate all ports and set the gs_dev struct to point to them.
+ * Return 0 if successful, or a negative error number.
+ *
+ * The device lock is normally held when calling this function.
+ */
+
+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags )
+{
+
+       int i;
+       struct gs_port *port;
+
+
+       if( dev == NULL )
+               return( -EIO );
+
+       for( i=0; i<GS_NUM_PORTS; i++ ) {
+
+               if( (port=(struct gs_port *)kmalloc( sizeof(struct gs_port),
+               kmalloc_flags )) == NULL )
+                       return( -ENOMEM );
+
+               memset( port, 0, sizeof( struct gs_port ) );
+               port->port_dev = dev;
+               port->port_num = i;
+               spin_lock_init( &port->port_lock );
+       init_waitqueue_head( &port->port_write_wait );
+
+               dev->dev_port[i] = port;
+
+       }
+
+       return( 0 );
+
+}
+
+
+/*
+ * gs_free_ports
+ *
+ * Free all closed ports.  Open ports are disconnected by
+ * freeing their write buffers, setting their device pointers
+ * and the pointers to them in the device to NULL.  These
+ * ports will be freed when closed.
+ *
+ * The device lock is normally held when calling this function.
+ */
+
+static void gs_free_ports( struct gs_dev *dev )
+{
+
+       int i;
+       unsigned long flags;
+       struct gs_port *port;
+
+
+       if( dev == NULL )
+               return;
+
+       for( i=0; i<GS_NUM_PORTS; i++ ) {
+
+               if( (port=dev->dev_port[i]) != NULL ) {
+
+                       dev->dev_port[i] = NULL;
+
+                       spin_lock_irqsave( &port->port_lock, flags );
+
+                       if( port->port_write_buf != NULL ) {
+                               gs_buf_free( port->port_write_buf );
+                               port->port_write_buf = NULL;
+                       }
+
+                       if( port->port_open_count > 0 || port->port_in_use ) {
+                               port->port_dev = NULL;
+                               wake_up_interruptible( &port->port_write_wait );
+                               wake_up_interruptible( &port->port_tty->read_wait );
+                               wake_up_interruptible( &port->port_tty->write_wait );
+                       } else {
+                               kfree( port );
+                       }
+
+                       spin_unlock_irqrestore( &port->port_lock, flags );
+
+               }
+
+       }
+
+}
+
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+
+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags )
+{
+
+       struct gs_buf *gb;
+
+
+       if( size == 0 )
+               return( NULL );
+
+       gb = (struct gs_buf *)kmalloc( sizeof(struct gs_buf), kmalloc_flags );
+       if( gb == NULL )
+               return( NULL );
+
+       gb->buf_buf = kmalloc( size, kmalloc_flags );
+       if( gb->buf_buf == NULL ) {
+               kfree( gb );
+               return( NULL );
+       }
+
+       gb->buf_size = size;
+       gb->buf_get = gb->buf_put = gb->buf_buf;
+
+       return( gb );
+
+}
+
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+
+void gs_buf_free( struct gs_buf *gb )
+{
+       if( gb != NULL ) {
+               if( gb->buf_buf != NULL )
+                       kfree( gb->buf_buf );
+               kfree( gb );
+       }
+}
+
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+
+void gs_buf_clear( struct gs_buf *gb )
+{
+       if( gb != NULL )
+               gb->buf_get = gb->buf_put;
+               /* equivalent to a get of all data available */
+}
+
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+
+unsigned int gs_buf_data_avail( struct gs_buf *gb )
+{
+       if( gb != NULL )
+               return( (gb->buf_size + gb->buf_put - gb->buf_get)
+                       % gb->buf_size );
+       else
+               return( 0 );
+}
+
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+
+unsigned int gs_buf_space_avail( struct gs_buf *gb )
+{
+       if( gb != NULL )
+               return( (gb->buf_size + gb->buf_get - gb->buf_put - 1)
+                       % gb->buf_size );
+       else
+               return( 0 );
+}
+
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+
+unsigned int gs_buf_put( struct gs_buf *gb, const char *buf,
+       unsigned int count )
+{
+
+       unsigned int len;
+
+
+       if( gb == NULL )
+               return( 0 );
+
+       len  = gs_buf_space_avail( gb );
+       if( count > len )
+               count = len;
+
+       if( count == 0 )
+               return( 0 );
+
+       len = gb->buf_buf + gb->buf_size - gb->buf_put;
+       if( count > len ) {
+               memcpy( gb->buf_put, buf, len );
+               memcpy( gb->buf_buf, buf+len, count - len );
+               gb->buf_put = gb->buf_buf + count - len;
+       } else {
+               memcpy( gb->buf_put, buf, count );
+               if( count < len )
+                       gb->buf_put += count;
+               else /* count == len */
+                       gb->buf_put = gb->buf_buf;
+       }
+
+       return( count );
+
+}
+
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+
+unsigned int gs_buf_get( struct gs_buf *gb, char *buf, unsigned int count )
+{
+
+       unsigned int len;
+
+
+       if( gb == NULL )
+               return( 0 );
+
+       len = gs_buf_data_avail( gb );
+       if( count > len )
+               count = len;
+
+       if( count == 0 )
+               return( 0 );
+
+       len = gb->buf_buf + gb->buf_size - gb->buf_get;
+       if( count > len ) {
+               memcpy( buf, gb->buf_get, len );
+               memcpy( buf+len, gb->buf_buf, count - len );
+               gb->buf_get = gb->buf_buf + count - len;
+       } else {
+               memcpy( buf, gb->buf_get, count );
+               if( count < len )
+                       gb->buf_get += count;
+               else /* count == len */
+                       gb->buf_get = gb->buf_buf;
+       }
+
+       return( count );
+
+}
index c699c98..7e0b659 100644 (file)
@@ -269,18 +269,19 @@ static void ohci_dump (struct ohci_hcd *controller, int verbose)
        ohci_dump_status (controller, NULL, 0);
        if (controller->hcca)
                ohci_dbg (controller,
-                       "hcca frame #%04x\n", controller->hcca->frame_no);
+                       "hcca frame #%04x\n", OHCI_FRAME_NO(controller->hcca));
        ohci_dump_roothub (controller, 1, NULL, 0);
 }
 
 static const char data0 [] = "DATA0";
 static const char data1 [] = "DATA1";
 
-static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
+static void ohci_dump_td (const struct ohci_hcd *ohci, const char *label,
+               const struct td *td)
 {
        u32     tmp = le32_to_cpup (&td->hwINFO);
 
-       ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x",
+       ohci_dbg (ohci, "%s td %p%s; urb %p index %d; hw next td %08x\n",
                label, td,
                (tmp & TD_DONE) ? " (DONE)" : "",
                td->urb, td->index,
@@ -301,28 +302,28 @@ static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
                case TD_DP_OUT: pid = "OUT"; break;
                default: pid = "(bad pid)"; break;
                }
-               ohci_dbg (ohci, "     info %08x CC=%x %s DI=%d %s %s", tmp,
+               ohci_dbg (ohci, "     info %08x CC=%x %s DI=%d %s %s\n", tmp,
                        TD_CC_GET(tmp), /* EC, */ toggle,
                        (tmp & TD_DI) >> 21, pid,
                        (tmp & TD_R) ? "R" : "");
                cbp = le32_to_cpup (&td->hwCBP);
                be = le32_to_cpup (&td->hwBE);
-               ohci_dbg (ohci, "     cbp %08x be %08x (len %d)", cbp, be,
+               ohci_dbg (ohci, "     cbp %08x be %08x (len %d)\n", cbp, be,
                        cbp ? (be + 1 - cbp) : 0);
        } else {
                unsigned        i;
-               ohci_dbg (ohci, "     info %08x CC=%x FC=%d DI=%d SF=%04x", tmp,
+               ohci_dbg (ohci, "  info %08x CC=%x FC=%d DI=%d SF=%04x\n", tmp,
                        TD_CC_GET(tmp),
                        (tmp >> 24) & 0x07,
                        (tmp & TD_DI) >> 21,
                        tmp & 0x0000ffff);
-               ohci_dbg (ohci, "     bp0 %08x be %08x",
+               ohci_dbg (ohci, "  bp0 %08x be %08x\n",
                        le32_to_cpup (&td->hwCBP) & ~0x0fff,
                        le32_to_cpup (&td->hwBE));
                for (i = 0; i < MAXPSW; i++) {
                        u16     psw = le16_to_cpup (&td->hwPSW [i]);
                        int     cc = (psw >> 12) & 0x0f;
-                       ohci_dbg (ohci, "       psw [%d] = %2x, CC=%x %s=%d", i,
+                       ohci_dbg (ohci, "    psw [%d] = %2x, CC=%x %s=%d\n", i,
                                psw, cc,
                                (cc >= 0x0e) ? "OFFSET" : "SIZE",
                                psw & 0x0fff);
@@ -332,12 +333,13 @@ static void ohci_dump_td (struct ohci_hcd *ohci, char *label, struct td *td)
 
 /* caller MUST own hcd spinlock if verbose is set! */
 static void __attribute__((unused))
-ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
+ohci_dump_ed (const struct ohci_hcd *ohci, const char *label,
+               const struct ed *ed, int verbose)
 {
        u32     tmp = ed->hwINFO;
        char    *type = "";
 
-       ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x",
+       ohci_dbg (ohci, "%s, ed %p state 0x%x type %s; next ed %08x\n",
                label,
                ed, ed->state, edstring (ed->type),
                le32_to_cpup (&ed->hwNextED));
@@ -347,7 +349,7 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
        /* else from TDs ... control */
        }
        ohci_dbg (ohci,
-               "  info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d", le32_to_cpu (tmp),
+               "  info %08x MAX=%d%s%s%s%s EP=%d%s DEV=%d\n", le32_to_cpu (tmp),
                0x03ff & (le32_to_cpu (tmp) >> 16),
                (tmp & ED_DEQUEUE) ? " DQ" : "",
                (tmp & ED_ISO) ? " ISO" : "",
@@ -356,7 +358,7 @@ ohci_dump_ed (struct ohci_hcd *ohci, char *label, struct ed *ed, int verbose)
                0x000f & (le32_to_cpu (tmp) >> 7),
                type,
                0x007f & le32_to_cpu (tmp));
-       ohci_dbg (ohci, "  tds: head %08x %s%s tail %08x%s",
+       ohci_dbg (ohci, "  tds: head %08x %s%s tail %08x%s\n",
                tmp = le32_to_cpup (&ed->hwHeadP),
                (ed->hwHeadP & ED_C) ? data1 : data0,
                (ed->hwHeadP & ED_H) ? " HALT" : "",
@@ -541,24 +543,29 @@ show_periodic (struct class_device *class_dev, char *buf)
                        if (temp == seen_count) {
                                u32     info = ed->hwINFO;
                                u32     scratch = cpu_to_le32p (&ed->hwINFO);
+                               struct list_head        *entry;
+                               unsigned                qlen = 0;
+
+                               /* qlen measured here in TDs, not urbs */
+                               list_for_each (entry, &ed->td_list)
+                                       qlen++;
 
                                temp = snprintf (next, size,
-                                       " (%cs dev%d%s ep%d%s"
+                                       " (%cs dev%d ep%d%s-%s qlen %u"
                                        " max %d %08x%s%s)",
                                        (info & ED_LOWSPEED) ? 'l' : 'f',
                                        scratch & 0x7f,
-                                       (info & ED_ISO) ? " iso" : "",
                                        (scratch >> 7) & 0xf,
                                        (info & ED_IN) ? "in" : "out",
+                                       (info & ED_ISO) ? "iso" : "int",
+                                       qlen,
                                        0x03ff & (scratch >> 16),
                                        scratch,
-                                       (info & ED_SKIP) ? " s" : "",
+                                       (info & ED_SKIP) ? " K" : "",
                                        (ed->hwHeadP & ED_H) ? " H" : "");
                                size -= temp;
                                next += temp;
 
-                               // FIXME some TD info too
-
                                if (seen_count < DBG_SCHED_LIMIT)
                                        seen [seen_count++] = ed;
 
@@ -617,7 +624,7 @@ show_registers (struct class_device *class_dev, char *buf)
        /* hcca */
        if (ohci->hcca)
                ohci_dbg_sw (ohci, &next, &size,
-                       "hcca frame 0x%04x\n", ohci->hcca->frame_no);
+                       "hcca frame 0x%04x\n", OHCI_FRAME_NO(ohci->hcca));
 
        /* other registers mostly affect frame timings */
        rdata = readl (&regs->fminterval);
index 4372ea7..83076a7 100644 (file)
@@ -226,7 +226,7 @@ static int ohci_urb_enqueue (
                if (retval < 0)
                        goto fail;
                if (ed->type == PIPE_ISOCHRONOUS) {
-                       u16     frame = le16_to_cpu (ohci->hcca->frame_no);
+                       u16     frame = OHCI_FRAME_NO(ohci->hcca);
 
                        /* delay a few frames before the first TD */
                        frame += max_t (u16, 8, ed->interval);
@@ -281,7 +281,7 @@ static int ohci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
                urb_priv = urb->hcpriv;
                if (urb_priv) {
                        if (urb_priv->ed->state == ED_OPER)
-                               start_urb_unlink (ohci, urb_priv->ed);
+                               start_ed_unlink (ohci, urb_priv->ed);
                }
        } else {
                /*
@@ -363,7 +363,7 @@ static int ohci_get_frame (struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
 
-       return le16_to_cpu (ohci->hcca->frame_no);
+       return OHCI_FRAME_NO(ohci->hcca);
 }
 
 /*-------------------------------------------------------------------------*
@@ -591,7 +591,7 @@ static void ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
         */
        spin_lock (&ohci->lock);
        if (ohci->ed_rm_list)
-               finish_unlinks (ohci, le16_to_cpu (ohci->hcca->frame_no),
+               finish_unlinks (ohci, OHCI_FRAME_NO(ohci->hcca),
                                ptregs);
        if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list
                        && HCD_IS_RUNNING(ohci->hcd.state))
index 7a6a1d4..9b26e81 100644 (file)
@@ -430,7 +430,7 @@ done:
  * put the ep on the rm_list
  * real work is done at the next start frame (SF) hardware interrupt
  */
-static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
+static void start_ed_unlink (struct ohci_hcd *ohci, struct ed *ed)
 {    
        ed->hwINFO |= ED_DEQUEUE;
        ed->state = ED_UNLINK;
@@ -441,7 +441,7 @@ static void start_urb_unlink (struct ohci_hcd *ohci, struct ed *ed)
         * behave.  frame_no wraps every 2^16 msec, and changes right before
         * SF is triggered.
         */
-       ed->tick = le16_to_cpu (ohci->hcca->frame_no) + 1;
+       ed->tick = OHCI_FRAME_NO(ohci->hcca) + 1;
 
        /* rm_list is just singly linked, for simplicity */
        ed->ed_next = ohci->ed_rm_list;
@@ -479,7 +479,8 @@ td_fill (struct ohci_hcd *ohci, u32 info,
         * and iso; other urbs rarely need more than one TD per urb.
         * this way, only final tds (or ones with an error) cause IRQs.
         * at least immediately; use DI=6 in case any control request is
-        * tempted to die part way through.
+        * tempted to die part way through.  (and to force the hc to flush
+        * its donelist soonish, even on unlink paths.)
         *
         * NOTE: could delay interrupts even for the last TD, and get fewer
         * interrupts ... increasing per-urb latency by sharing interrupts.
@@ -879,12 +880,27 @@ rescan_all:
                u32                     *prev;
 
                /* only take off EDs that the HC isn't using, accounting for
-                * frame counter wraps.
+                * frame counter wraps and EDs with partially retired TDs
                 */
-               if (tick_before (tick, ed->tick)
-                               && HCD_IS_RUNNING(ohci->hcd.state)) {
-                       last = &ed->ed_next;
-                       continue;
+               if (likely (HCD_IS_RUNNING(ohci->hcd.state))) {
+                       if (tick_before (tick, ed->tick)) {
+skip_ed:
+                               last = &ed->ed_next;
+                               continue;
+                       }
+
+                       if (!list_empty (&ed->td_list)) {
+                               struct td       *td;
+                               u32             head;
+
+                               td = list_entry (ed->td_list.next, struct td,
+                                                       td_list);
+                               head = cpu_to_le32 (ed->hwHeadP) & TD_MASK;
+
+                               /* INTR_WDH may need to clean up first */
+                               if (td->td_dma != head)
+                                       goto skip_ed;
+                       }
                }
 
                /* reentrancy:  if we drop the schedule lock, someone might
index 7419f7f..51bb20a 100644 (file)
@@ -172,8 +172,14 @@ static const int cc_to_error [16] = {
 struct ohci_hcca {
 #define NUM_INTS 32
        __u32   int_table [NUM_INTS];   /* periodic schedule */
-       __u16   frame_no;               /* current frame number */
-       __u16   pad1;                   /* set to 0 on each frame_no change */
+
+       /* 
+        * OHCI defines u16 frame_no, followed by u16 zero pad.
+        * Since some processors can't do 16 bit bus accesses,
+        * portable access must be a 32 bit byteswapped access.
+        */
+       u32     frame_no;               /* current frame number */
+#define OHCI_FRAME_NO(hccap) ((u16)le32_to_cpup(&(hccap)->frame_no))
        __u32   done_head;              /* info returned for an interrupt */
        u8      reserved_for_hc [116];
        u8      what [4];               /* spec only identifies 252 bytes :) */
index 55d1aa6..3e914d6 100644 (file)
  *      Visioneer scanners.
  *    - Added test for USB_CLASS_CDC_DATA which is used by some fingerprint scanners.
  *
+ * 0.4.16  2003-11-04
+ *    - Added vendor/product ids for Epson, Genius, Microtek, Plustek, Reflecta, and
+ *      Visioneer scanners. Removed ids for HP PSC devices as these are supported by
+ *      the hpoj userspace driver.
  *
  * TODO
  *    - Performance
index 915a4f5..8b8a3ff 100644 (file)
@@ -43,7 +43,7 @@
 
 // #define DEBUG
 
-#define DRIVER_VERSION "0.4.15"
+#define DRIVER_VERSION "0.4.16"
 #define DRIVER_DESC "USB Scanner Driver"
 
 #include <linux/usb.h>
@@ -146,7 +146,12 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x0458, 0x2015) }, /* ColorPage HR7LE */
        { USB_DEVICE(0x0458, 0x2016) }, /* ColorPage HR6X */
        { USB_DEVICE(0x0458, 0x2018) }, /* ColorPage HR7X */
+       { USB_DEVICE(0x0458, 0x201b) }, /* Colorpage Vivid 4x */
        /* Hewlett Packard */
+       /* IMPORTANT: Hewlett-Packard multi-function peripherals (OfficeJet, 
+          Printer/Scanner/Copier (PSC), LaserJet, or PhotoSmart printer)
+          should not be added to this table because they are accessed by a
+          userspace driver (hpoj) */
        { USB_DEVICE(0x03f0, 0x0101) }, /* ScanJet 4100C */
        { USB_DEVICE(0x03f0, 0x0102) }, /* PhotoSmart S20 */
        { USB_DEVICE(0x03f0, 0x0105) }, /* ScanJet 4200C */
@@ -168,10 +173,10 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x03F0, 0x1105) }, /* ScanJet 5470C */
        { USB_DEVICE(0x03f0, 0x1205) }, /* ScanJet 5550C */
        { USB_DEVICE(0x03f0, 0x1305) }, /* Scanjet 4570c */
-       { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 */
+       //      { USB_DEVICE(0x03f0, 0x1411) }, /* PSC 750 - NOT SUPPORTED - use hpoj userspace driver */
        { USB_DEVICE(0x03f0, 0x2005) }, /* ScanJet 3570c */
        { USB_DEVICE(0x03f0, 0x2205) }, /* ScanJet 3500c */
-       { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 */
+       //      { USB_DEVICE(0x03f0, 0x2f11) }, /* PSC 1210 - NOT SUPPORTED - use hpoj userspace driver */
        /* Lexmark */
        { USB_DEVICE(0x043d, 0x002d) }, /* X70/X73 */
        { USB_DEVICE(0x043d, 0x003d) }, /* X83 */
@@ -187,6 +192,7 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x05da, 0x30ce) }, /* ScanMaker 3800 */
        { USB_DEVICE(0x05da, 0x30cf) }, /* ScanMaker 4800 */
        { USB_DEVICE(0x05da, 0x30d4) }, /* ScanMaker 3830 + 3840 */
+       { USB_DEVICE(0x05da, 0x30d8) }, /* ScanMaker 5900 */
        { USB_DEVICE(0x04a7, 0x0224) }, /* Scanport 3000 (actually Visioneer?)*/
        /* The following SCSI-over-USB Microtek devices are supported by the
           microtek driver: Enable SCSI and USB Microtek in kernel config */
@@ -245,6 +251,7 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x07b3, 0x0400) }, /* OpticPro 1248U */
        { USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U (another one) */
        { USB_DEVICE(0x07b3, 0x0403) }, /* U16B */
+       { USB_DEVICE(0x07b3, 0x0413) }, /* OpticSlim 1200 */
        /* Primax/Colorado */
        { USB_DEVICE(0x0461, 0x0300) }, /* G2-300 #1 */
        { USB_DEVICE(0x0461, 0x0301) }, /* G2E-300 #1 */
@@ -261,6 +268,8 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x0461, 0x0383) }, /* G2E-600 */
        /* Prolink */
        { USB_DEVICE(0x06dc, 0x0014) }, /* Winscan Pro 2448U */
+       /* Reflecta  */
+       { USB_DEVICE(0x05e3, 0x0120) }, /* iScan 1800 */
        /* Relisis */
        // { USB_DEVICE(0x0475, 0x0103) },      /* Episode - undetected endpoint */
        { USB_DEVICE(0x0475, 0x0210) }, /* Scorpio Ultra 3 */
@@ -285,6 +294,7 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x04b8, 0x011c) }, /* Perfection 3200 */
        { USB_DEVICE(0x04b8, 0x011d) }, /* Perfection 1260 */
        { USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */
+       { USB_DEVICE(0x04b8, 0x011f) }, /* Perfection 1670 */
        { USB_DEVICE(0x04b8, 0x0801) }, /* Stylus CX5200 */
        { USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
        /* Siemens */
@@ -309,6 +319,7 @@ static struct usb_device_id scanner_device_ids [] = {
        { USB_DEVICE(0x04a7, 0x0221) }, /* OneTouch 5300 USB */
        { USB_DEVICE(0x04a7, 0x0224) }, /* OneTouch 4800 USB */
        { USB_DEVICE(0x04a7, 0x0226) }, /* OneTouch 5800 USB */
+       { USB_DEVICE(0x04a7, 0x0229) }, /* OneTouch 7100 USB */
        { USB_DEVICE(0x04a7, 0x022c) }, /* OneTouch 9020 USB */
        { USB_DEVICE(0x04a7, 0x0231) }, /* 6100 USB */
        { USB_DEVICE(0x04a7, 0x0311) }, /* 6200 EPP/USB */
index 4d3efad..f02d155 100644 (file)
@@ -177,3 +177,30 @@ config USB_STV680
          To compile this driver as a module, choose M here: the
          module will be called stv680.
 
+config USB_W9968CF
+       tristate "USB W996[87]CF JPEG Dual Mode Camera support"
+       depends on USB && VIDEO_DEV && I2C
+       ---help---
+         Say Y here if you want support for cameras based on
+         Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips.
+       
+         This driver has an optional plugin, which is distributed as a
+         separate module only (released under GPL). It contains code that 
+         allows you to use higher resolutions and framerates, and can't
+         be included into the official Linux kernel for performance 
+         purposes.
+         At the moment the driver needs a third-part module for the CMOS 
+         sensors, which is available on internet: it is recommended to read
+         <file:Documentation/usb/w9968cf.txt> for more informations and for
+         a list of supported cameras.
+       
+         This driver uses the Video For Linux and the I2C APIs. 
+         You must say Y or M to both "Video For Linux" and 
+         "I2C Support" 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 w9968cf.o. If you want to compile it as a
+         module, say M here and read <file:Documentation/modules.txt>.
index e297d6e..550a31b 100644 (file)
@@ -13,3 +13,4 @@ obj-$(CONFIG_USB_PWC)         += pwc.o
 obj-$(CONFIG_USB_SE401)                += se401.o
 obj-$(CONFIG_USB_STV680)       += stv680.o
 obj-$(CONFIG_USB_VICAM)                += vicam.o usbvideo.o
+obj-$(CONFIG_USB_W9968CF)      += w9968cf.o
diff --git a/drivers/usb/media/w9968cf.c b/drivers/usb/media/w9968cf.c
new file mode 100644 (file)
index 0000000..2a305e1
--- /dev/null
@@ -0,0 +1,3712 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip.       *
+ *                                                                         *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>            *
+ *                                                                         *
+ * - Memory management code from bttv driver by Ralph Metzler,             *
+ *   Marcus Metzler and Gerd Knorr.                                        *
+ * - I2C interface to kernel, high-level CMOS sensor control routines and  *
+ *   some symbolic names from OV511 driver by Mark W. McClelland.          *
+ * - Low-level I2C fast write function by Piotr Czerczak.                  *
+ * - Low-level I2C read function by Frédéric Jouault.                      *
+ *                                                                         *
+ * 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.               *
+ ***************************************************************************/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+
+#include "w9968cf.h"
+#include "w9968cf_decoder.h"
+
+
+
+/****************************************************************************
+ * Modules paramaters                                                       *
+ ****************************************************************************/
+
+static u8 vppmod_load = W9968CF_VPPMOD_LOAD;
+static u8 simcams = W9968CF_SIMCAMS;
+static int video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /* -1=first free */
+static u16 packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_PACKET_SIZE};
+static u8 max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS};
+static u8 double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = 
+                             W9968CF_DOUBLE_BUFFER};
+static u8 clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING};
+static u8 filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE};
+static u8 largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW};
+static u8 decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = 
+                             W9968CF_DECOMPRESSION};
+static u8 upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING};
+static u8 force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0};
+static u8 force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB};
+static u8 autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT};
+static u8 autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP};
+static u8 lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ};
+static u8 bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]=
+                             W9968CF_BANDINGFILTER};
+static int clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV};
+static u8 backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT};
+static u8 mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR};
+static u8 sensor_mono[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_SENSOR_MONO};
+static u16 brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS};
+static u16 hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE};
+static u16 colour[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR};
+static u16 contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST};
+static u16 whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS};
+#ifdef W9968CF_DEBUG
+static u8 debug = W9968CF_DEBUG_LEVEL;
+static u8 specific_debug = W9968CF_SPECIFIC_DEBUG;
+#endif
+
+MODULE_AUTHOR("Luca Risolia <luca_ing@libero.it>");
+
+MODULE_DESCRIPTION("Video4Linux driver for "
+                   "W996[87]CF JPEG USB Dual Mode Camera Chip");
+
+MODULE_SUPPORTED_DEVICE("Video");
+
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(vppmod_load, "i");
+MODULE_PARM(simcams, "i");
+MODULE_PARM(video_nr, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(packet_size, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(max_buffers, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(double_buffer, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(clamping, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(filter_type, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(largeview, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(decompression, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(upscaling, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(force_palette, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(force_rgb, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "i");
+MODULE_PARM(autobright, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(autoexp, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(lightfreq, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(bandingfilter, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(clockdiv, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(backlight, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(mirror, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(sensor_mono, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(brightness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(hue, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(colour, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(contrast, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+MODULE_PARM(whiteness, "0-" __MODULE_STRING(W9968CF_MAX_DEVICES) "l");
+#ifdef W9968CF_DEBUG
+MODULE_PARM(debug, "i");
+MODULE_PARM(specific_debug, "i");
+#endif
+
+MODULE_PARM_DESC(vppmod_load, 
+                 "\n<0|1> Automatic 'w9968cf-vpp' module loading."
+                 "\n0 disable, 1 enable."
+                 "\nIf enabled, every time an application attempts to open a"
+                 "\ncamera, 'insmod' searches for the video post-processing"
+                 "\nmodule in the system and loads it automatically (if"
+                 "\npresent). The 'w9968cf-vpp' module adds extra image"
+                 "\nmanipulation functions to the 'w9968cf' module, like"
+                 "\nsoftware up-scaling,colour conversions and video decoding."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"."
+                 "\n");
+MODULE_PARM_DESC(simcams, 
+                 "\n<n> Number of cameras allowed to stream simultaneously."
+                 "\nn may vary from 0 to "
+                 __MODULE_STRING(W9968CF_MAX_DEVICES)"."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"."
+                 "\n");
+MODULE_PARM_DESC(video_nr,
+                 "\n<-1|n[,...]> Specify V4L minor mode number."
+                 "\n -1 = use next available (default)"
+                 "\n  n = use minor number n (integer >= 0)"
+                 "\nYou can specify " __MODULE_STRING(W9968CF_MAX_DEVICES)
+                 " cameras this way."
+                 "\nFor example:"
+                 "\nvideo_nr=-1,2,-1 would assign minor number 2 to"
+                 "\nthe second camera and use auto for the first"
+                 "\none and for every other camera."
+                 "\n");
+MODULE_PARM_DESC(packet_size,
+                 "\n<n[,...]> Specify the maximum data payload"
+                 "\nsize in bytes for alternate settings, for each device."
+                 "\nn is scaled between 63 and 1023 "
+                 "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")."
+                 "\n");
+MODULE_PARM_DESC(max_buffers,
+                 "\n<n[,...]> Only for advanced users."
+                 "\nSpecify the maximum number of video frame buffers"
+                 "\nto allocate for each device, from 2 to "
+                 __MODULE_STRING(W9968CF_MAX_BUFFERS)
+                 ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")."
+                 "\n");
+MODULE_PARM_DESC(double_buffer, 
+                 "\n<0|1[,...]> "
+                 "Hardware double buffering: 0 disabled, 1 enabled."
+                 "\nIt should be enabled if you want smooth video output: if"
+                 "\nyou obtain out of sync. video, disable it at all, or try"
+                 "\nto decrease the 'clockdiv' module paramater value."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(clamping, 
+                 "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(filter_type, 
+                 "\n<0|1|2[,...]> Video filter type."
+                 "\n0 none, 1 (1-2-1) 3-tap filter, "
+                 "2 (2-3-6-3-2) 5-tap filter."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE)
+                 " for every device."
+                 "\nThe filter is used to reduce noise and aliasing artifacts"
+                 "\nproduced by the CCD or CMOS sensor, and the scaling"
+                 " process."
+                 "\n");
+MODULE_PARM_DESC(largeview, 
+                 "\n<0|1[,...]> Large view: 0 disabled, 1 enabled."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(upscaling, 
+                 "\n<0|1[,...]> Software scaling (for non-compressed video):"
+                 "\n0 disabled, 1 enabled."
+                 "\nDisable it if you have a slow CPU or you don't have"
+                 " enough memory."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING)
+                 " for every device."
+                 "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+                 " set to 0.");
+MODULE_PARM_DESC(decompression,
+                 "\n<0|1|2[,...]> Software video decompression:"
+                 "\n- 0 disables decompression (doesn't allow formats needing"
+                 " decompression)"
+                 "\n- 1 forces decompression (allows formats needing"
+                 " decompression only);"
+                 "\n- 2 allows any permitted formats."
+                 "\nFormats supporting compressed video are YUV422P and"
+                 " YUV420P/YUV420 "
+                 "\nin any resolutions where both width and height are "
+                 "a multiple of 16."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION)
+                 " for every device."
+                 "\nIf 'w9968cf-vpp' is not loaded, forcing decompression is "
+                 "\nnot allowed; in this case this paramater is set to 2.");
+MODULE_PARM_DESC(force_palette,
+                 "\n<0"
+                 "|" __MODULE_STRING(VIDEO_PALETTE_UYVY)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_YUV420)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_YUYV)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_YUV422)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_GREY)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_RGB555)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_RGB565)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_RGB24)
+                 "|" __MODULE_STRING(VIDEO_PALETTE_RGB32)
+                 "[,...]>"
+                 " Force picture palette."
+                 "\nIn order:"
+                 "\n- 0 allows any of the following formats:"
+                 "\n- UYVY    16 bpp - Original video, compression disabled"
+                 "\n- YUV420  12 bpp - Original video, compression enabled"
+                 "\n- YUV422P 16 bpp - Original video, compression enabled"
+                 "\n- YUV420P 12 bpp - Original video, compression enabled"
+                 "\n- YUVY    16 bpp - Software conversion from UYVY"
+                 "\n- YUV422  16 bpp - Software conversion from UYVY"
+                 "\n- GREY     8 bpp - Software conversion from UYVY"
+                 "\n- RGB555  16 bpp - Software conversion from UYVY"
+                 "\n- RGB565  16 bpp - Software conversion from UYVY"
+                 "\n- RGB24   24 bpp - Software conversion from UYVY"
+                 "\n- RGB32   32 bpp - Software conversion from UYVY"
+                 "\nWhen not 0, this paramater will override 'decompression'."
+                 "\nDefault value is 0 for every device."
+                 "\nInitial palette is "
+                 __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"."
+                 "\nIf 'w9968cf-vpp' is not loaded, this paramater is"
+                 " set to 9 (UYVY).");
+MODULE_PARM_DESC(force_rgb, 
+                 "\n<0|1[,...]> Read RGB video data instead of BGR:"
+                 "\n 1 = use RGB component ordering."
+                 "\n 0 = use BGR component ordering."
+                 "\nThis parameter has effect when using RGBX palettes only."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(autobright,
+                 "\n<0|1[,...]> CMOS sensor automatically changes brightness:"
+                 "\n 0 = no, 1 = yes"
+                 "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(autoexp,
+                 "\n<0|1[,...]> CMOS sensor automatically changes exposure:"
+                 "\n 0 = no, 1 = yes"
+                 "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(lightfreq,
+                 "\n<50|60[,...]> Light frequency in Hz:"
+                 "\n 50 for European and Asian lighting,"
+                 " 60 for American lighting."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(bandingfilter,
+                 "\n<0|1[,...]> Banding filter to reduce effects of"
+                 " fluorescent lighting:"
+                 "\n 0 disabled, 1 enabled."
+                 "\nThis filter tries to reduce the pattern of horizontal"
+                 "\nlight/dark bands caused by some (usually fluorescent)"
+                 " lighting."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(clockdiv,
+                 "\n<-1|n[,...]> "
+                 "Force pixel clock divisor to a specific value (for experts):"
+                 "\n  n may vary from 0 to 127."
+                 "\n -1 for automatic value."
+                 "\nSee also the 'double_buffer' module paramater."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(backlight,
+                 "\n<0|1[,...]> Objects are lit from behind:"
+                 "\n 0 = no, 1 = yes"
+                 "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(mirror,
+                 "\n<0|1[,...]> Reverse image horizontally:"
+                 "\n 0 = no, 1 = yes"
+                 "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(sensor_mono,
+                 "\n<0|1[,...]> The OV CMOS sensor is monochrome:"
+                 "\n 0 = no, 1 = yes"
+                 "\nDefault value is "__MODULE_STRING(W9968CF_SENSOR_MONO)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(brightness, 
+                 "\n<n[,...]> Set picture brightness (0-65535)."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS)
+                 " for every device."
+                 "\nThis parameter has no effect if 'autobright' is enabled."
+                 "\n");
+MODULE_PARM_DESC(hue, 
+                 "\n<n[,...]> Set picture hue (0-65535)."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_HUE)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(colour, 
+                 "\n<n[,...]> Set picture saturation (0-65535)."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(contrast, 
+                 "\n<n[,...]> Set picture contrast (0-65535)."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST)
+                 " for every device."
+                 "\n");
+MODULE_PARM_DESC(whiteness, 
+                 "\n<n[,...]> Set picture whiteness (0-65535)."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS)
+                 " for every device."
+                 "\n");
+#ifdef W9968CF_DEBUG
+MODULE_PARM_DESC(debug,
+                 "\n<n> Debugging information level, from 0 to 6:"
+                 "\n0 = none (be cautious)"
+                 "\n1 = critical errors"
+                 "\n2 = significant informations"
+                 "\n3 = configuration or general messages"
+                 "\n4 = warnings"
+                 "\n5 = called functions"
+                 "\n6 = function internals"
+                 "\nLevel 5 and 6 are useful for testing only, when just "
+                 "one device is used."
+                 "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"."
+                 "\n");
+MODULE_PARM_DESC(specific_debug,
+                 "\n<0|1> Enable or disable specific debugging messages:"
+                 "\n0 = print messages concerning every level"
+                 " <= 'debug' level."
+                 "\n1 = print messages concerning the level"
+                 " indicated by 'debug'."
+                 "\nDefault value is "
+                 __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"."
+                 "\n");
+#endif /* W9968CF_DEBUG */
+
+
+
+/****************************************************************************
+ * Some prototypes                                                          *
+ ****************************************************************************/
+
+/* Video4linux interface */
+static struct file_operations w9968cf_fops;
+static int w9968cf_open(struct inode*, struct file*);
+static int w9968cf_release(struct inode*, struct file*);
+static ssize_t w9968cf_read(struct file*, char*, size_t, loff_t*);
+static int w9968cf_mmap(struct file*, struct vm_area_struct*);
+static int w9968cf_ioctl(struct inode*, struct file*,
+                         unsigned int, unsigned long);
+static int w9968cf_do_ioctl(struct w9968cf_device*, unsigned int, void*);
+
+/* USB-specific */
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs);
+static int w9968cf_start_transfer(struct w9968cf_device*);
+static int w9968cf_stop_transfer(struct w9968cf_device*);
+static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index);
+static int w9968cf_read_reg(struct w9968cf_device*, u16 index);
+static int w9968cf_write_fsb(struct w9968cf_device*, u16* data);
+static int w9968cf_write_sb(struct w9968cf_device*, u16 value);
+static int w9968cf_read_sb(struct w9968cf_device*);
+static int w9968cf_upload_quantizationtables(struct w9968cf_device*);
+
+/* Low-level I2C (SMBus) I/O */
+static int w9968cf_smbus_start(struct w9968cf_device*);
+static int w9968cf_smbus_stop(struct w9968cf_device*);
+static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v);
+static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v);
+static int w9968cf_smbus_write_ack(struct w9968cf_device*);
+static int w9968cf_smbus_read_ack(struct w9968cf_device*);
+static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+                                      u16 address, u8* value);
+static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, 
+                                           u8 subaddress, u8* value);
+static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*,
+                                       u16 address, u8 subaddress);
+static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*,
+                                                u16 address, u8 subaddress,
+                                                u8 value);
+
+/* I2C interface to kernel */
+static int w9968cf_i2c_init(struct w9968cf_device*);
+static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, 
+                                  unsigned short flags, char read_write, 
+                                  u8 command, int size, union i2c_smbus_data*);
+static u32 w9968cf_i2c_func(struct i2c_adapter*);
+static int w9968cf_i2c_attach_inform(struct i2c_client*);
+static int w9968cf_i2c_detach_inform(struct i2c_client*);
+static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd,
+                               unsigned long arg);
+
+/* Memory management */
+static inline unsigned long kvirt_to_pa(unsigned long adr);
+static void* rvmalloc(unsigned long size);
+static void rvfree(void *mem, unsigned long size);
+static void w9968cf_deallocate_memory(struct w9968cf_device*);
+static int  w9968cf_allocate_memory(struct w9968cf_device*);
+static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device*);
+
+/* High-level CMOS sensor control functions */
+static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val);
+static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val);
+static inline int w9968cf_sensor_cmd(struct w9968cf_device*, 
+                                     unsigned int cmd, void *arg);
+static void w9968cf_sensor_configure(struct w9968cf_device*);
+static int w9968cf_sensor_change_settings(struct w9968cf_device*);
+static int w9968cf_sensor_get_picture(struct w9968cf_device*, 
+                                      struct video_picture*);
+static int w9968cf_sensor_set_picture(struct w9968cf_device*, 
+                                      struct video_picture pict);
+
+/* Other helper functions */
+static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*,
+                                     enum w9968cf_model_id, 
+                                     const unsigned short dev_nr);
+static int w9968cf_turn_on_led(struct w9968cf_device*);
+static int w9968cf_init_chip(struct w9968cf_device*);
+static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture);
+static int w9968cf_set_window(struct w9968cf_device*, struct video_window);
+static inline u16 w9968cf_valid_palette(u16 palette);
+static inline u16 w9968cf_valid_depth(u16 palette);
+static inline u8 w9968cf_need_decompression(u16 palette);
+static int w9968cf_postprocess_frame(struct w9968cf_device*, 
+                                     struct w9968cf_frame_t*);
+static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h);
+static void w9968cf_init_framelist(struct w9968cf_device*);
+static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num);
+static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**);
+static void w9968cf_release_resources(struct w9968cf_device*);
+
+/* Intermodule communication */
+static int w9968cf_vppmod_detect(void);
+static void w9968cf_vppmod_release(void);
+
+/* Pointers to registered video post-processing functions */
+static void (*w9968cf_vpp_init_decoder)(void);
+static int (*w9968cf_vpp_check_headers)(const unsigned char*,
+                                        const unsigned long);
+static int (*w9968cf_vpp_decode)(const char*, const unsigned, 
+                                 const unsigned, const unsigned, char*);
+static void (*w9968cf_vpp_swap_yuvbytes)(void*, unsigned long);
+static void (*w9968cf_vpp_uyvy_to_rgbx)(u8*, unsigned long, u8*, u16, u8);
+static void (*w9968cf_vpp_scale_up)(u8*, u8*, u16, u16, u16, u16, u16);
+
+
+
+/****************************************************************************
+ * Symbolic names                                                           *
+ ****************************************************************************/
+
+/* Used to represent a list of values and their respective symbolic names */
+struct w9968cf_symbolic_list {
+       const int num;
+       const char *name;
+};
+
+/*-------------------------------------------------------------------------- 
+  Returns the name of the matching element in the symbolic_list array. The
+  end of the list must be marked with an element that has a NULL name.
+  --------------------------------------------------------------------------*/
+static inline const char * 
+symbolic(struct w9968cf_symbolic_list list[], const int num)
+{
+       int i;
+
+       for (i = 0; list[i].name != NULL; i++)
+               if (list[i].num == num)
+                       return (list[i].name);
+
+       return "Unknown";
+}
+
+static struct w9968cf_symbolic_list camlist[] = {
+       { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" },
+       { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" },
+
+       /* Other cameras (having the same descriptors as Generic W996[87]CF) */
+       { W9968CF_MOD_ADPA5R, "Aroma Digi Pen ADG-5000 Refurbished" },
+       { W9986CF_MOD_AU, "AVerTV USB" },
+       { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" },
+       { W9968CF_MOD_DLLDK, "Die Lebon LDC-D35A Digital Kamera" },
+       { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" },
+       { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" },
+
+       {  -1, NULL }
+};
+
+static struct w9968cf_symbolic_list senlist[] = {
+       { CC_OV76BE,   "OV76BE" },
+       { CC_OV7610,   "OV7610" },
+       { CC_OV7620,   "OV7620" },
+       { CC_OV7620AE, "OV7620AE" },
+       { CC_OV6620,   "OV6620" },
+       { CC_OV6630,   "OV6630" },
+       { CC_OV6630AE, "OV6630AE" },
+       { CC_OV6630AF, "OV6630AF" },
+       { -1, NULL }
+};
+
+/* Video4Linux1 palettes */
+static struct w9968cf_symbolic_list v4l1_plist[] = {
+       { VIDEO_PALETTE_GREY,    "GREY" },
+       { VIDEO_PALETTE_HI240,   "HI240" },
+       { VIDEO_PALETTE_RGB565,  "RGB565" },
+       { VIDEO_PALETTE_RGB24,   "RGB24" },
+       { VIDEO_PALETTE_RGB32,   "RGB32" },
+       { VIDEO_PALETTE_RGB555,  "RGB555" },
+       { VIDEO_PALETTE_YUV422,  "YUV422" },
+       { VIDEO_PALETTE_YUYV,    "YUYV" },
+       { VIDEO_PALETTE_UYVY,    "UYVY" },
+       { VIDEO_PALETTE_YUV420,  "YUV420" },
+       { VIDEO_PALETTE_YUV411,  "YUV411" },
+       { VIDEO_PALETTE_RAW,     "RAW" },
+       { VIDEO_PALETTE_YUV422P, "YUV422P" },
+       { VIDEO_PALETTE_YUV411P, "YUV411P" },
+       { VIDEO_PALETTE_YUV420P, "YUV420P" },
+       { VIDEO_PALETTE_YUV410P, "YUV410P" },
+       { -1, NULL }
+};
+
+/* Decoder error codes: */
+static struct w9968cf_symbolic_list decoder_errlist[] = {
+       { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" },
+       { W9968CF_DEC_ERR_BUF_OVERFLOW,   "Buffer overflow" },
+       { W9968CF_DEC_ERR_NO_SOI,         "SOI marker not found" },     
+       { W9968CF_DEC_ERR_NO_SOF0,        "SOF0 marker not found" },
+       { W9968CF_DEC_ERR_NO_SOS,         "SOS marker not found" },
+       { W9968CF_DEC_ERR_NO_EOI,         "EOI marker not found" },
+       { -1, NULL }
+};
+
+/* URB error codes: */
+static struct w9968cf_symbolic_list urb_errlist[] = {
+       { -ENOMEM,    "No memory for allocation of internal structures" },
+       { -ENOSPC,    "The host controller's bandwidth is already consumed" },
+       { -ENOENT,    "URB was canceled by unlink_urb" },
+       { -EXDEV,     "ISO transfer only partially completed" },
+       { -EAGAIN,    "Too match scheduled for the future" },
+       { -ENXIO,     "URB already queued" },
+       { -EFBIG,     "Too much ISO frames requested" },
+       { -ENOSR,     "Buffer error (overrun)" },
+       { -EPIPE,     "Specified endpoint is stalled (device not responding)"},
+       { -EOVERFLOW, "Babble (bad cable?)" },
+       { -EPROTO,    "Bit-stuff error (bad cable?)" },
+       { -EILSEQ,    "CRC/Timeout" },
+       { -ETIMEDOUT, "NAK (device does not respond)" },
+       { -1, NULL }
+};
+
+
+
+/****************************************************************************
+ * Memory management functions                                              *
+ ****************************************************************************/
+
+/* Shameless copy from bttv-driver.c */
+
+/* Here we want the physical address of the memory.
+   This is used when initializing the contents of the area. */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+       unsigned long kva, ret;
+
+       kva = (unsigned long) page_address(vmalloc_to_page((void *)adr));
+       kva |= adr & (PAGE_SIZE-1); /* restore the offset */
+       ret = __pa(kva);
+       return ret;
+}
+
+
+static void* rvmalloc(unsigned long size)
+{
+       void* mem;
+       unsigned long adr;
+
+       size = PAGE_ALIGN(size);
+       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) {
+               SetPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+
+       return mem;
+}
+
+
+static void rvfree(void* mem, unsigned long size)
+{
+       unsigned long adr;
+
+       if (!mem)
+               return;
+
+       adr = (unsigned long) mem;
+       while ((long) size > 0) {
+               ClearPageReserved(vmalloc_to_page((void *)adr));
+               adr += PAGE_SIZE;
+               size -= PAGE_SIZE;
+       }
+       vfree(mem);
+}
+/* End of shameless copy */
+
+
+/*--------------------------------------------------------------------------
+  Return the maximum size (in bytes) of a frame buffer.
+  --------------------------------------------------------------------------*/
+static inline unsigned long w9968cf_get_max_bufsize(struct w9968cf_device* cam)
+{
+       u8 bpp = (w9968cf_vppmod_present) ? 4 : 2;
+       return (cam->upscaling) ? W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp :
+                                 cam->maxwidth*cam->maxheight*bpp;
+}
+
+
+/*--------------------------------------------------------------------------
+  Deallocate previously allocated memory.
+  --------------------------------------------------------------------------*/
+static void w9968cf_deallocate_memory(struct w9968cf_device* cam)
+{
+       u8 i;
+
+       /* Free the isochronous transfer buffers */
+       for (i = 0; i < W9968CF_URBS; i++) {
+               kfree(cam->transfer_buffer[i]);
+               cam->transfer_buffer[i] = NULL;
+       }
+
+       /* Free temporary frame buffer */
+       if (cam->frame_tmp.buffer) {
+               rvfree(cam->frame_tmp.buffer, W9968CF_HW_BUF_SIZE);
+               cam->frame_tmp.buffer = NULL;
+       }
+
+       /* Free helper buffer */
+       if (cam->vpp_buffer) {
+               rvfree(cam->vpp_buffer, w9968cf_get_max_bufsize(cam));
+               cam->vpp_buffer = NULL;
+       }
+       
+       /* Free video frame buffers */
+       if (cam->frame[0].buffer) {
+               rvfree(cam->frame[0].buffer, 
+                      cam->nbuffers * w9968cf_get_max_bufsize(cam));
+               cam->frame[0].buffer = NULL;
+       }
+
+       cam->nbuffers = 0;
+
+       DBG(5, "Memory successfully deallocated.")
+}
+
+
+/*--------------------------------------------------------------------------
+  Allocate memory buffers for USB transfers and video frames.
+  This function is called by open() only.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_allocate_memory(struct w9968cf_device* cam)
+{
+       const unsigned long bufsize = w9968cf_get_max_bufsize(cam);
+       const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+       void* buff = NULL;
+       u8 i;
+
+       /* NOTE: Deallocation is done elsewhere in case of error */
+
+       /* Allocate memory for the isochronous transfer buffers */
+       for (i = 0; i < W9968CF_URBS; i++) {
+               if (!(cam->transfer_buffer[i] =
+                     kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) {
+                       DBG(1, "Couldn't allocate memory for the isochronous "
+                              "transfer buffers (%d bytes).", 
+                           p_size * W9968CF_ISO_PACKETS)
+                       return -ENOMEM;
+               }
+               memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size);
+       }
+
+       /* Allocate memory for the temporary frame buffer */
+       if (!(cam->frame_tmp.buffer = rvmalloc(W9968CF_HW_BUF_SIZE))) {
+               DBG(1, "Couldn't allocate memory for the temporary "
+                      "video frame buffer (%i bytes).", W9968CF_HW_BUF_SIZE)
+               return -ENOMEM;
+       }
+
+       /* Allocate memory for the helper buffer */
+       if (w9968cf_vppmod_present) {
+               if (!(cam->vpp_buffer = rvmalloc(bufsize))) {
+                       DBG(1, "Couldn't allocate memory for the helper buffer"
+                              " (%li bytes).", bufsize)
+                       return -ENOMEM;
+               }
+       } else
+               cam->vpp_buffer = NULL;
+
+       /* Allocate memory for video frame buffers */   
+       cam->nbuffers = cam->max_buffers;
+       while (cam->nbuffers >= 2) {
+               if ((buff = rvmalloc(cam->nbuffers * bufsize)))
+                       break;
+               else
+                       cam->nbuffers--;
+       }
+
+       if (!buff) {
+               DBG(1, "Couldn't allocate memory for the video frame buffers.")
+               cam->nbuffers = 0;
+               return -ENOMEM;
+       }
+
+       if (cam->nbuffers != cam->max_buffers)
+               DBG(2, "Couldn't allocate memory for %d video frame buffers. "
+                      "Only memory for %d buffers has been allocated.",
+                   cam->max_buffers, cam->nbuffers)
+
+       for (i = 0; i < cam->nbuffers; i++) {
+               cam->frame[i].buffer = buff + i*bufsize;
+               /* Circular list */
+               if (i != cam->nbuffers-1)
+                       cam->frame[i].next = &cam->frame[i+1];
+               else
+                       cam->frame[i].next = &cam->frame[0];
+               cam->frame[i].status = F_UNUSED;
+       }
+
+       DBG(5, "Memory successfully allocated.")
+       return 0;
+}
+
+
+
+/****************************************************************************
+ * USB-specific functions                                                   *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+  This is an handler function which is called after the URBs are completed.
+  It collects multiple data packets coming from the camera by putting them
+  into frame buffers: one or more zero data length data packets are used to
+  mark the end of a video frame; the first non-zero data packet is the start
+  of the next video frame; if an error is encountered in a packet, the entire
+  video frame is discarded and grabbed again.
+  If there are no requested frames in the FIFO list, packets are collected into
+  a temporary buffer. 
+  --------------------------------------------------------------------------*/
+static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs)
+{
+       struct w9968cf_device* cam = (struct w9968cf_device*)urb->context;
+       struct w9968cf_frame_t** f;
+       unsigned long maxbufsize;
+       unsigned int len, status;
+       void* pos;
+       u8 i;
+       int err = 0;
+
+       if ((!cam->streaming) || cam->disconnected) {
+               DBG(4, "Got interrupt, but not streaming.")
+               return;
+       }
+
+       maxbufsize = min( (unsigned long)W9968CF_HW_BUF_SIZE, 
+                         w9968cf_get_max_bufsize(cam) );
+
+       /* "(*f)" will be used instead of "cam->frame_current" */
+       f = &cam->frame_current;
+
+       /* If a frame has been requested and we are grabbing into  
+          the temporary frame, we'll switch to that requested frame */
+       if ((*f) == &cam->frame_tmp && *cam->requested_frame) {
+               if (cam->frame_tmp.status == F_GRABBING) {
+                       w9968cf_pop_frame(cam, &cam->frame_current);
+                       (*f)->status = F_GRABBING;
+                       (*f)->length = cam->frame_tmp.length;
+                       memcpy((*f)->buffer, cam->frame_tmp.buffer,
+                              (*f)->length);
+                       DBG(6, "Switched from temp. frame to frame #%d", 
+                           (*f) - &cam->frame[0])
+               }
+       }
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               len    = urb->iso_frame_desc[i].actual_length;
+               status = urb->iso_frame_desc[i].status;
+               pos    = urb->iso_frame_desc[i].offset + urb->transfer_buffer;
+
+               if (status && len != 0) {
+                       DBG(4, "URB failed, error in data packet "
+                              "(error #%d, %s).",
+                           status, symbolic(urb_errlist, status))
+                       (*f)->status = F_ERROR;
+                       continue;
+               }
+
+               if (len) { /* start of frame */
+
+                       if ((*f)->status == F_UNUSED) {
+                               (*f)->status = F_GRABBING;
+                               (*f)->length = 0;
+                       }
+
+                       /* Buffer overflows shouldn't happen, however...*/
+                       if ((*f)->length + len > maxbufsize) {
+                               DBG(4, "Buffer overflow: bad data packets.")
+                               (*f)->status = F_ERROR;
+                       }
+
+                       if ((*f)->status == F_GRABBING) {
+                               memcpy((*f)->buffer + (*f)->length, pos, len);
+                               (*f)->length += len;
+                       }
+
+               } else if ((*f)->status == F_GRABBING) { /* end of frame */
+
+                       DBG(6, "Frame #%d successfully grabbed.",
+                           ((*f)==&cam->frame_tmp ? -1 : (*f)-&cam->frame[0]))
+
+                       if (cam->vpp_flag & VPP_DECOMPRESSION) {
+                               err=(*w9968cf_vpp_check_headers)((*f)->buffer,
+                                                                (*f)->length);
+                               if (err) {
+                                       DBG(4, "Skip corrupted frame: %s",
+                                           symbolic(decoder_errlist, err))
+                                       (*f)->status = F_UNUSED;
+                                       continue; /* grab this frame again */
+                               }
+                       }
+
+                       (*f)->status = F_READY;
+                       (*f)->queued = 0;
+
+                       /* Take a pointer to the new frame from the FIFO list.
+                          If the list is empty,we'll use the temporary frame*/
+                       if (*cam->requested_frame)
+                               w9968cf_pop_frame(cam, &cam->frame_current);
+                       else {
+                               cam->frame_current = &cam->frame_tmp;
+                               (*f)->status = F_UNUSED;
+                       }
+
+               } else if ((*f)->status == F_ERROR)
+                       (*f)->status = F_UNUSED; /* grab it again */
+
+               PDBGG("Frame length %li | pack.#%d | pack.len. %d | state %d",
+                     (unsigned long)(*f)->length, i, len, (*f)->status)
+
+       } /* end for */
+
+       /* Resubmit this URB */
+       urb->dev = cam->usbdev;
+       urb->status = 0;
+       spin_lock(&cam->urb_lock);
+       if (cam->streaming)
+               if ((err = usb_submit_urb(urb, GFP_ATOMIC))) {
+                       cam->misconfigured = 1;
+                       DBG(1, "Couldn't resubmit the URB: error %d, %s",
+                           err, symbolic(urb_errlist, err));
+               }
+       spin_unlock(&cam->urb_lock);
+
+       /* Wake up the user process */
+       if (waitqueue_active(&cam->wait_queue))
+               wake_up_interruptible(&cam->wait_queue);
+}
+
+
+/*---------------------------------------------------------------------------
+  Setup the URB structures for the isochronous transfer.
+  Submit the URBs so that the data transfer begins.
+  Return 0 on success, a negative number otherwise.
+  ---------------------------------------------------------------------------*/
+static int w9968cf_start_transfer(struct w9968cf_device* cam)
+{
+       struct usb_device *udev = cam->usbdev;
+       struct urb* urb;
+       const u16 p_size = wMaxPacketSize[cam->altsetting-1];
+       u16 w, h, d;
+       int vidcapt;
+       u32 t_size;
+       int err = 0;
+       s8 i, j;
+
+       for (i = 0; i < W9968CF_URBS; i++) {
+               urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL);
+               cam->urb[i] = urb;
+
+               if (!urb) {
+                       for (j = 0; j < i; j++)
+                               usb_free_urb(cam->urb[j]);
+                       DBG(1, "Couldn't allocate the URB structures.")
+                       return -ENOMEM;
+               }
+
+               urb->dev = udev;
+               urb->context = (void*)cam;
+               urb->pipe = usb_rcvisocpipe(udev, 1);
+               urb->transfer_flags = URB_ISO_ASAP;
+               urb->number_of_packets = W9968CF_ISO_PACKETS;
+               urb->complete = w9968cf_urb_complete;
+               urb->transfer_buffer = cam->transfer_buffer[i];
+               urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS;
+               urb->interval = 1;
+               for (j = 0; j < W9968CF_ISO_PACKETS; j++) {
+                       urb->iso_frame_desc[j].offset = p_size*j;
+                       urb->iso_frame_desc[j].length = p_size;
+               }
+       }
+
+       /* Transfer size per frame, in WORD ! */
+       d = cam->hw_depth;
+       w = cam->hw_width;
+       h = cam->hw_height;
+
+       t_size = (w*h*d)/16;
+
+       err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset  everything */
+       err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+
+       /* Transfer size */
+       err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */
+       err += w9968cf_write_reg(cam, t_size >> 16, 0x3e);    /* high bits */
+
+       if (cam->vpp_flag & VPP_DECOMPRESSION)
+               err += w9968cf_upload_quantizationtables(cam);
+
+       vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */
+       err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */
+
+       err += usb_set_interface(udev, 0, cam->altsetting);
+       err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */
+
+       if (err || (vidcapt < 0)) {
+               for (i = 0; i < W9968CF_URBS; i++)
+                       usb_free_urb(cam->urb[i]);
+               DBG(1, "Couldn't tell the camera to start the data transfer.")
+               return err;
+       }
+
+       w9968cf_init_framelist(cam);
+
+       /* Begin to grab into the temporary buffer */
+       cam->frame_tmp.status = F_UNUSED;
+       cam->frame_tmp.queued = 0;
+       cam->frame_current = &cam->frame_tmp;
+
+       if (!(cam->vpp_flag & VPP_DECOMPRESSION))
+               DBG(5, "Isochronous transfer size: %li bytes/frame.",
+                   (unsigned long)t_size*2)
+
+       DBG(5, "Starting the isochronous transfer...")
+
+       /* Submit the URBs */
+       for (i = 0; i < W9968CF_URBS; i++) {
+               err = usb_submit_urb(cam->urb[i], GFP_KERNEL);
+               if (err) {
+                       for (j = i-1; j >= 0; j--)
+                               if (!usb_unlink_urb(cam->urb[j]))
+                                       usb_free_urb(cam->urb[j]);
+                       DBG(1, "Couldn't send a transfer request to the "
+                              "USB core (error #%d, %s).", err, 
+                           symbolic(urb_errlist, err))
+               }
+       }
+
+       cam->streaming = 1;
+
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Stop the isochronous transfer and set alternate setting to 0 (0Mb/s).
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_stop_transfer(struct w9968cf_device* cam)
+{
+       struct usb_device *udev = cam->usbdev;
+       unsigned long lock_flags;
+       int err = 0;
+       s8 i;
+
+       /* This avoids race conditions with usb_submit_urb() 
+          in the URB completition handler */
+       spin_lock_irqsave(&cam->urb_lock, lock_flags);
+       cam->streaming = 0;
+       spin_unlock_irqrestore(&cam->urb_lock, lock_flags);
+
+       for (i = W9968CF_URBS-1; i >= 0; i--)
+               if (cam->urb[i])
+                       if (!usb_unlink_urb(cam->urb[i])) {
+                               usb_free_urb(cam->urb[i]);
+                               cam->urb[i] = NULL;
+                       }
+
+       if (cam->disconnected)
+               goto exit;
+
+       err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */
+       err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */
+       err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */
+       err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */
+
+       if (err) {
+               DBG(2, "Failed to tell the camera to stop the isochronous "
+                      "transfer. However this is not a critical error.")
+               return -EIO;
+       }
+
+exit:
+       DBG(5, "Isochronous transfer stopped.")
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Write a W9968CF register. 
+  Return 0 on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index)
+{
+       struct usb_device* udev = cam->usbdev;
+       int res;
+
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+                             value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT);
+
+       if (res < 0)
+               DBG(4, "Failed to write a register "
+                      "(value 0x%04X, index 0x%02X, error #%d, %s).",
+                   value, index, res, symbolic(urb_errlist, res))
+
+       return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+  Read a W9968CF register. 
+  Return the register value on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index)
+{
+       struct usb_device* udev = cam->usbdev;
+       u16* buff = cam->control_buffer;
+       int res;
+
+       res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1,
+                             USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+                             0, index, (void*)buff,
+                             2, W9968CF_USB_CTRL_TIMEOUT);
+
+       if (res < 0)
+               DBG(4, "Failed to read a register "
+                      "(index 0x%02X, error #%d, %s).",
+                   index, res, symbolic(urb_errlist, res))
+
+       return (res >= 0) ? (int)(*buff) : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+  Write data to the fast serial bus registers.
+  Return 0 on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data)
+{
+       struct usb_device* udev = cam->usbdev;
+       u16 value;
+       int res;
+
+       value = *data++;
+
+       res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
+                             USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
+                             value, 0x06, (void*)data, 6,
+                             W9968CF_USB_CTRL_TIMEOUT);
+
+       if (res < 0)
+               DBG(4, "Failed to write the FSB registers "
+                      "(error #%d, %s).", res, symbolic(urb_errlist, res))
+
+       return (res >= 0) ? 0 : -1;
+}
+
+
+/*--------------------------------------------------------------------------
+  Write data to the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value)
+{
+       int err = 0;
+
+       err = w9968cf_write_reg(cam, value, 0x01);
+       udelay(W9968CF_I2C_BUS_DELAY);
+
+       return err;
+}
+
+
+/*--------------------------------------------------------------------------
+  Read data from the serial bus control register.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_read_sb(struct w9968cf_device* cam)
+{
+       int v = 0;
+
+       v = w9968cf_read_reg(cam, 0x01);
+       udelay(W9968CF_I2C_BUS_DELAY);
+
+       return v;
+}
+
+
+/*--------------------------------------------------------------------------
+  Upload quantization tables for the JPEG compression.
+  This function is called by w9968cf_start_transfer().
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam)
+{
+       u16 a, b;
+       int err = 0, i, j;
+
+       err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */
+
+       for (i = 0, j = 0; i < 32; i++, j += 2) {
+               a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
+               b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
+               err += w9968cf_write_reg(cam, a, 0x40+i);
+               err += w9968cf_write_reg(cam, b, 0x60+i);
+       }
+       err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */
+
+       return err;
+}
+
+
+
+/****************************************************************************
+ * Low-level I2C I/O functions.                                             *
+ * The adapter supports the following I2C transfer functions:               *
+ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only)           *
+ * i2c_adap_read_byte_data()                                                *
+ * i2c_adap_read_byte()                                                     *
+ ****************************************************************************/
+
+static int w9968cf_smbus_start(struct w9968cf_device* cam)
+{
+       int err = 0;
+
+       err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+       err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+       return err;
+}
+
+
+static int w9968cf_smbus_stop(struct w9968cf_device* cam)
+{
+       int err = 0;
+
+       err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+       err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+
+       return err;
+}
+
+
+static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v)
+{
+       u8 bit;
+       int err = 0, sda;
+
+       for (bit = 0 ; bit < 8 ; bit++) {
+               sda = (v & 0x80) ? 2 : 0;
+               v <<= 1;
+               /* SDE=1, SDA=sda, SCL=0 */
+               err += w9968cf_write_sb(cam, 0x10 | sda);
+               /* SDE=1, SDA=sda, SCL=1 */
+               err += w9968cf_write_sb(cam, 0x11 | sda);
+               /* SDE=1, SDA=sda, SCL=0 */
+               err += w9968cf_write_sb(cam, 0x10 | sda);
+       }
+
+       return err;
+}
+
+
+static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v)
+{
+       u8 bit;
+       int err = 0;
+
+       *v = 0;
+       for (bit = 0 ; bit < 8 ; bit++) {
+               *v <<= 1;
+               err += w9968cf_write_sb(cam, 0x0013);
+               *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0;
+               err += w9968cf_write_sb(cam, 0x0012);
+       }
+
+       return err;
+}
+
+
+static int w9968cf_smbus_write_ack(struct w9968cf_device* cam)
+{
+       int err = 0;
+
+       err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+       err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */
+       err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */
+
+       return err;
+}
+
+
+static int w9968cf_smbus_read_ack(struct w9968cf_device* cam)
+{
+       int err = 0, sda;
+
+       err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */
+       sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */
+       err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */
+       if (sda < 0)
+               err += sda;
+       if (sda == 1) {
+               DBG(6, "Couldn't receive the ACK.")
+               err += -1;
+       }
+
+       return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
+static int 
+w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, 
+                                     u16 address, u8 subaddress,u8 value)
+{
+       u16* data = cam->data_buffer;
+       int err = 0;
+
+        /* Enable SBUS outputs */
+       err += w9968cf_write_reg(cam, 0x0020, 0x01);
+
+       data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0);
+       data[0] |= (address & 0x40) ? 0x4000 : 0x0;
+       data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0);
+       data[1] |= (address & 0x20) ? 0x0150 : 0x0;
+       data[1] |= (address & 0x10) ? 0x5400 : 0x0;
+       data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0);
+       data[2] |= (address & 0x04) ? 0x0540 : 0x0;
+       data[2] |= (address & 0x02) ? 0x5000 : 0x0;
+       data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0);
+       data[3] |= (address & 0x01) ? 0x0054 : 0x0;
+
+       err += w9968cf_write_fsb(cam, data);
+
+       data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0);
+       data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0;
+       data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0;
+       data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0);
+       data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0;
+       data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0;
+       data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0;
+       data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0);
+       data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0;
+       data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0;
+       data[3] = 0x001d;
+
+       err += w9968cf_write_fsb(cam, data);
+
+       data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
+       data[0] |= (value & 0x40) ? 0x0540 : 0x0;
+       data[0] |= (value & 0x20) ? 0x5000 : 0x0;
+       data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
+       data[1] |= (value & 0x10) ? 0x0054 : 0x0;
+       data[1] |= (value & 0x08) ? 0x1500 : 0x0;
+       data[1] |= (value & 0x04) ? 0x4000 : 0x0;
+       data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
+       data[2] |= (value & 0x02) ? 0x0150 : 0x0;
+       data[2] |= (value & 0x01) ? 0x5400 : 0x0;
+       data[3] = 0xfe1d;
+
+       err += w9968cf_write_fsb(cam, data);
+
+       /* Disable SBUS outputs */
+       err += w9968cf_write_reg(cam, 0x0000, 0x01);
+
+       if (!err)
+               DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X "
+                      "value 0x%02X.", address, subaddress, value)
+       else
+               DBG(5, "I2C write byte data failed, addr.0x%04X, "
+                      "subaddr.0x%02X, value 0x%02X.", 
+                   address, subaddress, value)
+
+       return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
+static int 
+w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, 
+                                u16 address, u8 subaddress, 
+                                u8* value)
+{
+       int err = 0;
+
+       /* Serial data enable */
+       err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */
+
+       err += w9968cf_smbus_start(cam);
+       err += w9968cf_smbus_write_byte(cam, address);
+       err += w9968cf_smbus_read_ack(cam);
+       err += w9968cf_smbus_write_byte(cam, subaddress);
+       err += w9968cf_smbus_read_ack(cam);
+       err += w9968cf_smbus_stop(cam);
+       err += w9968cf_smbus_start(cam);
+       err += w9968cf_smbus_write_byte(cam, address + 1);
+       err += w9968cf_smbus_read_ack(cam);
+       err += w9968cf_smbus_read_byte(cam, value);
+       err += w9968cf_smbus_write_ack(cam);
+       err += w9968cf_smbus_stop(cam);
+       /* Serial data disable */
+       err += w9968cf_write_sb(cam, 0x0000);
+
+       if (!err)
+               DBG(5, "I2C read byte data done, addr.0x%04X, "
+                      "subaddr.0x%02X, value 0x%02X.", 
+                   address, subaddress, *value)
+       else
+               DBG(5, "I2C read byte data failed, addr.0x%04X, "
+                      "subaddr.0x%02X, wrong value 0x%02X.",
+                   address, subaddress, *value)
+
+       return err;
+}
+
+
+/* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */
+static int 
+w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam,
+                           u16 address, u8* value)
+{
+       int err = 0;
+
+       /* Serial data enable */
+       err += w9968cf_write_sb(cam, 0x0013);
+
+       err += w9968cf_smbus_start(cam);
+       err += w9968cf_smbus_write_byte(cam, address + 1);
+       err += w9968cf_smbus_read_ack(cam);
+       err += w9968cf_smbus_read_byte(cam, value);
+       err += w9968cf_smbus_write_ack(cam);
+       err += w9968cf_smbus_stop(cam);
+       /* Serial data disable */
+       err += w9968cf_write_sb(cam, 0x0000);
+
+       if (!err)
+               DBG(5, "I2C read byte done, addr.0x%04X."
+                      "value 0x%02X.", address, *value)
+       else
+               DBG(5, "I2C read byte failed, addr.0x%04X."
+                      "wrong value 0x%02X.", address, *value)
+
+       return err;
+}
+
+
+/* SMBus protocol: S Addr Wr [A] Value [A] P */
+static int 
+w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam,
+                            u16 address, u8 value)
+{
+       DBG(4, "i2c_write_byte() is an unsupported transfer mode.")
+       return -EINVAL;
+}
+
+
+
+/****************************************************************************
+ * I2C interface to kernel                                                  *
+ ****************************************************************************/
+
+static int
+w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, 
+                       unsigned short flags, char read_write, u8 command,
+                       int size, union i2c_smbus_data *data)
+{
+       struct w9968cf_device* cam = i2c_get_adapdata(adapter);
+       u8 i, j;
+       int rc = 0, err = 0; 
+
+       switch (addr) {
+               case OV6xx0_SID:
+               case OV7xx0_SID:
+                       break;
+               default:
+                       DBG(4, "Rejected slave ID 0x%04X", addr)
+                       return -EINVAL;
+       }
+
+       if (size == I2C_SMBUS_BYTE) {
+               /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */
+               addr <<= 1;
+
+               if (read_write == I2C_SMBUS_WRITE)
+                       rc = w9968cf_i2c_adap_write_byte(cam, addr, command);
+               else if (read_write == I2C_SMBUS_READ) 
+                       rc = w9968cf_i2c_adap_read_byte(cam,addr, &data->byte);
+
+       } else if (size == I2C_SMBUS_BYTE_DATA) {
+               addr <<= 1;
+
+               if (read_write == I2C_SMBUS_WRITE)
+                       rc = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr,
+                                                         command, data->byte);
+               else if (read_write == I2C_SMBUS_READ) {
+                       for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
+                               rc = w9968cf_i2c_adap_read_byte_data(cam, addr, 
+                                                        command, &data->byte);
+                               if (rc < 0) {
+                                       /* Work around: this seems to wake up  
+                                          the EEPROM from the stall state */
+                                       for (j = 0; j <= 10; j++) {
+                                          err += w9968cf_write_sb(cam,0x0020);
+                                          err += w9968cf_write_sb(cam,0x0000);
+                                          if (err)
+                                               break;
+                                       }
+                               }
+                               else
+                                       break;
+                       }
+
+               } else
+                       return -EINVAL;
+
+       } else {
+               DBG(4, "Unsupported I2C transfer mode (%d)", size)
+               return -EINVAL;
+       }
+
+       /* This works around a bug in the I2C core */
+       if (rc > 0)
+               rc = 0;
+
+       return rc;
+}
+
+
+static u32 w9968cf_i2c_func(struct i2c_adapter* adap)
+{
+       return I2C_FUNC_SMBUS_READ_BYTE |
+              I2C_FUNC_SMBUS_READ_BYTE_DATA  |
+              I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
+}
+
+
+static int w9968cf_i2c_attach_inform(struct i2c_client* client)
+{
+       struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+       const char* clientname = i2c_clientname(client);
+       int id = client->driver->id;
+
+       if (id == I2C_DRIVERID_OVCAMCHIP) {
+               int rc = 0;
+
+               cam->sensor_client = client;
+
+               rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_INITIALIZE, 
+                                       &cam->sensor_mono);
+               if (rc < 0) {
+                       DBG(1, "CMOS sensor initialization failed (rc=%d)",rc);
+                       cam->sensor_client = NULL;
+                       return rc;
+               }
+
+               if (w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_Q_SUBTYPE, 
+                                      &cam->sensor) < 0)
+                       rc = -EIO;
+               else if (client->addr==OV7xx0_SID || client->addr==OV6xx0_SID)
+                       w9968cf_sensor_configure(cam);
+               else
+                       rc = -EINVAL;
+
+               if (rc < 0) {
+                       cam->sensor_client = NULL;
+                       cam->sensor = CC_UNKNOWN;
+                       return rc;
+               }
+       } else  {
+               DBG(4, "Rejected client [%s] with [%s]", 
+                   clientname, client->driver->name)
+               return -1;
+       }
+
+       DBG(2, "I2C attach client [%s] with [%s]",
+           clientname, client->driver->name)
+
+       return 0;
+}
+
+
+static int w9968cf_i2c_detach_inform(struct i2c_client* client)
+{
+       struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+       const char* clientname = i2c_clientname(client);
+
+       if (cam->sensor_client == client) {
+               cam->sensor_client = NULL;
+       }
+
+       DBG(2, "I2C detach [%s]", clientname)
+
+       return 0;
+}
+
+
+static int 
+w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd,
+                    unsigned long arg)
+{
+       return 0;
+}
+
+
+static int w9968cf_i2c_init(struct w9968cf_device* cam)
+{
+       int rc = 0;
+
+       static struct i2c_algorithm algo = {
+               .name =          "W996[87]CF algorithm",
+               .id =            I2C_ALGO_SMBUS,
+               .smbus_xfer =    w9968cf_i2c_smbus_xfer,
+               .algo_control =  w9968cf_i2c_control,
+               .functionality = w9968cf_i2c_func,
+       };
+
+       static struct i2c_adapter adap = {
+               .id =                I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF,
+               .class =             I2C_ADAP_CLASS_CAM_DIGITAL,
+               .owner =             THIS_MODULE,
+               .client_register =   w9968cf_i2c_attach_inform,
+               .client_unregister = w9968cf_i2c_detach_inform,
+               .algo =              &algo,
+       };
+
+       memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter));
+       strcpy(cam->i2c_adapter.name, "w9968cf");
+       i2c_set_adapdata(&cam->i2c_adapter, cam);
+
+       DBG(6, "Registering I2C bus with kernel...")
+
+       rc = i2c_add_adapter(&cam->i2c_adapter);
+       if (rc)
+               DBG(5, "Failed to register the I2C bus.")
+       else
+               DBG(5, "I2C bus registered.")
+
+       return rc;
+}
+
+
+
+/****************************************************************************
+ * Helper functions                                                         *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+  Turn on the LED on some webcams. A beep should be heard too.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_turn_on_led(struct w9968cf_device* cam)
+{
+       int err = 0;
+
+       err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */
+       err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */
+       err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */
+       err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */
+       err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */
+       err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */
+
+       if (err)
+               DBG(2, "Couldn't turn on the LED.")
+
+       DBG(5, "LED turned on.")
+
+       return err;
+}
+
+
+/*--------------------------------------------------------------------------
+  Write some registers for the device initialization.
+  This function is called once on open().
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_init_chip(struct w9968cf_device* cam)
+{
+       int err = 0, rc = 0;
+
+       err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */
+       err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */
+
+       err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */
+       err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */
+
+       err += w9968cf_write_reg(cam, 0x0000, 0x20); /* Y frame buf.0, low */
+       err += w9968cf_write_reg(cam, 0x0000, 0x21); /* Y frame buf.0, high */
+       err += w9968cf_write_reg(cam, 0xb000, 0x22); /* Y frame buf.1, low */
+       err += w9968cf_write_reg(cam, 0x0004, 0x23); /* Y frame buf.1, high */
+       err += w9968cf_write_reg(cam, 0x5800, 0x24); /* U frame buf.0, low */
+       err += w9968cf_write_reg(cam, 0x0002, 0x25); /* U frame buf.0, high */
+       err += w9968cf_write_reg(cam, 0x0800, 0x26); /* U frame buf.1, low */
+       err += w9968cf_write_reg(cam, 0x0007, 0x27); /* U frame buf.1, high */
+       err += w9968cf_write_reg(cam, 0x8400, 0x28); /* V frame buf.0, low */
+       err += w9968cf_write_reg(cam, 0x0003, 0x29); /* V frame buf.0, high */
+       err += w9968cf_write_reg(cam, 0x3400, 0x2a); /* V frame buf.1, low */
+       err += w9968cf_write_reg(cam, 0x0008, 0x2b); /* V frame buf.1, high */
+
+       err += w9968cf_write_reg(cam, 0x6000, 0x32); /* JPEG bitstream buf 0 */
+       err += w9968cf_write_reg(cam, 0x0009, 0x33); /* JPEG bitstream buf 0 */
+       err += w9968cf_write_reg(cam, 0x2000, 0x34); /* JPEG bitstream buf 1 */
+       err += w9968cf_write_reg(cam, 0x000d, 0x35); /* JPEG bitstream buf 1 */
+
+       err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */
+       err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/
+       err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */
+       err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */
+
+       err += w9968cf_set_picture(cam, cam->picture); /* this before */
+       err += w9968cf_set_window(cam, cam->window);
+
+       if (err)
+               goto error;
+
+       rc = w9968cf_sensor_change_settings(cam);
+       if (rc)
+               goto error;
+
+       DBG(5, "Chip successfully initialized.");
+       
+       return 0;
+
+error:
+       DBG(1, "Chip initialization failed.")
+       if (err)
+               return err;
+       else
+               return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+  Change the picture settings of the camera.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int
+w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict)
+{
+       u16 fmt, hw_depth, hw_palette, reg_v = 0x0000;
+       int err = 0, rc = 0;
+
+       /* Make sure we are using a valid depth */
+       pict.depth = w9968cf_valid_depth(pict.palette);
+
+       fmt = pict.palette;
+
+       hw_depth = pict.depth; /* depth used by the winbond chip */
+       hw_palette = pict.palette; /* palette used by the winbond chip */
+
+       /* VS & HS polarities */
+       reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11);
+
+       switch (fmt)
+       {
+               case VIDEO_PALETTE_UYVY:
+                       reg_v |= 0x0000;
+                       cam->vpp_flag = VPP_NONE;
+                       break;
+               case VIDEO_PALETTE_YUV422P:
+                       reg_v |= 0x0002;
+                       cam->vpp_flag = VPP_DECOMPRESSION;
+                       break;
+               case VIDEO_PALETTE_YUV420:
+               case VIDEO_PALETTE_YUV420P:
+                       reg_v |= 0x0003;
+                       cam->vpp_flag = VPP_DECOMPRESSION;
+                       break;
+               case VIDEO_PALETTE_YUYV:
+               case VIDEO_PALETTE_YUV422:
+                       reg_v |= 0x0000;
+                       cam->vpp_flag = VPP_SWAP_YUV_BYTES;
+                       hw_palette = VIDEO_PALETTE_UYVY;
+                       break;
+               /* Original video is used instead of RGBX palettes. 
+                  Software conversion later. */
+               case VIDEO_PALETTE_GREY:
+               case VIDEO_PALETTE_RGB555:
+               case VIDEO_PALETTE_RGB565:
+               case VIDEO_PALETTE_RGB24:
+               case VIDEO_PALETTE_RGB32:
+                       reg_v |= 0x0000; /* UYVY 16 bit is used */
+                       hw_depth = 16;
+                       hw_palette = VIDEO_PALETTE_UYVY;
+                       cam->vpp_flag = VPP_UYVY_TO_RGBX;
+                       break;
+       }
+
+       /* FIXME: 'hardware double buffer' doesn't work when compressed video
+                 is enabled (corrupted frames). */
+       if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION))
+               reg_v |= 0x0080;
+
+       if (cam->clamping)
+               reg_v |= 0x0020;
+
+       if (cam->filter_type == 1)
+               reg_v |= 0x0008;
+       else if (cam->filter_type == 2)
+               reg_v |= 0x000c;
+
+       err = w9968cf_write_reg(cam, reg_v, 0x16);
+       if (err)
+               goto error;
+
+       rc = w9968cf_sensor_set_picture(cam, pict);
+       if (rc)
+               goto error;
+
+       /* If all went well, update the device data structure */
+       memcpy(&cam->picture, &pict, sizeof(pict));
+       cam->hw_depth = hw_depth;
+       cam->hw_palette = hw_palette;
+
+       /* Settings changed, so we clear the frame buffers */
+       memset(cam->frame[0].buffer, 0, 
+              cam->nbuffers*w9968cf_get_max_bufsize(cam));
+
+       DBG(4, "Palette is %s, depth is %d bpp.",
+           symbolic(v4l1_plist, pict.palette), pict.depth)
+
+       return 0;
+
+error:
+       DBG(1, "Failed to change picture settings.")
+       if (err)
+               return err;
+       else 
+               return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+  Change the capture area size of the camera.
+  This function _must_ be called _after_ w9968cf_set_picture().
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int
+w9968cf_set_window(struct w9968cf_device* cam, struct video_window win)
+{
+       u16 x, y, w, h, scx, scy, cw, ch, ax, ay;
+       unsigned long fw, fh;
+       struct ovcamchip_window s_win;
+       int err=0, rc=0;
+
+       /* Work around to avoid FP arithmetics */
+       #define __SC(x) ((x) << 10)
+       #define __UNSC(x) ((x) >> 10)
+
+       /* Make sure we are using a supported resolution */
+       if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, 
+                                             (u16*)&win.height)))
+               goto error;
+
+       /* Scaling factors */
+       fw = __SC(win.width) / cam->maxwidth;
+       fh = __SC(win.height) / cam->maxheight;
+
+       /* Set up the width and height values used by the chip */
+       if ((win.width > cam->maxwidth) || (win.height > cam->maxheight)) {
+               cam->vpp_flag |= VPP_UPSCALE;
+               /* Calculate largest w,h mantaining the same w/h ratio */
+               w = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+               h = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+               if (w < cam->minwidth) /* just in case */
+                       w = cam->minwidth;
+               if (h < cam->minheight) /* just in case */
+                       h = cam->minheight;
+       } else {
+               cam->vpp_flag &= ~VPP_UPSCALE;
+               w = win.width;
+               h = win.height;
+       }
+
+       /* x,y offsets of the cropped area */
+       scx = cam->start_cropx;
+       scy = cam->start_cropy;
+
+       /* Calculate cropped area manteining the right w/h ratio */
+       if (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE)) {
+               cw = (fw >= fh) ? cam->maxwidth : __SC(win.width)/fh;
+               ch = (fw >= fh) ? __SC(win.height)/fw : cam->maxheight;
+       } else {
+               cw = w;
+               ch = h;
+       }
+
+       /* Setup the sensor window */
+       s_win.format = SENSOR_FORMAT;
+       s_win.width = cam->maxwidth;
+       s_win.height = cam->maxheight;
+       s_win.quarter = 0; /* full progressive video */
+
+       /* Center it */
+       s_win.x = (s_win.width - cw) / 2;
+       s_win.y = (s_win.height - ch) / 2;
+
+       /* Clock divisor */
+       if (cam->clockdiv >= 0)
+               s_win.clockdiv = cam->clockdiv; /* manual override */
+       else
+               switch (cam->sensor) {
+                       case CC_OV6620:
+                               s_win.clockdiv = 0;
+                               break;
+                       case CC_OV6630:
+                               s_win.clockdiv = 0;
+                               break;
+                       case CC_OV76BE:
+                       case CC_OV7610:
+                       case CC_OV7620:
+                               s_win.clockdiv = 0;
+                               break;
+                       default:
+                               s_win.clockdiv = W9968CF_DEF_CLOCKDIVISOR;
+               }
+
+       /* We have to scale win.x and win.y offsets */  
+       if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+            || (cam->vpp_flag & VPP_UPSCALE) ) {
+               ax = __SC(win.x)/fw;
+               ay = __SC(win.y)/fh;
+       } else {
+               ax = win.x;
+               ay = win.y;
+       }
+
+       if ((ax + cw) > cam->maxwidth)
+               ax = cam->maxwidth - cw;
+
+       if ((ay + ch) > cam->maxheight)
+               ay = cam->maxheight - ch;
+
+       /* Adjust win.x, win.y */
+       if ( (cam->largeview && !(cam->vpp_flag & VPP_UPSCALE))
+            || (cam->vpp_flag & VPP_UPSCALE) ) {
+               win.x = __UNSC(ax*fw);
+               win.y = __UNSC(ay*fh);
+       } else {
+               win.x = ax;
+               win.y = ay;
+       }
+
+       /* Offsets used by the chip */
+       x = ax + s_win.x;
+       y = ay + s_win.y;
+
+       /* Go ! */
+       if ((rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_MODE, &s_win)))
+               goto error;
+
+       err += w9968cf_write_reg(cam, scx + x, 0x10);
+       err += w9968cf_write_reg(cam, scy + y, 0x11);
+       err += w9968cf_write_reg(cam, scx + x + cw, 0x12);
+       err += w9968cf_write_reg(cam, scy + y + ch, 0x13);
+       err += w9968cf_write_reg(cam, w, 0x14);
+       err += w9968cf_write_reg(cam, h, 0x15);
+
+       /* JPEG width & height */
+       err += w9968cf_write_reg(cam, w, 0x30);
+       err += w9968cf_write_reg(cam, h, 0x31);
+
+       /* Y & UV frame buffer strides (in WORD) */
+       if (cam->vpp_flag & VPP_DECOMPRESSION) {
+               err += w9968cf_write_reg(cam, w/2, 0x2c);
+               err += w9968cf_write_reg(cam, w/4, 0x2d);
+       } else
+               err += w9968cf_write_reg(cam, w, 0x2c);
+
+       if (err)
+               goto error;
+
+       /* If all went well, update the device data structure */
+       memcpy(&cam->window, &win, sizeof(win));
+       cam->hw_width = w;
+       cam->hw_height = h;
+
+       /* Settings changed, so we clear the frame buffers */
+       memset(cam->frame[0].buffer, 0, 
+              cam->nbuffers*w9968cf_get_max_bufsize(cam));
+
+       DBG(4, "The capture area is %dx%d, Offset (x,y)=(%d,%d).", 
+           win.width, win.height, win.x, win.y)
+
+       PDBGG("x=%d ,y=%d, w=%d, h=%d, ax=%d, ay=%d, s_win.x=%d, s_win.y=%d, "
+             "cw=%d, ch=%d, win.x=%d ,win.y=%d, win.width=%d, win.height=%d",
+             x, y, w, h, ax, ay, s_win.x, s_win.y, cw, ch, win.x, win.y,
+             win.width, win.height)
+
+       return 0;
+
+error:
+       DBG(1, "Failed to change the capture area size.")
+       if (err)
+               return err;
+       else
+               return rc;
+}
+
+
+/*--------------------------------------------------------------------------
+  Return non-zero if the palette is supported, 0 otherwise.
+  --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_palette(u16 palette)
+{
+       u8 i = 0;
+       while (w9968cf_formatlist[i].palette != 0) {
+               if (palette == w9968cf_formatlist[i].palette)
+                       return palette;
+               i++;
+       }
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Return the depth corresponding to the given palette.
+  Palette _must_ be supported !
+  --------------------------------------------------------------------------*/
+static inline u16 w9968cf_valid_depth(u16 palette)
+{
+       u8 i=0;
+       while (w9968cf_formatlist[i].palette != palette)
+               i++;
+
+       return w9968cf_formatlist[i].depth;
+}
+
+
+/*--------------------------------------------------------------------------
+  Return non-zero if the format requires decompression, 0 otherwise.
+  --------------------------------------------------------------------------*/
+static inline u8 w9968cf_need_decompression(u16 palette)
+{
+       u8 i = 0;
+       while (w9968cf_formatlist[i].palette != 0) {
+               if (palette == w9968cf_formatlist[i].palette)
+                       return w9968cf_formatlist[i].compression;
+               i++;
+       }
+       return 0;
+}
+
+
+/*-------------------------------------------------------------------------- 
+  Adjust the asked values for window width and height.
+  Return 0 on success, -1 otherwise.
+  --------------------------------------------------------------------------*/
+static int 
+w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height)
+{
+       u16 maxw, maxh;
+
+       if ((*width < cam->minwidth) || (*height < cam->minheight))
+               return -ERANGE;
+
+       maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
+              && w9968cf_vppmod_present ? W9968CF_MAX_WIDTH : cam->maxwidth;
+       maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION)
+              && w9968cf_vppmod_present ? W9968CF_MAX_HEIGHT : cam->maxheight;
+
+       if (*width > maxw)
+               *width = maxw;
+       if (*height > maxh)
+               *height = maxh;
+
+       if (cam->vpp_flag & VPP_DECOMPRESSION) {
+               *width  &= ~15L; /* multiple of 16 */
+               *height &= ~15L;
+       }
+
+       PDBGG("Window size adjusted w=%d, h=%d ", *width, *height)
+
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Initialize the FIFO list of requested frames.
+  --------------------------------------------------------------------------*/
+static void w9968cf_init_framelist(struct w9968cf_device* cam)
+{
+       u8 i;
+
+       for (i = 0; i < cam->nbuffers; i++) {
+               cam->requested_frame[i] = NULL;
+               cam->frame[i].queued = 0;
+               cam->frame[i].status = F_UNUSED;
+       }
+}
+
+
+/*--------------------------------------------------------------------------
+  Add a frame in the FIFO list of requested frames.
+  This function is called in process context.
+  --------------------------------------------------------------------------*/
+static void w9968cf_push_frame(struct w9968cf_device* cam, u8 f_num)
+{
+       u8 f;
+       unsigned long lock_flags;
+
+       spin_lock_irqsave(&cam->flist_lock, lock_flags);
+
+       for (f=0; cam->requested_frame[f] != NULL; f++);
+       cam->requested_frame[f] = &cam->frame[f_num];
+       cam->frame[f_num].queued = 1;
+       cam->frame[f_num].status = F_UNUSED; /* clear the status */
+
+       spin_unlock_irqrestore(&cam->flist_lock, lock_flags);
+
+       DBG(6, "Frame #%d pushed into the FIFO list. Position %d.", f_num, f)
+}
+
+
+/*--------------------------------------------------------------------------
+  Read, store and remove the first pointer in the FIFO list of requested
+  frames. This function is called in interrupt context.
+  --------------------------------------------------------------------------*/
+static void 
+w9968cf_pop_frame(struct w9968cf_device* cam, struct w9968cf_frame_t** framep)
+{
+       u8 i;
+
+       spin_lock(&cam->flist_lock);
+
+       *framep = cam->requested_frame[0];
+
+       /* Shift the list of pointers */
+       for (i = 0; i < cam->nbuffers-1; i++)
+               cam->requested_frame[i] = cam->requested_frame[i+1];
+       cam->requested_frame[i] = NULL;
+
+       spin_unlock(&cam->flist_lock);
+
+       DBG(6,"Popped frame #%d from the list.",*framep-&cam->frame[0])
+}
+
+
+/*--------------------------------------------------------------------------
+  High-level video post-processing routine on grabbed frames.
+  Return 0 on success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int 
+w9968cf_postprocess_frame(struct w9968cf_device* cam, 
+                          struct w9968cf_frame_t* fr)
+{
+       void *pIn = fr->buffer, *pOut = cam->vpp_buffer, *tmp;
+       u16 w = cam->window.width,
+           h = cam->window.height,
+           d = cam->picture.depth,
+           fmt = cam->picture.palette,
+           rgb = cam->force_rgb,
+           hw_w = cam->hw_width,
+           hw_h = cam->hw_height,
+           hw_d = cam->hw_depth;
+       int err = 0;
+
+       #define _PSWAP(pIn, pOut) {tmp = (pIn); (pIn) = (pOut); (pOut) = tmp;}
+
+       if (cam->vpp_flag & VPP_DECOMPRESSION) {
+               memcpy(pOut, pIn, fr->length);
+               _PSWAP(pIn, pOut)
+               err = (*w9968cf_vpp_decode)(pIn, fr->length, hw_w, hw_h, pOut);
+               PDBGG("Compressed frame length: %li",(unsigned long)fr->length)
+               fr->length = (hw_w*hw_h*hw_d)/8;
+               _PSWAP(pIn, pOut)
+               if (err) {
+                       DBG(4, "An error occurred while decoding the frame: "
+                              "%s.", symbolic(decoder_errlist, err))
+                       return err;
+               } else
+                       DBG(6, "Frame decoded")
+       }
+
+       if (cam->vpp_flag & VPP_SWAP_YUV_BYTES) {
+               (*w9968cf_vpp_swap_yuvbytes)(pIn, fr->length);
+               DBG(6, "Original UYVY component ordering changed.")
+       }
+
+       if (cam->vpp_flag & VPP_UPSCALE) {
+               (*w9968cf_vpp_scale_up)(pIn, pOut, hw_w, hw_h, hw_d, w, h);
+               fr->length = (w*h*hw_d)/8;
+               _PSWAP(pIn, pOut)
+               DBG(6, "Vertical up-scaling done: %d,%d,%dbpp->%d,%d",
+                   hw_w, hw_h, hw_d, w, h)
+       }
+
+       if (cam->vpp_flag & VPP_UYVY_TO_RGBX) {
+               (*w9968cf_vpp_uyvy_to_rgbx)(pIn, fr->length, pOut, fmt, rgb);
+               fr->length = (w*h*d)/8;
+               _PSWAP(pIn, pOut)
+               DBG(6, "UYVY-16bit to %s conversion done.", 
+                   symbolic(v4l1_plist, fmt))
+       }
+
+       if (pOut == fr->buffer)
+               memcpy(fr->buffer, cam->vpp_buffer, fr->length);
+
+       return 0;
+}
+
+
+
+/****************************************************************************
+ * CMOS sensor control routines                                             *
+ ****************************************************************************/
+
+static int 
+w9968cf_sensor_set_control(struct w9968cf_device* cam, int cid, int val)
+{
+       struct ovcamchip_control ctl;
+       int rc;
+
+       ctl.id = cid;
+       ctl.value = val;
+
+       rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_S_CTRL, &ctl);
+
+       return rc;
+}
+
+static int 
+w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int *val)
+{
+       struct ovcamchip_control ctl;
+       int rc;
+
+       ctl.id = cid;
+
+       rc = w9968cf_sensor_cmd(cam, OVCAMCHIP_CMD_G_CTRL, &ctl);
+       if (rc >= 0)
+               *val = ctl.value;
+
+       return rc;
+}
+
+
+static inline int
+w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void *arg)
+{
+       struct i2c_client* c = cam->sensor_client;
+
+       DBG(6, "Executing CMOS sensor command...")
+
+       if (c && c->driver->command)
+               return c->driver->command(cam->sensor_client, cmd, arg);
+       else
+               return -ENODEV;
+}
+
+
+/*--------------------------------------------------------------------------
+  Change some settings of the CMOS sensor.
+  Returns: 0 for success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int w9968cf_sensor_change_settings(struct w9968cf_device* cam)
+{
+       int rc;
+
+       /* Auto brightness */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOBRIGHT, 
+                                       cam->auto_brt);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       /* Auto exposure */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_AUTOEXP, 
+                                       cam->auto_exp);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       /* Banding filter */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BANDFILT, 
+                                       cam->bandfilt);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       /* Light frequency */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_FREQ,
+                                       cam->lightfreq);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       /* Back light */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BACKLIGHT,
+                                       cam->backlight);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       /* Mirror */
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_MIRROR,
+                                       cam->mirror);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Get some current picture settings from the CMOS sensor.
+  Returns: 0 for success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_get_picture(struct w9968cf_device* cam, 
+                           struct video_picture* pict)
+{
+       int rc, v;
+
+       /* Don't return error if a setting is unsupported, or rest of settings
+          will not be performed */
+
+       rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_CONT, &v);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+       pict->contrast = v;
+
+       rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_BRIGHT, &v);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+       pict->brightness = v;
+
+       rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_SAT, &v);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+       pict->colour = v;
+
+       rc = w9968cf_sensor_get_control(cam, OVCAMCHIP_CID_HUE, &v);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+       pict->hue = v;
+
+       pict->whiteness = W9968CF_WHITENESS; /* to do! */
+
+       DBG(5, "Got picture settings from the CMOS sensor.")
+
+       PDBGG("Brightness, contrast, hue, colour, whiteness are "
+             "%d,%d,%d,%d,%d.", pict->brightness, pict->contrast,
+             pict->hue, pict->colour, pict->whiteness)
+
+       return 0;
+}
+
+
+/*--------------------------------------------------------------------------
+  Change picture settings of the CMOS sensor.
+  Returns: 0 for success, a negative number otherwise.
+  --------------------------------------------------------------------------*/
+static int
+w9968cf_sensor_set_picture(struct w9968cf_device* cam, 
+                           struct video_picture pict)
+{
+       int rc;
+
+       rc = w9968cf_sensor_set_control(cam,OVCAMCHIP_CID_CONT, pict.contrast);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       if (!cam->auto_brt) {
+               rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_BRIGHT, 
+                                               pict.brightness);
+               if (SENSOR_FATAL_ERROR(rc))
+                       return rc;
+       }
+
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_SAT, pict.colour);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       rc = w9968cf_sensor_set_control(cam, OVCAMCHIP_CID_HUE, pict.hue);
+       if (SENSOR_FATAL_ERROR(rc))
+               return rc;
+
+       PDBGG("Brightness, contrast, hue, colour, whiteness are "
+             "%d,%d,%d,%d,%d.", pict.brightness, pict.contrast,
+             pict.hue, pict.colour, pict.whiteness)
+
+       return 0;
+}
+
+
+
+/****************************************************************************
+ * Camera configuration                                                     *
+ ****************************************************************************/
+
+/*--------------------------------------------------------------------------
+  This function is called when the CMOS sensor is detected.
+  --------------------------------------------------------------------------*/
+static void w9968cf_sensor_configure(struct w9968cf_device* cam)
+{
+       /* NOTE: Make sure width and height are a multiple of 16 */
+
+       switch (cam->sensor_client->addr) {
+               case OV6xx0_SID:
+                       cam->maxwidth = 352;
+                       cam->maxheight = 288;
+                       cam->minwidth = 64;
+                       cam->minheight = 48;
+                       break;
+               case OV7xx0_SID:
+                       cam->maxwidth = 640;
+                       cam->maxheight = 480;
+                       cam->minwidth = 64;
+                       cam->minheight = 48;
+                       break;
+       }
+
+       /* These values depend on the ones in the ovxxx0.c sources */
+       switch (cam->sensor) {
+               case CC_OV7620:
+                       cam->start_cropx = 287;
+                       cam->start_cropy = 35;
+                       /* Seems to work around a bug in the CMOS sensor */
+                       cam->vs_polarity = 1;
+                       cam->hs_polarity = 1;
+                       break;
+               default:
+                       cam->start_cropx = 320;
+                       cam->start_cropy = 35;
+                       cam->vs_polarity = 1;
+                       cam->hs_polarity = 0;
+       }
+
+       DBG(5, "CMOS sensor %s configured.", symbolic(senlist, cam->sensor))
+}
+
+
+/*--------------------------------------------------------------------------
+  Fill some basic fields in the main device data structure.
+  This function is called once on w9968cf_usb_probe() for each recognized 
+  camera.
+  --------------------------------------------------------------------------*/
+static void
+w9968cf_configure_camera(struct w9968cf_device* cam,
+                         struct usb_device* udev,
+                         enum w9968cf_model_id mod_id,
+                         const unsigned short dev_nr)
+{
+       init_MUTEX(&cam->fileop_sem);
+       init_waitqueue_head(&cam->open);
+       spin_lock_init(&cam->urb_lock);
+       spin_lock_init(&cam->flist_lock);
+
+       cam->users = 0;
+       cam->disconnected = 0;
+       cam->usbdev = udev;
+       cam->id = mod_id;
+       cam->sensor = CC_UNKNOWN;
+
+       strcpy(cam->v4ldev.name, symbolic(camlist, mod_id));
+       cam->v4ldev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES;
+       cam->v4ldev.hardware = VID_HARDWARE_W9968CF;
+       cam->v4ldev.fops = &w9968cf_fops;
+       cam->v4ldev.priv = (void*)cam;
+       cam->v4ldev.minor = video_nr[dev_nr];
+
+       /* Calculate the alternate setting number (from 1 to 16)
+          according to the 'packet_size' module parameter */
+       if (packet_size[dev_nr] < W9968CF_MIN_PACKET_SIZE)
+               packet_size[dev_nr] = W9968CF_MIN_PACKET_SIZE;
+       for (cam->altsetting = 1;
+            packet_size[dev_nr] < wMaxPacketSize[cam->altsetting-1];
+            cam->altsetting++);
+
+       cam->max_buffers = (max_buffers[dev_nr] < 2 || 
+                           max_buffers[dev_nr] > W9968CF_MAX_BUFFERS)
+                          ? W9968CF_BUFFERS : max_buffers[dev_nr];
+
+       cam->double_buffer = (double_buffer[dev_nr] == 0 || 
+                             double_buffer[dev_nr] == 1)
+                            ? double_buffer[dev_nr] : W9968CF_DOUBLE_BUFFER;
+
+       cam->clamping = (clamping[dev_nr] == 0 || clamping[dev_nr] == 1)
+                       ? clamping[dev_nr] : W9968CF_CLAMPING;
+       
+       cam->filter_type = (filter_type[dev_nr] == 0 ||
+                           filter_type[dev_nr] == 1 ||
+                           filter_type[dev_nr] == 2)
+                          ? filter_type[dev_nr] : W9968CF_FILTER_TYPE;
+
+       cam->capture = 1;
+
+       cam->largeview = (largeview[dev_nr] == 0 || largeview[dev_nr] == 1)
+                        ? largeview[dev_nr] : W9968CF_LARGEVIEW;
+
+       cam->decompression = (decompression[dev_nr] == 0 || 
+                             decompression[dev_nr] == 1 ||
+                             decompression[dev_nr] == 2)
+                            ? decompression[dev_nr] : W9968CF_DECOMPRESSION;
+
+       cam->upscaling = (upscaling[dev_nr] == 0 || 
+                         upscaling[dev_nr] == 1)
+                        ? upscaling[dev_nr] : W9968CF_UPSCALING;
+
+       cam->auto_brt = (autobright[dev_nr] == 0 || autobright[dev_nr] == 1)
+                       ? autobright[dev_nr] : W9968CF_AUTOBRIGHT;
+
+       cam->auto_exp = (autoexp[dev_nr] == 0 || autoexp[dev_nr] == 1)
+                       ? autoexp[dev_nr] : W9968CF_AUTOEXP;
+
+       cam->lightfreq = (lightfreq[dev_nr] == 50 || lightfreq[dev_nr] == 60)
+                        ? lightfreq[dev_nr] : W9968CF_LIGHTFREQ;
+
+       cam->bandfilt = (bandingfilter[dev_nr] == 0 || 
+                        bandingfilter[dev_nr] == 1)
+                       ? bandingfilter[dev_nr] : W9968CF_BANDINGFILTER;
+
+       cam->backlight = (backlight[dev_nr] == 0 || backlight[dev_nr] == 1)
+                        ? backlight[dev_nr] : W9968CF_BACKLIGHT;
+
+       cam->clockdiv = (clockdiv[dev_nr] == -1 || clockdiv[dev_nr] >= 0)
+                       ? clockdiv[dev_nr] : W9968CF_CLOCKDIV;
+
+       cam->mirror = (mirror[dev_nr] == 0 || mirror[dev_nr] == 1)
+                     ? mirror[dev_nr] : W9968CF_MIRROR;
+
+       cam->sensor_mono = (sensor_mono[dev_nr]==0 || sensor_mono[dev_nr]==1)
+                          ? sensor_mono[dev_nr] : W9968CF_SENSOR_MONO;
+
+       cam->picture.brightness = brightness[dev_nr];
+       cam->picture.hue = hue[dev_nr];
+       cam->picture.colour = colour[dev_nr];
+       cam->picture.contrast = contrast[dev_nr];
+       cam->picture.whiteness = whiteness[dev_nr];
+       if (w9968cf_valid_palette(force_palette[dev_nr])) {
+               cam->picture.palette = force_palette[dev_nr];
+               cam->force_palette = 1;
+       } else {
+               cam->force_palette = 0;
+               if (cam->decompression == 0)
+                       cam->picture.palette = W9968CF_PALETTE_DECOMP_OFF;
+               else if (cam->decompression == 1)
+                       cam->picture.palette = W9968CF_PALETTE_DECOMP_FORCE;
+               else
+                       cam->picture.palette = W9968CF_PALETTE_DECOMP_ON;
+       }
+
+       cam->force_rgb = (force_rgb[dev_nr] == 0 || force_rgb[dev_nr] == 1)
+                        ? force_rgb[dev_nr] : W9968CF_FORCE_RGB;
+
+       cam->window.x = 0;
+       cam->window.y = 0;
+       cam->window.width = W9968CF_WIDTH;
+       cam->window.height = W9968CF_HEIGHT;
+       cam->window.chromakey = 0;
+       cam->window.clipcount = 0;
+       cam->window.flags = 0;
+
+       /* If the video post-processing module is not present, some paramaters
+          must be overridden: */
+       if (!w9968cf_vppmod_present) {
+               if (cam->decompression == 1)
+                       cam->decompression = 2;
+               cam->upscaling = 0;
+               if (cam->picture.palette != VIDEO_PALETTE_UYVY)
+                       cam->force_palette = 0;
+               cam->picture.palette = VIDEO_PALETTE_UYVY;
+       }
+
+       cam->picture.depth = w9968cf_valid_depth(cam->picture.palette);
+
+       DBG(3, "%s configured with settings #%d:", 
+           symbolic(camlist, cam->id), dev_nr)
+       
+       DBG(3, "- Data packet size for USB isochrnous transfer: %d bytes.",
+           wMaxPacketSize[cam->altsetting-1])
+       
+       DBG(3, "- Number of requested video frame buffers: %d", 
+           cam->max_buffers)
+
+       if (cam->double_buffer)
+               DBG(3, "- Hardware double buffering enabled.")
+       else 
+               DBG(3, "- Hardware double buffering disabled.")
+
+       if (cam->filter_type == 0)
+               DBG(3, "- Video filtering disabled.")
+       else if (cam->filter_type == 1)
+               DBG(3, "- Video filtering enabled: type 1-2-1.")
+       else if (cam->filter_type == 2)
+               DBG(3, "- Video filtering enabled: type 2-3-6-3-2.")
+
+       if (cam->clamping)
+               DBG(3, "- Video data clamping (CCIR-601 format) enabled.")
+       else
+               DBG(3, "- Video data clamping (CCIR-601 format) disabled.")
+
+       if (cam->largeview)
+               DBG(3, "- Large view enabled.")
+       else
+               DBG(3, "- Large view disabled.")
+
+       if ((cam->decompression) == 0 && (!cam->force_palette))
+               DBG(3, "- Decompression disabled.")
+       else if ((cam->decompression) == 1 && (!cam->force_palette))
+               DBG(3, "- Decompression forced.")
+       else if ((cam->decompression) == 2 && (!cam->force_palette))
+               DBG(3, "- Decompression allowed.")
+
+       if (cam->upscaling)
+               DBG(3, "- Software image scaling enabled.")
+       else
+               DBG(3, "- Software image scaling disabled.")
+
+       if (cam->force_palette)
+               DBG(3, "- Image palette forced to %s.",
+                   symbolic(v4l1_plist, cam->picture.palette))
+
+       if (cam->force_rgb)
+               DBG(3, "- RGB component ordering will be used instead of BGR.")
+
+       if (cam->auto_brt)
+               DBG(3, "- Auto brightness enabled.")
+       else
+               DBG(3, "- Auto brightness disabled.")
+
+       if (cam->auto_exp)
+               DBG(3, "- Auto exposure enabled.")
+       else
+               DBG(3, "- Auto exposure disabled.")
+
+       if (cam->backlight)
+               DBG(3, "- Backlight exposure algorithm enabled.")
+       else
+               DBG(3, "- Backlight exposure algorithm disabled.")
+
+       if (cam->mirror)
+               DBG(3, "- Mirror enabled.")
+       else
+               DBG(3, "- Mirror disabled.")
+
+       if (cam->bandfilt)
+               DBG(3, "- Banding filter enabled.")
+       else
+               DBG(3, "- Banding filter disabled.")
+
+       DBG(3, "- Power lighting frequency: %d", cam->lightfreq)
+
+       if (cam->clockdiv == -1)
+               DBG(3, "- Automatic clock divisor enabled.")
+       else
+               DBG(3, "- Clock divisor: %d", cam->clockdiv)
+
+       if (cam->sensor_mono)
+               DBG(3, "- CMOS sensor used as monochrome.")
+       else
+               DBG(3, "- CMOS sensor not used as monochrome.")
+}
+
+
+/*--------------------------------------------------------------------------
+  Release the resources used by the driver.
+  This function is called on disconnect 
+  (or on close if deallocation has been deferred)
+  --------------------------------------------------------------------------*/
+static void w9968cf_release_resources(struct w9968cf_device* cam)
+{
+       down(&w9968cf_devlist_sem);
+
+       DBG(2, "V4L device deregistered: /dev/video%d", cam->v4ldev.minor)
+
+       video_unregister_device(&cam->v4ldev);
+       list_del(&cam->v4llist);
+       i2c_del_adapter(&cam->i2c_adapter);
+       w9968cf_deallocate_memory(cam);
+       kfree(cam->control_buffer);
+       kfree(cam->data_buffer);
+
+       up(&w9968cf_devlist_sem);
+
+       DBG(5, "Resources released.")
+}
+
+
+
+/****************************************************************************
+ * Video4Linux interface                                                    *
+ ****************************************************************************/
+
+static int w9968cf_open(struct inode* inode, struct file* filp)
+{
+       struct w9968cf_device* cam =
+         (struct w9968cf_device*)video_devdata(filp)->priv;    
+       int err;
+
+       down(&cam->dev_sem);
+
+       if (cam->sensor == CC_UNKNOWN) {
+               DBG(2, "No supported CMOS sensor has been detected by the "
+                      "'ovcamchip' module for the %s (/dev/video%d). Make "
+                      "sure it is loaded *before* the 'w9968cf' module.", 
+                   symbolic(camlist, cam->id),cam->v4ldev.minor)
+               up(&cam->dev_sem);
+               return -ENODEV;
+       }
+
+       if (cam->users) {
+               DBG(2, "%s (/dev/video%d) has been already occupied by '%s'.",
+                   symbolic(camlist, cam->id),cam->v4ldev.minor, cam->command)
+               if ((filp->f_flags & O_NONBLOCK)||(filp->f_flags & O_NDELAY)) {
+                       up(&cam->dev_sem);
+                       return -EWOULDBLOCK;
+               }
+               up(&cam->dev_sem);
+               err = wait_event_interruptible(cam->open, cam->disconnected ||
+                                              (cam->users == 0));
+               if (err)
+                       return err;
+               if (cam->disconnected)
+                       return -ENODEV;
+               down(&cam->dev_sem);
+       }
+
+       DBG(5, "Opening the %s, /dev/video%d ...",
+           symbolic(camlist, cam->id), cam->v4ldev.minor)
+
+       cam->streaming = 0;
+       cam->misconfigured = 0;
+
+       if (!w9968cf_vppmod_present)
+               w9968cf_vppmod_detect();
+
+       if ((err = w9968cf_allocate_memory(cam)))
+               goto deallocate_memory;
+
+       if ((err = w9968cf_init_chip(cam)))
+               goto deallocate_memory;
+
+       if ((err = w9968cf_start_transfer(cam)))
+               goto deallocate_memory;
+
+       filp->private_data = (void*)cam;
+
+       cam->users++;
+       strcpy(cam->command, current->comm);
+
+       init_waitqueue_head(&cam->wait_queue);
+
+       up(&cam->dev_sem);
+
+       DBG(5, "Video device is open.")
+       return 0;
+
+deallocate_memory:
+       w9968cf_deallocate_memory(cam);
+       DBG(2, "Failed to open the video device.")
+       up(&cam->dev_sem);
+       return err;
+}
+
+
+static int w9968cf_release(struct inode* inode, struct file* filp)
+{
+       struct w9968cf_device* cam = 
+         (struct w9968cf_device*)video_devdata(filp)->priv;
+
+       down(&cam->dev_sem); /* prevent disconnect() to be called */
+
+       w9968cf_stop_transfer(cam);
+
+       if (cam->disconnected) {
+               w9968cf_release_resources(cam);
+               up(&cam->dev_sem);
+               kfree(cam);
+               return 0;
+       }
+
+       cam->users--;
+       w9968cf_deallocate_memory(cam);
+
+       if (waitqueue_active(&cam->open))
+               wake_up_interruptible(&cam->open);
+
+       DBG(5, "Video device closed.")
+       up(&cam->dev_sem);
+       return 0;
+}
+
+
+static ssize_t
+w9968cf_read(struct file* filp, char* buf, size_t count, loff_t* f_pos)
+{
+       struct w9968cf_device* cam =
+         (struct w9968cf_device*)video_devdata(filp)->priv;
+       struct w9968cf_frame_t* fr;
+       int err = 0;
+
+       if (filp->f_flags & O_NONBLOCK)
+               return -EWOULDBLOCK;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return -ERESTARTSYS;
+
+       if (cam->disconnected) {
+               DBG(2, "Device not present.")
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       if (cam->misconfigured) {
+               DBG(2, "The camera is misconfigured. Close and open it again.")
+               up(&cam->fileop_sem);
+               return -EIO;
+       }
+
+       if (!cam->frame[0].queued)
+               w9968cf_push_frame(cam, 0);
+
+       if (!cam->frame[1].queued)
+               w9968cf_push_frame(cam, 1);
+
+       err = wait_event_interruptible(cam->wait_queue,
+                                      cam->frame[0].status == F_READY ||
+                                      cam->frame[1].status == F_READY ||
+                                      cam->disconnected);
+       if (err) {
+               up(&cam->fileop_sem);
+               return err;
+       }
+       if (cam->disconnected) {
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       fr = (cam->frame[0].status == F_READY) ? &cam->frame[0]:&cam->frame[1];
+
+       if (w9968cf_vppmod_present)
+               w9968cf_postprocess_frame(cam, fr);
+
+       if (count > fr->length)
+               count = fr->length;
+
+       if (copy_to_user(buf, fr->buffer, count)) {
+               fr->status = F_UNUSED;
+               up(&cam->fileop_sem);
+               return -EFAULT;
+       }
+       *f_pos += count;
+
+       fr->status = F_UNUSED;
+
+       DBG(5, "%d bytes read.", count)
+
+       up(&cam->fileop_sem);
+       return count;
+}
+
+
+static int w9968cf_mmap(struct file* filp, struct vm_area_struct *vma)
+{
+       struct w9968cf_device* cam =
+         (struct w9968cf_device*)video_devdata(filp)->priv;
+
+       unsigned long vsize = vma->vm_end - vma->vm_start,
+                     psize = cam->nbuffers * w9968cf_get_max_bufsize(cam),
+                     start = vma->vm_start,
+                     pos = (unsigned long)cam->frame[0].buffer,
+                     page;
+
+       if (cam->disconnected) {
+               DBG(2, "Device not present.")
+               return -ENODEV;
+       }
+
+       if (cam->misconfigured) {
+               DBG(2, "The camera is misconfigured. Close and open it again.")
+               return -EIO;
+       }
+
+       PDBGG("mmapping %li bytes...", vsize)
+
+        if (vsize > psize - (vma->vm_pgoff << PAGE_SHIFT))
+               return -EAGAIN;
+
+       while (vsize > 0) {
+               page = kvirt_to_pa(pos) + vma->vm_pgoff;
+               if (remap_page_range(vma, start, page, PAGE_SIZE, 
+                                    vma->vm_page_prot))
+                       return -EAGAIN;
+               start += PAGE_SIZE;
+               pos += PAGE_SIZE;
+               vsize = (vsize > PAGE_SIZE) ? vsize-PAGE_SIZE : 0;
+       }
+
+       DBG(5, "mmap method successfully called.")
+       return 0;
+}
+
+
+static int
+w9968cf_ioctl(struct inode* inode, struct file* filp,
+              unsigned int cmd, unsigned long arg)
+{
+       struct w9968cf_device* cam =
+         (struct w9968cf_device*)video_devdata(filp)->priv;
+       int err;
+
+       if (down_interruptible(&cam->fileop_sem))
+               return -ERESTARTSYS;
+
+       if (cam->disconnected) {
+               DBG(2, "Device not present.")
+               up(&cam->fileop_sem);
+               return -ENODEV;
+       }
+
+       if (cam->misconfigured) {
+               DBG(2, "The camera is misconfigured. Close and open it again.")
+               up(&cam->fileop_sem);
+               return -EIO;
+       }
+
+       err = w9968cf_do_ioctl(cam, cmd, (void*)arg);
+
+       up(&cam->fileop_sem);
+       return err;
+}
+
+
+static int 
+w9968cf_do_ioctl(struct w9968cf_device* cam, unsigned cmd, void* arg)
+{
+       const char* v4l1_ioctls[] = {
+               "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", 
+               "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF",
+               "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO",
+               "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE",
+               "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", 
+               "GVBIFMT", "SVBIFMT" 
+       };
+
+       #define V4L1_IOCTL(cmd) \
+               ((_IOC_NR((cmd)) < sizeof(v4l1_ioctls)/sizeof(char*)) ? \
+               v4l1_ioctls[_IOC_NR((cmd))] : "???")
+
+       switch (cmd) {
+
+       case VIDIOCGCAP: /* get video capability */
+       {
+               struct video_capability cap = {
+                       .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES,
+                       .channels = 1,
+                       .audios = 0,
+                       .minwidth = cam->minwidth,
+                       .minheight = cam->minheight,
+               };
+               sprintf(cap.name, "W996[87]CF USB Camera #%d", 
+                       cam->v4ldev.minor);
+               cap.maxwidth = (cam->upscaling && w9968cf_vppmod_present)
+                              ? W9968CF_MAX_WIDTH : cam->maxwidth;
+               cap.maxheight = (cam->upscaling && w9968cf_vppmod_present)
+                               ? W9968CF_MAX_HEIGHT : cam->maxheight;
+
+               if (copy_to_user(arg, &cap, sizeof(cap)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGCAP successfully called.")
+               return 0;
+       }
+
+       case VIDIOCGCHAN: /* get video channel informations */
+       {
+               struct video_channel chan;
+               if (copy_from_user(&chan, arg, sizeof(chan)))
+                       return -EFAULT;
+
+               if (chan.channel != 0)
+                       return -EINVAL;
+
+               strcpy(chan.name, "Camera");
+               chan.tuners = 0;
+               chan.flags = 0;
+               chan.type = VIDEO_TYPE_CAMERA;
+               chan.norm = VIDEO_MODE_AUTO;
+
+               if (copy_to_user(arg, &chan, sizeof(chan)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGCHAN successfully called.")
+               return 0;
+       }
+
+       case VIDIOCSCHAN: /* set active channel */
+       {
+               struct video_channel chan;
+
+               if (copy_from_user(&chan, arg, sizeof(chan)))
+                       return -EFAULT;
+
+               if (chan.channel != 0)
+                       return -EINVAL;
+
+               DBG(5, "VIDIOCSCHAN successfully called.")
+               return 0;
+       }
+
+       case VIDIOCGPICT: /* get image properties of the picture */
+       {
+               struct video_picture pict;
+
+               if (w9968cf_sensor_get_picture(cam, &pict))
+                       return -EIO;
+
+               pict.depth = cam->picture.depth;
+               pict.palette = cam->picture.palette;
+
+               if (copy_to_user(arg, &pict, sizeof(pict)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGPICT successfully called.")
+               return 0;
+       }
+
+       case VIDIOCSPICT: /* change picture settings */
+       {
+               struct video_picture pict;
+               int err = 0;
+
+               if (copy_from_user(&pict, arg, sizeof(pict)))
+                       return -EFAULT;
+
+               if ( (cam->force_palette || !w9968cf_vppmod_present) 
+                    && pict.palette != cam->picture.palette ) {
+                       DBG(4, "Palette %s rejected. Only %s is allowed.",
+                           symbolic(v4l1_plist, pict.palette),
+                           symbolic(v4l1_plist, cam->picture.palette))
+                       return -EINVAL;
+               }
+
+               if (!w9968cf_valid_palette(pict.palette)) {
+                       DBG(4, "Palette %s not supported. VIDIOCSPICT failed.",
+                           symbolic(v4l1_plist, pict.palette))
+                       return -EINVAL;
+               }
+
+               if (pict.depth != w9968cf_valid_depth(pict.palette)) {
+                       DBG(4, "Depth %d bpp is not supported for %s palette. "
+                              "VIDIOCSPICT failed.", 
+                           pict.depth, symbolic(v4l1_plist, pict.palette))
+                       return -EINVAL;
+               }
+
+               if (!cam->force_palette) {
+                  if (cam->decompression == 0) {
+                     if (w9968cf_need_decompression(pict.palette)) {
+                        DBG(4, "Decompression disabled: palette %s is not "
+                               "allowed. VIDIOCSPICT failed.",
+                            symbolic(v4l1_plist, pict.palette))
+                        return -EINVAL;
+                     }
+                  } else if (cam->decompression == 1) {
+                     if (!w9968cf_need_decompression(pict.palette)) {
+                        DBG(4, "Decompression forced: palette %s is not "
+                               "allowed. VIDIOCSPICT failed.",
+                            symbolic(v4l1_plist, pict.palette))
+                        return -EINVAL;
+                     }
+                  }
+               }
+
+               if (pict.palette != cam->picture.palette ||
+                   pict.depth   != cam->picture.depth)
+               {
+                       if(*cam->requested_frame
+                          || cam->frame_current->queued) {
+                               err = wait_event_interruptible
+                                     ( cam->wait_queue,
+                                       cam->disconnected ||
+                                       (!*cam->requested_frame &&
+                                        !cam->frame_current->queued) );
+                               if (err)
+                                       return err;
+                               if (cam->disconnected)
+                                       return -ENODEV;
+                       }
+
+                       if (w9968cf_stop_transfer(cam))
+                               goto ioctl_fail;
+
+                       if (w9968cf_set_picture(cam, pict))
+                               goto ioctl_fail;
+
+                       if (w9968cf_start_transfer(cam))
+                               goto ioctl_fail;
+
+               } else if ( ((pict.brightness != cam->picture.brightness) &&
+                           (!cam->auto_brt)) ||
+                           pict.hue != cam->picture.hue ||
+                           pict.colour != cam->picture.colour ||
+                           pict.contrast != cam->picture.contrast ||
+                           pict.whiteness != cam->picture.whiteness ) {
+                       if (w9968cf_sensor_set_picture(cam, pict))
+                               return -EIO;
+               }
+
+               DBG(5, "VIDIOCSPICT successfully called.")
+               return 0;
+       }
+
+       case VIDIOCSWIN: /* set capture area */
+       {
+               struct video_window win;
+               int err = 0;
+
+               if (copy_from_user(&win, arg, sizeof(win)))
+                       return -EFAULT;
+
+               DBG(6, "VIDIOCSWIN called: clipcount=%d, flags=%d, "
+                      "x=%d, y=%d, %dx%d", win.clipcount, win.flags,
+                   win.x, win.y, win.width, win.height)
+
+               if (win.clipcount != 0 || win.flags != 0)
+                       return -EINVAL;
+
+               if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width,
+                                                     (u16*)&win.height))) {
+                       DBG(4, "Resolution not supported (%dx%d)."
+                              "VIDIOCSWIN failed.", win.width, win.height)
+                       return err;
+               }
+
+               if (win.x != cam->window.x ||
+                   win.y != cam->window.y ||
+                   win.width != cam->window.width ||
+                   win.height != cam->window.height) {
+
+                       if(*cam->requested_frame
+                          || cam->frame_current->queued) {
+                               err = wait_event_interruptible
+                                     ( cam->wait_queue,
+                                       cam->disconnected ||
+                                       (!*cam->requested_frame &&
+                                        !cam->frame_current->queued) );
+                               if (err)
+                                       return err;
+                               if (cam->disconnected)
+                                       return -ENODEV;
+                       }
+
+                       if (w9968cf_stop_transfer(cam))
+                               goto ioctl_fail;
+
+                       /* This _must_ be called before set_window() */
+                       if (w9968cf_set_picture(cam, cam->picture))
+                               goto ioctl_fail;
+
+                       if (w9968cf_set_window(cam, win))
+                               goto ioctl_fail;
+
+                       if (w9968cf_start_transfer(cam))
+                               goto ioctl_fail;
+               }
+
+               DBG(5, "VIDIOCSWIN successfully called. ")
+               return 0;
+       }
+
+       case VIDIOCGWIN: /* get current window properties */
+       {
+               if (copy_to_user(arg,&cam->window,sizeof(struct video_window)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGWIN successfully called.")
+               return 0;
+       }
+
+       case VIDIOCGMBUF: /* request for memory (mapped) buffer */
+       {
+               struct video_mbuf mbuf;
+               u8 i;
+
+               mbuf.size = cam->nbuffers * w9968cf_get_max_bufsize(cam);
+               mbuf.frames = cam->nbuffers;
+               for (i = 0; i < cam->nbuffers; i++)
+                       mbuf.offsets[i] = (unsigned long)cam->frame[i].buffer -
+                                         (unsigned long)cam->frame[0].buffer;
+
+               if (copy_to_user(arg, &mbuf, sizeof(mbuf)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGMBUF successfully called.")
+               return 0;
+       }
+
+       case VIDIOCMCAPTURE: /* start the capture to a frame */
+       {
+               struct video_mmap mmap;
+               struct w9968cf_frame_t* fr;
+               int err = 0;
+
+               if (copy_from_user(&mmap, arg, sizeof(mmap)))
+                       return -EFAULT;
+
+               DBG(6, "VIDIOCMCAPTURE called: frame #%d, format=%s, %dx%d",
+                   mmap.frame, symbolic(v4l1_plist, mmap.format), 
+                   mmap.width, mmap.height)
+
+               if (mmap.frame >= cam->nbuffers) {
+                       DBG(4, "Invalid frame number (%d). "
+                              "VIDIOCMCAPTURE failed.", mmap.frame)
+                       return -EINVAL;
+               }
+
+               if (mmap.format!=cam->picture.palette && 
+                   (cam->force_palette || !w9968cf_vppmod_present)) {
+                       DBG(4, "Palette %s rejected. Only %s is allowed.",
+                           symbolic(v4l1_plist, mmap.format),
+                           symbolic(v4l1_plist, cam->picture.palette))
+                       return -EINVAL;
+               }
+
+               if (!w9968cf_valid_palette(mmap.format)) {
+                       DBG(4, "Palette %s not supported. "
+                              "VIDIOCMCAPTURE failed.", 
+                           symbolic(v4l1_plist, mmap.format))
+                       return -EINVAL;
+               }
+
+               if (!cam->force_palette) {
+                  if (cam->decompression == 0) {
+                     if (w9968cf_need_decompression(mmap.format)) {
+                        DBG(4, "Decompression disabled: palette %s is not "
+                               "allowed. VIDIOCSPICT failed.",
+                            symbolic(v4l1_plist, mmap.format))
+                        return -EINVAL;
+                     }
+                  } else if (cam->decompression == 1) {
+                     if (!w9968cf_need_decompression(mmap.format)) {
+                        DBG(4, "Decompression forced: palette %s is not "
+                               "allowed. VIDIOCSPICT failed.",
+                            symbolic(v4l1_plist, mmap.format))
+                        return -EINVAL;
+                     }
+                  }
+               }
+
+               if (w9968cf_adjust_window_size(cam, (u16*)&mmap.width, 
+                                              (u16*)&mmap.height)) {
+                       DBG(4, "Resolution not supported (%dx%d). "
+                              "VIDIOCMCAPTURE failed.",
+                           mmap.width, mmap.height)
+                       return -EINVAL;
+               }
+
+               fr = &cam->frame[mmap.frame];
+
+               if (mmap.width  != cam->window.width ||
+                   mmap.height != cam->window.height ||
+                   mmap.format != cam->picture.palette) {
+
+                       struct video_window win;
+                       struct video_picture pict;
+
+                       if(*cam->requested_frame
+                          || cam->frame_current->queued) {
+                               DBG(6, "VIDIOCMCAPTURE. Change settings for "
+                                      "frame #%d: %dx%d, format %s. Wait...",
+                                   mmap.frame, mmap.width, mmap.height,
+                                   symbolic(v4l1_plist, mmap.format))
+                               err = wait_event_interruptible
+                                     ( cam->wait_queue,
+                                       cam->disconnected ||
+                                       (!*cam->requested_frame &&
+                                        !cam->frame_current->queued) );
+                               if (err)
+                                       return err;
+                               if (cam->disconnected)
+                                       return -ENODEV;
+                       }
+
+                       memcpy(&win, &cam->window, sizeof(win));
+                       memcpy(&pict, &cam->picture, sizeof(pict));
+                       win.width = mmap.width;
+                       win.height = mmap.height;
+                       pict.palette = mmap.format;
+
+                       if (w9968cf_stop_transfer(cam))
+                               goto ioctl_fail;
+
+                       /* This before set_window */
+                       if (w9968cf_set_picture(cam, pict)) 
+                               goto ioctl_fail;
+
+                       if (w9968cf_set_window(cam, win))
+                               goto ioctl_fail;
+
+                       if (w9968cf_start_transfer(cam))
+                               goto ioctl_fail;
+
+               } else  if (fr->queued) {
+
+                       DBG(6, "Wait until frame #%d is free.", mmap.frame)
+                       
+                       err = wait_event_interruptible(cam->wait_queue, 
+                                                      cam->disconnected ||
+                                                      (!fr->queued));
+                       if (err)
+                               return err;
+                       if (cam->disconnected)
+                               return -ENODEV;
+               }
+
+               w9968cf_push_frame(cam, mmap.frame);
+               DBG(5, "VIDIOCMCAPTURE(%d): successfully called.", mmap.frame)
+               return 0;
+       }
+
+       case VIDIOCSYNC: /* wait until the capture of a frame is finished */
+       {
+               unsigned int f_num = *((unsigned int *) arg);
+               struct w9968cf_frame_t* fr;
+               int err = 0;
+
+               if (f_num >= cam->nbuffers) {
+                       DBG(4, "Invalid frame number (%d). "
+                              "VIDIOCMCAPTURE failed.", f_num)
+                       return -EINVAL;
+               }
+
+               DBG(6, "VIDIOCSYNC called for frame #%d", f_num)
+
+               fr = &cam->frame[f_num];
+
+               switch (fr->status) {
+               case F_UNUSED:
+                       if (!fr->queued) {
+                               DBG(4, "VIDIOSYNC: Frame #%d not requested!",
+                                   f_num)
+                               return -EFAULT;
+                       }
+               case F_ERROR:
+               case F_GRABBING:
+                       err = wait_event_interruptible(cam->wait_queue, 
+                                                      (fr->status == F_READY)
+                                                      || cam->disconnected);
+                       if (err)
+                               return err;
+                       if (cam->disconnected)
+                               return -ENODEV;
+                       break;
+               case F_READY:
+                       break;
+               }
+
+               if (w9968cf_vppmod_present)
+                       w9968cf_postprocess_frame(cam, fr);
+
+               fr->status = F_UNUSED;
+
+               DBG(5, "VIDIOCSYNC(%d) successfully called.", f_num)
+               return 0;
+       }
+
+       case VIDIOCGUNIT:/* report the unit numbers of the associated devices*/
+       {
+               struct video_unit unit = {
+                       .video = cam->v4ldev.minor,
+                       .vbi = VIDEO_NO_UNIT,
+                       .radio = VIDEO_NO_UNIT,
+                       .audio = VIDEO_NO_UNIT,
+                       .teletext = VIDEO_NO_UNIT,
+               };
+
+               if (copy_to_user(arg, &unit, sizeof(unit)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGUNIT successfully called.")
+               return 0;
+       }
+
+       case VIDIOCKEY:
+               return 0;
+
+       case VIDIOCGFBUF:
+       {
+               struct video_buffer* buffer = (struct video_buffer*)arg;
+
+               memset(buffer, 0, sizeof(struct video_buffer));
+
+               DBG(5, "VIDIOCGFBUF successfully called.")
+               return 0;
+       }
+
+       case VIDIOCGTUNER:
+       {
+               struct video_tuner tuner;
+               if (copy_from_user(&tuner, arg, sizeof(tuner)))
+                       return -EFAULT;
+
+               if (tuner.tuner != 0);
+                       return -EINVAL;
+
+               strcpy(tuner.name, "no_tuner");
+               tuner.rangelow = 0;
+               tuner.rangehigh = 0;
+               tuner.flags = VIDEO_TUNER_NORM;
+               tuner.mode = VIDEO_MODE_AUTO;
+               tuner.signal = 0xffff;
+
+               if (copy_to_user(arg, &tuner, sizeof(tuner)))
+                       return -EFAULT;
+
+               DBG(5, "VIDIOCGTUNER successfully called.")
+               return 0;
+       }
+
+       case VIDIOCSTUNER:
+       {
+               struct video_tuner tuner;
+               if (copy_from_user(&tuner, arg, sizeof(tuner)))
+                       return -EFAULT;
+
+               if (tuner.tuner != 0)
+                       return -EINVAL;
+
+               if (tuner.mode != VIDEO_MODE_AUTO)
+                       return -EINVAL;
+
+               DBG(5, "VIDIOCSTUNER successfully called.")
+               return 0;
+       }
+
+       case VIDIOCSFBUF:
+       case VIDIOCCAPTURE:
+       case VIDIOCGFREQ:
+       case VIDIOCSFREQ:
+       case VIDIOCGAUDIO:
+       case VIDIOCSAUDIO:
+       case VIDIOCSPLAYMODE:
+       case VIDIOCSWRITEMODE:
+       case VIDIOCGPLAYINFO:
+       case VIDIOCSMICROCODE:
+       case VIDIOCGVBIFMT:
+       case VIDIOCSVBIFMT:
+               DBG(4, "Unsupported V4L1 IOCtl: VIDIOC%s "
+                      "(type 0x%01X, "
+                      "n. 0x%01X, "
+                      "dir. 0x%01X, " 
+                      "size 0x%02X).",
+                   V4L1_IOCTL(cmd),
+                   _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+               return -EINVAL;
+
+       default:
+               DBG(4, "Invalid V4L1 IOCtl: VIDIOC%s "
+                      "type 0x%01X, "
+                      "n. 0x%01X, "
+                      "dir. 0x%01X, "
+                      "size 0x%02X.",
+                   V4L1_IOCTL(cmd),
+                   _IOC_TYPE(cmd),_IOC_NR(cmd),_IOC_DIR(cmd),_IOC_SIZE(cmd))
+
+               return -ENOIOCTLCMD;
+
+       } /* end of switch */
+
+ioctl_fail:
+       cam->misconfigured = 1;
+       DBG(1, "VIDIOC%s failed because of hardware problems. "
+              "To use the camera, close and open it again.", V4L1_IOCTL(cmd))
+       return -EFAULT;
+}
+
+
+static struct file_operations w9968cf_fops = {
+       .owner =   THIS_MODULE,
+       .open =    w9968cf_open,
+       .release = w9968cf_release,
+       .read =    w9968cf_read,
+       .ioctl =   w9968cf_ioctl,
+       .mmap =    w9968cf_mmap,
+       .llseek =  no_llseek,
+};
+
+
+
+/****************************************************************************
+ * USB probe and V4L registration, disconnect and id_table[] definition     *
+ ****************************************************************************/
+
+static int
+w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+{
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct w9968cf_device* cam;
+       int err = 0;
+       enum w9968cf_model_id mod_id;
+       struct list_head* ptr;
+       u8 sc = 0; /* number of simultaneous cameras */
+       static unsigned short dev_nr = 0; /* we are handling device number n */
+
+       if (udev->descriptor.idVendor  == winbond_id_table[0].idVendor &&
+           udev->descriptor.idProduct == winbond_id_table[0].idProduct)
+               mod_id = W9968CF_MOD_CLVBWGP; /* see camlist[] table */
+
+       else if (udev->descriptor.idVendor  == winbond_id_table[1].idVendor &&
+                udev->descriptor.idProduct == winbond_id_table[1].idProduct)
+               mod_id = W9968CF_MOD_GENERIC; /* see camlist[] table */
+
+       else
+               return -ENODEV;
+
+       /* We don't handle multi-config cameras */
+       if (udev->descriptor.bNumConfigurations != 1)
+               return -ENODEV;
+
+       DBG(2, "%s detected.", symbolic(camlist, mod_id))
+
+       if (simcams > W9968CF_MAX_DEVICES)
+               simcams = W9968CF_SIMCAMS;
+
+       /* How many cameras are connected ? */
+       down(&w9968cf_devlist_sem);
+       list_for_each(ptr, &w9968cf_dev_list)
+               sc++;
+       up(&w9968cf_devlist_sem);
+
+       if (sc >= simcams) {
+               DBG(2, "Device rejected: too many connected cameras "
+                      "(max. %d)", simcams)
+               return -EPERM;
+       }
+
+       err = usb_set_configuration(udev, 1);
+       err += usb_set_interface(udev, 0, 0);
+
+       if (err) {
+               DBG(1, "Device configuration failed.")
+               return -EIO;
+       }
+
+       cam = (struct w9968cf_device*)
+                 kmalloc(sizeof(struct w9968cf_device), GFP_KERNEL);
+
+       if (!cam) {
+               DBG(1, "Couldn't allocate %d bytes of kernel memory.",
+                   sizeof(struct w9968cf_device))
+               err = -ENOMEM;
+               goto fail;
+       }
+       memset(cam, 0, sizeof(*cam));
+
+       init_MUTEX(&cam->dev_sem);
+       down(&cam->dev_sem);
+
+       /* Allocate 2 bytes of memory for camera control USB transfers */
+       if (!(cam->control_buffer = (u16*)kmalloc(2, GFP_KERNEL))) {
+               DBG(1,"Couldn't allocate memory for camera control transfers.")
+               err = -ENOMEM;
+               goto fail;
+       }
+       memset(cam->control_buffer, 0, 2);
+
+       /* Allocate 8 bytes of memory for USB data transfers to the FSB */
+       if (!(cam->data_buffer = (u16*)kmalloc(8, GFP_KERNEL))) {
+               DBG(1, "Couldn't allocate memory for data "
+                      "transfers to the FSB.")
+               err = -ENOMEM;
+               goto fail;
+       }
+       memset(cam->data_buffer, 0, 8);
+
+       /* Set some basic constants */
+       w9968cf_configure_camera(cam, udev, mod_id, dev_nr);
+
+       err = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER,
+                                   video_nr[dev_nr]);
+       if (err) {
+               DBG(1, "V4L device registration failed.")
+               if (err == -ENFILE && video_nr[dev_nr] == -1)
+                       DBG(2, "Couldn't find a free /dev/videoX node.")
+               video_nr[dev_nr] = -1;
+               dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+               goto fail;
+       }
+
+       DBG(2, "V4L device registered as /dev/video%d", cam->v4ldev.minor)
+
+       /* Ok, add a new entry into the list of V4L registered devices */
+       down(&w9968cf_devlist_sem);
+       list_add(&cam->v4llist, &w9968cf_dev_list);
+       up(&w9968cf_devlist_sem);
+
+       dev_nr = (dev_nr < W9968CF_MAX_DEVICES-1) ? dev_nr+1 : 0;
+
+       w9968cf_turn_on_led(cam);
+
+       w9968cf_i2c_init(cam);
+
+       up(&cam->dev_sem);
+
+       dev_set_drvdata(&intf->dev, (void*)cam);
+
+       return 0;
+
+fail: /* Free unused memory */
+       if (cam) {
+               if (cam->control_buffer)
+                       kfree(cam->control_buffer);
+               if (cam->data_buffer)
+                       kfree(cam->data_buffer);
+               up(&cam->dev_sem);
+               kfree(cam);
+       }
+       return err;
+}
+
+
+static void w9968cf_usb_disconnect(struct usb_interface* intf)
+{
+       struct w9968cf_device* cam = 
+          (struct w9968cf_device*)dev_get_drvdata(&intf->dev);
+
+       dev_set_drvdata(&intf->dev, NULL);
+
+       if (cam) {
+               /* Prevent concurrent accesses to data */
+               down(&cam->dev_sem); 
+
+               cam->streaming = 0;
+               cam->disconnected = 1;
+
+               DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
+
+               if (waitqueue_active(&cam->open))
+                       wake_up_interruptible(&cam->open);
+
+               if (cam->users) {
+                       DBG(2, "The device is open (/dev/video%d)! "
+                              "Process name: %s. Deregistration and memory "
+                              "deallocation are deferred on close.",
+                           cam->v4ldev.minor, cam->command)
+
+                       cam->misconfigured = 1;
+
+                       if (waitqueue_active(&cam->wait_queue))
+                               wake_up_interruptible(&cam->wait_queue);
+               } else
+                       w9968cf_release_resources(cam);
+
+               up(&cam->dev_sem);
+
+               if (!cam->users)
+                       kfree(cam);
+       }
+}
+
+
+static struct usb_driver w9968cf_usb_driver = {
+       .owner =      THIS_MODULE,
+       .name =       "w9968cf",
+       .id_table =   winbond_id_table,
+       .probe =      w9968cf_usb_probe,
+       .disconnect = w9968cf_usb_disconnect,
+};
+
+
+
+/****************************************************************************
+ * Module init, exit and intermodule communication                          *
+ ****************************************************************************/
+
+static int w9968cf_vppmod_detect(void)
+{
+       w9968cf_vpp_init_decoder = inter_module_get("w9968cf_init_decoder");
+
+       if (!w9968cf_vpp_init_decoder) {
+               if (vppmod_load)
+                       w9968cf_vpp_init_decoder = inter_module_get_request
+                                                 ( "w9968cf_init_decoder",
+                                                   "w9968cf-vpp" );
+               if (!w9968cf_vpp_init_decoder) {
+                       w9968cf_vppmod_present = 0;
+                       DBG(4, "Video post-processing module not detected.")
+                       return -ENODEV;
+               }
+       }
+
+       w9968cf_vpp_check_headers = inter_module_get("w9968cf_check_headers");
+       w9968cf_vpp_decode = inter_module_get("w9968cf_decode");
+       w9968cf_vpp_swap_yuvbytes = inter_module_get("w9968cf_swap_yuvbytes");
+       w9968cf_vpp_uyvy_to_rgbx = inter_module_get("w9968cf_uyvy_to_rgbx");
+       w9968cf_vpp_scale_up = inter_module_get("w9968cf_scale_up");
+
+       w9968cf_vppmod_present = 1;
+
+       /* Initialization */
+       (*w9968cf_vpp_init_decoder)();
+
+       DBG(2, "Video post-processing module detected.")
+       return 0;
+}
+
+
+static void w9968cf_vppmod_release(void)
+{
+       inter_module_put("w9968cf_init_decoder");
+       inter_module_put("w9968cf_check_headers");
+       inter_module_put("w9968cf_decode");
+       inter_module_put("w9968cf_swap_yuvbytes");
+       inter_module_put("w9968cf_uyvy_to_rgbx");
+       inter_module_put("w9968cf_scale_up");
+
+       DBG(2, "Video post-processing module released.")
+}
+
+
+static int __init w9968cf_module_init(void)
+{
+       int err;
+
+       DBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+       DBG(3, W9968CF_MODULE_AUTHOR)
+
+       init_MUTEX(&w9968cf_devlist_sem);
+
+       w9968cf_vppmod_detect();
+
+       if ((err = usb_register(&w9968cf_usb_driver))) {
+               if (w9968cf_vppmod_present)
+                       w9968cf_vppmod_release();
+               return err;
+       }
+
+       return 0;
+}
+
+
+static void __exit w9968cf_module_exit(void)
+{
+       /* w9968cf_usb_disconnect() will be called */
+       usb_deregister(&w9968cf_usb_driver);
+
+       if (w9968cf_vppmod_present)
+               w9968cf_vppmod_release();
+
+       DBG(2, W9968CF_MODULE_NAME" deregistered.")
+}
+
+
+module_init(w9968cf_module_init);
+module_exit(w9968cf_module_exit);
diff --git a/drivers/usb/media/w9968cf.h b/drivers/usb/media/w9968cf.h
new file mode 100644 (file)
index 0000000..9b9d33d
--- /dev/null
@@ -0,0 +1,312 @@
+/***************************************************************************
+ * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip.       *
+ *                                                                         *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>            *
+ *                                                                         *
+ * This program is free software; you can redistribute it and/or modify    *
+ * it under the terms of the GNU General Public License as published by    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _W9968CF_H_
+#define _W9968CF_H_
+
+#include <linux/videodev.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/config.h>
+#include <asm/semaphore.h>
+#include <asm/types.h>
+
+#include "w9968cf_externaldef.h"
+
+
+/****************************************************************************
+ * Default values                                                           *
+ ****************************************************************************/
+
+#define W9968CF_VPPMOD_LOAD     1  /* automatic 'w9968cf-vpp' module loading */
+
+/* Comment/uncomment the following line to enable/disable debugging messages */
+#define W9968CF_DEBUG
+
+/* These have effect only if W9968CF_DEBUG is defined */
+#define W9968CF_DEBUG_LEVEL    2 /* from 0 to 6. 0 for no debug informations */
+#define W9968CF_SPECIFIC_DEBUG 0 /* 0 or 1 */
+
+#define W9968CF_MAX_DEVICES    32
+#define W9968CF_SIMCAMS        W9968CF_MAX_DEVICES /* simultaneous cameras */
+
+#define W9968CF_MAX_BUFFERS   32
+#define W9968CF_BUFFERS       2 /* n. of frame buffers from 2 to MAX_BUFFERS */
+
+/* Maximum data payload sizes in bytes for alternate settings */
+static const u16 wMaxPacketSize[] = {1023, 959, 895, 831, 767, 703, 639, 575,
+                                      511, 447, 383, 319, 255, 191, 127,  63};
+#define W9968CF_PACKET_SIZE      1023 /* according to wMaxPacketSizes[] */
+#define W9968CF_MIN_PACKET_SIZE  63 /* minimum value */
+#define W9968CF_ISO_PACKETS      5 /* n.of packets for isochronous transfers */
+#define W9968CF_USB_CTRL_TIMEOUT HZ /* timeout for usb control commands */
+#define W9968CF_URBS             2 /* n. of scheduled URBs for ISO transfer */
+
+#define W9968CF_I2C_BUS_DELAY    4 /* delay in us for I2C bit r/w operations */
+#define W9968CF_I2C_RW_RETRIES   15 /* number of max I2C r/w retries */
+
+/* Available video formats */
+struct w9968cf_format {
+       const u16 palette;
+       const u16 depth;
+       const u8 compression;
+};
+
+static const struct w9968cf_format w9968cf_formatlist[] = {
+       { VIDEO_PALETTE_UYVY,    16, 0 }, /* original video */
+       { VIDEO_PALETTE_YUV422P, 16, 1 }, /* with JPEG compression */
+       { VIDEO_PALETTE_YUV420P, 12, 1 }, /* with JPEG compression */
+       { VIDEO_PALETTE_YUV420,  12, 1 }, /* same as YUV420P */
+       { VIDEO_PALETTE_YUYV,    16, 0 }, /* software conversion */
+       { VIDEO_PALETTE_YUV422,  16, 0 }, /* software conversion */
+       { VIDEO_PALETTE_GREY,     8, 0 }, /* software conversion */
+       { VIDEO_PALETTE_RGB555,  16, 0 }, /* software conversion */
+       { VIDEO_PALETTE_RGB565,  16, 0 }, /* software conversion */
+       { VIDEO_PALETTE_RGB24,   24, 0 }, /* software conversion */
+       { VIDEO_PALETTE_RGB32,   32, 0 }, /* software conversion */
+       { 0,                      0, 0 }  /* 0 is a terminating entry */
+};
+
+#define W9968CF_DECOMPRESSION    2 /* decomp:0=disable,1=force,2=any formats */
+#define W9968CF_PALETTE_DECOMP_OFF   VIDEO_PALETTE_UYVY    /* when decomp=0 */
+#define W9968CF_PALETTE_DECOMP_FORCE VIDEO_PALETTE_YUV420P /* when decomp=1 */
+#define W9968CF_PALETTE_DECOMP_ON    VIDEO_PALETTE_UYVY    /* when decomp=2 */
+
+#define W9968CF_FORCE_RGB        0  /* read RGB instead of BGR, yes=1/no=0 */
+
+#define W9968CF_MAX_WIDTH      800 /* should be >= 640 */
+#define W9968CF_MAX_HEIGHT     600 /* should be >= 480 */
+#define W9968CF_WIDTH          320 /* from 128 to 352, multiple of 16 */
+#define W9968CF_HEIGHT         240 /* from  96 to 288, multiple of 16 */
+
+#define W9968CF_CLAMPING       0 /* 0 disable, 1 enable video data clamping */
+#define W9968CF_FILTER_TYPE    0 /* 0 disable  1 (1-2-1), 2 (2-3-6-3-2) */
+#define W9968CF_DOUBLE_BUFFER  1 /* 0 disable, 1 enable double buffer */
+#define W9968CF_LARGEVIEW      1 /* 0 disable, 1 enable */
+#define W9968CF_UPSCALING      0 /* 0 disable, 1 enable */
+
+#define W9968CF_SENSOR_MONO    0 /* 0 not monochrome, 1 monochrome sensor */
+#define W9968CF_BRIGHTNESS     31000 /* from 0 to 65535 */
+#define W9968CF_HUE            32768 /* from 0 to 65535 */
+#define W9968CF_COLOUR         32768 /* from 0 to 65535 */
+#define W9968CF_CONTRAST       50000 /* from 0 to 65535 */
+#define W9968CF_WHITENESS      32768 /* from 0 to 65535 */
+
+#define W9968CF_AUTOBRIGHT     0 /* 0 disable, 1 enable automatic brightness */
+#define W9968CF_AUTOEXP        1 /* 0 disable, 1 enable automatic exposure */
+#define W9968CF_LIGHTFREQ      50 /* light frequency. 50Hz (Europe) or 60Hz */
+#define W9968CF_BANDINGFILTER  0 /* 0 disable, 1 enable banding filter */
+#define W9968CF_BACKLIGHT      0 /* 0 or 1, 1=object is lit from behind */
+#define W9968CF_MIRROR         0 /* 0 or 1 [don't] reverse image horizontally*/
+
+#define W9968CF_CLOCKDIV         -1 /* -1 = automatic clock divisor */
+#define W9968CF_DEF_CLOCKDIVISOR  0 /* default sensor clock divisor value */
+
+
+/****************************************************************************
+ * Globals                                                                  *
+ ****************************************************************************/
+
+#define W9968CF_MODULE_NAME     "V4L driver for W996[87]CF JPEG USB " \
+                                "Dual Mode Camera Chip"
+#define W9968CF_MODULE_VERSION  "v1.22"
+#define W9968CF_MODULE_AUTHOR   "(C) 2002 2003 Luca Risolia"
+#define W9968CF_AUTHOR_EMAIL    "<luca_ing@libero.it>"
+
+static u8 w9968cf_vppmod_present; /* status flag: yes=1, no=0 */
+
+static const struct usb_device_id winbond_id_table[] = {
+       {
+               /* Creative Labs Video Blaster WebCam Go Plus */
+               USB_DEVICE(0x041e, 0x4003),
+               .driver_info = (unsigned long)"w9968cf",
+       },
+       {
+               /* Generic W996[87]CF JPEG USB Dual Mode Camera */
+               USB_DEVICE(0x1046, 0x9967),
+               .driver_info = (unsigned long)"w9968cf",
+       },
+       { } /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, winbond_id_table);
+
+/* W996[87]CF camera models, internal ids: */
+enum w9968cf_model_id {
+       W9968CF_MOD_GENERIC = 1, /* Generic W996[87]CF based device */
+       W9968CF_MOD_CLVBWGP = 11,/*Creative Labs Video Blaster WebCam Go Plus*/
+       W9968CF_MOD_ADPA5R = 21, /* Aroma Digi Pen ADG-5000 Refurbished */
+       W9986CF_MOD_AU = 31,     /* AVerTV USB */
+       W9968CF_MOD_CLVBWG = 34, /* Creative Labs Video Blaster WebCam Go */
+       W9968CF_MOD_DLLDK = 37,  /* Die Lebon LDC-D35A Digital Kamera */
+       W9968CF_MOD_EEEMC = 40,   /* Ezonics EZ-802 EZMega Cam */
+       W9968CF_MOD_ODPVDMPC = 43,/* OPCOM Digi Pen VGA Dual Mode Pen Camera */
+};
+
+enum w9968cf_frame_status {
+       F_READY,            /* finished grabbing & ready to be read/synced */
+       F_GRABBING,         /* in the process of being grabbed into */
+       F_ERROR,            /* something bad happened while processing */
+       F_UNUSED            /* unused (no VIDIOCMCAPTURE) */
+};
+
+struct w9968cf_frame_t {
+       void* buffer;
+       u32 length;
+       enum w9968cf_frame_status status;
+       struct w9968cf_frame_t* next;
+       u8 queued;
+};
+
+enum w9968cf_vpp_flag {
+       VPP_NONE = 0x00,
+       VPP_UPSCALE = 0x01,
+       VPP_SWAP_YUV_BYTES = 0x02,
+       VPP_DECOMPRESSION = 0x04,
+       VPP_UYVY_TO_RGBX = 0x08,
+};
+
+struct list_head w9968cf_dev_list; /* head of V4L registered cameras list */
+LIST_HEAD(w9968cf_dev_list);
+struct semaphore w9968cf_devlist_sem; /* semaphore for list traversal */
+
+/* Main device driver structure */
+struct w9968cf_device {
+       enum w9968cf_model_id id;   /* private device identifier */
+
+       struct video_device v4ldev; /* V4L structure */
+       struct list_head v4llist;   /* entry of the list of V4L cameras */
+
+       struct usb_device* usbdev;           /* -> main USB structure */
+       struct urb* urb[W9968CF_URBS];       /* -> USB request block structs */
+       void* transfer_buffer[W9968CF_URBS]; /* -> ISO transfer buffers */
+       u16* control_buffer;                 /* -> buffer for control req.*/
+       u16* data_buffer;                    /* -> data to send to the FSB */
+
+       struct w9968cf_frame_t frame[W9968CF_MAX_BUFFERS];
+       struct w9968cf_frame_t frame_tmp;  /* temporary frame */
+       struct w9968cf_frame_t* frame_current; /* -> frame being grabbed */
+       struct w9968cf_frame_t* requested_frame[W9968CF_MAX_BUFFERS];
+       void* vpp_buffer; /* -> helper buffer for post-processing routines */
+
+       u8 max_buffers,   /* number of requested buffers */        
+          force_palette, /* yes=1/no=0 */
+          force_rgb,     /* read RGB instead of BGR, yes=1, no=0 */
+          double_buffer, /* hardware double buffering yes=1/no=0 */
+          clamping,      /* video data clamping yes=1/no=0 */
+          filter_type,   /* 0=disabled, 1=3 tap, 2=5 tap filter */
+          capture,       /* 0=disabled, 1=enabled */
+          largeview,     /* 0=disabled, 1=enabled */
+          decompression, /* 0=disabled, 1=forced, 2=allowed */
+          upscaling;     /* software image scaling, 0=enabled, 1=disabled */
+
+       struct video_picture picture; /* current window settings */
+       struct video_window window;   /* current picture settings */
+
+       u16 hw_depth,    /* depth (used by the chip) */
+           hw_palette,  /* palette (used by the chip) */
+           hw_width,    /* width (used by the chip) */
+           hw_height,   /* height (used by the chip) */
+           hs_polarity, /* 0=negative sync pulse, 1=positive sync pulse */
+           vs_polarity; /* 0=negative sync pulse, 1=positive sync pulse */
+
+       enum w9968cf_vpp_flag vpp_flag; /* post-processing routines in use */
+
+       u8 nbuffers,      /* number of allocated frame buffers */
+          altsetting,    /* camera alternate setting */
+          disconnected,  /* flag: yes=1, no=0 */
+          misconfigured, /* flag: yes=1, no=0 */
+          users,         /* flag: number of users holding the device */
+          streaming;     /* flag: yes=1, no=0 */
+
+       int sensor; /* type of image CMOS sensor chip (CC_*) */
+
+       /* Determined by CMOS sensor type */
+       u16 maxwidth,
+           maxheight,
+           minwidth,
+           minheight,
+           start_cropx,
+           start_cropy;
+
+       u8  auto_brt,     /* auto brightness enabled flag */
+           auto_exp,     /* auto exposure enabled flag */
+           backlight,    /* backlight exposure algorithm flag */
+           mirror,       /* image is reversed horizontally */
+           lightfreq,    /* power (lighting) frequency */
+           bandfilt;     /* banding filter enabled flag */
+       s8  clockdiv;     /* clock divisor */
+       int sensor_mono;  /* CMOS sensor is (probably) monochrome */
+
+       /* I2C interface to kernel */
+       struct i2c_adapter i2c_adapter;
+       struct i2c_client* sensor_client;
+
+       /* Locks */
+       struct semaphore dev_sem,    /* for probe, disconnect,open and close */
+                        fileop_sem; /* for read and ioctl */
+       spinlock_t urb_lock,   /* for submit_urb() and unlink_urb() */
+                  flist_lock; /* for requested frame list accesses */
+       char command[16]; /* name of the program holding the device */
+       wait_queue_head_t open, wait_queue;
+};
+
+#define W9968CF_HW_BUF_SIZE 640*480*2 /* buf. size for original video frames */
+
+#define SENSOR_FORMAT          VIDEO_PALETTE_UYVY
+#define SENSOR_FATAL_ERROR(rc) ((rc) < 0 && (rc) != -EPERM)
+
+
+/****************************************************************************
+ * Macros and other constants                                               *
+ ****************************************************************************/
+
+#undef DBG
+#ifdef W9968CF_DEBUG
+#      define DBG(level, fmt, args...) \
+{ \
+if ( ((specific_debug) && (debug == (level))) || \
+     ((!specific_debug) && (debug >= (level))) ) { \
+       if ((level) == 1) \
+               err(fmt, ## args); \
+       else if ((level) == 2 || (level) == 3) \
+               info(fmt, ## args); \
+       else if ((level) == 4) \
+               warn(fmt, ## args); \
+       else if ((level) >= 5) \
+               info("[%s,%d] " fmt, \
+                    __PRETTY_FUNCTION__, __LINE__ , ## args); \
+} \
+}
+#else
+       /* Not debugging: nothing */
+#      define DBG(level, fmt, args...) do {;} while(0);
+#endif
+
+#undef PDBG
+#undef PDBGG
+#define PDBG(fmt, args...) info("[%s, %d] "fmt, \
+                               __PRETTY_FUNCTION__, __LINE__ , ## args);
+#define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
+
+#endif /* _W9968CF_H_ */
diff --git a/drivers/usb/media/w9968cf_decoder.h b/drivers/usb/media/w9968cf_decoder.h
new file mode 100644 (file)
index 0000000..ec8371f
--- /dev/null
@@ -0,0 +1,86 @@
+/***************************************************************************
+ * Video decoder for the W996[87]CF driver for Linux.                      *
+ *                                                                         *
+ * Copyright (C) 2003 by Luca Risolia <luca_ing@libero.it>                 *
+ *                                                                         *
+ * This program is free software; you can redistribute it and/or modify    *
+ * it under the terms of the GNU General Public License as published by    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _W9968CF_DECODER_H_
+#define _W9968CF_DECODER_H_
+
+/* Comment/uncomment this for high/low quality of compressed video */
+#define W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+
+#ifdef W9968CF_DEC_FAST_LOWQUALITY_VIDEO
+static const unsigned char Y_QUANTABLE[64] = {
+       16,  11,  10,  16,  24,  40,  51,  61,
+       12,  12,  14,  19,  26,  58,  60,  55,
+       14,  13,  16,  24,  40,  57,  69,  56,
+       14,  17,  22,  29,  51,  87,  80,  62,
+       18,  22,  37,  56,  68, 109, 103,  77,
+       24,  35,  55,  64,  81, 104, 113,  92,
+       49,  64,  78,  87, 103, 121, 120, 101,
+       72,  92,  95,  98, 112, 100, 103,  99
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+       17,  18,  24,  47,  99,  99,  99,  99,
+       18,  21,  26,  66,  99,  99,  99,  99,
+       24,  26,  56,  99,  99,  99,  99,  99,
+       47,  66,  99,  99,  99,  99,  99,  99,
+       99,  99,  99,  99,  99,  99,  99,  99,
+       99,  99,  99,  99,  99,  99,  99,  99,
+       99,  99,  99,  99,  99,  99,  99,  99,
+       99,  99,  99,  99,  99,  99,  99,  99
+};
+#else
+static const unsigned char Y_QUANTABLE[64] = {
+        8,   5,   5,   8,  12,  20,  25,  30,
+        6,   6,   7,   9,  13,  29,  30,  27,
+        7,   6,   8,  12,  20,  28,  34,  28,
+        7,   8,  11,  14,  25,  43,  40,  31,
+        9,  11,  18,  28,  34,  54,  51,  38,
+       12,  17,  27,  32,  40,  52,  56,  46,
+       24,  32,  39,  43,  51,  60,  60,  50,
+       36,  46,  47,  49,  56,  50,  51,  49
+};
+
+static const unsigned char UV_QUANTABLE[64] = {
+        8,   9,  12,  23,  49,  49,  49,  49,
+        9,  10,  13,  33,  49,  49,  49,  49,
+       12,  13,  28,  49,  49,  49,  49,  49,
+       23,  33,  49,  49,  49,  49,  49,  49,
+       49,  49,  49,  49,  49,  49,  49,  49,
+       49,  49,  49,  49,  49,  49,  49,  49,
+       49,  49,  49,  49,  49,  49,  49,  49,
+       49,  49,  49,  49,  49,  49,  49,  49
+};
+#endif
+
+#define W9968CF_DEC_ERR_CORRUPTED_DATA  -1
+#define W9968CF_DEC_ERR_BUF_OVERFLOW    -2
+#define W9968CF_DEC_ERR_NO_SOI          -3
+#define W9968CF_DEC_ERR_NO_SOF0         -4
+#define W9968CF_DEC_ERR_NO_SOS          -5
+#define W9968CF_DEC_ERR_NO_EOI          -6
+
+extern void w9968cf_init_decoder(void);
+extern int w9968cf_check_headers(const unsigned char* Pin, 
+                                 const unsigned long BUF_SIZE);
+extern int w9968cf_decode(const char* Pin, const unsigned long BUF_SIZE, 
+                          const unsigned W, const unsigned H, char* Pout);
+
+#endif /* _W9968CF_DECODER_H_ */
diff --git a/drivers/usb/media/w9968cf_externaldef.h b/drivers/usb/media/w9968cf_externaldef.h
new file mode 100644 (file)
index 0000000..8e1a920
--- /dev/null
@@ -0,0 +1,95 @@
+/***************************************************************************
+ * Various definitions for compatibility with external modules.            *
+ * This file is part of the W996[87]CF driver for Linux.                   *
+ *                                                                         *
+ * Copyright (C) 2002 2003 by Luca Risolia <luca_ing@libero.it>            *
+ *                                                                         *
+ * This program is free software; you can redistribute it and/or modify    *
+ * it under the terms of the GNU General Public License as published by    *
+ * the Free Software Foundation; either version 2 of the License, or       *
+ * (at your option) any later version.                                     *
+ *                                                                         *
+ * This program is distributed in the hope that it will be useful,         *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
+ * GNU General Public License for more details.                            *
+ *                                                                         *
+ * You should have received a copy of the GNU General Public License       *
+ * along with this program; if not, write to the Free Software             *
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.               *
+ ***************************************************************************/
+
+#ifndef _W9968CF_EXTERNALDEF_H_
+#define _W9968CF_EXTERNALDEF_H_
+
+#include <linux/videodev.h>
+#include <linux/i2c.h>
+#include <asm/ioctl.h>
+#include <asm/types.h>
+
+/* The following values have been copied from the "ovcamchip" module. */
+
+#ifndef I2C_DRIVERID_OVCAMCHIP
+#      define I2C_DRIVERID_OVCAMCHIP   0xf00f
+#endif
+
+/* Controls */
+enum {
+       OVCAMCHIP_CID_CONT,       /* Contrast */
+       OVCAMCHIP_CID_BRIGHT,     /* Brightness */
+       OVCAMCHIP_CID_SAT,        /* Saturation */
+       OVCAMCHIP_CID_HUE,        /* Hue */
+       OVCAMCHIP_CID_EXP,        /* Exposure */
+       OVCAMCHIP_CID_FREQ,       /* Light frequency */
+       OVCAMCHIP_CID_BANDFILT,   /* Banding filter */
+       OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */
+       OVCAMCHIP_CID_AUTOEXP,    /* Auto exposure */
+       OVCAMCHIP_CID_BACKLIGHT,  /* Back light compensation */
+       OVCAMCHIP_CID_MIRROR,     /* Mirror horizontally */
+};
+
+/* I2C addresses */
+#define OV7xx0_SID   (0x42 >> 1)
+#define OV6xx0_SID   (0xC0 >> 1)
+
+/* Sensor types */
+enum {
+       CC_UNKNOWN,
+       CC_OV76BE,
+       CC_OV7610,
+       CC_OV7620,
+       CC_OV7620AE,
+       CC_OV6620,
+       CC_OV6630,
+       CC_OV6630AE,
+       CC_OV6630AF,
+};
+
+/* API */
+struct ovcamchip_control {
+       __u32 id;
+       __s32 value;
+};
+
+struct ovcamchip_window {
+       int x;
+       int y;
+       int width;
+       int height;
+       int format;
+       int quarter;   /* Scale width and height down 2x */
+
+       /* This stuff will be removed eventually */
+       int clockdiv;  /* Clock divisor setting */
+};
+
+/* Commands. 
+   You must call OVCAMCHIP_CMD_INITIALIZE before any of other commands */
+#define OVCAMCHIP_CMD_Q_SUBTYPE  _IOR  (0x88, 0x00, int)
+#define OVCAMCHIP_CMD_INITIALIZE _IOW  (0x88, 0x01, int)
+#define OVCAMCHIP_CMD_S_CTRL     _IOW  (0x88, 0x02, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_G_CTRL     _IOWR (0x88, 0x03, struct ovcamchip_control)
+#define OVCAMCHIP_CMD_S_MODE     _IOW  (0x88, 0x04, struct ovcamchip_window)
+#define OVCAMCHIP_MAX_CMD        _IO   (0x88, 0x3f)
+
+#endif /* _W9968CF_EXTERNALDEF_H_ */
index d82f8de..bbc1afd 100644 (file)
@@ -58,6 +58,18 @@ config USB_RIO500
          To compile this driver as a module, choose M here: the
          module will be called rio500.
 
+config USB_LEGOTOWER
+       tristate "USB Lego Infrared Tower support (EXPERIMENTAL)"
+       depends on USB && EXPERIMENTAL
+       help
+         Say Y here if you want to connect a USB Lego Infrared Tower to your
+         computer's USB port.
+
+         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 legousbtower. If you want to compile it as
+         a module, say M here and read <file:Documentation/modules.txt>.
+
 config USB_BRLVGER
        tristate "Tieman Voyager USB Braille display support (EXPERIMENTAL)"
        depends on USB && EXPERIMENTAL
index c24cebd..aae6fe0 100644 (file)
@@ -12,3 +12,4 @@ obj-$(CONFIG_USB_SPEEDTOUCH)  += speedtch.o
 obj-$(CONFIG_USB_TEST)         += usbtest.o
 obj-$(CONFIG_USB_TIGL)         += tiglusb.o
 obj-$(CONFIG_USB_USS720)       += uss720.o
+obj-$(CONFIG_USB_LEGOTOWER)    += legousbtower.o
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
new file mode 100644 (file)
index 0000000..14abec8
--- /dev/null
@@ -0,0 +1,878 @@
+/*
+ * LEGO USB Tower driver
+ *
+ * Copyright (C) 2003 David Glance <davidgsf@sourceforge.net> 
+ *               2001 Juergen Stuber <stuber@loria.fr>
+ *
+ *     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.
+ *
+ * derived from USB Skeleton driver - 0.5
+ * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * History:
+ *
+ * 2001-10-13 - 0.1 js
+ *   - first version
+ * 2001-11-03 - 0.2 js
+ *   - simplified buffering, one-shot URBs for writing
+ * 2001-11-10 - 0.3 js
+ *   - removed IOCTL (setting power/mode is more complicated, postponed)
+ * 2001-11-28 - 0.4 js
+ *   - added vendor commands for mode of operation and power level in open
+ * 2001-12-04 - 0.5 js
+ *   - set IR mode by default (by oversight 0.4 set VLL mode)
+ * 2002-01-11 - 0.5? pcchan
+ *   - make read buffer reusable and work around bytes_to_write issue between
+ *     uhci and legusbtower
+ * 2002-09-23 - 0.52 david (david@csse.uwa.edu.au)
+ *   - imported into lejos project
+ *   - changed wake_up to wake_up_interruptible
+ *   - changed to use lego0 rather than tower0
+ *   - changed dbg() to use __func__ rather than deprecated __FUNCTION__
+ * 2003-01-12 - 0.53 david (david@csse.uwa.edu.au)
+ *   - changed read and write to write everything or timeout (from a patch by Chris Riesen and 
+ *     Brett Thaeler driver)
+ *   - added ioctl functionality to set timeouts
+ * 2003-07-18 - 0.54 davidgsf (david@csse.uwa.edu.au) 
+ *   - initial import into LegoUSB project
+ *   - merge of existing LegoUSB.c driver
+ * 2003-07-18 - 0.56 davidgsf (david@csse.uwa.edu.au) 
+ *   - port to 2.6 style driver
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/completion.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+
+#ifdef CONFIG_USB_DEBUG
+       static int debug = 4;
+#else
+       static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...) do { if (debug >= lvl) printk(KERN_DEBUG  __FILE__ " : " format " \n", ## arg); } while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.56"
+#define DRIVER_AUTHOR "David Glance, davidgsf@sourceforge.net"
+#define DRIVER_DESC "LEGO USB Tower Driver"
+
+/* Module paramaters */
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+
+/* Define these values to match your device */
+#define LEGO_USB_TOWER_VENDOR_ID       0x0694
+#define LEGO_USB_TOWER_PRODUCT_ID      0x0001
+
+/* table of devices that work with this driver */
+static struct usb_device_id tower_table [] = {
+       { USB_DEVICE(LEGO_USB_TOWER_VENDOR_ID, LEGO_USB_TOWER_PRODUCT_ID) },
+       { }                                     /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, tower_table);
+
+#define LEGO_USB_TOWER_MINOR_BASE      160
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES            16
+
+#define COMMAND_TIMEOUT                (2*HZ)  /* 2 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct lego_usb_tower {
+       struct semaphore        sem;            /* locks this structure */
+       struct usb_device*      udev;           /* save off the usb device pointer */
+       struct usb_interface*   interface;
+       unsigned char           minor;          /* the starting minor number for this device */
+
+       int                     open_count;     /* number of times this port has been opened */
+
+       char*                   read_buffer;
+       int                     read_buffer_length;
+
+       wait_queue_head_t       read_wait;
+       wait_queue_head_t       write_wait;
+
+       char*                   interrupt_in_buffer;
+       struct usb_endpoint_descriptor* interrupt_in_endpoint;
+       struct urb*             interrupt_in_urb;
+
+       char*                   interrupt_out_buffer;
+       struct usb_endpoint_descriptor* interrupt_out_endpoint;
+       struct urb*             interrupt_out_urb;
+
+};
+
+/* Note that no locking is needed:
+ * read_buffer is arbitrated by read_buffer_length == 0
+ * interrupt_out_buffer is arbitrated by interrupt_out_urb->status == -EINPROGRESS
+ * interrupt_in_buffer belongs to urb alone and is overwritten on overflow
+ */
+
+/* local function prototypes */
+static ssize_t tower_read      (struct file *file, char *buffer, size_t count, loff_t *ppos);
+static ssize_t tower_write     (struct file *file, const char *buffer, size_t count, loff_t *ppos);
+static inline void tower_delete (struct lego_usb_tower *dev);
+static int tower_open          (struct inode *inode, struct file *file);
+static int tower_release       (struct inode *inode, struct file *file);
+static int tower_release_internal (struct lego_usb_tower *dev);
+static void tower_abort_transfers (struct lego_usb_tower *dev);
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs);
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs);
+
+static int  tower_probe        (struct usb_interface *interface, const struct usb_device_id *id);
+static void tower_disconnect   (struct usb_interface *interface);
+
+
+/* prevent races between open() and disconnect */
+static DECLARE_MUTEX (disconnect_sem);
+
+/* file operations needed when we register this driver */
+static struct file_operations tower_fops = {
+       .owner =        THIS_MODULE,
+       .read  =        tower_read,
+       .write =        tower_write,
+       .open =         tower_open,
+       .release =      tower_release,
+};
+
+/* 
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver tower_class = {
+       .name =         "usb/legousbtower%d",
+       .fops =         &tower_fops,
+       .mode =         S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH,
+       .minor_base =   LEGO_USB_TOWER_MINOR_BASE,
+};
+
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver tower_driver = {
+       .owner =        THIS_MODULE,
+       .name =         "legousbtower",
+       .probe =        tower_probe,
+       .disconnect =   tower_disconnect,
+       .id_table =     tower_table,
+};
+
+
+/**
+ *     lego_usb_tower_debug_data
+ */
+static inline void lego_usb_tower_debug_data (int level, const char *function, int size, const unsigned char *data)
+{
+       int i;
+
+       if (debug < level)
+               return; 
+       
+       printk (KERN_DEBUG __FILE__": %s - length = %d, data = ", function, size);
+       for (i = 0; i < size; ++i) {
+               printk ("%.2x ", data[i]);
+       }
+       printk ("\n");
+}
+
+
+/**
+ *     tower_delete
+ */
+static inline void tower_delete (struct lego_usb_tower *dev)
+{
+       dbg(2, "%s enter", __func__);
+
+       tower_abort_transfers (dev);
+
+       /* free data structures */
+       if (dev->interrupt_in_urb != NULL) {
+               usb_free_urb (dev->interrupt_in_urb);
+       }
+       if (dev->interrupt_out_urb != NULL) {
+               usb_free_urb (dev->interrupt_out_urb);
+       }
+       kfree (dev->read_buffer);
+       kfree (dev->interrupt_in_buffer);
+       kfree (dev->interrupt_out_buffer);
+       kfree (dev);
+
+       dbg(2, "%s : leave", __func__);
+}
+
+
+/**
+ *     tower_open
+ */
+static int tower_open (struct inode *inode, struct file *file)
+{
+       struct lego_usb_tower *dev = NULL;
+       int subminor;
+       int retval = 0;
+       struct usb_interface *interface;
+
+       dbg(2,"%s : enter", __func__);
+
+       subminor = iminor(inode);
+
+       down (&disconnect_sem);
+
+       interface = usb_find_interface (&tower_driver, subminor);
+
+       if (!interface) {
+               err ("%s - error, can't find device for minor %d",
+                    __FUNCTION__, subminor);
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       dev = usb_get_intfdata(interface);
+
+       if (!dev) {
+               retval = -ENODEV;
+               goto exit_no_device;
+       }
+
+       /* lock this device */
+       down (&dev->sem);
+
+       
+       /* increment our usage count for the device */
+       ++dev->open_count;
+
+       /* save device in the file's private structure */
+       file->private_data = dev;
+
+
+       /* initialize in direction */
+       dev->read_buffer_length = 0;
+
+       up (&dev->sem);
+
+exit_no_device:
+
+       up (&disconnect_sem);
+
+       dbg(2,"%s : leave, return value %d ", __func__, retval);
+
+       return retval;
+}
+
+/**
+ *     tower_release
+ */
+static int tower_release (struct inode *inode, struct file *file)
+{
+       struct lego_usb_tower *dev;
+       int retval = 0;
+
+       dbg(2," %s : enter", __func__);
+
+       dev = (struct lego_usb_tower *)file->private_data;
+
+       if (dev == NULL) {
+               dbg(1," %s : object is NULL", __func__);
+               retval = -ENODEV;
+               goto exit;
+       }
+
+
+       /* lock our device */
+       down (&dev->sem);
+
+       if (dev->open_count <= 0) {
+               dbg(1," %s : device not opened", __func__);
+               retval = -ENODEV;
+               goto exit;
+       }
+
+       /* do the work */
+       retval = tower_release_internal (dev);
+
+exit:
+       up (&dev->sem);
+       dbg(2," %s : leave, return value %d", __func__, retval);
+       return retval;
+}
+
+
+/**
+ *     tower_release_internal
+ */
+static int tower_release_internal (struct lego_usb_tower *dev)
+{
+       int retval = 0;
+
+       dbg(2," %s : enter", __func__);
+
+       if (dev->udev == NULL) {
+               /* the device was unplugged before the file was released */
+               tower_delete (dev);
+               goto exit;
+       }
+
+       /* decrement our usage count for the device */
+       --dev->open_count;
+       if (dev->open_count <= 0) {
+               tower_abort_transfers (dev);
+               dev->open_count = 0;
+       }
+
+exit:
+       dbg(2," %s : leave", __func__);
+       return retval;
+}
+
+
+/**
+ *     tower_abort_transfers
+ *      aborts transfers and frees associated data structures
+ */
+static void tower_abort_transfers (struct lego_usb_tower *dev)
+{
+       dbg(2," %s : enter", __func__);
+
+       if (dev == NULL) {
+               dbg(1," %s : dev is null", __func__);
+               goto exit;
+       }
+
+       /* shutdown transfer */
+       if (dev->interrupt_in_urb != NULL) {
+               usb_unlink_urb (dev->interrupt_in_urb);
+       }
+       if (dev->interrupt_out_urb != NULL) {
+               usb_unlink_urb (dev->interrupt_out_urb);
+       }
+
+exit:
+       dbg(2," %s : leave", __func__);
+}
+
+
+/**
+ *     tower_read
+ */
+static ssize_t tower_read (struct file *file, char *buffer, size_t count, loff_t *ppos)
+{
+       struct lego_usb_tower *dev;
+       size_t bytes_read = 0;
+       size_t bytes_to_read;
+       int i;
+       int retval = 0;
+       int timeout = 0;
+
+       dbg(2," %s : enter, count = %Zd", __func__, count);
+
+       dev = (struct lego_usb_tower *)file->private_data;
+       
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL) {
+               retval = -ENODEV;
+               err("No device or device unplugged %d", retval);
+               goto exit;
+       }
+
+       /* verify that we actually have some data to read */
+       if (count == 0) {
+               dbg(1," %s : read request of 0 bytes", __func__);
+               goto exit;
+       }
+
+
+       timeout = COMMAND_TIMEOUT;
+
+       while (1) {
+               if (dev->read_buffer_length == 0) {
+
+                       /* start reading */
+                       usb_fill_int_urb (dev->interrupt_in_urb,dev->udev,
+                                         usb_rcvintpipe(dev->udev, dev->interrupt_in_endpoint->bEndpointAddress),
+                                         dev->interrupt_in_buffer,
+                                         dev->interrupt_in_endpoint->wMaxPacketSize,
+                                         tower_interrupt_in_callback,
+                                         dev,
+                                         dev->interrupt_in_endpoint->bInterval);
+                       
+                       retval = usb_submit_urb (dev->interrupt_in_urb, GFP_KERNEL);
+                       
+                       if (retval < 0) {
+                               err("Couldn't submit interrupt_in_urb");
+                               goto exit;
+                       }
+
+                       if (timeout <= 0) {
+                               retval = -ETIMEDOUT;
+                               goto exit;
+                       }
+
+                       if (signal_pending(current)) {
+                               retval = -EINTR;
+                               goto exit;
+                       }
+
+                       up (&dev->sem);
+                       timeout = interruptible_sleep_on_timeout (&dev->read_wait, timeout);
+                       down (&dev->sem);
+
+               } else {
+                       /* copy the data from read_buffer into userspace */
+                       bytes_to_read = count > dev->read_buffer_length ? dev->read_buffer_length : count;
+                       if (copy_to_user (buffer, dev->read_buffer, bytes_to_read) != 0) {
+                               retval = -EFAULT;
+                               goto exit;
+                       }
+                       dev->read_buffer_length -= bytes_to_read;
+                       for (i=0; i<dev->read_buffer_length; i++) {
+                               dev->read_buffer[i] = dev->read_buffer[i+bytes_to_read];
+                       }
+
+                       buffer += bytes_to_read;
+                       count -= bytes_to_read;
+                       bytes_read += bytes_to_read;
+                       if (count == 0) {
+                               break;
+                       }
+               }
+       }
+
+       retval = bytes_read;
+
+exit:
+       /* unlock the device */
+       up (&dev->sem);
+
+       dbg(2," %s : leave, return value %d", __func__, retval);
+       return retval;
+}
+
+
+/**
+ *     tower_write
+ */
+static ssize_t tower_write (struct file *file, const char *buffer, size_t count, loff_t *ppos)
+{
+       struct lego_usb_tower *dev;
+       size_t bytes_written = 0;
+       size_t bytes_to_write;
+       size_t buffer_size;
+       int retval = 0;
+       int timeout = 0;
+
+       dbg(2," %s : enter, count = %Zd", __func__, count);
+
+       dev = (struct lego_usb_tower *)file->private_data;
+
+       /* lock this object */
+       down (&dev->sem);
+
+       /* verify that the device wasn't unplugged */
+       if (dev->udev == NULL) {
+               retval = -ENODEV;
+               err("No device or device unplugged %d", retval);
+               goto exit;
+       }
+
+       /* verify that we actually have some data to write */
+       if (count == 0) {
+               dbg(1," %s : write request of 0 bytes", __func__);
+               goto exit;
+       }
+
+
+       while (count > 0) {
+               if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+                       timeout = COMMAND_TIMEOUT;
+
+                       while (timeout > 0) {
+                               if (signal_pending(current)) {
+                                       dbg(1," %s : interrupted", __func__);
+                                       retval = -EINTR;
+                                       goto exit;
+                               }
+                               up (&dev->sem);
+                               timeout = interruptible_sleep_on_timeout (&dev->write_wait, timeout);
+                               down (&dev->sem);
+                               if (timeout > 0) {
+                                       break;
+                               }
+                               dbg(1," %s : interrupted timeout: %d", __func__, timeout);
+                       }
+
+
+                       dbg(1," %s : final timeout: %d", __func__, timeout);
+
+                       if (timeout == 0) {
+                               dbg(1, "%s - command timed out.", __func__);
+                               retval = -ETIMEDOUT;
+                               goto exit;
+                       }
+
+                       dbg(4," %s : in progress, count = %Zd", __func__, count);
+               } else {
+                       dbg(4," %s : sending, count = %Zd", __func__, count);
+
+                       /* write the data into interrupt_out_buffer from userspace */
+                       buffer_size = dev->interrupt_out_endpoint->wMaxPacketSize;
+                       bytes_to_write = count > buffer_size ? buffer_size : count;
+                       dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd", __func__, buffer_size, count, bytes_to_write);
+
+                       if (copy_from_user (dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+                               retval = -EFAULT;
+                               goto exit;
+                       }
+
+                       /* send off the urb */
+                       usb_fill_int_urb(dev->interrupt_out_urb,
+                                       dev->udev, 
+                                       usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+                                       dev->interrupt_out_buffer,
+                                       bytes_to_write,
+                                       tower_interrupt_out_callback,
+                                       dev,
+                                       dev->interrupt_in_endpoint->bInterval);
+
+                       dev->interrupt_out_urb->actual_length = bytes_to_write;
+                       retval = usb_submit_urb (dev->interrupt_out_urb, GFP_KERNEL);
+
+                       if (retval < 0) {
+                               err("Couldn't submit interrupt_out_urb %d", retval);
+                               goto exit;
+                       }
+
+                       buffer += bytes_to_write;
+                       count -= bytes_to_write;
+
+                       bytes_written += bytes_to_write;
+               }
+       }
+
+       retval = bytes_written;
+
+exit:
+       /* unlock the device */
+       up (&dev->sem);
+
+       dbg(2," %s : leave, return value %d", __func__, retval);
+
+       return retval;
+}
+
+
+/**
+ *     tower_interrupt_in_callback
+ */
+static void tower_interrupt_in_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+       dbg(4," %s : enter, status %d", __func__, urb->status);
+
+       lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+       if (urb->status != 0) {
+               if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+                       dbg(1," %s : nonzero status received: %d", __func__, urb->status);
+               }
+               goto exit;
+       }
+
+       down (&dev->sem);
+
+       if (urb->actual_length > 0) {
+               if (dev->read_buffer_length < (4 * dev->interrupt_in_endpoint->wMaxPacketSize) - (urb->actual_length)) {
+
+                       memcpy (dev->read_buffer+dev->read_buffer_length, dev->interrupt_in_buffer, urb->actual_length);
+
+                       dev->read_buffer_length += urb->actual_length;
+                       dbg(1," %s reading  %d ", __func__, urb->actual_length);
+                       wake_up_interruptible (&dev->read_wait);
+
+               } else {
+                       dbg(1," %s : read_buffer overflow", __func__);
+               }
+       }
+
+       up (&dev->sem);
+
+exit:
+       lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+       dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ *     tower_interrupt_out_callback
+ */
+static void tower_interrupt_out_callback (struct urb *urb, struct pt_regs *regs)
+{
+       struct lego_usb_tower *dev = (struct lego_usb_tower *)urb->context;
+
+       dbg(4," %s : enter, status %d", __func__, urb->status);
+       lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+
+       if (urb->status != 0) {
+               if ((urb->status != -ENOENT) && 
+                   (urb->status != -ECONNRESET)) {
+                       dbg(1, " %s :nonzero status received: %d", __func__, urb->status);
+               }
+               goto exit;
+       }                        
+
+       wake_up_interruptible(&dev->write_wait);
+exit:
+
+       lego_usb_tower_debug_data(5,__func__, urb->actual_length, urb->transfer_buffer);
+       dbg(4," %s : leave, status %d", __func__, urb->status);
+}
+
+
+/**
+ *     tower_probe
+ *
+ *     Called by the usb core when a new device is connected that it thinks
+ *     this driver might be interested in.
+ */
+static int tower_probe (struct usb_interface *interface, const struct usb_device_id *id)
+{
+       struct usb_device *udev = interface_to_usbdev(interface);
+       struct lego_usb_tower *dev = NULL;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor* endpoint;
+       int i;
+       int retval = -ENOMEM;
+
+       dbg(2," %s : enter", __func__);
+
+       if (udev == NULL) {
+               info ("udev is NULL.");
+       }
+       
+       /* See if the device offered us matches what we can accept */
+       if ((udev->descriptor.idVendor != LEGO_USB_TOWER_VENDOR_ID) ||
+           (udev->descriptor.idProduct != LEGO_USB_TOWER_PRODUCT_ID)) {
+               return -ENODEV;
+       }
+
+
+       /* allocate memory for our device state and intialize it */
+
+       dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
+
+       if (dev == NULL) {
+               err ("Out of memory");
+               goto exit;
+       }
+
+       init_MUTEX (&dev->sem);
+
+       dev->udev = udev;
+       dev->open_count = 0;
+
+       dev->read_buffer = NULL;
+       dev->read_buffer_length = 0;
+
+       init_waitqueue_head (&dev->read_wait);
+       init_waitqueue_head (&dev->write_wait);
+
+       dev->interrupt_in_buffer = NULL;
+       dev->interrupt_in_endpoint = NULL;
+       dev->interrupt_in_urb = NULL;
+
+       dev->interrupt_out_buffer = NULL;
+       dev->interrupt_out_endpoint = NULL;
+       dev->interrupt_out_urb = NULL;
+
+
+       iface_desc = &interface->altsetting[0];
+
+       /* set up the endpoint information */
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+                       dev->interrupt_in_endpoint = endpoint;
+               }
+               
+               if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+                   ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+                       dev->interrupt_out_endpoint = endpoint;
+               }
+       }
+       if(dev->interrupt_in_endpoint == NULL) {
+               err("interrupt in endpoint not found");
+               goto error;
+       }
+       if (dev->interrupt_out_endpoint == NULL) {
+               err("interrupt out endpoint not found");
+               goto error;
+       }
+
+       dev->read_buffer = kmalloc ((4*dev->interrupt_in_endpoint->wMaxPacketSize), GFP_KERNEL);
+       if (!dev->read_buffer) {
+               err("Couldn't allocate read_buffer");
+               goto error;
+       }
+       dev->interrupt_in_buffer = kmalloc (dev->interrupt_in_endpoint->wMaxPacketSize, GFP_KERNEL);
+       if (!dev->interrupt_in_buffer) {
+               err("Couldn't allocate interrupt_in_buffer");
+               goto error;
+       }
+       dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->interrupt_in_urb) {
+               err("Couldn't allocate interrupt_in_urb");
+               goto error;
+       }
+       dev->interrupt_out_buffer = kmalloc (dev->interrupt_out_endpoint->wMaxPacketSize, GFP_KERNEL);
+       if (!dev->interrupt_out_buffer) {
+               err("Couldn't allocate interrupt_out_buffer");
+               goto error;
+       }
+       dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (!dev->interrupt_out_urb) {
+               err("Couldn't allocate interrupt_out_urb");
+               goto error;
+       }                
+
+       /* we can register the device now, as it is ready */
+       usb_set_intfdata (interface, dev);
+
+       retval = usb_register_dev (interface, &tower_class);
+
+       if (retval) {
+               /* something prevented us from registering this driver */
+               err ("Not able to get a minor for this device.");
+               usb_set_intfdata (interface, NULL);
+               goto error;
+       }
+
+       dev->minor = interface->minor;
+
+       /* let the user know what node this device is now attached to */
+       info ("LEGO USB Tower device now attached to /dev/usb/lego%d", (dev->minor - LEGO_USB_TOWER_MINOR_BASE));
+
+
+
+exit:
+       dbg(2," %s : leave, return value 0x%.8lx (dev)", __func__, (long) dev);
+
+       return retval;
+
+error:
+       tower_delete(dev);
+       return retval;
+}
+
+
+/**
+ *     tower_disconnect
+ *
+ *     Called by the usb core when the device is removed from the system.
+ */
+static void tower_disconnect (struct usb_interface *interface)
+{
+       struct lego_usb_tower *dev;
+       int minor;
+
+       dbg(2," %s : enter", __func__);
+
+       down (&disconnect_sem);
+
+       dev = usb_get_intfdata (interface);
+       usb_set_intfdata (interface, NULL);
+
+
+       down (&dev->sem);
+
+       minor = dev->minor;
+
+       /* give back our minor */
+       usb_deregister_dev (interface, &tower_class);
+
+       /* if the device is not opened, then we clean up right now */
+       if (!dev->open_count) {
+               up (&dev->sem);
+               tower_delete (dev);
+       } else {
+               dev->udev = NULL;
+               up (&dev->sem);
+       }
+
+       up (&disconnect_sem);
+
+       info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
+
+       dbg(2," %s : leave", __func__);
+}
+
+
+
+/**
+ *     lego_usb_tower_init
+ */
+static int __init lego_usb_tower_init(void)
+{
+       int result;
+       int retval = 0;
+
+       dbg(2," %s : enter", __func__);
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&tower_driver);
+       if (result < 0) {
+               err("usb_register failed for the "__FILE__" driver. Error number %d", result);
+               retval = -1;
+               goto exit;
+       }
+
+       info(DRIVER_DESC " " DRIVER_VERSION);
+
+exit:
+       dbg(2," %s : leave, return value %d", __func__, retval);
+
+       return retval;
+}
+
+
+/**
+ *     lego_usb_tower_exit
+ */
+static void __exit lego_usb_tower_exit(void)
+{
+       dbg(2," %s : enter", __func__);
+
+       /* deregister this driver with the USB subsystem */
+       usb_deregister (&tower_driver);
+
+       dbg(2," %s : leave", __func__);
+}
+
+module_init (lego_usb_tower_init);
+module_exit (lego_usb_tower_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
index 962f14c..2c68bea 100644 (file)
@@ -909,8 +909,7 @@ static int cdc_bind (struct usbnet *dev, struct usb_interface *intf)
        memset (info, 0, sizeof *info);
        info->control = intf;
        while (len > 3) {
-               /* ignore bDescriptorType != CS_INTERFACE */
-               if (buf [1] != 0x24)
+               if (buf [1] != USB_DT_CS_INTERFACE)
                        goto next_desc;
 
                /* bDescriptorSubType identifies three "must have" descriptors;
index 3ea41ef..54a433b 100644 (file)
@@ -342,6 +342,10 @@ static struct usb_device_id id_table_8U232AM [] = {
        { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0, 0x3ff) },
        { USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0, 0x3ff) },
        { USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0, 0x3ff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0, 0x3ff) },
        { }                                             /* Terminating entry */
 };
 
@@ -416,6 +420,10 @@ static struct usb_device_id id_table_FT232BM [] = {
        { USB_DEVICE_VER(SEALEVEL_VID, SEALEVEL_2803_8_PID, 0x400, 0xffff) },
        { USB_DEVICE_VER(IDTECH_VID, IDTECH_IDT1221U_PID, 0x400, 0xffff) },
        { USB_DEVICE_VER(OCT_VID, OCT_US101_PID, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_1, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_R2X0, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_3, 0x400, 0xffff) },
+       { USB_DEVICE_VER(FTDI_VID, PROTEGO_SPECIAL_4, 0x400, 0xffff) },
        { }                                             /* Terminating entry */
 };
 
@@ -505,6 +513,10 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(OCT_VID, OCT_US101_PID) },
        { USB_DEVICE_VER(FTDI_VID, FTDI_HE_TIRA1_PID, 0x400, 0xffff) },
        { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID) },
+       { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_1) },
+       { USB_DEVICE(FTDI_VID, PROTEGO_R2X0) },
+       { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_3) },
+       { USB_DEVICE(FTDI_VID, PROTEGO_SPECIAL_4) },
        { }                                             /* Terminating entry */
 };
 
index 2d0a06e..cb1ed36 100644 (file)
 /* Note: OCT US101 is also rebadged as Dick Smith Electronics (NZ) XH6381 */
 #define OCT_US101_PID          0x0421  /* OCT US101 USB to RS-232 */
 
+/*
+ * Protego product ids
+ */
+#define PROTEGO_SPECIAL_1      0xFC70  /* special/unknown device */
+#define PROTEGO_R2X0           0xFC71  /* R200-USB TRNG unit (R210, R220, and R230) */
+#define PROTEGO_SPECIAL_3      0xFC72  /* special/unknown device */
+#define PROTEGO_SPECIAL_4      0xFC73  /* special/unknown device */ 
+
 /* Commands */
 #define FTDI_SIO_RESET                 0 /* Reset the port */
 #define FTDI_SIO_MODEM_CTRL    1 /* Set the modem control register */
index f2b0037..30e7050 100644 (file)
@@ -78,6 +78,7 @@ static struct usb_device_id id_table [] = {
        { USB_DEVICE(TRIPP_VENDOR_ID, TRIPP_PRODUCT_ID) },
        { USB_DEVICE(RADIOSHACK_VENDOR_ID, RADIOSHACK_PRODUCT_ID) },
        { USB_DEVICE(DCU10_VENDOR_ID, DCU10_PRODUCT_ID) },
+       { USB_DEVICE(SITECOM_VENDOR_ID, SITECOM_PRODUCT_ID) },
        { }                                     /* Terminating entry */
 };
 
@@ -169,6 +170,7 @@ static struct usb_serial_device_type pl2303_device = {
 
 struct pl2303_private {
        spinlock_t lock;
+       wait_queue_head_t delta_msr_wait;
        u8 line_control;
        u8 line_status;
        u8 termios_initialized;
@@ -186,6 +188,7 @@ static int pl2303_startup (struct usb_serial *serial)
                        return -ENOMEM;
                memset (priv, 0x00, sizeof (struct pl2303_private));
                spin_lock_init(&priv->lock);
+               init_waitqueue_head(&priv->delta_msr_wait);
                usb_set_serial_port_data(serial->port[i], priv);
        }
        return 0;
@@ -556,11 +559,51 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
        return result;
 }
 
+static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
+{
+       struct pl2303_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+       unsigned int prevstatus;
+       unsigned int status;
+       unsigned int changed;
+
+       spin_lock_irqsave (&priv->lock, flags);
+       prevstatus = priv->line_status;
+       spin_unlock_irqrestore (&priv->lock, flags);
+
+       while (1) {
+               interruptible_sleep_on(&priv->delta_msr_wait);
+               /* see if a signal did it */
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               
+               spin_lock_irqsave (&priv->lock, flags);
+               status = priv->line_status;
+               spin_unlock_irqrestore (&priv->lock, flags);
+               
+               changed=prevstatus^status;
+               
+               if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
+                   ((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
+                   ((arg & TIOCM_CD)  && (changed & UART_DCD)) ||
+                   ((arg & TIOCM_CTS) && (changed & UART_CTS)) ) {
+                       return 0;
+               }
+               prevstatus = status;
+       }
+       /* NOTREACHED */
+       return 0;
+}
+
 static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
 {
        dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
 
        switch (cmd) {
+               case TIOCMIWAIT:
+                       dbg("%s (%d) TIOCMIWAIT", __FUNCTION__,  port->number);
+                       return wait_modem_info(port, arg);
+
                default:
                        dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
                        break;
@@ -703,6 +746,7 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
        spin_lock_irqsave(&priv->lock, flags);
        status = priv->line_status;
        spin_unlock_irqrestore(&priv->lock, flags);
+       wake_up_interruptible (&priv->delta_msr_wait);
 
        /* break takes precedence over parity, */
        /* which takes precedence over framing errors */
index 5e2abcc..33bb2f3 100644 (file)
@@ -37,3 +37,6 @@
 
 #define DCU10_VENDOR_ID                0x0731
 #define DCU10_PRODUCT_ID       0x0528
+
+#define SITECOM_VENDOR_ID      0x6189
+#define SITECOM_PRODUCT_ID     0x2068
index 9674754..c7a9610 100644 (file)
@@ -493,12 +493,15 @@ bailout:
        return retval;
 }
 
-static void __serial_close(struct usb_serial_port *port, struct file *filp)
+static void serial_close(struct tty_struct *tty, struct file * filp)
 {
-       if (!port->open_count) {
-               dbg ("%s - port not opened", __FUNCTION__);
+       struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+       struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+       if (!serial)
                return;
-       }
+
+       dbg("%s - port %d", __FUNCTION__, port->number);
 
        --port->open_count;
        if (port->open_count <= 0) {
@@ -506,30 +509,18 @@ static void __serial_close(struct usb_serial_port *port, struct file *filp)
                 * port is being closed by the last owner */
                port->serial->type->close(port, filp);
                port->open_count = 0;
+
+               if (port->tty) {
+                       if (port->tty->driver_data)
+                               port->tty->driver_data = NULL;
+                       port->tty = NULL;
+               }
        }
 
        module_put(port->serial->type->owner);
        kobject_put(&port->serial->kobj);
 }
 
-static void serial_close(struct tty_struct *tty, struct file * filp)
-{
-       struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-       struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-       if (!serial)
-               return;
-
-       dbg("%s - port %d", __FUNCTION__, port->number);
-
-       /* if disconnect beat us to the punch here, there's nothing to do */
-       if (tty && tty->driver_data) {
-               __serial_close(port, filp);
-               tty->driver_data = NULL;
-       }
-       port->tty = NULL;
-}
-
 static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
 {
        struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
@@ -848,19 +839,6 @@ static void destroy_serial (struct kobject *kobj)
        dbg ("%s - %s", __FUNCTION__, kobj->name);
 
        serial = to_usb_serial(kobj);
-
-       /* fail all future close/read/write/ioctl/etc calls */
-       for (i = 0; i < serial->num_ports; ++i) {
-               port = serial->port[i];
-               if (port->tty != NULL) {
-                       port->tty->driver_data = NULL;
-                       while (port->open_count > 0) {
-                               __serial_close(port, NULL);
-                       }
-                       port->tty = NULL;
-               }
-       }
-
        serial_shutdown (serial);
 
        /* return the minor range that this device had */
@@ -1242,7 +1220,7 @@ int usb_serial_probe(struct usb_interface *interface,
        /* register all of the individual ports with the driver core */
        for (i = 0; i < num_ports; ++i) {
                port = serial->port[i];
-               port->dev.parent = &serial->dev->dev;
+               port->dev.parent = &interface->dev;
                port->dev.driver = NULL;
                port->dev.bus = &usb_serial_bus_type;
                port->dev.release = &port_release;
index 1a41b08..d4f278c 100644 (file)
@@ -150,65 +150,6 @@ void usb_stor_show_command(Scsi_Cmnd *srb)
        US_DEBUGPX("\n");
 }
 
-void usb_stor_print_Scsi_Cmnd(Scsi_Cmnd *cmd)
-{
-       int i=0, bufferSize = cmd->request_bufflen;
-       u8 *buffer = cmd->request_buffer;
-       struct scatterlist *sg = (struct scatterlist*)cmd->request_buffer;
-
-       US_DEBUGP("Dumping information about %p.\n", cmd);
-       US_DEBUGP("cmd->cmnd[0] value is %d.\n", cmd->cmnd[0]);
-       US_DEBUGP("(MODE_SENSE is %d and MODE_SENSE_10 is %d)\n",
-                 MODE_SENSE, MODE_SENSE_10);
-
-       US_DEBUGP("buffer is %p with length %d.\n", buffer, bufferSize);
-       for (i=0; i<bufferSize; i+=16) {
-               US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
-                         "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-                         buffer[i],
-                         buffer[i+1],
-                         buffer[i+2],
-                         buffer[i+3],
-                         buffer[i+4],
-                         buffer[i+5],
-                         buffer[i+6],
-                         buffer[i+7],
-                         buffer[i+8],
-                         buffer[i+9],
-                         buffer[i+10],
-                         buffer[i+11],
-                         buffer[i+12],
-                         buffer[i+13],
-                         buffer[i+14],
-                         buffer[i+15] );
-       }
-
-       US_DEBUGP("Buffer has %d scatterlists.\n", cmd->use_sg );
-       for (i=0; i<cmd->use_sg; i++) {
-               char *adr = sg_address(sg[i]);
-               
-               US_DEBUGP("Length of scatterlist %d is %d.\n",i,sg[i].length);
-               US_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x\n"
-                         "%02x %02x %02x %02x %02x %02x %02x %02x\n",
-                         adr[0],
-                         adr[1],
-                         adr[2],
-                         adr[3],
-                         adr[4],
-                         adr[5],
-                         adr[6],
-                         adr[7],
-                         adr[8],
-                         adr[9],
-                         adr[10],
-                         adr[11],
-                         adr[12],
-                         adr[13],
-                         adr[14],
-                         adr[15]);
-       }
-}
-
 void usb_stor_show_sense(
                unsigned char key,
                unsigned char asc,
index cde713a..d5d6b3c 100644 (file)
@@ -53,7 +53,6 @@
 
 #ifdef CONFIG_USB_STORAGE_DEBUG
 void usb_stor_show_command(Scsi_Cmnd *srb);
-void usb_stor_print_Scsi_Cmnd( Scsi_Cmnd* cmd );
 void usb_stor_show_sense( unsigned char key,
                unsigned char asc, unsigned char ascq );
 #define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE x )
index a6082b0..c134913 100644 (file)
@@ -44,6 +44,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#include <linux/highmem.h>
 #include "protocol.h"
 #include "usb.h"
 #include "debug.h"
  * Helper routines
  ***********************************************************************/
 
-static void *
-find_data_location(Scsi_Cmnd *srb) {
-       if (srb->use_sg) {
-               /*
-                * This piece of code only works if the first page is
-                * big enough to hold more than 3 bytes -- which is
-                * _very_ likely.
-                */
-               struct scatterlist *sg;
-
-               sg = (struct scatterlist *) srb->request_buffer;
-               return (void *) sg_address(sg[0]);
-       } else
-               return (void *) srb->request_buffer;
-}
-
 /*
  * Fix-up the return data from an INQUIRY command to show 
  * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
  */
 static void fix_inquiry_data(Scsi_Cmnd *srb)
 {
-       unsigned char *data_ptr;
+       unsigned char databuf[3];
+       unsigned int index, offset;
 
        /* verify that it's an INQUIRY command */
        if (srb->cmnd[0] != INQUIRY)
                return;
 
-       /* oddly short buffer -- bail out */
-       if (srb->request_bufflen < 3)
+       index = offset = 0;
+       if (usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
+                       &index, &offset, FROM_XFER_BUF) != sizeof(databuf))
                return;
 
-       data_ptr = find_data_location(srb);
-
-       if ((data_ptr[2] & 7) == 2)
+       if ((databuf[2] & 7) == 2)
                return;
 
        US_DEBUGP("Fixing INQUIRY data to show SCSI rev 2 - was %d\n",
-                 data_ptr[2] & 7);
+                 databuf[2] & 7);
 
        /* Change the SCSI revision number */
-       data_ptr[2] = (data_ptr[2] & ~7) | 2;
+       databuf[2] = (databuf[2] & ~7) | 2;
+
+       index = offset = 0;
+       usb_stor_access_xfer_buf(databuf, sizeof(databuf), srb,
+                       &index, &offset, TO_XFER_BUF);
 }
 
 /*
@@ -104,23 +93,27 @@ static void fix_inquiry_data(Scsi_Cmnd *srb)
  */
 static void fix_read_capacity(Scsi_Cmnd *srb)
 {
-       unsigned char *dp;
+       unsigned int index, offset;
+       u32 c;
        unsigned long capacity;
 
        /* verify that it's a READ CAPACITY command */
        if (srb->cmnd[0] != READ_CAPACITY)
                return;
 
-       dp = find_data_location(srb);
+       index = offset = 0;
+       if (usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
+                       &index, &offset, FROM_XFER_BUF) != 4)
+               return;
 
-       capacity = (dp[0]<<24) + (dp[1]<<16) + (dp[2]<<8) + (dp[3]);
+       capacity = be32_to_cpu(c);
        US_DEBUGP("US: Fixing capacity: from %ld to %ld\n",
               capacity+1, capacity);
-       capacity--;
-       dp[0] = (capacity >> 24);
-       dp[1] = (capacity >> 16);
-       dp[2] = (capacity >> 8);
-       dp[3] = (capacity);
+       c = cpu_to_be32(capacity - 1);
+
+       index = offset = 0;
+       usb_stor_access_xfer_buf((unsigned char *) &c, 4, srb,
+                       &index, &offset, TO_XFER_BUF);
 }
 
 /***********************************************************************
@@ -234,3 +227,71 @@ void usb_stor_transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
                        fix_read_capacity(srb);
        }
 }
+
+/***********************************************************************
+ * Scatter-gather transfer buffer access routines
+ ***********************************************************************/
+
+/* Copy a buffer of length buflen to/from the srb's transfer buffer.
+ * Update the index and offset variables so that the next copy will
+ * pick up from where this one left off. */
+
+unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index,
+       unsigned int *offset, enum xfer_buf_dir dir)
+{
+       unsigned int cnt;
+
+       if (srb->use_sg == 0) {
+               if (*offset >= srb->request_bufflen)
+                       return 0;
+               cnt = min(buflen, srb->request_bufflen - *offset);
+               if (dir == TO_XFER_BUF)
+                       memcpy((unsigned char *) srb->request_buffer + *offset,
+                                       buffer, cnt);
+               else
+                       memcpy(buffer, (unsigned char *) srb->request_buffer +
+                                       *offset, cnt);
+               *offset += cnt;
+
+       } else {
+               struct scatterlist *sg =
+                               (struct scatterlist *) srb->request_buffer
+                               + *index;
+
+               cnt = 0;
+               while (cnt < buflen && *index < srb->use_sg) {
+                       struct page *page = sg->page +
+                                       ((sg->offset + *offset) >> PAGE_SHIFT);
+                       unsigned int poff =
+                                       (sg->offset + *offset) & (PAGE_SIZE-1);
+                       unsigned int sglen = sg->length - *offset;
+
+                       if (sglen > buflen - cnt) {
+                               sglen = buflen - cnt;
+                               *offset += sglen;
+                       } else {
+                               *offset = 0;
+                               ++sg;
+                               ++*index;
+                       }
+
+                       while (sglen > 0) {
+                               unsigned int plen = min(sglen, (unsigned int)
+                                               PAGE_SIZE - poff);
+                               unsigned char *ptr = kmap(page);
+
+                               if (dir == TO_XFER_BUF)
+                                       memcpy(ptr + poff, buffer + cnt, plen);
+                               else
+                                       memcpy(buffer + cnt, ptr + poff, plen);
+                               kunmap(page);
+                               poff = 0;
+                               ++page;
+                               cnt += plen;
+                               sglen -= plen;
+                       }
+               }
+       }
+       return cnt;
+}
index 8b5ecbe..b2c0b22 100644 (file)
 
 #define US_SC_DEVICE   0xff            /* Use device's value */
 
+/* Protocol handling routines */
 extern void usb_stor_ATAPI_command(Scsi_Cmnd*, struct us_data*);
 extern void usb_stor_qic157_command(Scsi_Cmnd*, struct us_data*);
 extern void usb_stor_ufi_command(Scsi_Cmnd*, struct us_data*);
 extern void usb_stor_transparent_scsi_command(Scsi_Cmnd*, struct us_data*);
 
+/* Scsi_Cmnd transfer buffer access utilities */
+enum xfer_buf_dir      {TO_XFER_BUF, FROM_XFER_BUF};
+
+extern unsigned int usb_stor_access_xfer_buf(unsigned char *buffer,
+       unsigned int buflen, Scsi_Cmnd *srb, unsigned int *index,
+       unsigned int *offset, enum xfer_buf_dir dir);
+
 #endif
index 82ed2af..c45a155 100644 (file)
@@ -96,7 +96,7 @@ us_copy_to_sgbuf(unsigned char *buffer, int buflen,
                        length = room;
                
                memcpy(ptr, buffer+transferred, length);
-               transferred += sg[i].length;
+               transferred += length;
                *offset += length;
                if (length == room) {
                        i++;
index 6385cb4..f5083e2 100644 (file)
@@ -28,7 +28,6 @@
  */
 
 #include "transport.h"
-#include "raw_bulk.h"
 #include "protocol.h"
 #include "usb.h"
 #include "debug.h"
@@ -66,7 +65,7 @@ struct nand_flash_dev {
  * NAND Flash Manufacturer ID Codes
  */
 #define NAND_MFR_AMD           0x01
-#define NAND_MFR_NS            0x8f
+#define NAND_MFR_NATSEMI       0x8f
 #define NAND_MFR_TOSHIBA       0x98
 #define NAND_MFR_SAMSUNG       0xec
 
@@ -74,8 +73,8 @@ static inline char *nand_flash_manufacturer(int manuf_id) {
        switch(manuf_id) {
        case NAND_MFR_AMD:
                return "AMD";
-       case NAND_MFR_NS:
-               return "NS";
+       case NAND_MFR_NATSEMI:
+               return "NATSEMI";
        case NAND_MFR_TOSHIBA:
                return "Toshiba";
        case NAND_MFR_SAMSUNG:
@@ -302,8 +301,7 @@ sddr09_request_sense(struct us_data *us, unsigned char *sensebuf, int buflen) {
        if (result != USB_STOR_XFER_GOOD) {
                US_DEBUGP("request sense bulk in failed\n");
                return USB_STOR_TRANSPORT_ERROR;
-       }
-       else {
+       } else {
                US_DEBUGP("request sense worked\n");
                return USB_STOR_TRANSPORT_GOOD;
        }
@@ -469,6 +467,8 @@ sddr09_erase(struct us_data *us, unsigned long Eaddress) {
        unsigned char *command = us->iobuf;
        int result;
 
+       US_DEBUGP("sddr09_erase: erase address %lu\n", Eaddress);
+
        memset(command, 0, 12);
        command[0] = 0xEA;
        command[1] = LUNBITS;
@@ -663,30 +663,27 @@ static int
 sddr09_read_data(struct us_data *us,
                 unsigned long address,
                 unsigned int sectors,
-                unsigned char *content,
+                unsigned char *buffer,
                 int use_sg) {
 
        struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
        unsigned int lba, maxlba, pba;
        unsigned int page, pages;
-       unsigned char *buffer = NULL;
-       unsigned char *ptr;
-       int result, len;
-
-       // If we're using scatter-gather, we have to create a new
-       // buffer to read all of the data in first, since a
-       // scatter-gather buffer could in theory start in the middle
-       // of a page, which would be bad. A developer who wants a
-       // challenge might want to write a limited-buffer
-       // version of this code.
-
-       len = sectors*info->pagesize;
+       unsigned int len, index, offset;
+       int result;
 
-       buffer = (use_sg ? kmalloc(len, GFP_NOIO) : content);
-       if (buffer == NULL)
-               return USB_STOR_TRANSPORT_ERROR;
+       // Since we only read in one block at a time, we have to create
+       // a bounce buffer if the transfer uses scatter-gather.
 
-       ptr = buffer;
+       if (use_sg) {
+               len = min(sectors, (unsigned int) info->blocksize) *
+                               info->pagesize;
+               buffer = kmalloc(len, GFP_NOIO);
+               if (buffer == NULL) {
+                       printk("sddr09_read_data: Out of memory\n");
+                       return USB_STOR_TRANSPORT_ERROR;
+               }
+       }
 
        // Figure out the initial LBA and page
        lba = address >> info->blockshift;
@@ -697,13 +694,13 @@ sddr09_read_data(struct us_data *us,
        // contiguous LBA's. Another exercise left to the student.
 
        result = USB_STOR_TRANSPORT_GOOD;
+       index = offset = 0;
 
        while (sectors > 0) {
 
                /* Find number of pages we can read in this block */
-               pages = info->blocksize - page;
-               if (pages > sectors)
-                       pages = sectors;
+               pages = min(sectors, info->blocksize - page);
+               len = pages << info->pageshift;
 
                /* Not overflowing capacity? */
                if (lba >= maxlba) {
@@ -726,7 +723,7 @@ sddr09_read_data(struct us_data *us,
                           Instead of returning USB_STOR_TRANSPORT_ERROR
                           it is better to return all zero data. */
 
-                       memset(ptr, 0, pages << info->pageshift);
+                       memset(buffer, 0, len);
 
                } else {
                        US_DEBUGP("Read %d pages, from PBA %d"
@@ -737,37 +734,48 @@ sddr09_read_data(struct us_data *us,
                                info->pageshift;
 
                        result = sddr09_read20(us, address>>1,
-                                              pages, info->pageshift, ptr, 0);
+                                       pages, info->pageshift, buffer, 0);
                        if (result != USB_STOR_TRANSPORT_GOOD)
                                break;
                }
+               if (use_sg)
+                       usb_stor_access_xfer_buf(buffer, len, us->srb,
+                                       &index, &offset, TO_XFER_BUF);
+               else
+                       buffer += len;
 
                page = 0;
                lba++;
                sectors -= pages;
-               ptr += (pages << info->pageshift);
        }
 
-       if (use_sg && result == USB_STOR_TRANSPORT_GOOD)
-               us_copy_to_sgbuf_all(buffer, len, content, use_sg);
-
        if (use_sg)
                kfree(buffer);
 
        return result;
 }
 
-/* we never free blocks, so lastpba can only increase */
 static unsigned int
-sddr09_find_unused_pba(struct sddr09_card_info *info) {
+sddr09_find_unused_pba(struct sddr09_card_info *info, unsigned int lba) {
        static unsigned int lastpba = 1;
-       int numblocks = info->capacity >> (info->blockshift + info->pageshift);
-       int i;
+       int zonestart, end, i;
+
+       zonestart = (lba/1000) << 10;
+       end = info->capacity >> (info->blockshift + info->pageshift);
+       end -= zonestart;
+       if (end > 1024)
+               end = 1024;
 
-       for (i = lastpba+1; i < numblocks; i++) {
-               if (info->pba_to_lba[i] == UNDEF) {
+       for (i = lastpba+1; i < end; i++) {
+               if (info->pba_to_lba[zonestart+i] == UNDEF) {
+                       lastpba = i;
+                       return zonestart+i;
+               }
+       }
+       for (i = 0; i <= lastpba; i++) {
+               if (info->pba_to_lba[zonestart+i] == UNDEF) {
                        lastpba = i;
-                       return i;
+                       return zonestart+i;
                }
        }
        return 0;
@@ -776,29 +784,31 @@ sddr09_find_unused_pba(struct sddr09_card_info *info) {
 static int
 sddr09_write_lba(struct us_data *us, unsigned int lba,
                 unsigned int page, unsigned int pages,
-                unsigned char *ptr) {
+                unsigned char *ptr, unsigned char *blockbuffer) {
 
        struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
        unsigned long address;
        unsigned int pba, lbap;
-       unsigned int pagelen, blocklen;
-       unsigned char *blockbuffer, *bptr, *cptr, *xptr;
+       unsigned int pagelen;
+       unsigned char *bptr, *cptr, *xptr;
        unsigned char ecc[3];
-       int i, result;
+       int i, result, isnew;
 
-       lbap = ((lba & 0x3ff) << 1) | 0x1000;
+       lbap = ((lba % 1000) << 1) | 0x1000;
        if (parity[MSB_of(lbap) ^ LSB_of(lbap)])
                lbap ^= 1;
        pba = info->lba_to_pba[lba];
+       isnew = 0;
 
        if (pba == UNDEF) {
-               pba = sddr09_find_unused_pba(info);
+               pba = sddr09_find_unused_pba(info, lba);
                if (!pba) {
                        printk("sddr09_write_lba: Out of unused blocks\n");
                        return USB_STOR_TRANSPORT_ERROR;
                }
                info->pba_to_lba[pba] = lba;
                info->lba_to_pba[lba] = pba;
+               isnew = 1;
        }
 
        if (pba == 1) {
@@ -809,22 +819,16 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
        }
 
        pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
-       blocklen = (pagelen << info->blockshift);
-       blockbuffer = kmalloc(blocklen, GFP_NOIO);
-       if (!blockbuffer) {
-               printk("sddr09_write_lba: Out of memory\n");
-               return USB_STOR_TRANSPORT_ERROR;
-       }
 
        /* read old contents */
        address = (pba << (info->pageshift + info->blockshift));
        result = sddr09_read22(us, address>>1, info->blocksize,
                               info->pageshift, blockbuffer, 0);
        if (result != USB_STOR_TRANSPORT_GOOD)
-               goto err;
+               return result;
 
-       /* check old contents */
-       for (i = 0; i < info->blockshift; i++) {
+       /* check old contents and fill lba */
+       for (i = 0; i < info->blocksize; i++) {
                bptr = blockbuffer + i*pagelen;
                cptr = bptr + info->pagesize;
                nand_compute_ecc(bptr, ecc);
@@ -839,6 +843,8 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
                                  i, pba);
                        nand_store_ecc(cptr+8, ecc);
                }
+               cptr[6] = cptr[11] = MSB_of(lbap);
+               cptr[7] = cptr[12] = LSB_of(lbap);
        }
 
        /* copy in new stuff and compute ECC */
@@ -852,8 +858,6 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
                nand_store_ecc(cptr+13, ecc);
                nand_compute_ecc(bptr+(info->pagesize / 2), ecc);
                nand_store_ecc(cptr+8, ecc);
-               cptr[6] = cptr[11] = MSB_of(lbap);
-               cptr[7] = cptr[12] = LSB_of(lbap);
        }
 
        US_DEBUGP("Rewrite PBA %d (LBA %d)\n", pba, lba);
@@ -880,11 +884,6 @@ sddr09_write_lba(struct us_data *us, unsigned int lba,
                int result2 = sddr09_test_unit_ready(us);
        }
 #endif
- err:
-       kfree(blockbuffer);
-
-       /* TODO: instead of doing kmalloc/kfree for each block,
-          add a bufferpointer to the info structure */
 
        return result;
 }
@@ -893,49 +892,77 @@ static int
 sddr09_write_data(struct us_data *us,
                  unsigned long address,
                  unsigned int sectors,
-                 unsigned char *content,
+                 unsigned char *buffer,
                  int use_sg) {
 
        struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
        unsigned int lba, page, pages;
-       unsigned char *buffer = NULL;
-       unsigned char *ptr;
-       int result, len;
+       unsigned int pagelen, blocklen;
+       unsigned char *blockbuffer;
+       unsigned int len, index, offset;
+       int result;
 
-       len = sectors*info->pagesize;
+       // blockbuffer is used for reading in the old data, overwriting
+       // with the new data, and performing ECC calculations
 
-       buffer = us_copy_from_sgbuf_all(content, len, use_sg);
-       if (buffer == NULL)
+       /* TODO: instead of doing kmalloc/kfree for each write,
+          add a bufferpointer to the info structure */
+
+       pagelen = (1 << info->pageshift) + (1 << CONTROL_SHIFT);
+       blocklen = (pagelen << info->blockshift);
+       blockbuffer = kmalloc(blocklen, GFP_NOIO);
+       if (!blockbuffer) {
+               printk("sddr09_write_data: Out of memory\n");
                return USB_STOR_TRANSPORT_ERROR;
+       }
 
-       ptr = buffer;
+       // Since we don't write the user data directly to the device,
+       // we have to create a bounce buffer if the transfer uses
+       // scatter-gather.
+
+       if (use_sg) {
+               len = min(sectors, (unsigned int) info->blocksize) *
+                               info->pagesize;
+               buffer = kmalloc(len, GFP_NOIO);
+               if (buffer == NULL) {
+                       printk("sddr09_write_data: Out of memory\n");
+                       kfree(blockbuffer);
+                       return USB_STOR_TRANSPORT_ERROR;
+               }
+       }
 
        // Figure out the initial LBA and page
        lba = address >> info->blockshift;
        page = (address & info->blockmask);
 
        result = USB_STOR_TRANSPORT_GOOD;
+       index = offset = 0;
 
        while (sectors > 0) {
 
                // Write as many sectors as possible in this block
 
-               pages = info->blocksize - page;
-               if (pages > sectors)
-                       pages = sectors;
+               pages = min(sectors, info->blocksize - page);
+               len = (pages << info->pageshift);
+               if (use_sg)
+                       usb_stor_access_xfer_buf(buffer, len, us->srb,
+                                       &index, &offset, FROM_XFER_BUF);
 
-               result = sddr09_write_lba(us, lba, page, pages, ptr);
+               result = sddr09_write_lba(us, lba, page, pages,
+                               buffer, blockbuffer);
                if (result != USB_STOR_TRANSPORT_GOOD)
                        break;
+               if (!use_sg)
+                       buffer += len;
 
                page = 0;
                lba++;
                sectors -= pages;
-               ptr += (pages << info->pageshift);
        }
 
        if (use_sg)
                kfree(buffer);
+       kfree(blockbuffer);
 
        return result;
 }
@@ -947,10 +974,11 @@ sddr09_read_control(struct us_data *us,
                unsigned char *content,
                int use_sg) {
 
-       US_DEBUGP("Read control address %08lX blocks %04X\n",
+       US_DEBUGP("Read control address %lu, blocks %d\n",
                address, blocks);
 
-       return sddr09_read21(us, address, blocks, CONTROL_SHIFT, content, use_sg);
+       return sddr09_read21(us, address, blocks,
+                            CONTROL_SHIFT, content, use_sg);
 }
 
 /*
@@ -997,7 +1025,7 @@ sddr09_get_wp(struct us_data *us, struct sddr09_card_info *info) {
                US_DEBUGP("sddr09_get_wp: read_status fails\n");
                return result;
        }
-       US_DEBUGP("sddr09_get_wp: status %02X", status);
+       US_DEBUGP("sddr09_get_wp: status 0x%02X", status);
        if ((status & 0x80) == 0) {
                info->flags |= SDDR09_WP;       /* write protected */
                US_DEBUGP(" WP");
@@ -1092,69 +1120,36 @@ sddr09_get_cardinfo(struct us_data *us, unsigned char flags) {
 static int
 sddr09_read_map(struct us_data *us) {
 
-       struct scatterlist *sg;
        struct sddr09_card_info *info = (struct sddr09_card_info *) us->extra;
        int numblocks, alloc_len, alloc_blocks;
        int i, j, result;
-       unsigned char *ptr;
+       unsigned char *buffer, *buffer_end, *ptr;
        unsigned int lba, lbact;
 
        if (!info->capacity)
                return -1;
 
-       // read 64 (1<<6) bytes for every block 
-       // ( 1 << ( blockshift + pageshift ) bytes)
-       //       of capacity:
-       // (1<<6)*capacity/(1<<(b+p)) =
-       // ((1<<6)*capacity)>>(b+p) =
-       // capacity>>(b+p-6)
-
-       alloc_len = info->capacity >> 
-               (info->blockshift + info->pageshift - CONTROL_SHIFT);
-
-       // Allocate a number of scatterlist structures according to
-       // the number of 128k blocks in the alloc_len. Adding 128k-1
-       // and then dividing by 128k gives the correct number of blocks.
-       // 128k = 1<<17
-
-       alloc_blocks = (alloc_len + (1<<17) - 1) >> 17;
-       sg = kmalloc(alloc_blocks*sizeof(struct scatterlist),
-                    GFP_NOIO);
-       if (sg == NULL)
-               return 0;
-
-       for (i=0; i<alloc_blocks; i++) {
-               int alloc_req = (i < alloc_blocks-1 ? 1 << 17 : alloc_len);
-               char *vaddr = kmalloc(alloc_req, GFP_NOIO);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3)
-               sg[i].page = virt_to_page(vaddr);
-               sg[i].offset = offset_in_page(vaddr);
-#else
-               sg[i].address = vaddr;
-#endif
-               sg[i].length = alloc_req;
-               alloc_len -= alloc_req;
-       }
-
-       for (i=0; i<alloc_blocks; i++)
-               if (sg[i].page == NULL) {
-                       for (i=0; i<alloc_blocks; i++)
-                               if (sg[i].page != NULL)
-                                       kfree(sg_address(sg[i]));
-                       kfree(sg);
-                       return 0;
-               }
+       // size of a block is 1 << (blockshift + pageshift) bytes
+       // divide into the total capacity to get the number of blocks
 
        numblocks = info->capacity >> (info->blockshift + info->pageshift);
 
-       result = sddr09_read_control(us, 0, numblocks,
-                                    (unsigned char *)sg, alloc_blocks);
-       if (result != USB_STOR_TRANSPORT_GOOD) {
-               for (i=0; i<alloc_blocks; i++)
-                       kfree(sg_address(sg[i]));
-               kfree(sg);
-               return -1;
+       // read 64 bytes for every block (actually 1 << CONTROL_SHIFT)
+       // but only use a 64 KB buffer
+       // buffer size used must be a multiple of (1 << CONTROL_SHIFT)
+#define SDDR09_READ_MAP_BUFSZ 65536
+
+       alloc_blocks = min(numblocks, SDDR09_READ_MAP_BUFSZ >> CONTROL_SHIFT);
+       alloc_len = (alloc_blocks << CONTROL_SHIFT);
+       buffer = kmalloc(alloc_len, GFP_NOIO);
+       if (buffer == NULL) {
+               printk("sddr09_read_map: out of memory\n");
+               result = -1;
+               goto done;
        }
+       buffer_end = buffer + alloc_len;
+
+#undef SDDR09_READ_MAP_BUFSZ
 
        kfree(info->lba_to_pba);
        kfree(info->pba_to_lba);
@@ -1162,29 +1157,35 @@ sddr09_read_map(struct us_data *us) {
        info->pba_to_lba = kmalloc(numblocks*sizeof(int), GFP_NOIO);
 
        if (info->lba_to_pba == NULL || info->pba_to_lba == NULL) {
-               kfree(info->lba_to_pba);
-               kfree(info->pba_to_lba);
-               info->lba_to_pba = NULL;
-               info->pba_to_lba = NULL;
-               for (i=0; i<alloc_blocks; i++)
-                       kfree(sg_address(sg[i]));
-               kfree(sg);
-               return 0;
+               printk("sddr09_read_map: out of memory\n");
+               result = -1;
+               goto done;
        }
 
        for (i = 0; i < numblocks; i++)
                info->lba_to_pba[i] = info->pba_to_lba[i] = UNDEF;
 
-       ptr = sg_address(sg[0]);
-
        /*
         * Define lba-pba translation table
         */
-       // Each block is 64 bytes of control data, so block i is located in
-       // scatterlist block i*64/128k = i*(2^6)*(2^-17) = i*(2^-11)
 
-       for (i=0; i<numblocks; i++) {
-               ptr = sg_address(sg[i>>11]) + ((i&0x7ff)<<6);
+       ptr = buffer_end;
+       for (i = 0; i < numblocks; i++) {
+               ptr += (1 << CONTROL_SHIFT);
+               if (ptr >= buffer_end) {
+                       unsigned long address;
+
+                       address = i << (info->pageshift + info->blockshift);
+                       result = sddr09_read_control(
+                               us, address>>1,
+                               min(alloc_blocks, numblocks - i),
+                               buffer, 0);
+                       if (result != USB_STOR_TRANSPORT_GOOD) {
+                               result = -1;
+                               goto done;
+                       }
+                       ptr = buffer;
+               }
 
                if (i == 0 || i == 1) {
                        info->pba_to_lba[i] = UNUSABLE;
@@ -1196,7 +1197,7 @@ sddr09_read_map(struct us_data *us) {
                        if (ptr[j] != 0)
                                goto nonz;
                info->pba_to_lba[i] = UNUSABLE;
-               printk("sddr09: PBA %04X has no logical mapping\n", i);
+               printk("sddr09: PBA %d has no logical mapping\n", i);
                continue;
 
        nonz:
@@ -1209,7 +1210,7 @@ sddr09_read_map(struct us_data *us) {
        nonff:
                /* normal PBAs start with six FFs */
                if (j < 6) {
-                       printk("sddr09: PBA %04X has no logical mapping: "
+                       printk("sddr09: PBA %d has no logical mapping: "
                               "reserved area = %02X%02X%02X%02X "
                               "data status %02X block status %02X\n",
                               i, ptr[0], ptr[1], ptr[2], ptr[3],
@@ -1219,7 +1220,7 @@ sddr09_read_map(struct us_data *us) {
                }
 
                if ((ptr[6] >> 4) != 0x01) {
-                       printk("sddr09: PBA %04X has invalid address field "
+                       printk("sddr09: PBA %d has invalid address field "
                               "%02X%02X/%02X%02X\n",
                               i, ptr[6], ptr[7], ptr[11], ptr[12]);
                        info->pba_to_lba[i] = UNUSABLE;
@@ -1228,7 +1229,7 @@ sddr09_read_map(struct us_data *us) {
 
                /* check even parity */
                if (parity[ptr[6] ^ ptr[7]]) {
-                       printk("sddr09: Bad parity in LBA for block %04X"
+                       printk("sddr09: Bad parity in LBA for block %d"
                               " (%02X %02X)\n", i, ptr[6], ptr[7]);
                        info->pba_to_lba[i] = UNUSABLE;
                        continue;
@@ -1247,27 +1248,32 @@ sddr09_read_map(struct us_data *us) {
                 */
 
                if (lba >= 1000) {
-                       unsigned long address;
-
-                       printk("sddr09: Bad LBA %04X for block %04X\n",
+                       printk("sddr09: Bad low LBA %d for block %d\n",
                               lba, i);
-                       info->pba_to_lba[i] = UNDEF /* UNUSABLE */;
-                       if (erase_bad_lba_entries) {
-                               /* some cameras cannot erase a card if it has
-                                  bad entries, so we supply this function */
-                               address = (i << (info->pageshift + info->blockshift));
-                               sddr09_erase(us, address>>1);
-                       }
-                       continue;
+                       goto possibly_erase;
                }
 
                lba += 1000*(i/0x400);
 
-               if (lba<0x10 || (lba >= 0x3E0 && lba < 0x3EF))
-                       US_DEBUGP("LBA %04X <-> PBA %04X\n", lba, i);
+               if (info->lba_to_pba[lba] != UNDEF) {
+                       printk("sddr09: LBA %d seen for PBA %d and %d\n",
+                              lba, info->lba_to_pba[lba], i);
+                       goto possibly_erase;
+               }
 
                info->pba_to_lba[i] = lba;
                info->lba_to_pba[lba] = i;
+               continue;
+
+       possibly_erase:
+               if (erase_bad_lba_entries) {
+                       unsigned long address;
+
+                       address = (i << (info->pageshift + info->blockshift));
+                       sddr09_erase(us, address>>1);
+                       info->pba_to_lba[i] = UNDEF;
+               } else
+                       info->pba_to_lba[i] = UNUSABLE;
        }
 
        /*
@@ -1292,11 +1298,17 @@ sddr09_read_map(struct us_data *us) {
        }
        info->lbact = lbact;
        US_DEBUGP("Found %d LBA's\n", lbact);
+       result = 0;
 
-       for (i=0; i<alloc_blocks; i++)
-               kfree(sg_address(sg[i]));
-       kfree(sg);
-       return 0;
+ done:
+       if (result != 0) {
+               kfree(info->lba_to_pba);
+               kfree(info->pba_to_lba);
+               info->lba_to_pba = NULL;
+               info->pba_to_lba = NULL;
+       }
+       kfree(buffer);
+       return result;
 }
 
 static void
@@ -1438,6 +1450,7 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
                cardinfo = sddr09_get_cardinfo(us, info->flags);
                if (!cardinfo) {
                        /* probably no media */
+               init_error:
                        sensekey = 0x02;        /* not ready */
                        sensecode = 0x3a;       /* medium not present */
                        return USB_STOR_TRANSPORT_FAILED;
@@ -1451,7 +1464,10 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
                info->blockmask = info->blocksize - 1;
 
                // map initialization, must follow get_cardinfo()
-               sddr09_read_map(us);
+               if (sddr09_read_map(us)) {
+                       /* probably out of memory */
+                       goto init_error;
+               }
 
                // Report capacity
 
@@ -1472,12 +1488,13 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
                return USB_STOR_TRANSPORT_GOOD;
        }
 
-       if (srb->cmnd[0] == MODE_SENSE) {
+       if (srb->cmnd[0] == MODE_SENSE || srb->cmnd[0] == MODE_SENSE_10) {
                int modepage = (srb->cmnd[2] & 0x3F);
                int len;
 
                /* They ask for the Read/Write error recovery page,
                   or for all pages. Give as much as they have room for. */
+               /* %% We should check DBD %% */
                if (modepage == 0x01 || modepage == 0x3F) {
 
                        US_DEBUGP("SDDR09: Dummy up request for "
@@ -1496,20 +1513,14 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
                        return USB_STOR_TRANSPORT_GOOD;
                }
 
-               return USB_STOR_TRANSPORT_ERROR;
+               sensekey = 0x05;        /* illegal request */
+               sensecode = 0x24;       /* invalid field in CDB */
+               return USB_STOR_TRANSPORT_FAILED;
        }
 
-       if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL) {
-
-               US_DEBUGP(
-                       "SDDR09: %s medium removal. Not that I can do"
-                       " anything about it...\n",
-                       (srb->cmnd[4]&0x03) ? "Prevent" : "Allow");
-
+       if (srb->cmnd[0] == ALLOW_MEDIUM_REMOVAL)
                return USB_STOR_TRANSPORT_GOOD;
 
-       }
-
        havefakesense = 0;
 
        if (srb->cmnd[0] == READ_10) {
@@ -1542,8 +1553,10 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
 
        if (srb->cmnd[0] != TEST_UNIT_READY &&
            srb->cmnd[0] != REQUEST_SENSE) {
+               sensekey = 0x05;        /* illegal request */
+               sensecode = 0x20;       /* invalid command */
                havefakesense = 1;
-               return USB_STOR_TRANSPORT_ERROR;
+               return USB_STOR_TRANSPORT_FAILED;
        }
 
        for (; srb->cmd_len<12; srb->cmd_len++)
@@ -1555,8 +1568,7 @@ int sddr09_transport(Scsi_Cmnd *srb, struct us_data *us)
        for (i=0; i<12; i++)
                sprintf(string+strlen(string), "%02X ", srb->cmnd[i]);
 
-       US_DEBUGP("SDDR09: Send control for command %s\n",
-                 string);
+       US_DEBUGP("SDDR09: Send control for command %s\n", string);
 
        result = sddr09_send_scsi_command(us, srb->cmnd, 12);
        if (result != USB_STOR_TRANSPORT_GOOD) {
index abbb98c..173cf43 100644 (file)
@@ -760,6 +760,7 @@ void usb_stor_stop_transport(struct us_data *us)
 int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
        unsigned int transfer_length = srb->request_bufflen;
+       unsigned int pipe = 0;
        int result;
 
        /* COMMAND STAGE */
@@ -785,7 +786,7 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
        /* DATA STAGE */
        /* transfer the data payload for this command, if one exists*/
        if (transfer_length) {
-               unsigned int pipe = srb->sc_data_direction == SCSI_DATA_READ ? 
+               pipe = srb->sc_data_direction == SCSI_DATA_READ ? 
                                us->recv_bulk_pipe : us->send_bulk_pipe;
                result = usb_stor_bulk_transfer_sg(us, pipe,
                                        srb->request_buffer, transfer_length,
@@ -813,12 +814,9 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
                if (srb->cmnd[0] == REQUEST_SENSE ||
                    srb->cmnd[0] == INQUIRY)
                        return USB_STOR_TRANSPORT_GOOD;
-               else {
-                       if (us->iobuf[0])
-                               return USB_STOR_TRANSPORT_FAILED;
-                       else
-                               return USB_STOR_TRANSPORT_GOOD;
-               }
+               if (us->iobuf[0])
+                       goto Failed;
+               return USB_STOR_TRANSPORT_GOOD;
        }
 
        /* If not UFI, we interpret the data as a result code 
@@ -835,13 +833,17 @@ int usb_stor_CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
                case 0x00: 
                        return USB_STOR_TRANSPORT_GOOD;
                case 0x01: 
-                       return USB_STOR_TRANSPORT_FAILED;
-               default: 
-                       return USB_STOR_TRANSPORT_ERROR;
+                       goto Failed;
        }
-
-       /* we should never get here, but if we do, we're in trouble */
        return USB_STOR_TRANSPORT_ERROR;
+
+       /* the CBI spec requires that the bulk pipe must be cleared
+        * following any data-in/out command failure (section 2.4.3.1.3)
+        */
+  Failed:
+       if (pipe)
+               usb_stor_clear_halt(us, pipe);
+       return USB_STOR_TRANSPORT_FAILED;
 }
 
 /*
@@ -924,6 +926,7 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
        struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf;
        struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf;
        unsigned int transfer_length = srb->request_bufflen;
+       unsigned int residue;
        int result;
        int fake_sense = 0;
 
@@ -999,9 +1002,10 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
                return USB_STOR_TRANSPORT_ERROR;
 
        /* check bulk status */
-       US_DEBUGP("Bulk Status S 0x%x T 0x%x R %d Stat 0x%x\n",
+       residue = le32_to_cpu(bcs->Residue);
+       US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
                        le32_to_cpu(bcs->Signature), bcs->Tag, 
-                       bcs->Residue, bcs->Status);
+                       residue, bcs->Status);
        if ((bcs->Signature != cpu_to_le32(US_BULK_CS_SIGN) &&
                    bcs->Signature != cpu_to_le32(US_BULK_CS_OLYMPUS_SIGN)) ||
                        bcs->Tag != srb->serial_number || 
@@ -1010,6 +1014,11 @@ int usb_stor_Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
                return USB_STOR_TRANSPORT_ERROR;
        }
 
+       /* try to compute the actual residue, based on how much data
+        * was really transferred and what the device tells us */
+       residue = min(residue, transfer_length);
+       srb->resid = max(srb->resid, (int) residue);
+
        /* based on the status code, we report good or bad */
        switch (bcs->Status) {
                case US_BULK_STAT_OK:
index fdec517..639950e 100644 (file)
@@ -234,15 +234,9 @@ struct usb_driver usb_storage_driver = {
  */
 
 void fill_inquiry_response(struct us_data *us, unsigned char *data,
-               unsigned int data_len) {
-
-       int i;
-       struct scatterlist *sg;
-       int len =
-               us->srb->request_bufflen > data_len ? data_len :
-               us->srb->request_bufflen;
-       int transferred;
-       int amt;
+               unsigned int data_len)
+{
+       unsigned int index, offset;
 
        if (data_len<36) // You lose.
                return;
@@ -270,22 +264,11 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
                data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F);
        }
 
-       if (us->srb->use_sg) {
-               sg = (struct scatterlist *)us->srb->request_buffer;
-               for (i=0; i<us->srb->use_sg; i++)
-                       memset(sg_address(sg[i]), 0, sg[i].length);
-               for (i=0, transferred=0; 
-                               i<us->srb->use_sg && transferred < len;
-                               i++) {
-                       amt = sg[i].length > len-transferred ? 
-                                       len-transferred : sg[i].length;
-                       memcpy(sg_address(sg[i]), data+transferred, amt);
-                       transferred -= amt;
-               }
-       } else {
-               memset(us->srb->request_buffer, 0, us->srb->request_bufflen);
-               memcpy(us->srb->request_buffer, data, len);
-       }
+       index = offset = 0;
+       usb_stor_access_xfer_buf(data, data_len, us->srb,
+                       &index, &offset, TO_XFER_BUF);
+       if (data_len < us->srb->request_bufflen)
+               us->srb->resid = us->srb->request_bufflen - data_len;
 }
 
 static int usb_stor_control_thread(void * __us)
index 2b728ed..9323e54 100644 (file)
@@ -1536,6 +1536,7 @@ aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
        /* fill in info */
        info->fbops = &aty128fb_ops;
        info->flags = FBINFO_FLAG_DEFAULT;
+       info->dev = &pdev->dev;
 
 #ifdef CONFIG_PMAC_PBOOK
        par->lcd_on = default_lcd_on;
index cec5a7d..369ab07 100644 (file)
@@ -2787,6 +2787,7 @@ int __init clgenfb_init(void)
        fb_info->gen.info.switch_con = &fbgen_switch;
        fb_info->gen.info.updatevar = &fbgen_update_var;
        fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT;
+       fb_info->gen.info.dev = fb_info->pdev;
 
        for (j = 0; j < 256; j++) {
                if (j < 16) {
index ecdd31f..64fa60e 100644 (file)
@@ -1366,6 +1366,7 @@ static int __devinit cyberpro_common_probe(struct cfb_info *cfb)
        cfb->fb.fix.smem_len   = smem_size;
        cfb->fb.fix.mmio_len   = MMIO_SIZE;
        cfb->fb.screen_base    = cfb->region;
+       cfb->fb.dev            = &cfb->dev->dev;
 
        err = -EINVAL;
        if (!fb_find_mode(&cfb->fb.var, &cfb->fb, NULL, NULL, 0,
index a89dbc7..3140310 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/kmod.h>
 #endif
 #include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
 
 #if defined(__mc68000__) || defined(CONFIG_APUS)
 #include <asm/setup.h>
@@ -1199,6 +1200,83 @@ static struct file_operations fb_fops = {
 #endif
 };
 
+struct fb_dev {
+       struct list_head node;
+       dev_t dev;
+       struct class_device class_dev;
+};
+#define to_fb_dev(d) container_of(d, struct fb_dev, class_dev)
+
+static void release_fb_dev(struct class_device *class_dev)
+{
+       struct fb_dev *fb_dev = to_fb_dev(class_dev);
+       kfree(fb_dev);
+}
+
+static struct class fb_class = {
+       .name           = "video",
+       .release        = &release_fb_dev,
+};
+
+static LIST_HEAD(fb_dev_list);
+static spinlock_t fb_dev_list_lock = SPIN_LOCK_UNLOCKED;
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct fb_dev *fb_dev = to_fb_dev(class_dev);
+       return print_dev_t(buf, fb_dev->dev);
+}
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+static void fb_add_class_device(int minor, struct device *device)
+{
+       struct fb_dev *fb_dev = NULL;
+       int retval;
+
+       fb_dev = kmalloc(sizeof(*fb_dev), GFP_KERNEL);
+       if (!fb_dev)
+               return;
+       memset(fb_dev, 0x00, sizeof(*fb_dev));
+
+       fb_dev->dev = MKDEV(FB_MAJOR, minor);
+       fb_dev->class_dev.dev = device;
+       fb_dev->class_dev.class = &fb_class;
+       snprintf(fb_dev->class_dev.class_id, BUS_ID_SIZE, "fb%d", minor);
+       retval = class_device_register(&fb_dev->class_dev);
+       if (retval)
+               goto error;
+       class_device_create_file(&fb_dev->class_dev, &class_device_attr_dev);
+       spin_lock(&fb_dev_list_lock);
+       list_add(&fb_dev->node, &fb_dev_list);
+       spin_unlock(&fb_dev_list_lock);
+       return;
+error:
+       kfree(fb_dev);
+}
+
+void fb_remove_class_device(int minor)
+{
+       struct fb_dev *fb_dev = NULL;
+       struct list_head *tmp;
+       int found = 0;
+
+       spin_lock(&fb_dev_list_lock);
+       list_for_each(tmp, &fb_dev_list) {
+               fb_dev = list_entry(tmp, struct fb_dev, node);
+               if ((MINOR(fb_dev->dev) == minor)) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               list_del(&fb_dev->node);
+               spin_unlock(&fb_dev_list_lock);
+               class_device_unregister(&fb_dev->class_dev);
+       } else {
+               spin_unlock(&fb_dev_list_lock);
+       }
+}
+
 /**
  *     register_framebuffer - registers a frame buffer device
  *     @fb_info: frame buffer info structure
@@ -1242,6 +1320,8 @@ register_framebuffer(struct fb_info *fb_info)
 
        devfs_mk_cdev(MKDEV(FB_MAJOR, i),
                        S_IFCHR | S_IRUGO | S_IWUGO, "fb/%d", i);
+
+       fb_add_class_device(i, fb_info->dev);
        return 0;
 }
 
@@ -1270,6 +1350,7 @@ unregister_framebuffer(struct fb_info *fb_info)
                kfree(fb_info->pixmap.addr);
        registered_fb[i]=NULL;
        num_registered_fb--;
+       fb_remove_class_device(i);
        return 0;
 }
 
@@ -1294,6 +1375,8 @@ fbmem_init(void)
        if (register_chrdev(FB_MAJOR,"fb",&fb_fops))
                printk("unable to get major %d for fb devs\n", FB_MAJOR);
 
+       class_register(&fb_class);
+
 #ifdef CONFIG_FB_OF
        if (ofonly) {
                offb_init();
index 451e1a4..8f8566d 100644 (file)
@@ -1880,6 +1880,7 @@ static int __devinit i810fb_init_pci (struct pci_dev *dev,
        info->fbops = &par->i810fb_ops;
        info->pseudo_palette = par->pseudo_palette;
        info->flags = FBINFO_FLAG_DEFAULT;
+       info->dev = &dev->dev;
        
        fb_alloc_cmap(&info->cmap, 256, 0);
 
index 7e0353a..0295696 100644 (file)
@@ -332,7 +332,7 @@ static struct fb_ops igafb_ops = {
 #endif
 };
 
-static int __init iga_init(struct fb_info *info, struct iga_par *par)
+static int __init iga_init(struct fb_info *info, struct iga_par *par, struct pci_dev *dev)
 {
         char vramsz = iga_inb(par, IGA_EXT_CNTRL, IGA_IDX_EXT_BUS_CNTL) 
                                                         & MEM_SIZE_ALIAS;
@@ -358,6 +358,7 @@ static int __init iga_init(struct fb_info *info, struct iga_par *par)
 
        info->fbops = &igafb_ops;
        info->flags = FBINFO_FLAG_DEFAULT;
+       info->dev = &dev->dev;
 
        fb_alloc_cmap(&info->cmap, video_cmap_len, 0);
 
@@ -529,7 +530,7 @@ int __init igafb_init(void)
        info->fix = igafb_fix;
        info->pseudo_palette = (void *)(par + 1);
 
-       if (!iga_init(info, par)) {
+       if (!iga_init(info, par, pdev)) {
                iounmap((void *)par->io_base);
                iounmap(info->screen_base);
                if (par->mmap_map)
index 3c7c422..ae22e11 100644 (file)
@@ -1348,7 +1348,7 @@ static struct fb_ops imsttfb_ops = {
 };
 
 static void __init 
-init_imstt(struct fb_info *info)
+init_imstt(struct fb_info *info, struct pci_dev *pdev)
 {
        struct imstt_par *par = (struct imstt_par *) info->par;
        __u32 i, tmp, *ip, *end;
@@ -1442,6 +1442,7 @@ init_imstt(struct fb_info *info)
 
        info->fbops = &imsttfb_ops;
        info->flags = FBINFO_FLAG_DEFAULT;
+       info->dev = &pdev->dev;
 
        fb_alloc_cmap(&info->cmap, 0, 0);
 
@@ -1520,7 +1521,7 @@ imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        par->cmap_regs = (__u8 *)ioremap(addr + 0x840000, 0x1000);
        info->par = par;
        info->pseudo_palette = (void *) (par + 1);
-       init_imstt(info);
+       init_imstt(info, pdev);
 
        pci_set_drvdata(pdev, info);
        return 0;
index 27221ec..a38123e 100644 (file)
@@ -605,6 +605,7 @@ static int matroxfb_dh_regit(CPMINFO struct matroxfb_dh_fb_info* m2info) {
        m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
        m2info->fbcon.currcon = -1;
        m2info->fbcon.pseudo_palette = m2info->cmap;
+       m2info->fbcon.dev = &m2info->primary_dev->pcidev->dev;
        fb_alloc_cmap(&m2info->fbcon.cmap, 256, 1);
 
        if (mem < 64)
index de02f51..ae37adb 100644 (file)
@@ -1943,6 +1943,7 @@ static struct fb_info *__devinit neo_alloc_fb_info(struct pci_dev *dev, const st
        info->flags = FBINFO_FLAG_DEFAULT;
        info->par = par;
        info->pseudo_palette = (void *) (par + 1);
+       info->dev = &dev->dev;
 
        fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
 
index af1a7fa..9e40393 100644 (file)
@@ -3582,6 +3582,7 @@ static int radeonfb_pci_register (struct pci_dev *pdev,
        pci_set_drvdata(pdev, rinfo);
        rinfo->next = board_list;
        board_list = rinfo;
+       rinfo->info.dev = &pdev->dev;
 
        if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
                printk ("radeonfb: could not register framebuffer\n");
index a53cad9..e6df08b 100644 (file)
@@ -1751,6 +1751,7 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
        if (info->pixmap.addr == NULL)
                goto err_out_kfree1;
        memset(info->pixmap.addr, 0, 64 * 1024);
+       info->dev = &pd->dev;
 
        strcat(rivafb_fix.id, rci->name);
        default_par->riva.Architecture = rci->arch_rev;
index 3866b83..4354f7f 100644 (file)
@@ -4507,6 +4507,7 @@ int __init sisfb_init(void)
                sis_fb_info.par = &ivideo;
                sis_fb_info.screen_base = ivideo.video_vbase;
                sis_fb_info.fbops = &sisfb_ops;
+               sis_fb_info.dev = &pdev->dev;
                sisfb_get_fix(&sis_fb_info.fix, -1, &sis_fb_info);
                sis_fb_info.pseudo_palette = pseudo_palette;
                
index d08d459..3c69b05 100644 (file)
@@ -1477,6 +1477,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
        info->fbops     = &sstfb_ops;
        info->currcon   = -1;
        info->pseudo_palette = &all->pseudo_palette;
+       info->dev       = &pdev->dev;
 
        fix->type       = FB_TYPE_PACKED_PIXELS;
        fix->visual     = FB_VISUAL_TRUECOLOR;
index 8936d55..0690b61 100644 (file)
@@ -1248,6 +1248,7 @@ static int __devinit tdfxfb_probe(struct pci_dev *pdev,
        info->par               = default_par;
        info->pseudo_palette    = (void *)(default_par + 1); 
        info->flags             = FBINFO_FLAG_DEFAULT;
+       info->dev               = &pdev->dev;
 
 #ifndef MODULE
        if (!mode_option)
@@ -1357,3 +1358,5 @@ void tdfxfb_setup(char *options, int *ints)
 }
 #endif
 
+
+
index 2d13c0c..6090844 100644 (file)
@@ -1430,6 +1430,7 @@ tgafb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
        all->info.currcon = -1;
        all->info.par = &all->par;
        all->info.pseudo_palette = all->pseudo_palette;
+       all->info.dev = &pdev->dev;
 
        /* This should give a reasonable default video mode.  */
 
index ae6d3c9..fe3214f 100644 (file)
@@ -1156,6 +1156,7 @@ static int __devinit trident_pci_probe(struct pci_dev * dev, const struct pci_de
                default_var.accel_flags &= ~FB_ACCELF_TEXT;
        default_var.activate |= FB_ACTIVATE_NOW;
        fb_info.var = default_var;
+       fb_info.dev = &dev->dev;
        if (register_framebuffer(&fb_info) < 0) {
                output("Could not register Trident framebuffer\n");
                return -EINVAL;
index 3cea188..ebaf6c4 100644 (file)
@@ -83,7 +83,8 @@ static void remove_dir(struct dentry * d)
        struct dentry * parent = dget(d->d_parent);
        down(&parent->d_inode->i_sem);
        d_delete(d);
-       simple_rmdir(parent->d_inode,d);
+       if (d->d_inode)
+               simple_rmdir(parent->d_inode,d);
 
        pr_debug(" o %s removing done (%d)\n",d->d_name.name,
                 atomic_read(&d->d_count));
index 6c43fdb..f41486e 100644 (file)
@@ -383,8 +383,20 @@ extern int firmware_register(struct subsystem *);
 extern void firmware_unregister(struct subsystem *);
 
 /* debugging and troubleshooting/diagnostic helpers. */
+#ifdef CONFIG_DEBUG_DEV_PRINTK
+#define dev_printk(level, dev, format, arg...)                 \
+       do {                                                    \
+               if (!(dev) || !(dev)->driver)                   \
+                       WARN_ON(1);                             \
+               else                                            \
+                       printk(level "%s %s: " format ,         \
+                               (dev)->driver->name ,           \
+                               (dev)->bus_id , ## arg);        \
+       } while (0)
+#else
 #define dev_printk(level, dev, format, arg...) \
        printk(level "%s %s: " format , (dev)->driver->name , (dev)->bus_id , ## arg)
+#endif
 
 #ifdef DEBUG
 #define dev_dbg(dev, format, arg...)           \
index 2c68529..7bafeca 100644 (file)
@@ -352,6 +352,7 @@ struct fb_pixmap {
 struct fb_info;
 struct vm_area_struct;
 struct file;
+struct device;
 
     /*
      *  Frame buffer operations
@@ -412,6 +413,7 @@ struct fb_info {
    struct vc_data *display_fg;         /* Console visible on this display */
    int currcon;                                /* Current VC. */       
    void *pseudo_palette;                /* Fake palette of 16 colors */ 
+   struct device *dev;                  /* pointer to the device for this fb */
 #ifdef CONFIG_BOOTSPLASH
    struct splash_data *splash_data;
    unsigned char *splash_pic;
index 7dc4abf..74a7286 100644 (file)
 #define I2C_DRIVERID_FS451 1037
 #define I2C_DRIVERID_W83627HF 1038
 #define I2C_DRIVERID_LM85 1039
+#define I2C_DRIVERID_LM83 1040
 
 /*
  * ---- Adapter types ----------------------------------------------------
 #define I2C_HW_SMBUS_AMD8111   0x0a
 #define I2C_HW_SMBUS_SCX200    0x0b
 #define I2C_HW_SMBUS_NFORCE2   0x0c
+#define I2C_HW_SMBUS_W9968CF   0x0d
 
 /* --- ISA pseudo-adapter                                              */
 #define I2C_HW_ISA 0x00
index 6b7f9c1..98f6166 100644 (file)
 
 #define TUN_MINOR           200
 
+struct device;
+
 struct miscdevice 
 {
        int minor;
        const char *name;
        struct file_operations *fops;
        struct list_head list;
+       struct device *dev;
        char devfs_name[64];
 };
 
index 4494574..b48e949 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kmod.h>
 #include <linux/elf.h>
 #include <linux/stringify.h>
+#include <linux/kobject.h>
 #include <asm/local.h>
 
 #include <asm/module.h>
@@ -187,6 +188,8 @@ enum module_state
 
 struct module
 {
+       struct kobject  kobj;
+
        enum module_state state;
 
        /* Member of list of modules */
index 654840d..8001647 100644 (file)
@@ -768,26 +768,7 @@ static inline int pci_module_init(struct pci_driver *drv)
 {
        int rc = pci_register_driver (drv);
 
-       if (rc > 0)
-               return 0;
-
-       /* iff CONFIG_HOTPLUG and built into kernel, we should
-        * leave the driver around for future hotplug events.
-        * For the module case, a hotplug daemon of some sort
-        * should load a module in response to an insert event. */
-#if defined(CONFIG_HOTPLUG) && !defined(MODULE)
-       if (rc == 0)
-               return 0;
-#else
-       if (rc == 0)
-               rc = -ENODEV;           
-#endif
-
-       /* if we get here, we need to clean up pci driver instance
-        * and return some sort of error */
-       pci_unregister_driver (drv);
-       
-       return rc;
+       return rc < 0 ? rc : 0;
 }
 
 /*
index 428f597..c2252b6 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <linux/fs.h>
+#include <linux/device.h>
 
 #define SND_DEV_CTL            0       /* Control port /dev/mixer */
 #define SND_DEV_SEQ            1       /* Sequencer output /dev/sequencer (FM
  *     Sound core interface functions
  */
  
-extern int register_sound_special(struct file_operations *fops, int unit);
-extern int register_sound_mixer(struct file_operations *fops, int dev);
-extern int register_sound_midi(struct file_operations *fops, int dev);
-extern int register_sound_dsp(struct file_operations *fops, int dev);
-extern int register_sound_synth(struct file_operations *fops, int dev);
+extern int register_sound_special(struct file_operations *fops, int unit, struct device *device);
+extern int register_sound_mixer(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_midi(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_dsp(struct file_operations *fops, int dev, struct device *device);
+extern int register_sound_synth(struct file_operations *fops, int dev, struct device *device);
+extern int sound_add_class_device(char *name, struct class_device *class_dev, struct device *dev);
 
 extern void unregister_sound_special(int unit);
 extern void unregister_sound_mixer(int unit);
 extern void unregister_sound_midi(int unit);
 extern void unregister_sound_dsp(int unit);
 extern void unregister_sound_synth(int unit);
+extern void sound_remove_class_device(struct class_device *class_dev);
index 916378f..682e951 100644 (file)
@@ -116,6 +116,17 @@ struct usb_ctrlrequest {
 #define USB_DT_DEVICE_QUALIFIER                0x06
 #define USB_DT_OTHER_SPEED_CONFIG      0x07
 #define USB_DT_INTERFACE_POWER         0x08
+/* these are from a minor usb 2.0 revision (ECN) */
+#define USB_DT_OTG                     0x09
+#define USB_DT_DEBUG                   0x0a
+#define USB_DT_INTERFACE_ASSOCIATION   0x0b
+
+/* conventional codes for class-specific descriptors */
+#define USB_DT_CS_DEVICE               0x21
+#define USB_DT_CS_CONFIG               0x22
+#define USB_DT_CS_STRING               0x23
+#define USB_DT_CS_INTERFACE            0x24
+#define USB_DT_CS_ENDPOINT             0x25
 
 /* All standard descriptors have these 2 fields at the beginning */
 struct usb_descriptor_header {
@@ -165,6 +176,7 @@ struct usb_device_descriptor {
 #define USB_CLASS_CDC_DATA             0x0a
 #define USB_CLASS_CSCID                        0x0b    /* chip+ smart card */
 #define USB_CLASS_CONTENT_SEC          0x0d    /* content security */
+#define USB_CLASS_VIDEO                        0x0e
 #define USB_CLASS_APP_SPEC             0xfe
 #define USB_CLASS_VENDOR_SPEC          0xff
 
@@ -283,6 +295,36 @@ struct usb_qualifier_descriptor {
 
 /*-------------------------------------------------------------------------*/
 
+/* USB_DT_OTG (from OTG 1.0a supplement) */
+struct usb_otg_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  bmAttributes;     /* support for HNP, SRP, etc */
+} __attribute__ ((packed));
+
+/* from usb_otg_descriptor.bmAttributes */
+#define USB_OTG_SRP            (1 << 0)
+#define USB_OTG_HNP            (1 << 1)        /* swap host/device roles */
+
+/*-------------------------------------------------------------------------*/
+
+/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */
+struct usb_interface_assoc_descriptor {
+       __u8  bLength;
+       __u8  bDescriptorType;
+
+       __u8  bFirstInterface;
+       __u8  bInterfaceCount;
+       __u8  bFunctionClass;
+       __u8  bFunctionSubClass;
+       __u8  bFunctionProtocol;
+       __u8  iFunction;
+} __attribute__ ((packed));
+
+
+/*-------------------------------------------------------------------------*/
+
 /* USB 2.0 defines three speeds, here's how Linux identifies them */
 
 enum usb_device_speed {
index 8abbd9e..9b00fb6 100644 (file)
@@ -429,6 +429,7 @@ struct video_code
 #define VID_HARDWARE_CPIA2     33
 #define VID_HARDWARE_VICAM      34
 #define VID_HARDWARE_SF16FMR2  35
+#define VID_HARDWARE_W9968CF   36      /* W996[87]CF JPEG USB Dual Mode Cam */
 #endif /* __LINUX_VIDEODEV_H */
 
 /*
index 5e72865..c24c6bc 100644 (file)
@@ -65,6 +65,9 @@ struct _snd_hwdep {
        int used;
        unsigned int dsp_loaded;
        unsigned int exclusive: 1;
+       void (*release)(snd_hwdep_t *hwdep);
+       struct class_device class_dev;
+       struct device *dev;
 };
 
 extern int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep);
index 48fcf7c..3a19c62 100644 (file)
@@ -310,6 +310,7 @@ struct snd_opl3 {
        spinlock_t sys_timer_lock;      /* Lock for system timer access */
 #endif
        struct semaphore access_mutex;  /* locking */
+       struct device *dev;
 };
 
 /* opl3.c */
index 9ad2cd1..b1cd4e7 100644 (file)
@@ -445,6 +445,9 @@ struct _snd_pcm {
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
        snd_pcm_oss_t oss;
 #endif
+       void (*release)(snd_pcm_t *pcm);
+       struct class_device class_dev[2];
+       struct device *dev;
 };
 
 typedef struct _snd_pcm_notify {
index af0493a..82fb41d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <asm/semaphore.h>
+#include <linux/device.h>
 
 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
 #include "seq_device.h"
@@ -143,6 +144,9 @@ struct _snd_rawmidi {
 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
        snd_seq_device_t *seq_dev;
 #endif
+       void (*release)(snd_rawmidi_t *rmidi);
+       struct class_device class_dev;
+       struct device *dev_ptr;
 };
 
 /* main rawmidi functions */
index 205e0cc..f9c996f 100644 (file)
@@ -272,4 +272,12 @@ config KMOD
          runs modprobe with the appropriate arguments, thereby
          loading the module if it is available.  If unsure, say Y.
 
+config MODULE_SIG
+       bool "Module signature checking"
+       depends on MODULES && SECURITY_RSA
+       help
+         If you say Y here, signed module support will be enabled.
+         If unsure, say N.
+
 endmenu
+
index 503aecd..44af2ab 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
 obj-$(CONFIG_SMP) += cpu.o
 obj-$(CONFIG_UID16) += uid16.o
 obj-$(CONFIG_MODULES) += module.o
+obj-$(CONFIG_MODULE_SIG) += module-sig.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-$(CONFIG_PM) += power/
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
diff --git a/kernel/module-sig.c b/kernel/module-sig.c
new file mode 100644 (file)
index 0000000..a91981b
--- /dev/null
@@ -0,0 +1,87 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/elf.h>
+#include <linux/crypto.h>
+#include <asm/scatterlist.h>
+
+#define SHA1_DIGEST_SIZE        20
+
+extern int rsa_check_sig(char *sig, u8 *sha1);
+
+static u8 sha1_result[SHA1_DIGEST_SIZE];
+int module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings)
+{
+       int i;
+       unsigned int sig_index = 0;
+       static struct crypto_tfm *sha1_tfm;
+       char *sig;
+
+       /* Can't use find_sec as it only checks for Alloc bit */
+       for (i = 1; i < hdr->e_shnum; i++)
+               if (strcmp(secstrings+sechdrs[i].sh_name, "module_sig") == 0) {
+                       sig_index = i;
+                       break;
+               }
+       if (sig_index <= 0)
+               return 0;
+       printk(KERN_DEBUG "GREG: sig_index = %d sh_size = %d\n", sig_index, sechdrs[sig_index].sh_size);
+
+       sig = (char *)sechdrs[sig_index].sh_addr;
+       printk(KERN_DEBUG "GREG: ");
+       for (i = 0; i < sechdrs[sig_index].sh_size; ++i)
+               printk("%.2x ", (unsigned char)sig[i]);
+       printk("\n");
+
+       sha1_tfm = crypto_alloc_tfm("sha1", 0);
+       if (sha1_tfm == NULL) {
+               printk(KERN_DEBUG "GREG: failed to load transform for sha1\n");
+               return 0;
+       }
+
+       crypto_digest_init(sha1_tfm);
+
+       for (i = 1; i < hdr->e_shnum; i++) {
+               struct scatterlist sg;
+               void *temp;
+               int size;
+               const char *name = secstrings+sechdrs[i].sh_name;
+
+               /* We only care about sections with "text" or "data" in their names */
+               if ((strstr(name, "text") == NULL) &&
+                   (strstr(name, "data") == NULL))
+                       continue;
+               /* avoid the ".rel.*" sections too. */
+               if (strstr(name, ".rel.") != NULL)
+                       continue;
+
+               temp = (void *)sechdrs[i].sh_addr;
+               size = sechdrs[i].sh_size;
+               printk(KERN_DEBUG "GREG: sha1ing the %s section, size %d\n", name, size);
+               do {
+                       memset(&sg, 0x00, sizeof(struct scatterlist));
+                       sg.page = virt_to_page(temp);
+                       sg.offset = offset_in_page(temp);
+                       sg.length = min((unsigned long)size, (PAGE_SIZE - (unsigned long)sg.offset));
+                       size -= sg.length;
+                       temp += sg.length;
+                       printk(KERN_DEBUG "GREG: page = %p, .offset = %d, .length = %d, temp = %p, size = %d\n",
+                               sg.page, sg.offset, sg.length, temp, size);
+                       crypto_digest_update(sha1_tfm, &sg, 1);
+               } while (size > 0);
+       }
+       crypto_digest_final(sha1_tfm, sha1_result);
+       crypto_free_tfm(sha1_tfm);
+       printk(KERN_DEBUG "GREG: sha1 is: ");
+       for (i = 0; i < sizeof(sha1_result); ++i)
+               printk("%.2x ", sha1_result[i]);
+       printk("\n");
+
+       /* see if this sha1 is really encrypted in the section */
+       return rsa_check_sig(sig, &sha1_result[0]);
+}
+
+
diff --git a/kernel/module-sig.h b/kernel/module-sig.h
new file mode 100644 (file)
index 0000000..c7a5707
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef MODULE_SIG
+#define MODULE_SIG
+
+#ifdef CONFIG_MODULE_SIG
+extern int module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings);
+#else
+static int inline module_check_sig(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings)
+{
+       return 0;
+}
+#endif
+
+#endif
index 2997c3e..4ae9a30 100644 (file)
@@ -36,6 +36,7 @@
 #include <asm/semaphore.h>
 #include <asm/pgalloc.h>
 #include <asm/cacheflush.h>
+#include "module-sig.h"
 
 #if 0
 #define DEBUGP printk
@@ -615,6 +616,7 @@ unsigned int module_refcount(struct module *mod)
 
        for (i = 0; i < NR_CPUS; i++)
                total += local_read(&mod->ref[i].count);
+       total += atomic_read(&mod->kobj.refcount);
        return total;
 }
 EXPORT_SYMBOL(module_refcount);
@@ -658,6 +660,8 @@ static void wait_for_zero_refcount(struct module *mod)
        down(&module_mutex);
 }
 
+static void mod_kobject_remove(struct module *mod);
+
 asmlinkage long
 sys_delete_module(const char __user *name_user, unsigned int flags)
 {
@@ -706,6 +710,10 @@ sys_delete_module(const char __user *name_user, unsigned int flags)
                        goto out;
                }
        }
+
+       /* unregister the kobject in this module */
+       mod_kobject_remove(mod);
+
        /* Stop the machine so refcounts can't move: irqs disabled. */
        DEBUGP("Stopping refcounts...\n");
        ret = stop_refcounts();
@@ -745,7 +753,7 @@ static void print_unload_info(struct seq_file *m, struct module *mod)
        struct module_use *use;
        int printed_something = 0;
 
-       seq_printf(m, " %u ", module_refcount(mod));
+       seq_printf(m, " %u ", module_refcount(mod) - atomic_read(&mod->kobj.refcount));
 
        /* Always include a trailing , so userspace can differentiate
            between this and the old multi-field proc format. */
@@ -1494,6 +1502,11 @@ static struct module *load_module(void __user *umod,
                goto free_hdr;
        }
 
+       if (module_check_sig(hdr, sechdrs, secstrings)) {
+               err = -EPERM;
+               goto free_hdr;
+       }
+
        /* Now copy in args */
        arglen = strlen_user(uargs);
        if (!arglen) {
@@ -1684,6 +1697,85 @@ static struct module *load_module(void __user *umod,
        else return ptr;
 }
 
+/* sysfs stuff */
+struct module_attribute {
+       struct attribute attr;
+       ssize_t (*show)(struct module *mod, char *);
+       ssize_t (*store)(struct module *mod, const char *, size_t);
+};
+#define to_module_attr(n) container_of(n, struct module_attribute, attr);
+#define to_module(n) container_of(n, struct module, kobj)
+
+static ssize_t module_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+       struct module *slot = to_module(kobj);
+       struct module_attribute *attribute = to_module_attr(attr);
+       return attribute->show ? attribute->show(slot, buf) : 0;
+}
+
+static ssize_t module_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t len)
+{
+       struct module *slot = to_module(kobj);
+       struct module_attribute *attribute = to_module_attr(attr);
+       return attribute->store ? attribute->store(slot, buf, len) : 0;
+}
+
+static struct sysfs_ops module_sysfs_ops = {
+       .show = module_attr_show,
+       .store = module_attr_store,
+};
+
+/* Huh?  A release() function that doesn't do anything?
+ * This is here only because a module has another reference count that
+ * it uses to determine if it should be cleaned up or not.  If the
+ * module wants to switch over to use the kobject reference instead of
+ * its own, then this release function needs to do some work.
+ */
+static void module_release(struct kobject *kobj)
+{
+}
+
+static struct kobj_type module_ktype = {
+       .sysfs_ops =    &module_sysfs_ops,
+       .release =      &module_release,
+};
+static decl_subsys(module, &module_ktype, NULL);
+
+static int __init module_subsys_init(void)
+{
+       return subsystem_register(&module_subsys);
+}
+core_initcall(module_subsys_init);
+
+static ssize_t show_mod_refcount(struct module *mod, char *buf)
+{
+       return sprintf(buf, "%d\n", module_refcount(mod) - atomic_read(&mod->kobj.refcount));
+}
+
+static struct module_attribute mod_refcount = {
+       .attr = {.name = "refcount", .mode = S_IRUGO},
+       .show = show_mod_refcount,
+};
+
+static int mod_kobject_init(struct module *mod)
+{
+       int retval;
+
+       memset(&mod->kobj, 0x00, sizeof(struct kobject));
+       kobject_set_name(&mod->kobj, mod->name);
+       kobj_set_kset_s(mod, module_subsys);
+       retval = kobject_register(&mod->kobj);
+       if (!retval)
+               retval = sysfs_create_file(&mod->kobj, &mod_refcount.attr);
+       return retval;
+}
+
+static void mod_kobject_remove(struct module *mod)
+{
+       sysfs_remove_file(&mod->kobj, &mod_refcount.attr);
+       kobject_unregister(&mod->kobj);
+}
+
 /* This is where the real work happens */
 asmlinkage long
 sys_init_module(void __user *umod,
@@ -1748,6 +1840,8 @@ sys_init_module(void __user *umod,
                return ret;
        }
 
+       mod_kobject_init(mod);
+
        /* Now it's a first class citizen! */
        down(&module_mutex);
        mod->state = MODULE_STATE_LIVE;
index 9521575..8e6836f 100644 (file)
@@ -24,5 +24,13 @@ config ZLIB_INFLATE
 config ZLIB_DEFLATE
        tristate
 
+
+config DEBUG_KOBJECT
+       bool "print lots of sysfs related debug infos"
+       default n
+       help
+         This option enables DEBUG in drivers/base/*.c and lib/kobject.c
+         If unsure, say N.
+
 endmenu
 
index 22bfc67..b550e15 100644 (file)
  * about using the kobject interface.
  */
 
+#ifdef DEBUG_KOBJECT
+#define DEBUG 1
+#else
 #undef DEBUG
+#endif
 
 #include <linux/kobject.h>
 #include <linux/string.h>
@@ -188,8 +192,8 @@ static void kset_hotplug(const char *action, struct kset *kset,
                }
        }
 
-       pr_debug ("%s: %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
-                 envp[0], envp[1], envp[2], envp[3]);
+       pr_debug ("%s: %s %s %s %s %s %s %s\n", __FUNCTION__, argv[0], argv[1],
+                 envp[0], envp[1], envp[2], envp[3], envp[4]);
        retval = call_usermodehelper (argv[0], argv, envp, 0);
        if (retval)
                pr_debug ("%s - call_usermodehelper returned %d\n",
@@ -216,6 +220,7 @@ static void kset_hotplug(const char *action, struct kset *kset,
 
 void kobject_init(struct kobject * kobj)
 {
+       WARN_ON(atomic_read(&kobj->refcount));
        atomic_set(&kobj->refcount,1);
        INIT_LIST_HEAD(&kobj->entry);
        kobj->kset = kset_get(kobj->kset);
@@ -239,8 +244,6 @@ static void unlink(struct kobject * kobj)
                list_del_init(&kobj->entry);
                up_write(&kobj->kset->subsys->rwsem);
        }
-       if (kobj->parent) 
-               kobject_put(kobj->parent);
        kobject_put(kobj);
 }
 
@@ -277,9 +280,11 @@ int kobject_add(struct kobject * kobj)
        kobj->parent = parent;
 
        error = create_dir(kobj);
-       if (error)
+       if (error) {
                unlink(kobj);
-       else {
+               if (parent)
+                       kobject_put(parent);
+       } else {
                /* If this kobj does not belong to a kset,
                   try to find a parent that does. */
                top_kobj = kobj;
@@ -443,6 +448,7 @@ struct kobject * kobject_get(struct kobject * kobj)
                atomic_inc(&kobj->refcount);
        } else
                ret = NULL;
+       WARN_ON((kobj != NULL) && (ret==NULL));
        return ret;
 }
 
@@ -455,6 +461,7 @@ void kobject_cleanup(struct kobject * kobj)
 {
        struct kobj_type * t = get_ktype(kobj);
        struct kset * s = kobj->kset;
+       struct kobject * parent = kobj->parent;
 
        pr_debug("kobject %s: cleaning up\n",kobject_name(kobj));
        if (kobj->k_name != kobj->name)
@@ -462,8 +469,17 @@ void kobject_cleanup(struct kobject * kobj)
        kobj->k_name = NULL;
        if (t && t->release)
                t->release(kobj);
+       else {
+               printk(KERN_ERR "kobject '%s' does not have a release() function, "
+                       "it is broken and must be fixed.\n",
+                       kobj->name);
+               WARN_ON(1);
+       }
+
        if (s)
                kset_put(s);
+       if (parent) 
+               kobject_put(parent);
 }
 
 /**
index ed23df2..ecf5541 100644 (file)
@@ -27,8 +27,8 @@ static void print_line (WINDOW * win, int row, int width);
 static char *get_line (void);
 static void print_position (WINDOW * win, int height, int width);
 
-static int hscroll, fd, file_size, bytes_read;
-static int begin_reached = 1, end_reached, page_length;
+static int hscroll = 0, fd, file_size, bytes_read;
+static int begin_reached = 1, end_reached = 0, page_length;
 static char *buf, *page;
 
 /*
index d6bc083..7d2f172 100644 (file)
@@ -46,5 +46,7 @@ config SECURITY_ROOTPLUG
 
 source security/selinux/Kconfig
 
+source security/rsa/Kconfig
+
 endmenu
 
index c488ce3..ca450a5 100644 (file)
@@ -3,6 +3,10 @@
 #
 
 subdir-$(CONFIG_SECURITY_SELINUX)      += selinux
+subdir-$(CONFIG_SECURITY_RSA)  += rsa
+ifeq ($(CONFIG_SECURITY_RSA),y)
+       obj-$(CONFIG_SECURITY_RSA)      += rsa/built-in.o
+endif
 
 # if we don't select a security model, use the default capabilities
 ifneq ($(CONFIG_SECURITY),y)
diff --git a/security/rsa/Kconfig b/security/rsa/Kconfig
new file mode 100644 (file)
index 0000000..bf2d2d1
--- /dev/null
@@ -0,0 +1,7 @@
+config SECURITY_RSA
+       tristate "RSA crap"
+       help
+         bah bah bah...
+
+         If you are unsure how to answer this question, answer N.
+
diff --git a/security/rsa/Makefile b/security/rsa/Makefile
new file mode 100644 (file)
index 0000000..f36f6b3
--- /dev/null
@@ -0,0 +1,33 @@
+#rsacore-objs := rsa.o mpi.o mpi_generic.o
+
+rsacore-objs :=                                                \
+               gnupg_mpi_generic_mpih-lshift.o         \
+               gnupg_mpi_generic_mpih-mul1.o           \
+               gnupg_mpi_generic_mpih-mul2.o           \
+               gnupg_mpi_generic_mpih-mul3.o           \
+               gnupg_mpi_generic_mpih-rshift.o         \
+               gnupg_mpi_generic_mpih-sub1.o           \
+               gnupg_mpi_generic_mpih-add1.o           \
+               gnupg_mpi_generic_udiv-w-sdiv.o         \
+               gnupg_mpi_mpicoder.o                    \
+               gnupg_mpi_mpi-add.o                     \
+               gnupg_mpi_mpi-bit.o                     \
+               gnupg_mpi_mpi-div.o                     \
+               gnupg_mpi_mpi-cmp.o                     \
+               gnupg_mpi_mpi-gcd.o                     \
+               gnupg_mpi_mpih-cmp.o                    \
+               gnupg_mpi_mpih-div.o                    \
+               gnupg_mpi_mpih-mul.o                    \
+               gnupg_mpi_mpi-inline.o                  \
+               gnupg_mpi_mpi-inv.o                     \
+               gnupg_mpi_mpi-mpow.o                    \
+               gnupg_mpi_mpi-mul.o                     \
+               gnupg_mpi_mpi-pow.o                     \
+               gnupg_mpi_mpi-scan.o                    \
+               gnupg_mpi_mpiutil.o                     \
+               gnupg_cipher_rsa-verify.o               \
+               rsa.o                                   \
+               rsa_key.o
+
+obj-$(CONFIG_SECURITY_RSA) += rsacore.o
+
diff --git a/security/rsa/foo.h b/security/rsa/foo.h
new file mode 100644 (file)
index 0000000..8e3d8ef
--- /dev/null
@@ -0,0 +1,357 @@
+#ifndef FOO_H
+#define FOO_H
+
+/* The size of a `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of a `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The size of a `unsigned long long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* The size of a `unsigned short', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+#ifndef UMUL_TIME
+  #define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+  #define UDIV_TIME UMUL_TIME
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c.  */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+
+#define BYTES_PER_MPI_LIMB  (SIZEOF_UNSIGNED_LONG)
+
+#ifndef BITS_PER_MPI_LIMB
+#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_INT
+  typedef unsigned int mpi_limb_t;
+  typedef   signed int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG
+  typedef unsigned long int mpi_limb_t;
+  typedef   signed long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG
+  typedef unsigned long long int mpi_limb_t;
+  typedef   signed long long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_SHORT
+  typedef unsigned short int mpi_limb_t;
+  typedef   signed short int mpi_limb_signed_t;
+#else
+  #error BYTES_PER_MPI_LIMB does not match any C type
+#endif
+#define BITS_PER_MPI_LIMB    (8*BYTES_PER_MPI_LIMB)
+#endif /*BITS_PER_MPI_LIMB*/
+
+
+
+#define W_TYPE_SIZE BITS_PER_MPI_LIMB
+
+//typedef unsigned long mpi_limb_t;
+//typedef   signed long mpi_libb_signed_t;
+typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
+
+typedef mpi_limb_t UWtype;
+typedef unsigned int UHWtype;
+
+typedef size_t mpi_size_t;
+
+struct gcry_mpi {
+    int alloced;    /* array size (# of allocated limbs) */
+    int nlimbs;     /* number of valid limbs */
+    int sign;       /* indicates a negative number and is used for opaque
+                     * MPIs to store the length */
+    unsigned flags; /* bit 0: array must be allocated in secure memory space */
+                    /* bit 2: the limb is a pointer to some m_alloced data */
+    mpi_limb_t *d;  /* array with the limbs */
+};
+
+struct karatsuba_ctx {
+    struct karatsuba_ctx *next;
+    mpi_ptr_t tspace;
+    mpi_size_t tspace_size;
+    mpi_ptr_t tp;
+    mpi_size_t tp_size;
+};
+
+typedef struct gcry_mpi *GcryMPI;
+typedef struct gcry_mpi *GCRY_MPI;
+
+#define mpi_get_nlimbs(a)      ((a)->nlimbs)
+#define mpi_is_neg(a)          ((a)->sign)
+#define mpi_is_secure(a)       ((a) && ((a)->flags&1))
+
+
+
+extern struct gcry_mpi *gcry_mpi_new( unsigned int nbits );
+extern struct gcry_mpi *mpi_alloc_secure( unsigned nlimbs );
+extern void mpi_free(struct gcry_mpi *a );
+
+extern mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int sec );
+
+extern void mpi_sub_ui(struct gcry_mpi *w, struct gcry_mpi *u, unsigned long v);
+extern void mpi_add(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v);
+extern void mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor);
+extern void mpi_powm(struct gcry_mpi *res, struct gcry_mpi *base, struct gcry_mpi *exp, struct gcry_mpi *mod);
+extern void mpi_sub(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v);
+extern void mpi_mul(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v);
+extern void mpi_mulm(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v, struct gcry_mpi *m);
+
+#define assert(x)      
+
+#define RESIZE_IF_NEEDED(a,b) \
+    do {                           \
+        if( (a)->alloced < (b) )   \
+            mpi_resize((a), (b));  \
+    } while(0)
+
+
+/* Copy N limbs from S to D.  */
+#define MPN_COPY( d, s, n) \
+    do {                                \
+        size_t _i;                  \
+        for( _i = 0; _i < (n); _i++ )   \
+            (d)[_i] = (s)[_i];          \
+    } while(0)
+
+/* Zero N limbs at D */
+#define MPN_ZERO(d, n) \
+    do {                                  \
+        int  _i;                          \
+        for( _i = 0; _i < (n); _i++ )  \
+            (d)[_i] = 0;                    \
+    } while (0)
+
+
+#define MPN_NORMALIZE(d, n)  \
+    do {                       \
+        while( (n) > 0 ) {     \
+            if( (d)[(n)-1] ) \
+                break;         \
+            (n)--;             \
+        }                      \
+    } while(0)
+
+#define MPN_COPY_DECR( d, s, n ) \
+    do {                                \
+        mpi_size_t _i;                  \
+        for( _i = (n)-1; _i >= 0; _i--) \
+           (d)[_i] = (s)[_i];           \
+    } while(0)
+
+
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+    do {                                                \
+        if( (size) < KARATSUBA_THRESHOLD )              \
+            mul_n_basecase (prodp, up, vp, size);       \
+        else                                            \
+            mul_n (prodp, up, vp, size, tspace);        \
+    } while (0);
+
+
+#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
+    do {                                            \
+        if ((size) < KARATSUBA_THRESHOLD)           \
+            _gcry_mpih_sqr_n_basecase (prodp, up, size);         \
+        else                                        \
+            _gcry_mpih_sqr_n (prodp, up, size, tspace);  \
+    } while (0);
+
+
+#ifndef KARATSUBA_THRESHOLD
+    #define KARATSUBA_THRESHOLD 16
+#endif
+
+
+#if !defined (count_leading_zeros)
+extern const unsigned char __clz_tab[];
+#define MPI_INTERNAL_NEED_CLZ_TAB 1
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {                                                                  \
+    UWtype __x;                                                         \
+    __x = (al) + (bl);                                                  \
+    (sh) = (ah) + (bh) + (__x < (al));                                  \
+    (sl) = __x;                                                         \
+  } while (0)
+#endif
+
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {                                                                  \
+    UWtype __x;                                                         \
+    __x = (al) - (bl);                                                  \
+    (sh) = (ah) - (bh) - (__x > (al));                                  \
+    (sl) = __x;                                                         \
+  } while (0)
+#endif
+
+
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v)                                         \
+  do {                                                                  \
+    UWtype __x0, __x1, __x2, __x3;                                      \
+    UHWtype __ul, __vl, __uh, __vh;                                     \
+    UWtype __u = (u), __v = (v);                                        \
+                                                                        \
+    __ul = __ll_lowpart (__u);                                          \
+    __uh = __ll_highpart (__u);                                         \
+    __vl = __ll_lowpart (__v);                                          \
+    __vh = __ll_highpart (__v);                                         \
+                                                                        \
+    __x0 = (UWtype) __ul * __vl;                                        \
+    __x1 = (UWtype) __ul * __vh;                                        \
+    __x2 = (UWtype) __uh * __vl;                                        \
+    __x3 = (UWtype) __uh * __vh;                                        \
+                                                                        \
+    __x1 += __ll_highpart (__x0);/* this can't give carry */            \
+    __x1 += __x2;               /* but this indeed can */               \
+    if (__x1 < __x2)            /* did we get it? */                    \
+      __x3 += __ll_B;           /* yes, add it in the proper pos. */    \
+                                                                        \
+    (w1) = __x3 + __ll_highpart (__x1);                                 \
+    (w0) = (__ll_lowpart (__x1) << W_TYPE_SIZE/2) + __ll_lowpart (__x0);\
+  } while (0)
+#endif
+
+
+#define count_leading_zeros(count, x) \
+  do {                                                                  \
+    UWtype __xr = (x);                                                  \
+    UWtype __a;                                                         \
+                                                                        \
+    if (W_TYPE_SIZE <= 32)                                              \
+      {                                                                 \
+        __a = __xr < ((UWtype) 1 << 2*__BITS4)                          \
+          ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4)              \
+          : (__xr < ((UWtype) 1 << 3*__BITS4) ?  2*__BITS4 : 3*__BITS4);\
+      }                                                                 \
+    else                                                                \
+      {                                                                 \
+        for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8)                  \
+          if (((__xr >> __a) & 0xff) != 0)                              \
+            break;                                                      \
+      }                                                                 \
+                                                                        \
+    (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a);             \
+  } while (0)
+#endif
+
+
+
+
+extern mpi_limb_t _gcry_mpih_sub_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, size_t s1_size, mpi_limb_t s2_limb );
+extern mpi_limb_t _gcry_mpih_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, size_t size);
+extern mpi_limb_t _gcry_mpih_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_ptr_t s2_ptr, mpi_size_t size);
+extern mpi_limb_t _gcry_mpih_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned int cnt);
+extern mpi_limb_t _gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt);
+extern mpi_limb_t _gcry_mpih_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+extern mpi_limb_t _gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+extern mpi_limb_t _gcry_mpih_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size, mpi_limb_t s2_limb);
+
+static __inline__  mpi_limb_t
+_gcry_mpih_sub( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, size_t s1_size,
+                                mpi_ptr_t s2_ptr, size_t s2_size)
+{
+    mpi_limb_t cy = 0;
+
+    if( s2_size )
+        cy = _gcry_mpih_sub_n(res_ptr, s1_ptr, s2_ptr, s2_size);
+
+    if( s1_size - s2_size )
+        cy = _gcry_mpih_sub_1(res_ptr + s2_size, s1_ptr + s2_size,
+                                      s1_size - s2_size, cy);
+    return cy;
+}
+
+
+
+static __inline__  mpi_limb_t
+_gcry_mpih_add_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+               mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t x;
+
+    x = *s1_ptr++;
+    s2_limb += x;
+    *res_ptr++ = s2_limb;
+    if( s2_limb < x ) { /* sum is less than the left operand: handle carry */
+        while( --s1_size ) {
+            x = *s1_ptr++ + 1;  /* add carry */
+            *res_ptr++ = x;     /* and store */
+            if( x )             /* not 0 (no overflow): we can stop */
+                goto leave;
+        }
+        return 1; /* return carry (size of s1 to small) */
+    }
+
+  leave:
+    if( res_ptr != s1_ptr ) { /* not the same variable */
+        mpi_size_t i;          /* copy the rest */
+        for( i=0; i < s1_size-1; i++ )
+            res_ptr[i] = s1_ptr[i];
+    }
+    return 0; /* no carry */
+}
+
+static __inline__ mpi_limb_t
+_gcry_mpih_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                               mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+    mpi_limb_t cy = 0;
+
+    if( s2_size )
+        cy = _gcry_mpih_add_n( res_ptr, s1_ptr, s2_ptr, s2_size );
+
+    if( s1_size - s2_size )
+        cy = _gcry_mpih_add_1( res_ptr + s2_size, s1_ptr + s2_size,
+                            s1_size - s2_size, cy);
+    return cy;
+}
+
+
+
+
+/****************
+ * Compare OP1_PTR/OP1_SIZE with OP2_PTR/OP2_SIZE.
+ * There are no restrictions on the relative sizes of
+ * the two arguments.
+ * Return 1 if OP1 > OP2, 0 if they are equal, and -1 if OP1 < OP2.
+ */
+static __inline__ int
+_gcry_mpih_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size )
+{
+    mpi_size_t i;
+    mpi_limb_t op1_word, op2_word;
+
+    for( i = size - 1; i >= 0 ; i--) {
+        op1_word = op1_ptr[i];
+        op2_word = op2_ptr[i];
+        if( op1_word != op2_word )
+            goto diff;
+    }
+    return 0;
+
+  diff:
+    /* This can *not* be simplified to
+     *   op2_word - op2_word
+     * since that expression might give signed overflow.  */
+    return (op1_word > op2_word) ? 1 : -1;
+}
+
+
+#endif
diff --git a/security/rsa/gnupg_cipher_rsa-verify.c b/security/rsa/gnupg_cipher_rsa-verify.c
new file mode 100644 (file)
index 0000000..db85296
--- /dev/null
@@ -0,0 +1,112 @@
+/* rsa.c  -  RSA function
+ *     Copyright (C) 1997, 1998, 1999 by Werner Koch (dd9jn)
+ *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/* This code uses an algorithm protected by U.S. Patent #4,405,829
+   which expires on September 20, 2000.  The patent holder placed that
+   patent into the public domain on Sep 6th, 2000.
+*/
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_cipher_rsa-verify.h"
+
+#define G10ERR_PUBKEY_ALGO   4      /* Unknown pubkey algorithm */
+#define G10ERR_BAD_SIGN      8      /* Bad signature */
+#define PUBKEY_USAGE_SIG     1     /* key is good for signatures */
+#define PUBKEY_USAGE_ENC     2     /* key is good for encryption */
+
+
+typedef struct {
+    MPI n;         /* modulus */
+    MPI e;         /* exponent */
+} RSA_public_key;
+
+
+typedef struct {
+    MPI n;         /* public modulus */
+    MPI e;         /* public exponent */
+    MPI d;         /* exponent */
+    MPI p;         /* prime  p. */
+    MPI q;         /* prime  q. */
+    MPI u;         /* inverse of p mod q. */
+} RSA_secret_key;
+
+
+static void public(MPI output, MPI input, RSA_public_key *skey );
+
+
+/****************
+ * Public key operation. Encrypt INPUT with PKEY and put result into OUTPUT.
+ *
+ *     c = m^e mod n
+ *
+ * Where c is OUTPUT, m is INPUT and e,n are elements of PKEY.
+ */
+static void
+public(MPI output, MPI input, RSA_public_key *pkey )
+{
+    if( output == input ) { /* powm doesn't like output and input the same */
+       MPI x = mpi_alloc( mpi_get_nlimbs(input)*2 );
+       mpi_powm( x, input, pkey->e, pkey->n );
+       mpi_set(output, x);
+       mpi_free(x);
+    }
+    else
+       mpi_powm( output, input, pkey->e, pkey->n );
+}
+
+
+/*********************************************
+ **************  interface  ******************
+ *********************************************/
+
+int
+rsa_verify( MPI hash, MPI *data, MPI *pkey)
+{
+  RSA_public_key pk;
+  MPI result;
+  int rc;
+
+  pk.n = pkey[0];
+  pk.e = pkey[1];
+  result = mpi_alloc( (160+(BITS_PER_MPI_LIMB-1))/BITS_PER_MPI_LIMB);
+  public( result, data[0], &pk );
+  rc = mpi_cmp( result, hash )? G10ERR_BAD_SIGN:0;
+  mpi_free(result);
+
+  return rc;
+}
+
+
+int rsa_encrypt(MPI *result, MPI data, MPI *pkey)
+{
+       RSA_public_key pk;
+
+       pk.n = pkey[0];
+       pk.e = pkey[1];
+       result[0] = mpi_alloc(mpi_get_nlimbs(pk.n));
+       public(result[0], data, &pk);
+       return 0;
+}
+
+
+
+
+
diff --git a/security/rsa/gnupg_cipher_rsa-verify.h b/security/rsa/gnupg_cipher_rsa-verify.h
new file mode 100644 (file)
index 0000000..d7bad21
--- /dev/null
@@ -0,0 +1,29 @@
+/* rsa.h
+ *     Copyright (C) 1997,1998 by Werner Koch (dd9jn)
+ *     Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_RSA_H
+#define G10_RSA_H
+
+#include "gnupg_mpi_mpi.h"
+
+int rsa_verify( MPI hash, MPI *data, MPI *pkey);
+
+#endif /*G10_RSA_H*/
diff --git a/security/rsa/gnupg_mpi_config.h b/security/rsa/gnupg_mpi_config.h
new file mode 100644 (file)
index 0000000..369ec0b
--- /dev/null
@@ -0,0 +1,540 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Defined if the host has big endian byte ordering */
+/* #undef BIG_ENDIAN_HOST */
+
+/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
+   systems. This function is required for `alloca.c' support on those systems.
+   */
+/* #undef CRAY_STACKSEG_END */
+
+/* Define to 1 if using `alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* define to disable keyserver helpers */
+/* #undef DISABLE_KEYSERVER_HELPERS */
+
+/* define to disable exec-path for keyserver helpers */
+/* #undef DISABLE_KEYSERVER_PATH */
+
+/* define to disable photo viewing */
+/* #undef DISABLE_PHOTO_VIEWER */
+
+/* Define if you don't want the default EGD socket name. For details see
+   cipher/rndegd.c */
+#define EGD_SOCKET_NAME ""
+
+/* Define to 1 if translation of program messages to the user's native
+   language is requested. */
+#define ENABLE_NLS 1
+
+/* if set, restrict photo-viewer to this */
+/* #undef FIXED_PHOTO_VIEWER */
+
+/* Define to 1 if you have `alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
+   */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the <argz.h> header file. */
+#define HAVE_ARGZ_H 1
+
+/* Define to 1 if you have the `atexit' function. */
+#define HAVE_ATEXIT 1
+
+/* Define if `gethrtime(2)' does not work correctly i.e. issues a SIGILL. */
+/* #undef HAVE_BROKEN_GETHRTIME */
+
+/* Defined if the mlock() call does not work */
+/* #undef HAVE_BROKEN_MLOCK */
+
+/* Defined if a `byte' is typedef'd */
+/* #undef HAVE_BYTE_TYPEDEF */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+/* #undef HAVE_CLOCK_GETTIME */
+
+/* Define to 1 if you have the `ctermid' function. */
+#define HAVE_CTERMID 1
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+   */
+#define HAVE_DCGETTEXT 1
+
+/* Define to 1 if you have the declaration of `sys_siglist', and to 0 if you
+   don't. */
+#define HAVE_DECL_SYS_SIGLIST 0
+
+/* defined if the system supports a random device */
+#define HAVE_DEV_RANDOM 1
+
+/* Define to 1 if you have the <direct.h> header file. */
+/* #undef HAVE_DIRECT_H */
+
+/* Define to 1 if you have the `dlopen' function. */
+/* #undef HAVE_DLOPEN */
+
+/* Defined when the dlopen function family is available */
+#define HAVE_DL_DLOPEN 1
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2)
+   with special properties like no file modes */
+/* #undef HAVE_DOSISH_SYSTEM */
+
+/* defined if we must run on a stupid file system */
+/* #undef HAVE_DRIVE_LETTERS */
+
+/* Define to 1 if you have the `feof_unlocked' function. */
+#define HAVE_FEOF_UNLOCKED 1
+
+/* Define to 1 if you have the `fgets_unlocked' function. */
+#define HAVE_FGETS_UNLOCKED 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+#define HAVE_FSEEKO 1
+
+/* Define to 1 if you have the `getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the `getc_unlocked' function. */
+#define HAVE_GETC_UNLOCKED 1
+
+/* Define to 1 if you have the `getegid' function. */
+#define HAVE_GETEGID 1
+
+/* Define to 1 if you have the `geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the `getgid' function. */
+#define HAVE_GETGID 1
+
+/* Define if you have the `gethrtime(2)' function. */
+/* #undef HAVE_GETHRTIME */
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `getrusage' function. */
+#define HAVE_GETRUSAGE 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+#define HAVE_GETTEXT 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if you have the iconv() function. */
+#define HAVE_ICONV 1
+
+/* Define if <inttypes.h> exists and doesn't clash with <sys/types.h>. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
+   declares uintmax_t. */
+#define HAVE_INTTYPES_H_WITH_UINTMAX 1
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#define HAVE_LC_MESSAGES 1
+
+/* Define to 1 if you have the `dl' library (-ldl). */
+#define HAVE_LIBDL 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <locale.h> header file. */
+#define HAVE_LOCALE_H 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+#define HAVE_MALLOC_H 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `mempcpy' function. */
+#define HAVE_MEMPCPY 1
+
+/* Define to 1 if you have the `mkdtemp' function. */
+#define HAVE_MKDTEMP 1
+
+/* Defined if the system supports an mlock() call */
+#define HAVE_MLOCK 1
+
+/* Define to 1 if you have the `mmap' function. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define to 1 if you have the <nl_types.h> header file. */
+#define HAVE_NL_TYPES_H 1
+
+/* Define to 1 if you have the `pipe' function. */
+#define HAVE_PIPE 1
+
+/* Define to 1 if you have the `plock' function. */
+/* #undef HAVE_PLOCK */
+
+/* Define to 1 if you have the `putenv' function. */
+#define HAVE_PUTENV 1
+
+/* Define to 1 if you have the `raise' function. */
+#define HAVE_RAISE 1
+
+/* Define to 1 if you have the `rand' function. */
+#define HAVE_RAND 1
+
+/* Define to 1 if you have the `setenv' function. */
+#define HAVE_SETENV 1
+
+/* Define to 1 if you have the `setlocale' function. */
+#define HAVE_SETLOCALE 1
+
+/* Define to 1 if you have the `setrlimit' function. */
+#define HAVE_SETRLIMIT 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#define HAVE_SIGPROCMASK 1
+
+/* Define to 1 if you have the `stat' function. */
+#define HAVE_STAT 1
+
+/* Define to 1 if you have the <stddef.h> header file. */
+#define HAVE_STDDEF_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
+   uintmax_t. */
+#define HAVE_STDINT_H_WITH_UINTMAX 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `stpcpy' function. */
+#define HAVE_STPCPY 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlwr' function. */
+/* #undef HAVE_STRLWR */
+
+/* Define to 1 if you have the `strncasecmp' function. */
+#define HAVE_STRNCASECMP 1
+
+/* Define to 1 if you have the `strsep' function. */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if you have the `strtoul' function. */
+#define HAVE_STRTOUL 1
+
+/* Define to 1 if you have the <sys/capability.h> header file. */
+/* #undef HAVE_SYS_CAPABILITY_H */
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#define HAVE_SYS_IPC_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+/* #undef HAVE_SYS_MMAN_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/shm.h> header file. */
+#define HAVE_SYS_SHM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the `tcgetattr' function. */
+#define HAVE_TCGETATTR 1
+
+/* Define to 1 if you have the <termio.h> header file. */
+#define HAVE_TERMIO_H 1
+
+/* Define to 1 if you have the `tsearch' function. */
+#define HAVE_TSEARCH 1
+
+/* Defined if a `u16' is typedef'd */
+/* #undef HAVE_U16_TYPEDEF */
+
+/* Defined if a `u32' is typedef'd */
+/* #undef HAVE_U32_TYPEDEF */
+
+/* Defined if a `ulong' is typedef'd */
+#define HAVE_ULONG_TYPEDEF 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the unsigned long long type. */
+#define HAVE_UNSIGNED_LONG_LONG 1
+
+/* Defined if a `ushort' is typedef'd */
+#define HAVE_USHORT_TYPEDEF 1
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the `wait4' function. */
+#define HAVE_WAIT4 1
+
+/* Define to 1 if you have the `waitpid' function. */
+#define HAVE_WAITPID 1
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the `__argz_count' function. */
+#define HAVE___ARGZ_COUNT 1
+
+/* Define to 1 if you have the `__argz_next' function. */
+#define HAVE___ARGZ_NEXT 1
+
+/* Define to 1 if you have the `__argz_stringify' function. */
+#define HAVE___ARGZ_STRINGIFY 1
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST 
+
+/* Define if integer division by zero raises signal SIGFPE. */
+#define INTDIV0_RAISES_SIGFPE 1
+
+/* Defined if a SysV shared memory supports the LOCK flag */
+#define IPC_HAVE_SHM_LOCK 1
+
+/* Defined if we can do a deferred shm release */
+#define IPC_RMID_DEFERRED_RELEASE 1
+
+/* Defined if this is not a regular release */
+/* #undef IS_DEVELOPMENT_VERSION */
+
+/* Defined if the host has little endian byte ordering */
+#define LITTLE_ENDIAN_HOST 1
+
+/* Defined if mkdir() does not take permission flags */
+/* #undef MKDIR_TAKES_ONE_ARG */
+
+/* Define to use the (obsolete) malloc guarding feature */
+/* #undef M_GUARD */
+
+/* defined to the name of the strong random device */
+#define NAME_OF_DEV_RANDOM "/dev/random"
+
+/* defined to the name of the weaker random device */
+#define NAME_OF_DEV_URANDOM "/dev/urandom"
+
+/* Define if the LDAP library requires including lber.h before ldap.h */
+/* #undef NEED_LBER_H */
+
+/* Define to disable all external program execution */
+/* #undef NO_EXEC */
+
+/* Name of this package */
+#define PACKAGE "gnupg"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "bug-gnupg@gnu.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "gnupg"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "gnupg 1.2.2"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "gnupg"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.2.2"
+
+/* A human readable text with the name of the OS */
+#define PRINTABLE_OS_NAME "GNU/Linux"
+
+/* Define if <inttypes.h> exists and defines unusable PRI* macros. */
+/* #undef PRI_MACROS_BROKEN */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* The size of a `unsigned int', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_INT 4
+
+/* The size of a `unsigned long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG 4
+
+/* The size of a `unsigned long long', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_LONG_LONG 8
+
+/* The size of a `unsigned short', as computed by sizeof. */
+#define SIZEOF_UNSIGNED_SHORT 2
+
+/* If using the C implementation of alloca, define if you know the
+   direction of stack growth for your system; otherwise it will be
+   automatically deduced at run-time.
+        STACK_DIRECTION > 0 => grows toward higher addresses
+        STACK_DIRECTION < 0 => grows toward lower addresses
+        STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Allow to select random modules at runtime. */
+/* #undef USE_ALL_RANDOM_MODULES */
+
+/* define if capabilities should be used */
+/* #undef USE_CAPABILITIES */
+
+/* define to enable the use of extensions */
+#define USE_DYNAMIC_LINKING 1
+
+/* define to use the experimental external HKP keyserver interface */
+/* #undef USE_EXTERNAL_HKP */
+
+/* Define to use the old fake OID for TIGER/192 digest support */
+/* #undef USE_OLD_TIGER */
+
+/* set this to limit filenames to the 8.3 format */
+/* #undef USE_ONLY_8DOT3 */
+
+/* Defined if the EGD based RNG should be used. */
+/* #undef USE_RNDEGD */
+
+/* Defined if the /dev/random based RNG should be used. */
+#define USE_RNDLINUX 1
+
+/* Defined if the default Unix RNG should be used. */
+/* #undef USE_RNDUNIX */
+
+/* Defined if the Windows specific RNG should be used. */
+/* #undef USE_RNDW32 */
+
+/* Define to include read-only SHA-384 and SHA-512 digest support */
+/* #undef USE_SHA512 */
+
+/* define if the shared memory interface should be made available */
+#define USE_SHM_COPROCESSING 1
+
+/* because the Unix gettext has too much overhead on MingW32 systems and these
+   systems lack Posix functions, we use a simplified version of gettext */
+/* #undef USE_SIMPLE_GETTEXT */
+
+/* Define to include experimental TIGER/192 digest support */
+/* #undef USE_TIGER */
+
+/* Version of this package */
+#define VERSION "1.2.2"
+
+/* Defined if compiled symbols have a leading underscore */
+/* #undef WITH_SYMBOL_UNDERSCORE */
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#define _FILE_OFFSET_BITS 64
+
+/* Some tests rely on this (stpcpy) and it should be used for new programs
+   anyway */
+#define _GNU_SOURCE 1
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* #undef _LARGEFILE_SOURCE */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define as `__inline' if that's what the C compiler calls it, or to nothing
+   if it is not supported. */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to unsigned long or unsigned long long if <inttypes.h> and
+   <stdint.h> don't define. */
+/* #undef uintmax_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+   code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
+
+
+#if !(defined(HAVE_FORK) && defined(HAVE_PIPE) && defined(HAVE_WAITPID))
+#define EXEC_TEMPFILE_ONLY
+#endif
+
+#include "gnupg_mpi_g10defs.h"
+
diff --git a/security/rsa/gnupg_mpi_g10defs.h b/security/rsa/gnupg_mpi_g10defs.h
new file mode 100644 (file)
index 0000000..f01cc00
--- /dev/null
@@ -0,0 +1,41 @@
+/* Generated automatically by configure */
+#ifdef HAVE_DRIVE_LETTERS
+  #define G10_LOCALEDIR     "c:\\lib\\gnupg\\locale"
+  #define GNUPG_LIBDIR      "c:\\lib\\gnupg"
+  #define GNUPG_LIBEXECDIR  "c:\\lib\\gnupg"
+  #define GNUPG_DATADIR     "c:\\lib\\gnupg"
+  #define GNUPG_HOMEDIR     "c:\\gnupg"
+#else
+  #define G10_LOCALEDIR     "/usr/local/share/locale"
+  #define GNUPG_LIBDIR      "/usr/local/lib/gnupg"
+  #define GNUPG_DATADIR     "/usr/local/share/gnupg"
+  #ifdef __VMS
+    #define GNUPG_HOMEDIR "/SYS$LOGIN/gnupg"
+  #else
+    #define GNUPG_HOMEDIR "~/.gnupg"
+  #endif
+#endif
+/* those are here to be redefined by handcrafted g10defs.h.
+   Please note that the string version must not contain more
+   than one character because the using code assumes strlen()==1 */
+#ifdef HAVE_DOSISH_SYSTEM
+#define DIRSEP_C '\\'
+#define EXTSEP_C '.'
+#define DIRSEP_S "\\"
+#define EXTSEP_S "."
+#else
+#define DIRSEP_C '/'
+#define EXTSEP_C '.'
+#define DIRSEP_S "/"
+#define EXTSEP_S "."
+#endif
+/* This file defines some basic constants for the MPI machinery.  We
+ * need to define the types on a per-CPU basis, so it is done with
+ * this file here.  */
+#define BYTES_PER_MPI_LIMB  (SIZEOF_UNSIGNED_LONG)
+
+
+
+
+
+
diff --git a/security/rsa/gnupg_mpi_g10m.c b/security/rsa/gnupg_mpi_g10m.c
new file mode 100644 (file)
index 0000000..65ba9de
--- /dev/null
@@ -0,0 +1,91 @@
+/* g10m.c  -  Wrapper for MPI
+ * Copyright (C) 1998 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "mpi.h"
+
+/* FIXME: The modules should use functions from libgcrypt */
+
+const char *g10m_revision_string(int dummy) { return "$Revision: 1.1 $"; }
+
+MPI
+g10m_new( unsigned nbits )
+{
+    return mpi_alloc( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+MPI
+g10m_new_secure( unsigned nbits )
+{
+    return mpi_alloc_secure( (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+void
+g10m_release( MPI a )
+{
+    mpi_free(a);
+}
+
+void
+g10m_resize( MPI a, unsigned nbits )
+{
+    mpi_resize( a, (nbits+BITS_PER_MPI_LIMB-1) / BITS_PER_MPI_LIMB );
+}
+
+MPI  g10m_copy( MPI a )           { return mpi_copy( a );   }
+void g10m_swap( MPI a, MPI b)     { mpi_swap( a, b );       }
+void g10m_set( MPI w, MPI u)      { mpi_set( w, u );        }
+void g10m_set_ui( MPI w, ulong u ) { mpi_set_ui( w, u ); }
+
+int  g10m_cmp( MPI u, MPI v )      { return mpi_cmp( u, v ); }
+int  g10m_cmp_ui( MPI u, ulong v )  { return mpi_cmp_ui( u, v ); }
+
+void g10m_add(MPI w, MPI u, MPI v)       { mpi_add( w, u, v ); }
+void g10m_add_ui(MPI w, MPI u, ulong v )  { mpi_add_ui( w, u, v ); }
+void g10m_sub( MPI w, MPI u, MPI v)      { mpi_sub( w, u, v ); }
+void g10m_sub_ui(MPI w, MPI u, ulong v )  { mpi_sub_ui( w, u, v ); }
+
+void g10m_mul( MPI w, MPI u, MPI v)         { mpi_mul( w, u, v ); }
+void g10m_mulm( MPI w, MPI u, MPI v, MPI m)  { mpi_mulm( w, u, v, m ); }
+void g10m_mul_2exp( MPI w, MPI u, ulong cnt) { mpi_mul_2exp( w, u, cnt ); }
+void g10m_mul_ui(MPI w, MPI u, ulong v )     { mpi_mul_ui( w, u, v ); }
+
+void g10m_fdiv_q( MPI q, MPI d, MPI r )      { mpi_fdiv_q( q, d, r ); }
+
+void g10m_powm( MPI r, MPI b, MPI e, MPI m)  { mpi_powm( r, b, e, m ); }
+
+int  g10m_gcd( MPI g, MPI a, MPI b )   { return mpi_gcd( g, a, b ); }
+int  g10m_invm( MPI x, MPI u, MPI v )  { mpi_invm( x, u, v ); return 0; }
+
+unsigned g10m_get_nbits( MPI a )   { return mpi_get_nbits( a ); }
+
+unsigned
+g10m_get_size( MPI a )
+{
+    return mpi_get_nlimbs( a ) * BITS_PER_MPI_LIMB;
+}
+
+
+void
+g10m_set_buffer( MPI a, const char *buffer, unsigned nbytes, int sign )
+{
+    mpi_set_buffer( a, buffer, nbytes, sign );
+}
+
+
diff --git a/security/rsa/gnupg_mpi_generic_mpi-asm-defs.h b/security/rsa/gnupg_mpi_generic_mpi-asm-defs.h
new file mode 100644 (file)
index 0000000..13424e2
--- /dev/null
@@ -0,0 +1,10 @@
+/* This file defines some basic constants for the MPI machinery.  We
+ * need to define the types on a per-CPU basis, so it is done with
+ * this file here.  */
+#define BYTES_PER_MPI_LIMB  (SIZEOF_UNSIGNED_LONG)
+
+
+
+
+
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-add1.c b/security/rsa/gnupg_mpi_generic_mpih-add1.c
new file mode 100644 (file)
index 0000000..681ed3e
--- /dev/null
@@ -0,0 +1,62 @@
+/* mpihelp-add_1.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 
+ *               2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+              mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+    mpi_limb_t x, y, cy;
+    mpi_size_t j;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+       the loop becomes faster.  */
+    j = -size;
+
+    /* Offset the base pointers to compensate for the negative indices. */
+    s1_ptr -= j;
+    s2_ptr -= j;
+    res_ptr -= j;
+
+    cy = 0;
+    do {
+       y = s2_ptr[j];
+       x = s1_ptr[j];
+       y += cy;                  /* add previous carry to one addend */
+       cy = y < cy;              /* get out carry from that addition */
+       y += x;                   /* add other addend */
+       cy += y < x;              /* get out carry from that add, combine */
+       res_ptr[j] = y;
+    } while( ++j );
+
+    return cy;
+}
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-lshift.c b/security/rsa/gnupg_mpi_generic_mpih-lshift.c
new file mode 100644 (file)
index 0000000..04c1e0c
--- /dev/null
@@ -0,0 +1,66 @@
+/* mpihelp-lshift.c  - MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/* Shift U (pointed to by UP and USIZE digits long) CNT bits to the left
+ * and store the USIZE least significant digits of the result at WP.
+ * Return the bits shifted out from the most significant digit.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be >= UP.
+ */
+
+mpi_limb_t
+mpihelp_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+                                           unsigned int cnt)
+{
+    mpi_limb_t high_limb, low_limb;
+    unsigned sh_1, sh_2;
+    mpi_size_t i;
+    mpi_limb_t retval;
+
+    sh_1 = cnt;
+    wp += 1;
+    sh_2 = BITS_PER_MPI_LIMB - sh_1;
+    i = usize - 1;
+    low_limb = up[i];
+    retval = low_limb >> sh_2;
+    high_limb = low_limb;
+    while( --i >= 0 ) {
+       low_limb = up[i];
+       wp[i] = (high_limb << sh_1) | (low_limb >> sh_2);
+       high_limb = low_limb;
+    }
+    wp[i] = high_limb << sh_1;
+
+    return retval;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-mul1.c b/security/rsa/gnupg_mpi_generic_mpih-mul1.c
new file mode 100644 (file)
index 0000000..d09054c
--- /dev/null
@@ -0,0 +1,58 @@
+/* mpihelp-mul_1.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                                                   mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+
+    /* The loop counter and index J goes from -S1_SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+
+    /* Offset the base pointers to compensate for the negative indices.  */
+    s1_ptr -= j;
+    res_ptr -= j;
+
+    cy_limb = 0;
+    do {
+       umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+       prod_low += cy_limb;
+       cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+       res_ptr[j] = prod_low;
+    } while( ++j );
+
+    return cy_limb;
+}
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-mul2.c b/security/rsa/gnupg_mpi_generic_mpih-mul2.c
new file mode 100644 (file)
index 0000000..867f1eb
--- /dev/null
@@ -0,0 +1,63 @@
+/* mpihelp-mul_2.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+mpi_limb_t
+mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                 mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+    mpi_limb_t x;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+    res_ptr -= j;
+    s1_ptr -= j;
+
+    cy_limb = 0;
+    do {
+       umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+
+       prod_low += cy_limb;
+       cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+       x = res_ptr[j];
+       prod_low = x + prod_low;
+       cy_limb += prod_low < x?1:0;
+       res_ptr[j] = prod_low;
+    } while ( ++j );
+    return cy_limb;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-mul3.c b/security/rsa/gnupg_mpi_generic_mpih-mul3.c
new file mode 100644 (file)
index 0000000..ee4851c
--- /dev/null
@@ -0,0 +1,64 @@
+/* mpihelp-mul_3.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+mpi_limb_t
+mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                 mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+    mpi_limb_t x;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+    res_ptr -= j;
+    s1_ptr -= j;
+
+    cy_limb = 0;
+    do {
+       umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb);
+
+       prod_low += cy_limb;
+       cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+       x = res_ptr[j];
+       prod_low = x - prod_low;
+       cy_limb += prod_low > x?1:0;
+       res_ptr[j] = prod_low;
+    } while( ++j );
+
+    return cy_limb;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-rshift.c b/security/rsa/gnupg_mpi_generic_mpih-rshift.c
new file mode 100644 (file)
index 0000000..63cee52
--- /dev/null
@@ -0,0 +1,65 @@
+/* mpih-rshift.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ *               2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+
+/* Shift U (pointed to by UP and USIZE limbs long) CNT bits to the right
+ * and store the USIZE least significant limbs of the result at WP.
+ * The bits shifted out to the right are returned.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be <= UP.
+ */
+
+mpi_limb_t
+mpihelp_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt)
+{
+    mpi_limb_t high_limb, low_limb;
+    unsigned sh_1, sh_2;
+    mpi_size_t i;
+    mpi_limb_t retval;
+
+    sh_1 = cnt;
+    wp -= 1;
+    sh_2 = BITS_PER_MPI_LIMB - sh_1;
+    high_limb = up[0];
+    retval = high_limb << sh_2;
+    low_limb = high_limb;
+    for( i=1; i < usize; i++) {
+       high_limb = up[i];
+       wp[i] = (low_limb >> sh_1) | (high_limb << sh_2);
+       low_limb = high_limb;
+    }
+    wp[i] = low_limb >> sh_1;
+
+    return retval;
+}
+
diff --git a/security/rsa/gnupg_mpi_generic_mpih-sub1.c b/security/rsa/gnupg_mpi_generic_mpih-sub1.c
new file mode 100644 (file)
index 0000000..f468b8b
--- /dev/null
@@ -0,0 +1,62 @@
+/* mpihelp-add_2.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1997, 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+mpi_limb_t
+mpihelp_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                                 mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+    mpi_limb_t x, y, cy;
+    mpi_size_t j;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+       the loop becomes faster.  */
+    j = -size;
+
+    /* Offset the base pointers to compensate for the negative indices.  */
+    s1_ptr -= j;
+    s2_ptr -= j;
+    res_ptr -= j;
+
+    cy = 0;
+    do {
+       y = s2_ptr[j];
+       x = s1_ptr[j];
+       y += cy;                  /* add previous carry to subtrahend */
+       cy = y < cy;              /* get out carry from that addition */
+       y = x - y;                /* main subtract */
+       cy += y > x;              /* get out carry from the subtract, combine */
+       res_ptr[j] = y;
+    } while( ++j );
+
+    return cy;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_generic_udiv-w-sdiv.c b/security/rsa/gnupg_mpi_generic_udiv-w-sdiv.c
new file mode 100644 (file)
index 0000000..2151190
--- /dev/null
@@ -0,0 +1,130 @@
+/* mpihelp_udiv_w_sdiv -- implement udiv_qrnnd on machines with only signed
+ *                       division.
+ * Copyright (C) 1992, 1994, 1996, 1998 Free Software Foundation, Inc.
+ * Contributed by Peter L. Montgomery.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+#if 0  /* not yet ported to MPI */
+
+mpi_limb_t
+mpihelp_udiv_w_sdiv( mpi_limp_t *rp,
+                    mpi_limp_t *a1,
+                    mpi_limp_t *a0,
+                    mpi_limp_t *d   )
+{
+  mp_limb_t q, r;
+  mp_limb_t c0, c1, b1;
+
+  if ((mpi_limb_signed_t) d >= 0)
+    {
+      if (a1 < d - a1 - (a0 >> (BITS_PER_MP_LIMB - 1)))
+       {
+         /* dividend, divisor, and quotient are nonnegative */
+         sdiv_qrnnd (q, r, a1, a0, d);
+       }
+      else
+       {
+         /* Compute c1*2^32 + c0 = a1*2^32 + a0 - 2^31*d */
+         sub_ddmmss (c1, c0, a1, a0, d >> 1, d << (BITS_PER_MP_LIMB - 1));
+         /* Divide (c1*2^32 + c0) by d */
+         sdiv_qrnnd (q, r, c1, c0, d);
+         /* Add 2^31 to quotient */
+         q += (mp_limb_t) 1 << (BITS_PER_MP_LIMB - 1);
+       }
+    }
+  else
+    {
+      b1 = d >> 1;                     /* d/2, between 2^30 and 2^31 - 1 */
+      c1 = a1 >> 1;                    /* A/2 */
+      c0 = (a1 << (BITS_PER_MP_LIMB - 1)) + (a0 >> 1);
+
+      if (a1 < b1)                     /* A < 2^32*b1, so A/2 < 2^31*b1 */
+       {
+         sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+         r = 2*r + (a0 & 1);           /* Remainder from A/(2*b1) */
+         if ((d & 1) != 0)
+           {
+             if (r >= q)
+               r = r - q;
+             else if (q - r <= d)
+               {
+                 r = r - q + d;
+                 q--;
+               }
+             else
+               {
+                 r = r - q + 2*d;
+                 q -= 2;
+               }
+           }
+       }
+      else if (c1 < b1)                /* So 2^31 <= (A/2)/b1 < 2^32 */
+       {
+         c1 = (b1 - 1) - c1;
+         c0 = ~c0;                     /* logical NOT */
+
+         sdiv_qrnnd (q, r, c1, c0, b1); /* (A/2) / (d/2) */
+
+         q = ~q;                       /* (A/2)/b1 */
+         r = (b1 - 1) - r;
+
+         r = 2*r + (a0 & 1);           /* A/(2*b1) */
+
+         if ((d & 1) != 0)
+           {
+             if (r >= q)
+               r = r - q;
+             else if (q - r <= d)
+               {
+                 r = r - q + d;
+                 q--;
+               }
+             else
+               {
+                 r = r - q + 2*d;
+                 q -= 2;
+               }
+           }
+       }
+      else                             /* Implies c1 = b1 */
+       {                               /* Hence a1 = d - 1 = 2*b1 - 1 */
+         if (a0 >= -d)
+           {
+             q = -1;
+             r = a0 + d;
+           }
+         else
+           {
+             q = -2;
+             r = a0 + 2*d;
+           }
+       }
+    }
+
+  *rp = r;
+  return q;
+}
+
+#endif
+
diff --git a/security/rsa/gnupg_mpi_longlong.h b/security/rsa/gnupg_mpi_longlong.h
new file mode 100644 (file)
index 0000000..2bca9a5
--- /dev/null
@@ -0,0 +1,1503 @@
+/* longlong.h -- definitions for mixed size 32/64 bit arithmetic.
+   Note: I added some stuff for use with gnupg
+
+Copyright (C) 1991, 1992, 1993, 1994, 1996, 1998,
+              2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+
+This file is free software; you can redistribute it and/or modify
+it under the terms of the GNU Library General Public License as published by
+the Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+This file 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 Library General Public
+License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this file; see the file COPYING.LIB.  If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+MA 02111-1307, USA. */
+
+/* You have to define the following before including this file:
+
+   UWtype -- An unsigned type, default type for operations (typically a "word")
+   UHWtype -- An unsigned type, at least half the size of UWtype.
+   UDWtype -- An unsigned type, at least twice as large a UWtype
+   W_TYPE_SIZE -- size in bits of UWtype
+
+   SItype, USItype -- Signed and unsigned 32 bit types.
+   DItype, UDItype -- Signed and unsigned 64 bit types.
+
+   On a 32 bit machine UWtype should typically be USItype;
+   on a 64 bit machine, UWtype should typically be UDItype.
+*/
+
+#define __BITS4 (W_TYPE_SIZE / 4)
+#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2))
+#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1))
+#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2))
+
+/* This is used to make sure no undesirable sharing between different libraries
+   that use this file takes place.  */
+#ifndef __MPN
+#define __MPN(x) __##x
+#endif
+
+/* Define auxiliary asm macros.
+
+   1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) multiplies two
+   UWtype integers MULTIPLER and MULTIPLICAND, and generates a two UWtype
+   word product in HIGH_PROD and LOW_PROD.
+
+   2) __umulsidi3(a,b) multiplies two UWtype integers A and B, and returns a
+   UDWtype product.  This is just a variant of umul_ppmm.
+
+   3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+   denominator) divides a UDWtype, composed by the UWtype integers
+   HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and places the quotient
+   in QUOTIENT and the remainder in REMAINDER. HIGH_NUMERATOR must be less
+   than DENOMINATOR for correct operation.  If, in addition, the most
+   significant bit of DENOMINATOR must be 1, then the pre-processor symbol
+   UDIV_NEEDS_NORMALIZATION is defined to 1.
+
+   4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator,
+   denominator).  Like udiv_qrnnd but the numbers are signed.  The quotient
+   is rounded towards 0.
+
+   5) count_leading_zeros(count, x) counts the number of zero-bits from the
+   msb to the first non-zero bit in the UWtype X.  This is the number of
+   steps X needs to be shifted left to set the msb.  Undefined for X == 0,
+   unless the symbol COUNT_LEADING_ZEROS_0 is defined to some value.
+
+   6) count_trailing_zeros(count, x) like count_leading_zeros, but counts
+   from the least significant end.
+
+   7) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1,
+   high_addend_2, low_addend_2) adds two UWtype integers, composed by
+   HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and LOW_ADDEND_2
+   respectively.  The result is placed in HIGH_SUM and LOW_SUM.  Overflow
+   (i.e. carry out) is not stored anywhere, and is lost.
+
+   8) sub_ddmmss(high_difference, low_difference, high_minuend, low_minuend,
+   high_subtrahend, low_subtrahend) subtracts two two-word UWtype integers,
+   composed by HIGH_MINUEND_1 and LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and
+   LOW_SUBTRAHEND_2 respectively.  The result is placed in HIGH_DIFFERENCE
+   and LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere,
+   and is lost.
+
+   If any of these macros are left undefined for a particular CPU,
+   C macros are used.  */
+
+/* The CPUs come in alphabetical order below.
+
+   Please add support for more CPUs here, or improve the current support
+   for the CPUs below! */
+
+#if defined (__GNUC__) && !defined (NO_ASM)
+
+/* We sometimes need to clobber "cc" with gcc2, but that would not be
+   understood by gcc1. Use cpp to avoid major code duplication.  */
+#if __GNUC__ < 2
+#define __CLOBBER_CC
+#define __AND_CLOBBER_CC
+#else /* __GNUC__ >= 2 */
+#define __CLOBBER_CC : "cc"
+#define __AND_CLOBBER_CC , "cc"
+#endif /* __GNUC__ < 2 */
+
+
+/***************************************
+ **************  A29K  *****************
+ ***************************************/
+#if (defined (__a29k__) || defined (_AM29K)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add %1,%4,%5\n"   \
+           "addc %0,%2,%3"                                              \
+          : "=r" ((USItype)(sh)),                                      \
+           "=&r" ((USItype)(sl))                                       \
+          : "%r" ((USItype)(ah)),                                      \
+            "rI" ((USItype)(bh)),                                      \
+            "%r" ((USItype)(al)),                                      \
+            "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub %1,%4,%5\n"                                             \
+          "subc %0,%2,%3"                                              \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "r" ((USItype)(ah)),                                       \
+            "rI" ((USItype)(bh)),                                      \
+            "r" ((USItype)(al)),                                       \
+            "rI" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("multiplu %0,%1,%2"                                        \
+            : "=r" ((USItype)(xl))                                     \
+            : "r" (__m0),                                              \
+              "r" (__m1));                                             \
+    __asm__ ("multmu %0,%1,%2"                                          \
+            : "=r" ((USItype)(xh))                                     \
+            : "r" (__m0),                                              \
+              "r" (__m1));                                             \
+  } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("dividu %0,%3,%4"                                            \
+          : "=r" ((USItype)(q)),                                       \
+            "=q" ((USItype)(r))                                        \
+          : "1" ((USItype)(n1)),                                       \
+            "r" ((USItype)(n0)),                                       \
+            "r" ((USItype)(d)))
+
+#define count_leading_zeros(count, x) \
+    __asm__ ("clz %0,%1"                                                \
+            : "=r" ((USItype)(count))                                  \
+            : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#endif /* __a29k__ */
+
+
+#if defined (__alpha) && W_TYPE_SIZE == 64
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {                                                                 \
+    UDItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("umulh %r1,%2,%0"                                          \
+            : "=r" ((UDItype) ph)                                      \
+            : "%rJ" (__m0),                                            \
+              "rI" (__m1));                                            \
+    (pl) = __m0 * __m1;                                                \
+  } while (0)
+#define UMUL_TIME 46
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  do { UDItype __r;                                                    \
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));                        \
+    (r) = __r;                                                         \
+  } while (0)
+extern UDItype __udiv_qrnnd ();
+#define UDIV_TIME 220
+#endif /* LONGLONG_STANDALONE */
+#endif /* __alpha */
+
+/***************************************
+ **************  ARM  ******************
+ ***************************************/
+#if defined (__arm__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("adds %1, %4, %5\n"                                          \
+          "adc  %0, %2, %3"                                            \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%r" ((USItype)(ah)),                                      \
+            "rI" ((USItype)(bh)),                                      \
+            "%r" ((USItype)(al)),                                      \
+            "rI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subs %1, %4, %5\n"                                          \
+          "sbc  %0, %2, %3"                                            \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "r" ((USItype)(ah)),                                       \
+            "rI" ((USItype)(bh)),                                      \
+            "r" ((USItype)(al)),                                       \
+            "rI" ((USItype)(bl)))
+#if defined __ARM_ARCH_2__ || defined __ARM_ARCH_3__
+#define umul_ppmm(xh, xl, a, b) \
+  __asm__ ("%@ Inlined umul_ppmm\n"                                     \
+       "mov    %|r0, %2, lsr #16               @ AAAA\n"               \
+       "mov    %|r2, %3, lsr #16               @ BBBB\n"               \
+       "bic    %|r1, %2, %|r0, lsl #16         @ aaaa\n"               \
+       "bic    %0, %3, %|r2, lsl #16           @ bbbb\n"               \
+       "mul    %1, %|r1, %|r2                  @ aaaa * BBBB\n"        \
+       "mul    %|r2, %|r0, %|r2                @ AAAA * BBBB\n"        \
+       "mul    %|r1, %0, %|r1                  @ aaaa * bbbb\n"        \
+       "mul    %0, %|r0, %0                    @ AAAA * bbbb\n"        \
+       "adds   %|r0, %1, %0                    @ central sum\n"        \
+       "addcs  %|r2, %|r2, #65536\n"                                   \
+       "adds   %1, %|r1, %|r0, lsl #16\n"                              \
+       "adc    %0, %|r2, %|r0, lsr #16"                                \
+          : "=&r" ((USItype)(xh)),                                     \
+            "=r" ((USItype)(xl))                                       \
+          : "r" ((USItype)(a)),                                        \
+            "r" ((USItype)(b))                                         \
+          : "r0", "r1", "r2")
+#else
+#define umul_ppmm(xh, xl, a, b)                                         \
+  __asm__ ("%@ Inlined umul_ppmm\n"                                     \
+          "umull %r1, %r0, %r2, %r3"                                   \
+                  : "=&r" ((USItype)(xh)),                             \
+                    "=r" ((USItype)(xl))                               \
+                  : "r" ((USItype)(a)),                                \
+                    "r" ((USItype)(b))                                 \
+                  : "r0", "r1")
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 100
+#endif /* __arm__ */
+
+/***************************************
+ **************  CLIPPER  **************
+ ***************************************/
+#if defined (__clipper__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __xx;                                                       \
+  __asm__ ("mulwux %2,%0"                                               \
+          : "=r" (__xx.__ll)                                           \
+          : "%0" ((USItype)(u)),                                       \
+            "r" ((USItype)(v)));                                       \
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define smul_ppmm(w1, w0, u, v) \
+  ({union {DItype __ll;                                                \
+          struct {SItype __l, __h;} __i;                               \
+         } __xx;                                                       \
+  __asm__ ("mulwx %2,%0"                                                \
+          : "=r" (__xx.__ll)                                           \
+          : "%0" ((SItype)(u)),                                        \
+            "r" ((SItype)(v)));                                        \
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+  ({UDItype __w;                                                       \
+    __asm__ ("mulwux %2,%0"                                             \
+            : "=r" (__w)                                               \
+            : "%0" ((USItype)(u)),                                     \
+              "r" ((USItype)(v)));                                     \
+    __w; })
+#endif /* __clipper__ */
+
+
+/***************************************
+ **************  GMICRO  ***************
+ ***************************************/
+#if defined (__gmicro__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add.w %5,%1\n"                                              \
+          "addx %3,%0"                                                 \
+          : "=g" ((USItype)(sh)),                                      \
+            "=&g" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "g" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub.w %5,%1\n"                                              \
+          "subx %3,%0"                                                 \
+          : "=g" ((USItype)(sh)),                                      \
+            "=&g" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "g" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "g" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+  __asm__ ("mulx %3,%0,%1"                                              \
+          : "=g" ((USItype)(ph)),                                      \
+            "=r" ((USItype)(pl))                                       \
+          : "%0" ((USItype)(m0)),                                      \
+            "g" ((USItype)(m1)))
+#define udiv_qrnnd(q, r, nh, nl, d) \
+  __asm__ ("divx %4,%0,%1"                                              \
+          : "=g" ((USItype)(q)),                                       \
+            "=r" ((USItype)(r))                                        \
+          : "1" ((USItype)(nh)),                                       \
+            "0" ((USItype)(nl)),                                       \
+            "g" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+  __asm__ ("bsch/1 %1,%0"                                               \
+          : "=g" (count)                                               \
+          : "g" ((USItype)(x)),                                        \
+            "0" ((USItype)0))
+#endif
+
+
+/***************************************
+ **************  HPPA  *****************
+ ***************************************/
+#if defined (__hppa) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("   add %4,%5,%1\n"                                             \
+          "    addc %2,%3,%0"                                              \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%rM" ((USItype)(ah)),                                     \
+            "rM" ((USItype)(bh)),                                      \
+            "%rM" ((USItype)(al)),                                     \
+            "rM" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("   sub %4,%5,%1\n"                                             \
+          "    subb %2,%3,%0"                                              \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "rM" ((USItype)(ah)),                                      \
+            "rM" ((USItype)(bh)),                                      \
+            "rM" ((USItype)(al)),                                      \
+            "rM" ((USItype)(bl)))
+#if defined (_PA_RISC1_1)
+#define umul_ppmm(wh, wl, u, v) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __h, __l;} __i;                              \
+         } __xx;                                                       \
+    __asm__ (" xmpyu %1,%2,%0"                                           \
+            : "=*f" (__xx.__ll)                                        \
+            : "*f" ((USItype)(u)),                                     \
+              "*f" ((USItype)(v)));                                    \
+    (wh) = __xx.__i.__h;                                               \
+    (wl) = __xx.__i.__l;                                               \
+  } while (0)
+#define UMUL_TIME 8
+#define UDIV_TIME 60
+#else
+#define UMUL_TIME 40
+#define UDIV_TIME 80
+#endif
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  do { USItype __r;                                                    \
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));                        \
+    (r) = __r;                                                         \
+  } while (0)
+extern USItype __udiv_qrnnd ();
+#endif /* LONGLONG_STANDALONE */
+#define count_leading_zeros(count, x) \
+  do {                                                                \
+    USItype __tmp;                                                    \
+    __asm__ (                                                         \
+       "       ldi             1,%0                                       \n" \
+       "       extru,=         %1,15,16,%%r0  ; Bits 31..16 zero?         \n" \
+       "       extru,tr        %1,15,16,%1    ; No.  Shift down, skip add.\n" \
+       "       ldo             16(%0),%0      ; Yes.   Perform add.       \n" \
+       "       extru,=         %1,23,8,%%r0   ; Bits 15..8 zero?          \n" \
+       "       extru,tr        %1,23,8,%1     ; No.  Shift down, skip add.\n" \
+       "       ldo             8(%0),%0       ; Yes.   Perform add.       \n" \
+       "       extru,=         %1,27,4,%%r0   ; Bits 7..4 zero?           \n" \
+       "       extru,tr        %1,27,4,%1     ; No.  Shift down, skip add.\n" \
+       "       ldo             4(%0),%0       ; Yes.   Perform add.       \n" \
+       "       extru,=         %1,29,2,%%r0   ; Bits 3..2 zero?           \n" \
+       "       extru,tr        %1,29,2,%1     ; No.  Shift down, skip add.\n" \
+       "       ldo             2(%0),%0       ; Yes.   Perform add.       \n" \
+       "       extru           %1,30,1,%1     ; Extract bit 1.            \n" \
+       "       sub             %0,%1,%0       ; Subtract it.              "   \
+       : "=r" (count), "=r" (__tmp) : "1" (x));                        \
+  } while (0)
+#endif /* hppa */
+
+
+/***************************************
+ **************  I370  *****************
+ ***************************************/
+#if (defined (__i370__) || defined (__mvs__)) && W_TYPE_SIZE == 32
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __h, __l;} __i;                              \
+         } __xx;                                                       \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("mr %0,%3"                                                 \
+            : "=r" (__xx.__i.__h),                                     \
+              "=r" (__xx.__i.__l)                                      \
+            : "%1" (__m0),                                             \
+              "r" (__m1));                                             \
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;                          \
+    (xh) += ((((SItype) __m0 >> 31) & __m1)                            \
+            + (((SItype) __m1 >> 31) & __m0));                         \
+  } while (0)
+#define smul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    union {DItype __ll;                                                \
+          struct {USItype __h, __l;} __i;                              \
+         } __xx;                                                       \
+    __asm__ ("mr %0,%3"                                                 \
+            : "=r" (__xx.__i.__h),                                     \
+              "=r" (__xx.__i.__l)                                      \
+            : "%1" (m0),                                               \
+              "r" (m1));                                               \
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;                          \
+  } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  do {                                                                 \
+    union {DItype __ll;                                                \
+          struct {USItype __h, __l;} __i;                              \
+         } __xx;                                                       \
+    __xx.__i.__h = n1; __xx.__i.__l = n0;                              \
+    __asm__ ("dr %0,%2"                                                 \
+            : "=r" (__xx.__ll)                                         \
+            : "0" (__xx.__ll), "r" (d));                               \
+    (q) = __xx.__i.__l; (r) = __xx.__i.__h;                            \
+  } while (0)
+#endif
+
+
+/***************************************
+ **************  I386  *****************
+ ***************************************/
+#undef __i386__
+#if (defined (__i386__) || defined (__i486__)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addl %5,%1\n"                                               \
+          "adcl %3,%0"                                                 \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "g" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subl %5,%1\n"                                               \
+          "sbbl %3,%0"                                                 \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "g" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "g" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("mull %3"                                                    \
+          : "=a" ((USItype)(w0)),                                      \
+            "=d" ((USItype)(w1))                                       \
+          : "%0" ((USItype)(u)),                                       \
+            "rm" ((USItype)(v)))
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divl %4"                                                    \
+          : "=a" ((USItype)(q)),                                       \
+            "=d" ((USItype)(r))                                        \
+          : "0" ((USItype)(n0)),                                       \
+            "1" ((USItype)(n1)),                                       \
+            "rm" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+  do {                                                                 \
+    USItype __cbtmp;                                                   \
+    __asm__ ("bsrl %1,%0"                                               \
+            : "=r" (__cbtmp) : "rm" ((USItype)(x)));                   \
+    (count) = __cbtmp ^ 31;                                            \
+  } while (0)
+#define count_trailing_zeros(count, x) \
+  __asm__ ("bsfl %1,%0" : "=r" (count) : "rm" ((USItype)(x)))
+#ifndef UMUL_TIME
+#define UMUL_TIME 40
+#endif
+#ifndef UDIV_TIME
+#define UDIV_TIME 40
+#endif
+#endif /* 80x86 */
+
+
+/***************************************
+ **************  I860  *****************
+ ***************************************/
+#if defined (__i860__) && W_TYPE_SIZE == 32
+#define rshift_rhlc(r,h,l,c) \
+  __asm__ ("shr %3,r0,r0\n"  \
+           "shrd %1,%2,%0"   \
+          "=r" (r) : "r" (h), "r" (l), "rn" (c))
+#endif /* i860 */
+
+/***************************************
+ **************  I960  *****************
+ ***************************************/
+#if defined (__i960__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("cmpo 1,0\n"      \
+           "addc %5,%4,%1\n" \
+           "addc %3,%2,%0"   \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%dI" ((USItype)(ah)),                                     \
+            "dI" ((USItype)(bh)),                                      \
+            "%dI" ((USItype)(al)),                                     \
+            "dI" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("cmpo 0,0\n"      \
+           "subc %5,%4,%1\n" \
+           "subc %3,%2,%0"   \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "dI" ((USItype)(ah)),                                      \
+            "dI" ((USItype)(bh)),                                      \
+            "dI" ((USItype)(al)),                                      \
+            "dI" ((USItype)(bl)))
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __xx;                                                       \
+  __asm__ ("emul        %2,%1,%0"                                       \
+          : "=d" (__xx.__ll)                                           \
+          : "%dI" ((USItype)(u)),                                      \
+            "dI" ((USItype)(v)));                                      \
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+  ({UDItype __w;                                                       \
+    __asm__ ("emul      %2,%1,%0"                                       \
+            : "=d" (__w)                                               \
+            : "%dI" ((USItype)(u)),                                    \
+              "dI" ((USItype)(v)));                                    \
+    __w; })
+#define udiv_qrnnd(q, r, nh, nl, d) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __nn;                                                       \
+    __nn.__i.__h = (nh); __nn.__i.__l = (nl);                          \
+    __asm__ ("ediv %d,%n,%0"                                            \
+          : "=d" (__rq.__ll)                                           \
+          : "dI" (__nn.__ll),                                          \
+            "dI" ((USItype)(d)));                                      \
+    (r) = __rq.__i.__l; (q) = __rq.__i.__h;                            \
+  } while (0)
+#define count_leading_zeros(count, x) \
+  do {                                                                 \
+    USItype __cbtmp;                                                   \
+    __asm__ ("scanbit %1,%0"                                            \
+            : "=r" (__cbtmp)                                           \
+            : "r" ((USItype)(x)));                                     \
+    (count) = __cbtmp ^ 31;                                            \
+  } while (0)
+#define COUNT_LEADING_ZEROS_0 (-32) /* sic */
+#if defined (__i960mx)         /* what is the proper symbol to test??? */
+#define rshift_rhlc(r,h,l,c) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __nn;                                                       \
+    __nn.__i.__h = (h); __nn.__i.__l = (l);                            \
+    __asm__ ("shre %2,%1,%0"                                            \
+            : "=d" (r) : "dI" (__nn.__ll), "dI" (c));                  \
+  }
+#endif /* i960mx */
+#endif /* i960 */
+
+
+/***************************************
+ **************  68000 ****************
+ ***************************************/
+#if (defined (__mc68000__) || defined (__mc68020__) || defined (__NeXT__) || defined(mc68020)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add%.l %5,%1\n"                                             \
+          "addx%.l %3,%0"                                              \
+          : "=d" ((USItype)(sh)),                                      \
+            "=&d" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "d" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub%.l %5,%1\n"                                             \
+          "subx%.l %3,%0"                                              \
+          : "=d" ((USItype)(sh)),                                      \
+            "=&d" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "d" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "g" ((USItype)(bl)))
+#if (defined (__mc68020__) || defined (__NeXT__) || defined(mc68020))
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("mulu%.l %3,%1:%0"                                           \
+          : "=d" ((USItype)(w0)),                                      \
+            "=d" ((USItype)(w1))                                       \
+          : "%0" ((USItype)(u)),                                       \
+            "dmi" ((USItype)(v)))
+#define UMUL_TIME 45
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divu%.l %4,%1:%0"                                           \
+          : "=d" ((USItype)(q)),                                       \
+            "=d" ((USItype)(r))                                        \
+          : "0" ((USItype)(n0)),                                       \
+            "1" ((USItype)(n1)),                                       \
+            "dmi" ((USItype)(d)))
+#define UDIV_TIME 90
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("divs%.l %4,%1:%0"                                           \
+          : "=d" ((USItype)(q)),                                       \
+            "=d" ((USItype)(r))                                        \
+          : "0" ((USItype)(n0)),                                       \
+            "1" ((USItype)(n1)),                                       \
+            "dmi" ((USItype)(d)))
+#define count_leading_zeros(count, x) \
+  __asm__ ("bfffo %1{%b2:%b2},%0"                                       \
+          : "=d" ((USItype)(count))                                    \
+          : "od" ((USItype)(x)), "n" (0))
+#define COUNT_LEADING_ZEROS_0 32
+#else /* not mc68020 */
+#define umul_ppmm(xh, xl, a, b) \
+  do { USItype __umul_tmp1, __umul_tmp2;                         \
+       __asm__ ("| Inlined umul_ppmm                         \n" \
+ "        move%.l %5,%3                                       \n" \
+ "        move%.l %2,%0                                       \n" \
+ "        move%.w %3,%1                                       \n" \
+ "        swap %3                                            \n" \
+ "        swap %0                                            \n" \
+ "        mulu %2,%1                                         \n" \
+ "        mulu %3,%0                                         \n" \
+ "        mulu %2,%3                                         \n" \
+ "        swap %2                                            \n" \
+ "        mulu %5,%2                                         \n" \
+ "        add%.l       %3,%2                                 \n" \
+ "        jcc  1f                                            \n" \
+ "        add%.l       %#0x10000,%0                          \n" \
+ "1:   move%.l %2,%3                                         \n" \
+ "        clr%.w       %2                                    \n" \
+ "        swap %2                                            \n" \
+ "        swap %3                                            \n" \
+ "        clr%.w       %3                                    \n" \
+ "        add%.l       %3,%1                                 \n" \
+ "        addx%.l %2,%0                                       \n" \
+ "        | End inlined umul_ppmm"                                \
+             : "=&d" ((USItype)(xh)), "=&d" ((USItype)(xl)),     \
+               "=d" (__umul_tmp1), "=&d" (__umul_tmp2)           \
+             : "%2" ((USItype)(a)), "d" ((USItype)(b)));         \
+  } while (0)
+#define UMUL_TIME 100
+#define UDIV_TIME 400
+#endif /* not mc68020 */
+#endif /* mc68000 */
+
+
+/***************************************
+ **************  88000 ****************
+ ***************************************/
+#if defined (__m88000__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addu.co %1,%r4,%r5\n"                                       \
+          "addu.ci %0,%r2,%r3"                                         \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%rJ" ((USItype)(ah)),                                     \
+            "rJ" ((USItype)(bh)),                                      \
+            "%rJ" ((USItype)(al)),                                     \
+            "rJ" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subu.co %1,%r4,%r5\n"                                       \
+          "subu.ci %0,%r2,%r3"                                         \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "rJ" ((USItype)(ah)),                                      \
+            "rJ" ((USItype)(bh)),                                      \
+            "rJ" ((USItype)(al)),                                      \
+            "rJ" ((USItype)(bl)))
+#define count_leading_zeros(count, x) \
+  do {                                                                 \
+    USItype __cbtmp;                                                   \
+    __asm__ ("ff1 %0,%1"                                                \
+            : "=r" (__cbtmp)                                           \
+            : "r" ((USItype)(x)));                                     \
+    (count) = __cbtmp ^ 31;                                            \
+  } while (0)
+#define COUNT_LEADING_ZEROS_0 63 /* sic */
+#if defined (__m88110__)
+#define umul_ppmm(wh, wl, u, v) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __h, __l;} __i;                              \
+         } __x;                                                        \
+    __asm__ ("mulu.d %0,%1,%2" : "=r" (__x.__ll) : "r" (u), "r" (v));   \
+    (wh) = __x.__i.__h;                                                \
+    (wl) = __x.__i.__l;                                                \
+  } while (0)
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __h, __l;} __i;                              \
+         } __x, __q;                                                   \
+  __x.__i.__h = (n1); __x.__i.__l = (n0);                              \
+  __asm__ ("divu.d %0,%1,%2"                                            \
+          : "=r" (__q.__ll) : "r" (__x.__ll), "r" (d));                \
+  (r) = (n0) - __q.__l * (d); (q) = __q.__l; })
+#define UMUL_TIME 5
+#define UDIV_TIME 25
+#else
+#define UMUL_TIME 17
+#define UDIV_TIME 150
+#endif /* __m88110__ */
+#endif /* __m88000__ */
+
+/***************************************
+ **************  MIPS  *****************
+ ***************************************/
+#if defined (__mips__) && W_TYPE_SIZE == 32
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("multu %2,%3"                                                \
+          : "=l" ((USItype)(w0)),                                      \
+            "=h" ((USItype)(w1))                                       \
+          : "d" ((USItype)(u)),                                        \
+            "d" ((USItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("multu %2,%3 \n" \
+          "mflo %0 \n"     \
+          "mfhi %1"                                                        \
+          : "=d" ((USItype)(w0)),                                      \
+            "=d" ((USItype)(w1))                                       \
+          : "d" ((USItype)(u)),                                        \
+            "d" ((USItype)(v)))
+#endif
+#define UMUL_TIME 10
+#define UDIV_TIME 100
+#endif /* __mips__ */
+
+/***************************************
+ **************  MIPS/64  **************
+ ***************************************/
+#if (defined (__mips) && __mips >= 3) && W_TYPE_SIZE == 64
+#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("dmultu %2,%3"                                               \
+          : "=l" ((UDItype)(w0)),                                      \
+            "=h" ((UDItype)(w1))                                       \
+          : "d" ((UDItype)(u)),                                        \
+            "d" ((UDItype)(v)))
+#else
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("dmultu %2,%3 \n"    \
+          "mflo %0 \n"         \
+          "mfhi %1"                                                        \
+          : "=d" ((UDItype)(w0)),                                      \
+            "=d" ((UDItype)(w1))                                       \
+          : "d" ((UDItype)(u)),                                        \
+            "d" ((UDItype)(v)))
+#endif
+#define UMUL_TIME 20
+#define UDIV_TIME 140
+#endif /* __mips__ */
+
+
+/***************************************
+ **************  32000 ****************
+ ***************************************/
+#if defined (__ns32000__) && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __xx;                                                       \
+  __asm__ ("meid %2,%0"                                                 \
+          : "=g" (__xx.__ll)                                           \
+          : "%0" ((USItype)(u)),                                       \
+            "g" ((USItype)(v)));                                       \
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#define __umulsidi3(u, v) \
+  ({UDItype __w;                                                       \
+    __asm__ ("meid %2,%0"                                               \
+            : "=g" (__w)                                               \
+            : "%0" ((USItype)(u)),                                     \
+              "g" ((USItype)(v)));                                     \
+    __w; })
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __xx;                                                       \
+  __xx.__i.__h = (n1); __xx.__i.__l = (n0);                            \
+  __asm__ ("deid %2,%0"                                                 \
+          : "=g" (__xx.__ll)                                           \
+          : "0" (__xx.__ll),                                           \
+            "g" ((USItype)(d)));                                       \
+  (r) = __xx.__i.__l; (q) = __xx.__i.__h; })
+#define count_trailing_zeros(count,x) \
+  do {
+    __asm__ ("ffsd      %2,%0"                                          \
+            : "=r" ((USItype) (count))                                 \
+            : "0" ((USItype) 0),                                       \
+              "r" ((USItype) (x)));                                    \
+  } while (0)
+#endif /* __ns32000__ */
+
+
+/***************************************
+ **************  PPC  ******************
+ ***************************************/
+#if (defined (_ARCH_PPC) || defined (_IBMR2)) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {                                                                 \
+    if (__builtin_constant_p (bh) && (bh) == 0)                        \
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{aze|addze} %0,%2"           \
+            : "=r" ((USItype)(sh)),                                    \
+              "=&r" ((USItype)(sl))                                    \
+            : "%r" ((USItype)(ah)),                                    \
+              "%r" ((USItype)(al)),                                    \
+              "rI" ((USItype)(bl)));                                   \
+    else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0)         \
+      __asm__ ("{a%I4|add%I4c} %1,%3,%4\n\t{ame|addme} %0,%2"           \
+            : "=r" ((USItype)(sh)),                                    \
+              "=&r" ((USItype)(sl))                                    \
+            : "%r" ((USItype)(ah)),                                    \
+              "%r" ((USItype)(al)),                                    \
+              "rI" ((USItype)(bl)));                                   \
+    else                                                               \
+      __asm__ ("{a%I5|add%I5c} %1,%4,%5\n\t{ae|adde} %0,%2,%3"          \
+            : "=r" ((USItype)(sh)),                                    \
+              "=&r" ((USItype)(sl))                                    \
+            : "%r" ((USItype)(ah)),                                    \
+              "r" ((USItype)(bh)),                                     \
+              "%r" ((USItype)(al)),                                    \
+              "rI" ((USItype)(bl)));                                   \
+  } while (0)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {                                                                 \
+    if (__builtin_constant_p (ah) && (ah) == 0)                        \
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfze|subfze} %0,%2"       \
+              : "=r" ((USItype)(sh)),                                  \
+                "=&r" ((USItype)(sl))                                  \
+              : "r" ((USItype)(bh)),                                   \
+                "rI" ((USItype)(al)),                                  \
+                "r" ((USItype)(bl)));                                  \
+    else if (__builtin_constant_p (ah) && (ah) ==~(USItype) 0)         \
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{sfme|subfme} %0,%2"       \
+              : "=r" ((USItype)(sh)),                                  \
+                "=&r" ((USItype)(sl))                                  \
+              : "r" ((USItype)(bh)),                                   \
+                "rI" ((USItype)(al)),                                  \
+                "r" ((USItype)(bl)));                                  \
+    else if (__builtin_constant_p (bh) && (bh) == 0)                   \
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{ame|addme} %0,%2"         \
+              : "=r" ((USItype)(sh)),                                  \
+                "=&r" ((USItype)(sl))                                  \
+              : "r" ((USItype)(ah)),                                   \
+                "rI" ((USItype)(al)),                                  \
+                "r" ((USItype)(bl)));                                  \
+    else if (__builtin_constant_p (bh) && (bh) ==~(USItype) 0)         \
+      __asm__ ("{sf%I3|subf%I3c} %1,%4,%3\n\t{aze|addze} %0,%2"         \
+              : "=r" ((USItype)(sh)),                                  \
+                "=&r" ((USItype)(sl))                                  \
+              : "r" ((USItype)(ah)),                                   \
+                "rI" ((USItype)(al)),                                  \
+                "r" ((USItype)(bl)));                                  \
+    else                                                               \
+      __asm__ ("{sf%I4|subf%I4c} %1,%5,%4\n\t{sfe|subfe} %0,%3,%2"      \
+              : "=r" ((USItype)(sh)),                                  \
+                "=&r" ((USItype)(sl))                                  \
+              : "r" ((USItype)(ah)),                                   \
+                "r" ((USItype)(bh)),                                   \
+                "rI" ((USItype)(al)),                                  \
+                "r" ((USItype)(bl)));                                  \
+  } while (0)
+#define count_leading_zeros(count, x) \
+  __asm__ ("{cntlz|cntlzw} %0,%1"                                       \
+          : "=r" ((USItype)(count))                                    \
+          : "r" ((USItype)(x)))
+#define COUNT_LEADING_ZEROS_0 32
+#if defined (_ARCH_PPC)
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {                                                                 \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("mulhwu %0,%1,%2"                                          \
+            : "=r" ((USItype) ph)                                      \
+            : "%r" (__m0),                                             \
+              "r" (__m1));                                             \
+    (pl) = __m0 * __m1;                                                \
+  } while (0)
+#define UMUL_TIME 15
+#define smul_ppmm(ph, pl, m0, m1) \
+  do {                                                                 \
+    SItype __m0 = (m0), __m1 = (m1);                                   \
+    __asm__ ("mulhw %0,%1,%2"                                           \
+            : "=r" ((SItype) ph)                                       \
+            : "%r" (__m0),                                             \
+              "r" (__m1));                                             \
+    (pl) = __m0 * __m1;                                                \
+  } while (0)
+#define SMUL_TIME 14
+#define UDIV_TIME 120
+#else
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("mul %0,%2,%3"                                             \
+            : "=r" ((USItype)(xh)),                                    \
+              "=q" ((USItype)(xl))                                     \
+            : "r" (__m0),                                              \
+              "r" (__m1));                                             \
+    (xh) += ((((SItype) __m0 >> 31) & __m1)                            \
+            + (((SItype) __m1 >> 31) & __m0));                         \
+  } while (0)
+#define UMUL_TIME 8
+#define smul_ppmm(xh, xl, m0, m1) \
+  __asm__ ("mul %0,%2,%3"                                               \
+          : "=r" ((SItype)(xh)),                                       \
+            "=q" ((SItype)(xl))                                        \
+          : "r" (m0),                                                  \
+            "r" (m1))
+#define SMUL_TIME 4
+#define sdiv_qrnnd(q, r, nh, nl, d) \
+  __asm__ ("div %0,%2,%4"                                               \
+          : "=r" ((SItype)(q)), "=q" ((SItype)(r))                     \
+          : "r" ((SItype)(nh)), "1" ((SItype)(nl)), "r" ((SItype)(d)))
+#define UDIV_TIME 100
+#endif
+#endif /* Power architecture variants. */
+
+
+/***************************************
+ **************  PYR  ******************
+ ***************************************/
+#if defined (__pyr__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addw        %5,%1 \n" \
+          "addwc       %3,%0"                                          \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "g" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subw        %5,%1 \n" \
+          "subwb       %3,%0"                                          \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "g" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "g" ((USItype)(bl)))
+/* This insn works on Pyramids with AP, XP, or MI CPUs, but not with SP.  */
+#define umul_ppmm(w1, w0, u, v) \
+  ({union {UDItype __ll;                                               \
+          struct {USItype __h, __l;} __i;                              \
+         } __xx;                                                       \
+  __asm__ ("movw %1,%R0 \n" \
+          "uemul %2,%0"                                                \
+          : "=&r" (__xx.__ll)                                          \
+          : "g" ((USItype) (u)),                                       \
+            "g" ((USItype)(v)));                                       \
+  (w1) = __xx.__i.__h; (w0) = __xx.__i.__l;})
+#endif /* __pyr__ */
+
+
+/***************************************
+ **************  RT/ROMP  **************
+ ***************************************/
+#if defined (__ibm032__) /* RT/ROMP */ && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("a %1,%5 \n" \
+          "ae %0,%3"                                                   \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "r" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "r" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("s %1,%5\n" \
+          "se %0,%3"                                                   \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "r" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "r" ((USItype)(bl)))
+#define umul_ppmm(ph, pl, m0, m1) \
+  do {                                                                 \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ (                                                          \
+       "s       r2,r2    \n" \
+       "mts    r10,%2   \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "m      r2,%3    \n" \
+       "cas    %0,r2,r0 \n" \
+       "mfs    r10,%1"                                                 \
+            : "=r" ((USItype)(ph)),                                    \
+              "=r" ((USItype)(pl))                                     \
+            : "%r" (__m0),                                             \
+               "r" (__m1)                                              \
+            : "r2");                                                   \
+    (ph) += ((((SItype) __m0 >> 31) & __m1)                            \
+            + (((SItype) __m1 >> 31) & __m0));                         \
+  } while (0)
+#define UMUL_TIME 20
+#define UDIV_TIME 200
+#define count_leading_zeros(count, x) \
+  do {                                                                 \
+    if ((x) >= 0x10000)                                                \
+      __asm__ ("clz     %0,%1"                                          \
+              : "=r" ((USItype)(count))                                \
+              : "r" ((USItype)(x) >> 16));                             \
+    else                                                               \
+      {                                                                \
+       __asm__ ("clz   %0,%1"                                          \
+                : "=r" ((USItype)(count))                              \
+                : "r" ((USItype)(x)));                                 \
+       (count) += 16;                                                  \
+      }                                                                \
+  } while (0)
+#endif /* RT/ROMP */
+
+
+/***************************************
+ **************  SH2  ******************
+ ***************************************/
+#if (defined (__sh2__) || defined(__sh3__) || defined(__SH4__) ) \
+    && W_TYPE_SIZE == 32
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ (                                                            \
+        "dmulu.l %2,%3\n"  \
+       "sts    macl,%1\n" \
+       "sts    mach,%0"                                                \
+          : "=r" ((USItype)(w1)),                                      \
+            "=r" ((USItype)(w0))                                       \
+          : "r" ((USItype)(u)),                                        \
+            "r" ((USItype)(v))                                         \
+          : "macl", "mach")
+#define UMUL_TIME 5
+#endif
+
+/***************************************
+ **************  SPARC ****************
+ ***************************************/
+#if defined (__sparc__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addcc %r4,%5,%1\n" \
+          "addx %r2,%3,%0"                                             \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "%rJ" ((USItype)(ah)),                                     \
+            "rI" ((USItype)(bh)),                                      \
+            "%rJ" ((USItype)(al)),                                     \
+            "rI" ((USItype)(bl))                                       \
+          __CLOBBER_CC)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subcc %r4,%5,%1\n" \
+          "subx %r2,%3,%0"                                             \
+          : "=r" ((USItype)(sh)),                                      \
+            "=&r" ((USItype)(sl))                                      \
+          : "rJ" ((USItype)(ah)),                                      \
+            "rI" ((USItype)(bh)),                                      \
+            "rJ" ((USItype)(al)),                                      \
+            "rI" ((USItype)(bl))                                       \
+          __CLOBBER_CC)
+#if defined (__sparc_v8__)
+/* Don't match immediate range because, 1) it is not often useful,
+   2) the 'I' flag thinks of the range as a 13 bit signed interval,
+   while we want to match a 13 bit interval, sign extended to 32 bits,
+   but INTERPRETED AS UNSIGNED.  */
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("umul %2,%3,%1;rd %%y,%0"                                    \
+          : "=r" ((USItype)(w1)),                                      \
+            "=r" ((USItype)(w0))                                       \
+          : "r" ((USItype)(u)),                                        \
+            "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#ifndef SUPERSPARC     /* SuperSPARC's udiv only handles 53 bit dividends */
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  do {                                                                 \
+    USItype __q;                                                       \
+    __asm__ ("mov %1,%%y;nop;nop;nop;udiv %2,%3,%0"                     \
+            : "=r" ((USItype)(__q))                                    \
+            : "r" ((USItype)(n1)),                                     \
+              "r" ((USItype)(n0)),                                     \
+              "r" ((USItype)(d)));                                     \
+    (r) = (n0) - __q * (d);                                            \
+    (q) = __q;                                                         \
+  } while (0)
+#define UDIV_TIME 25
+#endif /* SUPERSPARC */
+#else /* ! __sparc_v8__ */
+#if defined (__sparclite__)
+/* This has hardware multiply but not divide.  It also has two additional
+   instructions scan (ffs from high bit) and divscc.  */
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("umul %2,%3,%1;rd %%y,%0"                                    \
+          : "=r" ((USItype)(w1)),                                      \
+            "=r" ((USItype)(w0))                                       \
+          : "r" ((USItype)(u)),                                        \
+            "r" ((USItype)(v)))
+#define UMUL_TIME 5
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  __asm__ ("! Inlined udiv_qrnnd                                     \n" \
+ "        wr   %%g0,%2,%%y     ! Not a delayed write for sparclite  \n" \
+ "        tst  %%g0                                                 \n" \
+ "        divscc       %3,%4,%%g1                                   \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%%g1                                 \n" \
+ "        divscc       %%g1,%4,%0                                   \n" \
+ "        rd   %%y,%1                                               \n" \
+ "        bl,a 1f                                                    \n" \
+ "        add  %1,%4,%1                                             \n" \
+ "1:   ! End of inline udiv_qrnnd"                                     \
+          : "=r" ((USItype)(q)),                                       \
+            "=r" ((USItype)(r))                                        \
+          : "r" ((USItype)(n1)),                                       \
+            "r" ((USItype)(n0)),                                       \
+            "rI" ((USItype)(d))                                        \
+          : "%g1" __AND_CLOBBER_CC)
+#define UDIV_TIME 37
+#define count_leading_zeros(count, x) \
+  __asm__ ("scan %1,0,%0"                                               \
+          : "=r" ((USItype)(x))                                        \
+          : "r" ((USItype)(count)))
+/* Early sparclites return 63 for an argument of 0, but they warn that future
+   implementations might change this.  Therefore, leave COUNT_LEADING_ZEROS_0
+   undefined.  */
+#endif /* __sparclite__ */
+#endif /* __sparc_v8__ */
+/* Default to sparc v7 versions of umul_ppmm and udiv_qrnnd.  */
+#ifndef umul_ppmm
+#define umul_ppmm(w1, w0, u, v) \
+  __asm__ ("! Inlined umul_ppmm                                        \n" \
+ "        wr   %%g0,%2,%%y     ! SPARC has 0-3 delay insn after a wr  \n" \
+ "        sra  %3,31,%%g2      ! Don't move this insn                 \n" \
+ "        and  %2,%%g2,%%g2    ! Don't move this insn                 \n" \
+ "        andcc        %%g0,0,%%g1     ! Don't move this insn                 \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,%3,%%g1                                   \n" \
+ "        mulscc       %%g1,0,%%g1                                    \n" \
+ "        add  %%g1,%%g2,%0                                           \n" \
+ "        rd   %%y,%1"                                                 \
+          : "=r" ((USItype)(w1)),                                      \
+            "=r" ((USItype)(w0))                                       \
+          : "%rI" ((USItype)(u)),                                      \
+            "r" ((USItype)(v))                                         \
+          : "%g1", "%g2" __AND_CLOBBER_CC)
+#define UMUL_TIME 39           /* 39 instructions */
+#endif
+#ifndef udiv_qrnnd
+#ifndef LONGLONG_STANDALONE
+#define udiv_qrnnd(q, r, n1, n0, d) \
+  do { USItype __r;                                                    \
+    (q) = __udiv_qrnnd (&__r, (n1), (n0), (d));                        \
+    (r) = __r;                                                         \
+  } while (0)
+extern USItype __udiv_qrnnd ();
+#define UDIV_TIME 140
+#endif /* LONGLONG_STANDALONE */
+#endif /* udiv_qrnnd */
+#endif /* __sparc__ */
+
+
+/***************************************
+ **************  VAX  ******************
+ ***************************************/
+#if defined (__vax__) && W_TYPE_SIZE == 32
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("addl2 %5,%1\n" \
+          "adwc %3,%0"                                                 \
+          : "=g" ((USItype)(sh)),                                      \
+            "=&g" ((USItype)(sl))                                      \
+          : "%0" ((USItype)(ah)),                                      \
+            "g" ((USItype)(bh)),                                       \
+            "%1" ((USItype)(al)),                                      \
+            "g" ((USItype)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("subl2 %5,%1\n" \
+          "sbwc %3,%0"                                                 \
+          : "=g" ((USItype)(sh)),                                      \
+            "=&g" ((USItype)(sl))                                      \
+          : "0" ((USItype)(ah)),                                       \
+            "g" ((USItype)(bh)),                                       \
+            "1" ((USItype)(al)),                                       \
+            "g" ((USItype)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    union {UDItype __ll;                                               \
+          struct {USItype __l, __h;} __i;                              \
+         } __xx;                                                       \
+    USItype __m0 = (m0), __m1 = (m1);                                  \
+    __asm__ ("emul %1,%2,$0,%0"                                         \
+            : "=g" (__xx.__ll)                                         \
+            : "g" (__m0),                                              \
+              "g" (__m1));                                             \
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;                          \
+    (xh) += ((((SItype) __m0 >> 31) & __m1)                            \
+            + (((SItype) __m1 >> 31) & __m0));                         \
+  } while (0)
+#define sdiv_qrnnd(q, r, n1, n0, d) \
+  do {                                                                 \
+    union {DItype __ll;                                                \
+          struct {SItype __l, __h;} __i;                               \
+         } __xx;                                                       \
+    __xx.__i.__h = n1; __xx.__i.__l = n0;                              \
+    __asm__ ("ediv %3,%2,%0,%1"                                         \
+            : "=g" (q), "=g" (r)                                       \
+            : "g" (__xx.__ll), "g" (d));                               \
+  } while (0)
+#endif /* __vax__ */
+
+
+/***************************************
+ **************  Z8000 ****************
+ ***************************************/
+#if defined (__z8000__) && W_TYPE_SIZE == 16
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  __asm__ ("add %H1,%H5\n\tadc  %H0,%H3"                                \
+          : "=r" ((unsigned int)(sh)),                                 \
+            "=&r" ((unsigned int)(sl))                                 \
+          : "%0" ((unsigned int)(ah)),                                 \
+            "r" ((unsigned int)(bh)),                                  \
+            "%1" ((unsigned int)(al)),                                 \
+            "rQR" ((unsigned int)(bl)))
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  __asm__ ("sub %H1,%H5\n\tsbc  %H0,%H3"                                \
+          : "=r" ((unsigned int)(sh)),                                 \
+            "=&r" ((unsigned int)(sl))                                 \
+          : "0" ((unsigned int)(ah)),                                  \
+            "r" ((unsigned int)(bh)),                                  \
+            "1" ((unsigned int)(al)),                                  \
+            "rQR" ((unsigned int)(bl)))
+#define umul_ppmm(xh, xl, m0, m1) \
+  do {                                                                 \
+    union {long int __ll;                                              \
+          struct {unsigned int __h, __l;} __i;                         \
+         } __xx;                                                       \
+    unsigned int __m0 = (m0), __m1 = (m1);                             \
+    __asm__ ("mult      %S0,%H3"                                        \
+            : "=r" (__xx.__i.__h),                                     \
+              "=r" (__xx.__i.__l)                                      \
+            : "%1" (__m0),                                             \
+              "rQR" (__m1));                                           \
+    (xh) = __xx.__i.__h; (xl) = __xx.__i.__l;                          \
+    (xh) += ((((signed int) __m0 >> 15) & __m1)                        \
+            + (((signed int) __m1 >> 15) & __m0));                     \
+  } while (0)
+#endif /* __z8000__ */
+
+#endif /* __GNUC__ */
+
+
+/***************************************
+ ***********  Generic Versions ********
+ ***************************************/
+#if !defined (umul_ppmm) && defined (__umulsidi3)
+#define umul_ppmm(ph, pl, m0, m1) \
+  {                                                                    \
+    UDWtype __ll = __umulsidi3 (m0, m1);                               \
+    ph = (UWtype) (__ll >> W_TYPE_SIZE);                               \
+    pl = (UWtype) __ll;                                                \
+  }
+#endif
+
+#if !defined (__umulsidi3)
+#define __umulsidi3(u, v) \
+  ({UWtype __hi, __lo;                                                 \
+    umul_ppmm (__hi, __lo, u, v);                                      \
+    ((UDWtype) __hi << W_TYPE_SIZE) | __lo; })
+#endif
+
+/* If this machine has no inline assembler, use C macros.  */
+
+#if !defined (add_ssaaaa)
+#define add_ssaaaa(sh, sl, ah, al, bh, bl) \
+  do {                                                                 \
+    UWtype __x;                                                        \
+    __x = (al) + (bl);                                                 \
+    (sh) = (ah) + (bh) + (__x < (al));                                 \
+    (sl) = __x;                                                        \
+  } while (0)
+#endif
+
+#if !defined (sub_ddmmss)
+#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
+  do {                                                                 \
+    UWtype __x;                                                        \
+    __x = (al) - (bl);                                                 \
+    (sh) = (ah) - (bh) - (__x > (al));                                 \
+    (sl) = __x;                                                        \
+  } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define umul_ppmm(w1, w0, u, v)                                        \
+  do {                                                                 \
+    UWtype __x0, __x1, __x2, __x3;                                     \
+    UHWtype __ul, __vl, __uh, __vh;                                    \
+    UWtype __u = (u), __v = (v);                                       \
+                                                                       \
+    __ul = __ll_lowpart (__u);                                         \
+    __uh = __ll_highpart (__u);                                        \
+    __vl = __ll_lowpart (__v);                                         \
+    __vh = __ll_highpart (__v);                                        \
+                                                                       \
+    __x0 = (UWtype) __ul * __vl;                                       \
+    __x1 = (UWtype) __ul * __vh;                                       \
+    __x2 = (UWtype) __uh * __vl;                                       \
+    __x3 = (UWtype) __uh * __vh;                                       \
+                                                                       \
+    __x1 += __ll_highpart (__x0);/* this can't give carry */            \
+    __x1 += __x2;              /* but this indeed can */               \
+    if (__x1 < __x2)           /* did we get it? */                    \
+      __x3 += __ll_B;          /* yes, add it in the proper pos. */    \
+                                                                       \
+    (w1) = __x3 + __ll_highpart (__x1);                                \
+    (w0) = (__ll_lowpart (__x1) << W_TYPE_SIZE/2) + __ll_lowpart (__x0);\
+  } while (0)
+#endif
+
+#if !defined (umul_ppmm)
+#define smul_ppmm(w1, w0, u, v)                                        \
+  do {                                                                 \
+    UWtype __w1;                                                       \
+    UWtype __m0 = (u), __m1 = (v);                                     \
+    umul_ppmm (__w1, w0, __m0, __m1);                                  \
+    (w1) = __w1 - (-(__m0 >> (W_TYPE_SIZE - 1)) & __m1)                \
+               - (-(__m1 >> (W_TYPE_SIZE - 1)) & __m0);                \
+  } while (0)
+#endif
+
+/* Define this unconditionally, so it can be used for debugging.  */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+  do {                                                                 \
+    UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m;                    \
+    __d1 = __ll_highpart (d);                                          \
+    __d0 = __ll_lowpart (d);                                           \
+                                                                       \
+    __r1 = (n1) % __d1;                                                \
+    __q1 = (n1) / __d1;                                                \
+    __m = (UWtype) __q1 * __d0;                                        \
+    __r1 = __r1 * __ll_B | __ll_highpart (n0);                         \
+    if (__r1 < __m)                                                    \
+      {                                                                \
+       __q1--, __r1 += (d);                                            \
+       if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+         if (__r1 < __m)                                               \
+           __q1--, __r1 += (d);                                        \
+      }                                                                \
+    __r1 -= __m;                                                       \
+                                                                       \
+    __r0 = __r1 % __d1;                                                \
+    __q0 = __r1 / __d1;                                                \
+    __m = (UWtype) __q0 * __d0;                                        \
+    __r0 = __r0 * __ll_B | __ll_lowpart (n0);                          \
+    if (__r0 < __m)                                                    \
+      {                                                                \
+       __q0--, __r0 += (d);                                            \
+       if (__r0 >= (d))                                                \
+         if (__r0 < __m)                                               \
+           __q0--, __r0 += (d);                                        \
+      }                                                                \
+    __r0 -= __m;                                                       \
+                                                                       \
+    (q) = (UWtype) __q1 * __ll_B | __q0;                               \
+    (r) = __r0;                                                        \
+  } while (0)
+
+/* If the processor has no udiv_qrnnd but sdiv_qrnnd, go through
+   __udiv_w_sdiv (defined in libgcc or elsewhere).  */
+#if !defined (udiv_qrnnd) && defined (sdiv_qrnnd)
+#define udiv_qrnnd(q, r, nh, nl, d) \
+  do {                                                                 \
+    UWtype __r;                                                        \
+    (q) = __MPN(udiv_w_sdiv) (&__r, nh, nl, d);                        \
+    (r) = __r;                                                         \
+  } while (0)
+#endif
+
+/* If udiv_qrnnd was not defined for this processor, use __udiv_qrnnd_c.  */
+#if !defined (udiv_qrnnd)
+#define UDIV_NEEDS_NORMALIZATION 1
+#define udiv_qrnnd __udiv_qrnnd_c
+#endif
+
+#undef count_leading_zeros
+#if !defined (count_leading_zeros)
+extern
+#ifdef __STDC__
+const
+#endif
+unsigned char __clz_tab[];
+#define MPI_INTERNAL_NEED_CLZ_TAB 1
+#define count_leading_zeros(count, x) \
+  do {                                                                 \
+    UWtype __xr = (x);                                                 \
+    UWtype __a;                                                        \
+                                                                       \
+    if (W_TYPE_SIZE <= 32)                                             \
+      {                                                                \
+       __a = __xr < ((UWtype) 1 << 2*__BITS4)                          \
+         ? (__xr < ((UWtype) 1 << __BITS4) ? 0 : __BITS4)              \
+         : (__xr < ((UWtype) 1 << 3*__BITS4) ?  2*__BITS4 : 3*__BITS4);\
+      }                                                                \
+    else                                                               \
+      {                                                                \
+       for (__a = W_TYPE_SIZE - 8; __a > 0; __a -= 8)                  \
+         if (((__xr >> __a) & 0xff) != 0)                              \
+           break;                                                      \
+      }                                                                \
+                                                                       \
+    (count) = W_TYPE_SIZE - (__clz_tab[__xr >> __a] + __a);            \
+  } while (0)
+/* This version gives a well-defined value for zero. */
+#define COUNT_LEADING_ZEROS_0 W_TYPE_SIZE
+#endif
+
+#if !defined (count_trailing_zeros)
+/* Define count_trailing_zeros using count_leading_zeros.  The latter might be
+   defined in asm, but if it is not, the C version above is good enough.  */
+#define count_trailing_zeros(count, x) \
+  do {                                                                 \
+    UWtype __ctz_x = (x);                                              \
+    UWtype __ctz_c;                                                    \
+    count_leading_zeros (__ctz_c, __ctz_x & -__ctz_x);                 \
+    (count) = W_TYPE_SIZE - 1 - __ctz_c;                               \
+  } while (0)
+#endif
+
+#ifndef UDIV_NEEDS_NORMALIZATION
+#define UDIV_NEEDS_NORMALIZATION 0
+#endif
diff --git a/security/rsa/gnupg_mpi_memory.h b/security/rsa/gnupg_mpi_memory.h
new file mode 100644 (file)
index 0000000..a15b371
--- /dev/null
@@ -0,0 +1,60 @@
+/* memory.h - memory allocation
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_MEMORY_H
+#define G10_MEMORY_H
+
+#include <linux/mm.h>
+
+
+#define m_alloc(n)             kmalloc(n, GFP_KERNEL)
+#define m_alloc_clear(n)       kmalloc(n, GFP_KERNEL) /* can't memset, no size or don't know how to get it */
+#define m_alloc_secure(n)      kmalloc(n, GFP_KERNEL)
+#define m_alloc_secure_clear(n) kmalloc(n, GFP_KERNEL) /* can't memset, no size or don't know how to get it */
+#define m_free(n)              kfree(n) 
+#define m_check(n)             /* nothing to do here */
+#define m_size(n)               sizeof(n)
+#define m_is_secure(n)          1
+
+/* &&&& realloc kernel hack, should check this */
+#define m_realloc(n,m)         krealloc((n),(m))
+
+static inline void *
+krealloc(void *ptr, size_t size)
+{
+  void *tmp = NULL;
+  if (size) {
+    tmp = kmalloc(size,GFP_KERNEL);
+    if (ptr) memcpy(tmp,ptr,size);
+  }
+  kfree(ptr);
+  return tmp;
+}
+
+#define DBG_MEMORY    memory_debug_mode
+#define DBG_MEMSTAT   memory_stat_debug_mode
+
+#define EXTERN_UNLESS_MAIN_MODULE extern
+
+EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode;
+EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode;
+
+
+#endif /*G10_MEMORY_H*/
diff --git a/security/rsa/gnupg_mpi_mpi-add.c b/security/rsa/gnupg_mpi_mpi-add.c
new file mode 100644 (file)
index 0000000..16d00d3
--- /dev/null
@@ -0,0 +1,243 @@
+/* mpi-add.c  -  MPI functions
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_config.h"
+#include "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Add the unsigned integer V to the mpi-integer U and store the
+ * result in W. U and V may be the same.
+ */
+void
+mpi_add_ui(MPI w, MPI u, unsigned long v )
+{
+    mpi_ptr_t wp, up;
+    mpi_size_t usize, wsize;
+    int usign, wsign;
+
+    usize = u->nlimbs;
+    usign = u->sign;
+    wsign = 0;
+
+    /* If not space for W (and possible carry), increase space.  */
+    wsize = usize + 1;
+    if( w->alloced < wsize )
+       mpi_resize(w, wsize);
+
+    /* These must be after realloc (U may be the same as W).  */
+    up = u->d;
+    wp = w->d;
+
+    if( !usize ) {  /* simple */
+       wp[0] = v;
+       wsize = v? 1:0;
+    }
+    else if( !usign ) {  /* mpi is not negative */
+       mpi_limb_t cy;
+       cy = mpihelp_add_1(wp, up, usize, v);
+       wp[usize] = cy;
+       wsize = usize + cy;
+    }
+    else {  /* The signs are different.  Need exact comparison to determine
+            * which operand to subtract from which.  */
+       if( usize == 1 && up[0] < v ) {
+           wp[0] = v - up[0];
+           wsize = 1;
+       }
+       else {
+           mpihelp_sub_1(wp, up, usize, v);
+           /* Size can decrease with at most one limb. */
+           wsize = usize - (wp[usize-1]==0);
+           wsign = 1;
+       }
+    }
+
+    w->nlimbs = wsize;
+    w->sign   = wsign;
+}
+
+
+void
+mpi_add(MPI w, MPI u, MPI v)
+{
+    mpi_ptr_t wp, up, vp;
+    mpi_size_t usize, vsize, wsize;
+    int usign, vsign, wsign;
+
+    if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+       usize = v->nlimbs;
+       usign = v->sign;
+       vsize = u->nlimbs;
+       vsign = u->sign;
+       wsize = usize + 1;
+       RESIZE_IF_NEEDED(w, wsize);
+       /* These must be after realloc (u or v may be the same as w).  */
+       up    = v->d;
+       vp    = u->d;
+    }
+    else {
+       usize = u->nlimbs;
+       usign = u->sign;
+       vsize = v->nlimbs;
+       vsign = v->sign;
+       wsize = usize + 1;
+       RESIZE_IF_NEEDED(w, wsize);
+       /* These must be after realloc (u or v may be the same as w).  */
+       up    = u->d;
+       vp    = v->d;
+    }
+    wp = w->d;
+    wsign = 0;
+
+    if( !vsize ) {  /* simple */
+       MPN_COPY(wp, up, usize );
+       wsize = usize;
+       wsign = usign;
+    }
+    else if( usign != vsign ) { /* different sign */
+       /* This test is right since USIZE >= VSIZE */
+       if( usize != vsize ) {
+           mpihelp_sub(wp, up, usize, vp, vsize);
+           wsize = usize;
+           MPN_NORMALIZE(wp, wsize);
+           wsign = usign;
+       }
+       else if( mpihelp_cmp(up, vp, usize) < 0 ) {
+           mpihelp_sub_n(wp, vp, up, usize);
+           wsize = usize;
+           MPN_NORMALIZE(wp, wsize);
+           if( !usign )
+               wsign = 1;
+       }
+       else {
+           mpihelp_sub_n(wp, up, vp, usize);
+           wsize = usize;
+           MPN_NORMALIZE(wp, wsize);
+           if( usign )
+               wsign = 1;
+       }
+    }
+    else { /* U and V have same sign. Add them. */
+       mpi_limb_t cy = mpihelp_add(wp, up, usize, vp, vsize);
+       wp[usize] = cy;
+       wsize = usize + cy;
+       if( usign )
+           wsign = 1;
+    }
+
+    w->nlimbs = wsize;
+    w->sign = wsign;
+}
+
+
+/****************
+ * Subtract the unsigned integer V from the mpi-integer U and store the
+ * result in W.
+ */
+void
+mpi_sub_ui(MPI w, MPI u, unsigned long v )
+{
+    mpi_ptr_t wp, up;
+    mpi_size_t usize, wsize;
+    int usign, wsign;
+
+    usize = u->nlimbs;
+    usign = u->sign;
+    wsign = 0;
+
+    /* If not space for W (and possible carry), increase space.  */
+    wsize = usize + 1;
+    if( w->alloced < wsize )
+       mpi_resize(w, wsize);
+
+    /* These must be after realloc (U may be the same as W).  */
+    up = u->d;
+    wp = w->d;
+
+    if( !usize ) {  /* simple */
+       wp[0] = v;
+       wsize = v? 1:0;
+       wsign = 1;
+    }
+    else if( usign ) { /* mpi and v are negative */
+       mpi_limb_t cy;
+       cy = mpihelp_add_1(wp, up, usize, v);
+       wp[usize] = cy;
+       wsize = usize + cy;
+    }
+    else {  /* The signs are different.  Need exact comparison to determine
+            * which operand to subtract from which.  */
+       if( usize == 1 && up[0] < v ) {
+           wp[0] = v - up[0];
+           wsize = 1;
+           wsign = 1;
+       }
+       else {
+           mpihelp_sub_1(wp, up, usize, v);
+           /* Size can decrease with at most one limb. */
+           wsize = usize - (wp[usize-1]==0);
+       }
+    }
+
+    w->nlimbs = wsize;
+    w->sign   = wsign;
+}
+
+void
+mpi_sub(MPI w, MPI u, MPI v)
+{
+    if( w == v ) {
+       MPI vv = mpi_copy(v);
+       vv->sign = !vv->sign;
+       mpi_add( w, u, vv );
+       mpi_free(vv);
+    }
+    else {
+       /* fixme: this is not thread-save (we temp. modify v) */
+       v->sign = !v->sign;
+       mpi_add( w, u, v );
+       v->sign = !v->sign;
+    }
+}
+
+
+void
+mpi_addm( MPI w, MPI u, MPI v, MPI m)
+{
+    mpi_add(w, u, v);
+    mpi_fdiv_r( w, w, m );
+}
+
+void
+mpi_subm( MPI w, MPI u, MPI v, MPI m)
+{
+    mpi_sub(w, u, v);
+    mpi_fdiv_r( w, w, m );
+}
+
diff --git a/security/rsa/gnupg_mpi_mpi-bit.c b/security/rsa/gnupg_mpi_mpi-bit.c
new file mode 100644 (file)
index 0000000..22b8d47
--- /dev/null
@@ -0,0 +1,253 @@
+/* mpi-bit.c  -  MPI bit level fucntions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+#define MPI_INTERNAL_NEED_CLZ_TAB
+#ifdef MPI_INTERNAL_NEED_CLZ_TAB
+#ifdef __STDC__
+const
+#endif
+unsigned char
+__clz_tab[] =
+{
+  0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+#endif
+
+
+#define A_LIMB_1 ((mpi_limb_t)1)
+
+
+/****************
+ * Sometimes we have MSL (most significant limbs) which are 0;
+ * this is for some reasons not good, so this function removes them.
+ */
+void
+mpi_normalize( MPI a )
+{
+    if( mpi_is_opaque (a) )
+       return;
+
+    for( ; a->nlimbs && !a->d[a->nlimbs-1]; a->nlimbs-- )
+       ;
+}
+
+
+
+/****************
+ * Return the number of bits in A.
+ */
+unsigned
+mpi_get_nbits( MPI a )
+{
+    unsigned n;
+
+    mpi_normalize( a );
+
+    if( a->nlimbs ) {
+       mpi_limb_t alimb = a->d[a->nlimbs-1];
+       if( alimb ) {
+         count_leading_zeros( n, alimb );
+       }
+       else
+           n = BITS_PER_MPI_LIMB;
+       n = BITS_PER_MPI_LIMB - n + (a->nlimbs-1) * BITS_PER_MPI_LIMB;
+    }
+    else
+       n = 0;
+    return n;
+}
+
+
+/****************
+ * Test whether bit N is set.
+ */
+int
+mpi_test_bit( MPI a, unsigned n )
+{
+    unsigned limbno, bitno;
+    mpi_limb_t limb;
+
+    limbno = n / BITS_PER_MPI_LIMB;
+    bitno  = n % BITS_PER_MPI_LIMB;
+
+    if( limbno >= a->nlimbs )
+       return 0; /* too far left: this is a 0 */
+    limb = a->d[limbno];
+    return (limb & (A_LIMB_1 << bitno))? 1: 0;
+}
+
+
+/****************
+ * Set bit N of A.
+ */
+void
+mpi_set_bit( MPI a, unsigned n )
+{
+    unsigned limbno, bitno;
+
+    limbno = n / BITS_PER_MPI_LIMB;
+    bitno  = n % BITS_PER_MPI_LIMB;
+
+    if( limbno >= a->nlimbs ) { /* resize */
+       if( a->alloced >= limbno )
+           mpi_resize(a, limbno+1 );
+       a->nlimbs = limbno+1;
+    }
+    a->d[limbno] |= (A_LIMB_1<<bitno);
+}
+
+/****************
+ * Set bit N of A. and clear all bits above
+ */
+void
+mpi_set_highbit( MPI a, unsigned n )
+{
+    unsigned limbno, bitno;
+
+    limbno = n / BITS_PER_MPI_LIMB;
+    bitno  = n % BITS_PER_MPI_LIMB;
+
+    if( limbno >= a->nlimbs ) { /* resize */
+       if( a->alloced >= limbno )
+           mpi_resize(a, limbno+1 );
+       a->nlimbs = limbno+1;
+    }
+    a->d[limbno] |= (A_LIMB_1<<bitno);
+    for( bitno++; bitno < BITS_PER_MPI_LIMB; bitno++ )
+       a->d[limbno] &= ~(A_LIMB_1 << bitno);
+    a->nlimbs = limbno+1;
+}
+
+/****************
+ * clear bit N of A and all bits above
+ */
+void
+mpi_clear_highbit( MPI a, unsigned n )
+{
+    unsigned limbno, bitno;
+
+    limbno = n / BITS_PER_MPI_LIMB;
+    bitno  = n % BITS_PER_MPI_LIMB;
+
+    if( limbno >= a->nlimbs )
+       return; /* not allocated, so need to clear bits :-) */
+
+    for( ; bitno < BITS_PER_MPI_LIMB; bitno++ )
+       a->d[limbno] &= ~(A_LIMB_1 << bitno);
+    a->nlimbs = limbno+1;
+}
+
+/****************
+ * Clear bit N of A.
+ */
+void
+mpi_clear_bit( MPI a, unsigned n )
+{
+    unsigned limbno, bitno;
+
+    limbno = n / BITS_PER_MPI_LIMB;
+    bitno  = n % BITS_PER_MPI_LIMB;
+
+    if( limbno >= a->nlimbs )
+       return; /* don't need to clear this bit, it's to far to left */
+    a->d[limbno] &= ~(A_LIMB_1 << bitno);
+}
+
+
+/****************
+ * Shift A by N bits to the right
+ * FIXME: should use alloc_limb if X and A are same.
+ */
+void
+mpi_rshift( MPI x, MPI a, unsigned n )
+{
+    mpi_ptr_t xp;
+    mpi_size_t xsize;
+
+    xsize = a->nlimbs;
+    x->sign = a->sign;
+    RESIZE_IF_NEEDED(x, xsize);
+    xp = x->d;
+
+    if( xsize ) {
+       mpihelp_rshift( xp, a->d, xsize, n);
+       MPN_NORMALIZE( xp, xsize);
+    }
+    x->nlimbs = xsize;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the left
+ * This is used only within the MPI library
+ */
+void
+mpi_lshift_limbs( MPI a, unsigned int count )
+{
+    mpi_ptr_t ap = a->d;
+    int n = a->nlimbs;
+    int i;
+
+    if( !count || !n )
+       return;
+
+    RESIZE_IF_NEEDED( a, n+count );
+
+    for( i = n-1; i >= 0; i-- )
+       ap[i+count] = ap[i];
+    for(i=0; i < count; i++ )
+       ap[i] = 0;
+    a->nlimbs += count;
+}
+
+
+/****************
+ * Shift A by COUNT limbs to the right
+ * This is used only within the MPI library
+ */
+void
+mpi_rshift_limbs( MPI a, unsigned int count )
+{
+    mpi_ptr_t ap = a->d;
+    mpi_size_t n = a->nlimbs;
+    unsigned int i;
+
+    if( count >= n ) {
+       a->nlimbs = 0;
+       return;
+    }
+
+    for( i = 0; i < n - count; i++ )
+       ap[i] = ap[i+count];
+    ap[i] = 0;
+    a->nlimbs -= count;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpi-cmp.c b/security/rsa/gnupg_mpi_mpi-cmp.c
new file mode 100644 (file)
index 0000000..57e1f66
--- /dev/null
@@ -0,0 +1,71 @@
+/* mpi-cmp.c  -  MPI functions
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+int
+mpi_cmp_ui( MPI u, unsigned long v )
+{
+    mpi_limb_t limb = v;
+
+    mpi_normalize( u );
+    if( !u->nlimbs && !limb )
+       return 0;
+    if( u->sign )
+       return -1;
+    if( u->nlimbs > 1 )
+       return 1;
+
+    if( u->d[0] == limb )
+       return 0;
+    else if( u->d[0] > limb )
+       return 1;
+    else
+       return -1;
+}
+
+int
+mpi_cmp( MPI u, MPI v )
+{
+    mpi_size_t usize, vsize;
+    int cmp;
+
+    mpi_normalize( u );
+    mpi_normalize( v );
+    usize = u->nlimbs;
+    vsize = v->nlimbs;
+    if( !u->sign && v->sign )
+       return 1;
+    if( u->sign && !v->sign )
+       return -1;
+    if( usize != vsize && !u->sign && !v->sign )
+       return usize - vsize;
+    if( usize != vsize && u->sign && v->sign )
+       return vsize + usize;
+    if( !usize )
+       return 0;
+    if( !(cmp=mpihelp_cmp( u->d, v->d, usize )) )
+       return 0;
+    if( (cmp < 0?1:0) == (u->sign?1:0))
+       return 1;
+    return -1;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpi-div.c b/security/rsa/gnupg_mpi_mpi-div.c
new file mode 100644 (file)
index 0000000..5528ea2
--- /dev/null
@@ -0,0 +1,318 @@
+/* mpi-div.c  -  MPI functions
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+void
+mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor )
+{
+    int divisor_sign = divisor->sign;
+    MPI temp_divisor = NULL;
+
+    /* We need the original value of the divisor after the remainder has been
+     * preliminary calculated. We have to copy it to temporary space if it's
+     * the same variable as REM.  */
+    if( rem == divisor ) {
+       temp_divisor = mpi_copy( divisor );
+       divisor = temp_divisor;
+    }
+
+    mpi_tdiv_r( rem, dividend, divisor );
+
+    if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+       mpi_add( rem, rem, divisor);
+
+    if( temp_divisor )
+       mpi_free(temp_divisor);
+}
+
+
+
+/****************
+ * Division rounding the quotient towards -infinity.
+ * The remainder gets the same sign as the denominator.
+ * rem is optional
+ */
+
+ulong
+mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor )
+{
+    mpi_limb_t rlimb;
+
+    rlimb = mpihelp_mod_1( dividend->d, dividend->nlimbs, divisor );
+    if( rlimb && dividend->sign )
+       rlimb = divisor - rlimb;
+
+    if( rem ) {
+       rem->d[0] = rlimb;
+       rem->nlimbs = rlimb? 1:0;
+    }
+    return rlimb;
+}
+
+
+void
+mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor )
+{
+    MPI tmp = mpi_alloc( mpi_get_nlimbs(quot) );
+    mpi_fdiv_qr( quot, tmp, dividend, divisor);
+    mpi_free(tmp);
+}
+
+void
+mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor )
+{
+    int divisor_sign = divisor->sign;
+    MPI temp_divisor = NULL;
+
+    if( quot == divisor || rem == divisor ) {
+       temp_divisor = mpi_copy( divisor );
+       divisor = temp_divisor;
+    }
+
+    mpi_tdiv_qr( quot, rem, dividend, divisor );
+
+    if( (divisor_sign ^ dividend->sign) && rem->nlimbs ) {
+       mpi_sub_ui( quot, quot, 1 );
+       mpi_add( rem, rem, divisor);
+    }
+
+    if( temp_divisor )
+       mpi_free(temp_divisor);
+}
+
+
+/* If den == quot, den needs temporary storage.
+ * If den == rem, den needs temporary storage.
+ * If num == quot, num needs temporary storage.
+ * If den has temporary storage, it can be normalized while being copied,
+ *   i.e no extra storage should be allocated.
+ */
+
+void
+mpi_tdiv_r( MPI rem, MPI num, MPI den)
+{
+    mpi_tdiv_qr(NULL, rem, num, den );
+}
+
+void
+mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den)
+{
+    mpi_ptr_t np, dp;
+    mpi_ptr_t qp, rp;
+    mpi_size_t nsize = num->nlimbs;
+    mpi_size_t dsize = den->nlimbs;
+    mpi_size_t qsize, rsize;
+    mpi_size_t sign_remainder = num->sign;
+    mpi_size_t sign_quotient = num->sign ^ den->sign;
+    unsigned normalization_steps;
+    mpi_limb_t q_limb;
+    mpi_ptr_t marker[5];
+    int markidx=0;
+
+    /* Ensure space is enough for quotient and remainder.
+     * We need space for an extra limb in the remainder, because it's
+     * up-shifted (normalized) below.  */
+    rsize = nsize + 1;
+    mpi_resize( rem, rsize);
+
+    qsize = rsize - dsize;       /* qsize cannot be bigger than this.  */
+    if( qsize <= 0 ) {
+       if( num != rem ) {
+           rem->nlimbs = num->nlimbs;
+           rem->sign = num->sign;
+           MPN_COPY(rem->d, num->d, nsize);
+       }
+       if( quot ) {
+           /* This needs to follow the assignment to rem, in case the
+            * numerator and quotient are the same.  */
+           quot->nlimbs = 0;
+           quot->sign = 0;
+       }
+       return;
+    }
+
+    if( quot )
+       mpi_resize( quot, qsize);
+
+    /* Read pointers here, when reallocation is finished.  */
+    np = num->d;
+    dp = den->d;
+    rp = rem->d;
+
+    /* Optimize division by a single-limb divisor.  */
+    if( dsize == 1 ) {
+       mpi_limb_t rlimb;
+       if( quot ) {
+           qp = quot->d;
+           rlimb = mpihelp_divmod_1( qp, np, nsize, dp[0] );
+           qsize -= qp[qsize - 1] == 0;
+           quot->nlimbs = qsize;
+           quot->sign = sign_quotient;
+       }
+       else
+           rlimb = mpihelp_mod_1( np, nsize, dp[0] );
+       rp[0] = rlimb;
+       rsize = rlimb != 0?1:0;
+       rem->nlimbs = rsize;
+       rem->sign = sign_remainder;
+       return;
+    }
+
+
+    if( quot ) {
+       qp = quot->d;
+       /* Make sure QP and NP point to different objects.  Otherwise the
+        * numerator would be gradually overwritten by the quotient limbs.  */
+       if(qp == np) { /* Copy NP object to temporary space.  */
+           np = marker[markidx++] = mpi_alloc_limb_space(nsize,
+                                                         mpi_is_secure(quot));
+           MPN_COPY(np, qp, nsize);
+       }
+    }
+    else /* Put quotient at top of remainder. */
+       qp = rp + dsize;
+
+    count_leading_zeros( normalization_steps, dp[dsize - 1] );
+
+    /* Normalize the denominator, i.e. make its most significant bit set by
+     * shifting it NORMALIZATION_STEPS bits to the left.  Also shift the
+     * numerator the same number of steps (to keep the quotient the same!).
+     */
+    if( normalization_steps ) {
+       mpi_ptr_t tp;
+       mpi_limb_t nlimb;
+
+       /* Shift up the denominator setting the most significant bit of
+        * the most significant word.  Use temporary storage not to clobber
+        * the original contents of the denominator.  */
+       tp = marker[markidx++] = mpi_alloc_limb_space(dsize,mpi_is_secure(den));
+       mpihelp_lshift( tp, dp, dsize, normalization_steps );
+       dp = tp;
+
+       /* Shift up the numerator, possibly introducing a new most
+        * significant word.  Move the shifted numerator in the remainder
+        * meanwhile.  */
+       nlimb = mpihelp_lshift(rp, np, nsize, normalization_steps);
+       if( nlimb ) {
+           rp[nsize] = nlimb;
+           rsize = nsize + 1;
+       }
+       else
+           rsize = nsize;
+    }
+    else {
+       /* The denominator is already normalized, as required.  Copy it to
+        * temporary space if it overlaps with the quotient or remainder.  */
+       if( dp == rp || (quot && (dp == qp))) {
+           mpi_ptr_t tp;
+
+           tp = marker[markidx++] = mpi_alloc_limb_space(dsize, mpi_is_secure(den));
+           MPN_COPY( tp, dp, dsize );
+           dp = tp;
+       }
+
+       /* Move the numerator to the remainder.  */
+       if( rp != np )
+           MPN_COPY(rp, np, nsize);
+
+       rsize = nsize;
+    }
+
+    q_limb = mpihelp_divrem( qp, 0, rp, rsize, dp, dsize );
+
+    if( quot ) {
+       qsize = rsize - dsize;
+       if(q_limb) {
+           qp[qsize] = q_limb;
+           qsize += 1;
+       }
+
+       quot->nlimbs = qsize;
+       quot->sign = sign_quotient;
+    }
+
+    rsize = dsize;
+    MPN_NORMALIZE (rp, rsize);
+
+    if( normalization_steps && rsize ) {
+       mpihelp_rshift(rp, rp, rsize, normalization_steps);
+       rsize -= rp[rsize - 1] == 0?1:0;
+    }
+
+    rem->nlimbs = rsize;
+    rem->sign  = sign_remainder;
+    while( markidx )
+       mpi_free_limb_space(marker[--markidx]);
+}
+
+void
+mpi_tdiv_q_2exp( MPI w, MPI u, unsigned count )
+{
+    mpi_size_t usize, wsize;
+    mpi_size_t limb_cnt;
+
+    usize = u->nlimbs;
+    limb_cnt = count / BITS_PER_MPI_LIMB;
+    wsize = usize - limb_cnt;
+    if( limb_cnt >= usize )
+       w->nlimbs = 0;
+    else {
+       mpi_ptr_t wp;
+       mpi_ptr_t up;
+
+       RESIZE_IF_NEEDED( w, wsize );
+       wp = w->d;
+       up = u->d;
+
+       count %= BITS_PER_MPI_LIMB;
+       if( count ) {
+           mpihelp_rshift( wp, up + limb_cnt, wsize, count );
+           wsize -= !wp[wsize - 1];
+       }
+       else {
+           MPN_COPY_INCR( wp, up + limb_cnt, wsize);
+       }
+
+       w->nlimbs = wsize;
+    }
+}
+
+/****************
+ * Check whether dividend is divisible by divisor
+ * (note: divisor must fit into a limb)
+ */
+int
+mpi_divisible_ui(MPI dividend, ulong divisor )
+{
+    return !mpihelp_mod_1( dividend->d, dividend->nlimbs, divisor );
+}
+
diff --git a/security/rsa/gnupg_mpi_mpi-gcd.c b/security/rsa/gnupg_mpi_mpi-gcd.c
new file mode 100644 (file)
index 0000000..6c17518
--- /dev/null
@@ -0,0 +1,51 @@
+/* mpi-gcd.c  -  MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Find the greatest common divisor G of A and B.
+ * Return: true if this 1, false in all other cases
+ */
+int
+mpi_gcd( MPI g, MPI xa, MPI xb )
+{
+    MPI a, b;
+
+    a = mpi_copy(xa);
+    b = mpi_copy(xb);
+
+    /* TAOCP Vol II, 4.5.2, Algorithm A */
+    a->sign = 0;
+    b->sign = 0;
+    while( mpi_cmp_ui( b, 0 ) ) {
+       mpi_fdiv_r( g, a, b ); /* g used as temorary variable */
+       mpi_set(a,b);
+       mpi_set(b,g);
+    }
+    mpi_set(g, a);
+
+    mpi_free(a);
+    mpi_free(b);
+    return !mpi_cmp_ui( g, 1);
+}
+
+
+
diff --git a/security/rsa/gnupg_mpi_mpi-inline.c b/security/rsa/gnupg_mpi_mpi-inline.c
new file mode 100644 (file)
index 0000000..d20935f
--- /dev/null
@@ -0,0 +1,33 @@
+/* mpi-inline.c
+ * Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+/* put the inline functions as real functions into the lib */
+#define G10_MPI_INLINE_DECL
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/* always include the header becuase it is only
+ * included by mpi-internal if __GCC__ is defined but we
+ * need it here in all cases and the above definition of
+ * of the macro allows us to do so
+ */
+#include "gnupg_mpi_mpi-inline.h"
+
diff --git a/security/rsa/gnupg_mpi_mpi-inline.h b/security/rsa/gnupg_mpi_mpi-inline.h
new file mode 100644 (file)
index 0000000..02481b6
--- /dev/null
@@ -0,0 +1,128 @@
+/* mpi-inline.h  -  Internal to the Multi Precision Integers
+ *     Copyright (C) 1994, 1996, 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_INLINE_H
+#define G10_MPI_INLINE_H
+
+#ifndef G10_MPI_INLINE_DECL
+  #define G10_MPI_INLINE_DECL  extern __inline__
+#endif
+
+G10_MPI_INLINE_DECL  mpi_limb_t
+mpihelp_add_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+              mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t x;
+
+    x = *s1_ptr++;
+    s2_limb += x;
+    *res_ptr++ = s2_limb;
+    if( s2_limb < x ) { /* sum is less than the left operand: handle carry */
+       while( --s1_size ) {
+           x = *s1_ptr++ + 1;  /* add carry */
+           *res_ptr++ = x;     /* and store */
+           if( x )             /* not 0 (no overflow): we can stop */
+               goto leave;
+       }
+       return 1; /* return carry (size of s1 to small) */
+    }
+
+  leave:
+    if( res_ptr != s1_ptr ) { /* not the same variable */
+       mpi_size_t i;          /* copy the rest */
+       for( i=0; i < s1_size-1; i++ )
+           res_ptr[i] = s1_ptr[i];
+    }
+    return 0; /* no carry */
+}
+
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                              mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+    mpi_limb_t cy = 0;
+
+    if( s2_size )
+       cy = mpihelp_add_n( res_ptr, s1_ptr, s2_ptr, s2_size );
+
+    if( s1_size - s2_size )
+       cy = mpihelp_add_1( res_ptr + s2_size, s1_ptr + s2_size,
+                           s1_size - s2_size, cy);
+    return cy;
+}
+
+
+G10_MPI_INLINE_DECL mpi_limb_t
+mpihelp_sub_1(mpi_ptr_t res_ptr,  mpi_ptr_t s1_ptr,
+             mpi_size_t s1_size, mpi_limb_t s2_limb )
+{
+    mpi_limb_t x;
+
+    x = *s1_ptr++;
+    s2_limb = x - s2_limb;
+    *res_ptr++ = s2_limb;
+    if( s2_limb > x ) {
+       while( --s1_size ) {
+           x = *s1_ptr++;
+           *res_ptr++ = x - 1;
+           if( x )
+               goto leave;
+       }
+       return 1;
+    }
+
+  leave:
+    if( res_ptr != s1_ptr ) {
+       mpi_size_t i;
+       for( i=0; i < s1_size-1; i++ )
+           res_ptr[i] = s1_ptr[i];
+    }
+    return 0;
+}
+
+
+
+G10_MPI_INLINE_DECL   mpi_limb_t
+mpihelp_sub( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                               mpi_ptr_t s2_ptr, mpi_size_t s2_size)
+{
+    mpi_limb_t cy = 0;
+
+    if( s2_size )
+       cy = mpihelp_sub_n(res_ptr, s1_ptr, s2_ptr, s2_size);
+
+    if( s1_size - s2_size )
+       cy = mpihelp_sub_1(res_ptr + s2_size, s1_ptr + s2_size,
+                                     s1_size - s2_size, cy);
+    return cy;
+}
+
+
+#endif /*G10_MPI_INLINE_H*/
diff --git a/security/rsa/gnupg_mpi_mpi-internal.h b/security/rsa/gnupg_mpi_mpi-internal.h
new file mode 100644 (file)
index 0000000..0df0104
--- /dev/null
@@ -0,0 +1,259 @@
+/* mpi-internal.h  -  Internal to the Multi Precision Integers
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_INTERNAL_H
+#define G10_MPI_INTERNAL_H
+
+#include "gnupg_mpi_mpi.h"
+
+/* If KARATSUBA_THRESHOLD is not already defined, define it to a
+ * value which is good on most machines.  */
+
+/* tested 4, 16, 32 and 64, where 16 gave the best performance when
+ * checking a 768 and a 1024 bit ElGamal signature.
+ * (wk 22.12.97) */
+#ifndef KARATSUBA_THRESHOLD
+    #define KARATSUBA_THRESHOLD 16
+#endif
+
+/* The code can't handle KARATSUBA_THRESHOLD smaller than 2.  */
+#if KARATSUBA_THRESHOLD < 2
+    #undef KARATSUBA_THRESHOLD
+    #define KARATSUBA_THRESHOLD 2
+#endif
+
+
+typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
+typedef int mpi_size_t;        /* (must be a signed type) */
+
+#define ABS(x) (x >= 0 ? x : -x)
+#define MIN(l,o) ((l) < (o) ? (l) : (o))
+#define MAX(h,i) ((h) > (i) ? (h) : (i))
+#define RESIZE_IF_NEEDED(a,b) \
+    do {                          \
+       if( (a)->alloced < (b) )   \
+           mpi_resize((a), (b));  \
+    } while(0)
+
+/* Copy N limbs from S to D.  */
+#define MPN_COPY( d, s, n) \
+    do {                               \
+       mpi_size_t _i;                  \
+       for( _i = 0; _i < (n); _i++ )   \
+           (d)[_i] = (s)[_i];          \
+    } while(0)
+
+#define MPN_COPY_INCR( d, s, n)        \
+    do {                               \
+       mpi_size_t _i;                  \
+       for( _i = 0; _i < (n); _i++ )   \
+           (d)[_i] = (d)[_i];          \
+    } while (0)
+
+#define MPN_COPY_DECR( d, s, n ) \
+    do {                               \
+       mpi_size_t _i;                  \
+       for( _i = (n)-1; _i >= 0; _i--) \
+          (d)[_i] = (s)[_i];           \
+    } while(0)
+
+/* Zero N limbs at D */
+#define MPN_ZERO(d, n) \
+    do {                                 \
+       int  _i;                          \
+       for( _i = 0; _i < (n); _i++ )  \
+           (d)[_i] = 0;                    \
+    } while (0)
+
+#define MPN_NORMALIZE(d, n)  \
+    do {                      \
+       while( (n) > 0 ) {     \
+           if( (d)[(n)-1] ) \
+               break;         \
+           (n)--;             \
+       }                      \
+    } while(0)
+
+#define MPN_NORMALIZE_NOT_ZERO(d, n) \
+    do {                                   \
+       for(;;) {                           \
+           if( (d)[(n)-1] )                \
+               break;                      \
+           (n)--;                          \
+       }                                   \
+    } while(0)
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+    do {                                               \
+       if( (size) < KARATSUBA_THRESHOLD )              \
+           mul_n_basecase (prodp, up, vp, size);       \
+       else                                            \
+           mul_n (prodp, up, vp, size, tspace);        \
+    } while (0);
+
+
+/* Divide the two-limb number in (NH,,NL) by D, with DI being the largest
+ * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB).
+ * If this would yield overflow, DI should be the largest possible number
+ * (i.e., only ones).  For correct operation, the most significant bit of D
+ * has to be set.  Put the quotient in Q and the remainder in R.
+ */
+#define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \
+    do {                                                           \
+       mpi_limb_t _q, _ql, _r;                                     \
+       mpi_limb_t _xh, _xl;                                        \
+       umul_ppmm (_q, _ql, (nh), (di));                            \
+       _q += (nh);     /* DI is 2**BITS_PER_MPI_LIMB too small */  \
+       umul_ppmm (_xh, _xl, _q, (d));                              \
+       sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl);                 \
+       if( _xh ) {                                                 \
+           sub_ddmmss (_xh, _r, _xh, _r, 0, (d));                  \
+           _q++;                                                   \
+           if( _xh) {                                              \
+               sub_ddmmss (_xh, _r, _xh, _r, 0, (d));              \
+               _q++;                                               \
+           }                                                       \
+       }                                                           \
+       if( _r >= (d) ) {                                           \
+           _r -= (d);                                              \
+           _q++;                                                   \
+       }                                                           \
+       (r) = _r;                                                   \
+       (q) = _q;                                                   \
+    } while (0)
+
+
+/*-- mpiutil.c --*/
+#ifdef M_DEBUG
+  #define mpi_alloc_limb_space(n,f)  mpi_debug_alloc_limb_space((n),(f), M_DBGINFO( __LINE__ ) )
+  #define mpi_free_limb_space(n)  mpi_debug_free_limb_space((n),  M_DBGINFO( __LINE__ ) )
+  mpi_ptr_t mpi_debug_alloc_limb_space( unsigned nlimbs, int sec, const char *info  );
+  void mpi_debug_free_limb_space( mpi_ptr_t a, const char *info );
+#else
+  mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int sec );
+  void mpi_free_limb_space( mpi_ptr_t a );
+#endif
+void mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs );
+
+/*-- mpi-bit.c --*/
+void mpi_rshift_limbs( MPI a, unsigned int count );
+void mpi_lshift_limbs( MPI a, unsigned int count );
+
+
+/*-- mpihelp-add.c --*/
+mpi_limb_t mpihelp_add_1(mpi_ptr_t res_ptr,  mpi_ptr_t s1_ptr,
+                        mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t mpihelp_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                         mpi_ptr_t s2_ptr,  mpi_size_t size);
+mpi_limb_t mpihelp_add(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                      mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpihelp-sub.c --*/
+mpi_limb_t mpihelp_sub_1( mpi_ptr_t res_ptr,  mpi_ptr_t s1_ptr,
+                         mpi_size_t s1_size, mpi_limb_t s2_limb );
+mpi_limb_t mpihelp_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                         mpi_ptr_t s2_ptr, mpi_size_t size);
+mpi_limb_t mpihelp_sub(mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                      mpi_ptr_t s2_ptr, mpi_size_t s2_size);
+
+/*-- mpihelp-cmp.c --*/
+int mpihelp_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size );
+
+/*-- mpihelp-mul.c --*/
+
+struct karatsuba_ctx {
+    struct karatsuba_ctx *next;
+    mpi_ptr_t tspace;
+    mpi_size_t tspace_size;
+    mpi_ptr_t tp;
+    mpi_size_t tp_size;
+};
+
+void mpihelp_release_karatsuba_ctx( struct karatsuba_ctx *ctx );
+
+mpi_limb_t mpihelp_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                            mpi_size_t s1_size, mpi_limb_t s2_limb);
+mpi_limb_t mpihelp_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                            mpi_size_t s1_size, mpi_limb_t s2_limb);
+void mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+                                                  mpi_size_t size);
+mpi_limb_t mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+                                        mpi_ptr_t vp, mpi_size_t vsize);
+void mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size );
+void mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size,
+                                               mpi_ptr_t tspace);
+
+void mpihelp_mul_karatsuba_case( mpi_ptr_t prodp,
+                                mpi_ptr_t up, mpi_size_t usize,
+                                mpi_ptr_t vp, mpi_size_t vsize,
+                                struct karatsuba_ctx *ctx );
+
+
+/*-- mpihelp-mul_1.c (or xxx/cpu/ *.S) --*/
+mpi_limb_t mpihelp_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                         mpi_size_t s1_size, mpi_limb_t s2_limb);
+
+/*-- mpihelp-div.c --*/
+mpi_limb_t mpihelp_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                                                mpi_limb_t divisor_limb);
+mpi_limb_t mpihelp_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+                          mpi_ptr_t np, mpi_size_t nsize,
+                          mpi_ptr_t dp, mpi_size_t dsize);
+mpi_limb_t mpihelp_divmod_1( mpi_ptr_t quot_ptr,
+                            mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                            mpi_limb_t divisor_limb);
+
+/*-- mpihelp-shift.c --*/
+mpi_limb_t mpihelp_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+                                                          unsigned cnt);
+mpi_limb_t mpihelp_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+                                                          unsigned cnt);
+
+
+/* Define stuff for longlong.h.  */
+#define W_TYPE_SIZE BITS_PER_MPI_LIMB
+  typedef mpi_limb_t   UWtype;
+  typedef unsigned int UHWtype;
+#if defined (__GNUC__)
+  typedef unsigned int UQItype   __attribute__ ((mode (QI)));
+  typedef         int SItype     __attribute__ ((mode (SI)));
+  typedef unsigned int USItype   __attribute__ ((mode (SI)));
+  typedef         int DItype     __attribute__ ((mode (DI)));
+  typedef unsigned int UDItype   __attribute__ ((mode (DI)));
+#else
+  typedef unsigned char UQItype;
+  typedef         long SItype;
+  typedef unsigned long USItype;
+#endif
+
+#ifdef __GNUC__
+  #include "gnupg_mpi_mpi-inline.h"
+#endif
+
+#endif /*G10_MPI_INTERNAL_H*/
diff --git a/security/rsa/gnupg_mpi_mpi-inv.c b/security/rsa/gnupg_mpi_mpi-inv.c
new file mode 100644 (file)
index 0000000..d56dbb3
--- /dev/null
@@ -0,0 +1,267 @@
+/* mpi-inv.c  -  MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+
+/****************
+ * Calculate the multiplicative inverse X of A mod N
+ * That is: Find the solution x for
+ *             1 = (a*x) mod n
+ */
+void
+mpi_invm( MPI x, MPI a, MPI n )
+{
+  #if 0
+    MPI u, v, u1, u2, u3, v1, v2, v3, q, t1, t2, t3;
+    MPI ta, tb, tc;
+
+    u = mpi_copy(a);
+    v = mpi_copy(n);
+    u1 = mpi_alloc_set_ui(1);
+    u2 = mpi_alloc_set_ui(0);
+    u3 = mpi_copy(u);
+    v1 = mpi_alloc_set_ui(0);
+    v2 = mpi_alloc_set_ui(1);
+    v3 = mpi_copy(v);
+    q  = mpi_alloc( mpi_get_nlimbs(u)+1 );
+    t1 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+    t2 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+    t3 = mpi_alloc( mpi_get_nlimbs(u)+1 );
+    while( mpi_cmp_ui( v3, 0 ) ) {
+       mpi_fdiv_q( q, u3, v3 );
+       mpi_mul(t1, v1, q); mpi_mul(t2, v2, q); mpi_mul(t3, v3, q);
+       mpi_sub(t1, u1, t1); mpi_sub(t2, u2, t2); mpi_sub(t3, u3, t3);
+       mpi_set(u1, v1); mpi_set(u2, v2); mpi_set(u3, v3);
+       mpi_set(v1, t1); mpi_set(v2, t2); mpi_set(v3, t3);
+    }
+    /* log_debug("result:\n");
+       log_mpidump("q =", q );
+       log_mpidump("u1=", u1);
+       log_mpidump("u2=", u2);
+       log_mpidump("u3=", u3);
+       log_mpidump("v1=", v1);
+       log_mpidump("v2=", v2); */
+    mpi_set(x, u1);
+
+    mpi_free(u1);
+    mpi_free(u2);
+    mpi_free(u3);
+    mpi_free(v1);
+    mpi_free(v2);
+    mpi_free(v3);
+    mpi_free(q);
+    mpi_free(t1);
+    mpi_free(t2);
+    mpi_free(t3);
+    mpi_free(u);
+    mpi_free(v);
+  #elif 0
+    /* Extended Euclid's algorithm (See TAOPC Vol II, 4.5.2, Alg X)
+     * modified according to Michael Penk's solution for Exercice 35 */
+
+    /* FIXME: we can simplify this in most cases (see Knuth) */
+    MPI u, v, u1, u2, u3, v1, v2, v3, t1, t2, t3;
+    unsigned k;
+    int sign;
+
+    u = mpi_copy(a);
+    v = mpi_copy(n);
+    for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+       mpi_rshift(u, u, 1);
+       mpi_rshift(v, v, 1);
+    }
+
+
+    u1 = mpi_alloc_set_ui(1);
+    u2 = mpi_alloc_set_ui(0);
+    u3 = mpi_copy(u);
+    v1 = mpi_copy(v);                             /* !-- used as const 1 */
+    v2 = mpi_alloc( mpi_get_nlimbs(u) ); mpi_sub( v2, u1, u );
+    v3 = mpi_copy(v);
+    if( mpi_test_bit(u, 0) ) { /* u is odd */
+       t1 = mpi_alloc_set_ui(0);
+       t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+       t3 = mpi_copy(v); t3->sign = !t3->sign;
+       goto Y4;
+    }
+    else {
+       t1 = mpi_alloc_set_ui(1);
+       t2 = mpi_alloc_set_ui(0);
+       t3 = mpi_copy(u);
+    }
+    do {
+       do {
+           if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+               mpi_add(t1, t1, v);
+               mpi_sub(t2, t2, u);
+           }
+           mpi_rshift(t1, t1, 1);
+           mpi_rshift(t2, t2, 1);
+           mpi_rshift(t3, t3, 1);
+         Y4:
+           ;
+       } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+       if( !t3->sign ) {
+           mpi_set(u1, t1);
+           mpi_set(u2, t2);
+           mpi_set(u3, t3);
+       }
+       else {
+           mpi_sub(v1, v, t1);
+           sign = u->sign; u->sign = !u->sign;
+           mpi_sub(v2, u, t2);
+           u->sign = sign;
+           sign = t3->sign; t3->sign = !t3->sign;
+           mpi_set(v3, t3);
+           t3->sign = sign;
+       }
+       mpi_sub(t1, u1, v1);
+       mpi_sub(t2, u2, v2);
+       mpi_sub(t3, u3, v3);
+       if( t1->sign ) {
+           mpi_add(t1, t1, v);
+           mpi_sub(t2, t2, u);
+       }
+    } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+    /* mpi_lshift( u3, k ); */
+    mpi_set(x, u1);
+
+    mpi_free(u1);
+    mpi_free(u2);
+    mpi_free(u3);
+    mpi_free(v1);
+    mpi_free(v2);
+    mpi_free(v3);
+    mpi_free(t1);
+    mpi_free(t2);
+    mpi_free(t3);
+  #else
+    /* Extended Euclid's algorithm (See TAOPC Vol II, 4.5.2, Alg X)
+     * modified according to Michael Penk's solution for Exercice 35
+     * with further enhancement */
+    MPI u, v, u1, u2=NULL, u3, v1, v2=NULL, v3, t1, t2=NULL, t3;
+    unsigned k;
+    int sign;
+    int odd ;
+
+    u = mpi_copy(a);
+    v = mpi_copy(n);
+
+    for(k=0; !mpi_test_bit(u,0) && !mpi_test_bit(v,0); k++ ) {
+       mpi_rshift(u, u, 1);
+       mpi_rshift(v, v, 1);
+    }
+    odd = mpi_test_bit(v,0);
+
+    u1 = mpi_alloc_set_ui(1);
+    if( !odd )
+       u2 = mpi_alloc_set_ui(0);
+    u3 = mpi_copy(u);
+    v1 = mpi_copy(v);
+    if( !odd ) {
+       v2 = mpi_alloc( mpi_get_nlimbs(u) );
+       mpi_sub( v2, u1, u ); /* U is used as const 1 */
+    }
+    v3 = mpi_copy(v);
+    if( mpi_test_bit(u, 0) ) { /* u is odd */
+       t1 = mpi_alloc_set_ui(0);
+       if( !odd ) {
+           t2 = mpi_alloc_set_ui(1); t2->sign = 1;
+       }
+       t3 = mpi_copy(v); t3->sign = !t3->sign;
+       goto Y4;
+    }
+    else {
+       t1 = mpi_alloc_set_ui(1);
+       if( !odd )
+           t2 = mpi_alloc_set_ui(0);
+       t3 = mpi_copy(u);
+    }
+    do {
+       do {
+           if( !odd ) {
+               if( mpi_test_bit(t1, 0) || mpi_test_bit(t2, 0) ) { /* one is odd */
+                   mpi_add(t1, t1, v);
+                   mpi_sub(t2, t2, u);
+               }
+               mpi_rshift(t1, t1, 1);
+               mpi_rshift(t2, t2, 1);
+               mpi_rshift(t3, t3, 1);
+           }
+           else {
+               if( mpi_test_bit(t1, 0) )
+                   mpi_add(t1, t1, v);
+               mpi_rshift(t1, t1, 1);
+               mpi_rshift(t3, t3, 1);
+           }
+         Y4:
+           ;
+       } while( !mpi_test_bit( t3, 0 ) ); /* while t3 is even */
+
+       if( !t3->sign ) {
+           mpi_set(u1, t1);
+           if( !odd )
+               mpi_set(u2, t2);
+           mpi_set(u3, t3);
+       }
+       else {
+           mpi_sub(v1, v, t1);
+           sign = u->sign; u->sign = !u->sign;
+           if( !odd )
+               mpi_sub(v2, u, t2);
+           u->sign = sign;
+           sign = t3->sign; t3->sign = !t3->sign;
+           mpi_set(v3, t3);
+           t3->sign = sign;
+       }
+       mpi_sub(t1, u1, v1);
+       if( !odd )
+           mpi_sub(t2, u2, v2);
+       mpi_sub(t3, u3, v3);
+       if( t1->sign ) {
+           mpi_add(t1, t1, v);
+           if( !odd )
+               mpi_sub(t2, t2, u);
+       }
+    } while( mpi_cmp_ui( t3, 0 ) ); /* while t3 != 0 */
+    /* mpi_lshift( u3, k ); */
+    mpi_set(x, u1);
+
+    mpi_free(u1);
+    mpi_free(v1);
+    mpi_free(t1);
+    if( !odd ) {
+       mpi_free(u2);
+       mpi_free(v2);
+       mpi_free(t2);
+    }
+    mpi_free(u3);
+    mpi_free(v3);
+    mpi_free(t3);
+
+    mpi_free(u);
+    mpi_free(v);
+  #endif
+}
+
+
+
diff --git a/security/rsa/gnupg_mpi_mpi-mpow.c b/security/rsa/gnupg_mpi_mpi-mpow.c
new file mode 100644 (file)
index 0000000..4d35cdc
--- /dev/null
@@ -0,0 +1,98 @@
+/* mpi-mpow.c  -  MPI functions
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+static int
+build_index( MPI *exparray, int k, int i, int t )
+{
+    int j, bitno;
+    int index = 0;
+
+    bitno = t-i;
+    for(j=k-1; j >= 0; j-- ) {
+       index <<= 1;
+       if( mpi_test_bit( exparray[j], bitno ) )
+           index |= 1;
+    }
+    return index;
+}
+
+/****************
+ * RES = (BASE[0] ^ EXP[0]) *  (BASE[1] ^ EXP[1]) * ... * mod M
+ */
+void
+mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI m)
+{
+    int k;     /* number of elements */
+    int t;     /* bit size of largest exponent */
+    int i, j, idx;
+    MPI *G;    /* table with precomputed values of size 2^k */
+    MPI tmp;
+
+    for(k=0; basearray[k]; k++ )
+       ;
+    assert(k);
+    for(t=0, i=0; (tmp=exparray[i]); i++ ) {
+       j = mpi_get_nbits(tmp);
+       if( j > t )
+           t = j;
+    }
+    assert(i==k);
+    assert(t);
+    assert( k < 10 );
+
+    G = m_alloc_clear( (1<<k) * sizeof *G );
+    /* and calculate */
+    tmp =  mpi_alloc( mpi_get_nlimbs(m)+1 );
+    mpi_set_ui( res, 1 );
+    for(i = 1; i <= t; i++ ) {
+       mpi_mulm(tmp, res, res, m );
+       idx = build_index( exparray, k, i, t );
+       assert( idx >= 0 && idx < (1<<k) );
+       if( !G[idx] ) {
+           if( !idx )
+                G[0] = mpi_alloc_set_ui( 1 );
+           else {
+               for(j=0; j < k; j++ ) {
+                   if( (idx & (1<<j) ) ) {
+                       if( !G[idx] )
+                           G[idx] = mpi_copy( basearray[j] );
+                       else
+                           mpi_mulm( G[idx], G[idx], basearray[j], m );
+                   }
+               }
+               if( !G[idx] )
+                   G[idx] = mpi_alloc(0);
+           }
+       }
+       mpi_mulm(res, tmp, G[idx], m );
+    }
+
+    /* cleanup */
+    mpi_free(tmp);
+    for(i=0; i < (1<<k); i++ )
+       mpi_free(G[i]);
+    m_free(G);
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpi-mul.c b/security/rsa/gnupg_mpi_mpi-mul.c
new file mode 100644 (file)
index 0000000..9da71bc
--- /dev/null
@@ -0,0 +1,212 @@
+/* mpi-mul.c  -  MPI functions
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+
+void
+mpi_mul_ui( MPI prod, MPI mult, unsigned long small_mult )
+{
+    mpi_size_t size, prod_size;
+    mpi_ptr_t  prod_ptr;
+    mpi_limb_t cy;
+    int sign;
+
+    size = mult->nlimbs;
+    sign = mult->sign;
+
+    if( !size || !small_mult ) {
+       prod->nlimbs = 0;
+       prod->sign = 0;
+       return;
+    }
+
+    prod_size = size + 1;
+    if( prod->alloced < prod_size )
+       mpi_resize( prod, prod_size );
+    prod_ptr = prod->d;
+
+    cy = mpihelp_mul_1( prod_ptr, mult->d, size, (mpi_limb_t)small_mult );
+    if( cy )
+       prod_ptr[size++] = cy;
+    prod->nlimbs = size;
+    prod->sign = sign;
+}
+
+
+void
+mpi_mul_2exp( MPI w, MPI u, unsigned long cnt)
+{
+    mpi_size_t usize, wsize, limb_cnt;
+    mpi_ptr_t wp;
+    mpi_limb_t wlimb;
+    int usign, wsign;
+
+    usize = u->nlimbs;
+    usign = u->sign;
+
+    if( !usize ) {
+       w->nlimbs = 0;
+       w->sign = 0;
+       return;
+    }
+
+    limb_cnt = cnt / BITS_PER_MPI_LIMB;
+    wsize = usize + limb_cnt + 1;
+    if( w->alloced < wsize )
+       mpi_resize(w, wsize );
+    wp = w->d;
+    wsize = usize + limb_cnt;
+    wsign = usign;
+
+    cnt %= BITS_PER_MPI_LIMB;
+    if( cnt ) {
+       wlimb = mpihelp_lshift( wp + limb_cnt, u->d, usize, cnt );
+       if( wlimb ) {
+           wp[wsize] = wlimb;
+           wsize++;
+       }
+    }
+    else {
+       MPN_COPY_DECR( wp + limb_cnt, u->d, usize );
+    }
+
+    /* Zero all whole limbs at low end.  Do it here and not before calling
+     * mpn_lshift, not to lose for U == W.  */
+    MPN_ZERO( wp, limb_cnt );
+
+    w->nlimbs = wsize;
+    w->sign = wsign;
+}
+
+
+
+void
+mpi_mul( MPI w, MPI u, MPI v)
+{
+    mpi_size_t usize, vsize, wsize;
+    mpi_ptr_t up, vp, wp;
+    mpi_limb_t cy;
+    int usign, vsign, usecure, vsecure, sign_product;
+    int assign_wp=0;
+    mpi_ptr_t tmp_limb=NULL;
+
+
+    if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+       usize = v->nlimbs;
+       usign = v->sign;
+       usecure = mpi_is_secure(v);
+       up    = v->d;
+       vsize = u->nlimbs;
+       vsign = u->sign;
+       vsecure = mpi_is_secure(u);
+       vp    = u->d;
+    }
+    else {
+       usize = u->nlimbs;
+       usign = u->sign;
+       usecure = mpi_is_secure(u);
+       up    = u->d;
+       vsize = v->nlimbs;
+       vsign = v->sign;
+       vsecure = mpi_is_secure(v);
+       vp    = v->d;
+    }
+    sign_product = usign ^ vsign;
+    wp = w->d;
+
+    /* Ensure W has space enough to store the result.  */
+    wsize = usize + vsize;
+    if ( !mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v)) ) {
+        /* w is not allocated in secure space but u or v is.  To make sure
+         * that no temporray results are stored in w, we temporary use 
+         * a newly allocated limb space for w */
+        wp = mpi_alloc_limb_space( wsize, 1 );
+        assign_wp = 2; /* mark it as 2 so that we can later copy it back to
+                        * mormal memory */
+    }
+    else if( w->alloced < wsize ) {
+       if( wp == up || wp == vp ) {
+           wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) );
+           assign_wp = 1;
+       }
+       else {
+           mpi_resize(w, wsize );
+           wp = w->d;
+       }
+    }
+    else { /* Make U and V not overlap with W. */
+       if( wp == up ) {
+           /* W and U are identical.  Allocate temporary space for U.  */
+           up = tmp_limb = mpi_alloc_limb_space( usize, usecure  );
+           /* Is V identical too?  Keep it identical with U.  */
+           if( wp == vp )
+               vp = up;
+           /* Copy to the temporary space.  */
+           MPN_COPY( up, wp, usize );
+       }
+       else if( wp == vp ) {
+           /* W and V are identical.  Allocate temporary space for V.  */
+           vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure );
+           /* Copy to the temporary space.  */
+           MPN_COPY( vp, wp, vsize );
+       }
+    }
+
+    if( !vsize )
+       wsize = 0;
+    else {
+       cy = mpihelp_mul( wp, up, usize, vp, vsize );
+       wsize -= cy? 0:1;
+    }
+
+    if( assign_wp ) {
+        if (assign_wp == 2) {
+            /* copy the temp wp from secure memory back to normal memory */
+           mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0);
+           MPN_COPY (tmp_wp, wp, wsize);
+            mpi_free_limb_space (wp);
+            wp = tmp_wp;
+        }
+       mpi_assign_limb_space( w, wp, wsize );
+    }
+    w->nlimbs = wsize;
+    w->sign = sign_product;
+    if( tmp_limb )
+       mpi_free_limb_space( tmp_limb );
+}
+
+
+void
+mpi_mulm( MPI w, MPI u, MPI v, MPI m)
+{
+    mpi_mul(w, u, v);
+    mpi_fdiv_r( w, w, m );
+}
+
diff --git a/security/rsa/gnupg_mpi_mpi-pow.c b/security/rsa/gnupg_mpi_mpi-pow.c
new file mode 100644 (file)
index 0000000..bd7ea36
--- /dev/null
@@ -0,0 +1,290 @@
+/* mpi-pow.c  -  MPI functions
+ *     Copyright (C) 1994, 1996, 1998, 2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include <linux/string.h>
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+/****************
+ * RES = BASE ^ EXP mod MOD
+ */
+void
+mpi_powm( MPI res, MPI base, MPI exp, MPI mod)
+{
+    mpi_ptr_t  rp, ep, mp, bp;
+    mpi_size_t esize, msize, bsize, rsize;
+    int        esign, msign, bsign, rsign;
+    int        esec,  msec,  bsec,  rsec;
+    mpi_size_t size;
+    int mod_shift_cnt;
+    int negative_result;
+    mpi_ptr_t mp_marker=NULL, bp_marker=NULL, ep_marker=NULL;
+    mpi_ptr_t xp_marker=NULL;
+    int assign_rp=0;
+    mpi_ptr_t tspace = NULL;
+    mpi_size_t tsize=0;   /* to avoid compiler warning */
+                         /* fixme: we should check that the warning is void*/
+
+    esize = exp->nlimbs;
+    msize = mod->nlimbs;
+    size = 2 * msize;
+    esign = exp->sign;
+    msign = mod->sign;
+
+    esec = mpi_is_secure(exp);
+    msec = mpi_is_secure(mod);
+    bsec = mpi_is_secure(base);
+    rsec = mpi_is_secure(res);
+
+    rp = res->d;
+    ep = exp->d;
+
+    if( !msize )
+       msize = 1 / msize;          /* provoke a signal */
+
+    if( !esize ) {
+       /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
+        * depending on if MOD equals 1.  */
+       rp[0] = 1;
+       res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+       res->sign = 0;
+       goto leave;
+    }
+
+    /* Normalize MOD (i.e. make its most significant bit set) as required by
+     * mpn_divrem.  This will make the intermediate values in the calculation
+     * slightly larger, but the correct result is obtained after a final
+     * reduction using the original MOD value. */
+    mp = mp_marker = mpi_alloc_limb_space(msize, msec);
+    count_leading_zeros( mod_shift_cnt, mod->d[msize-1] );
+    if( mod_shift_cnt )
+       mpihelp_lshift( mp, mod->d, msize, mod_shift_cnt );
+    else
+       MPN_COPY( mp, mod->d, msize );
+
+    bsize = base->nlimbs;
+    bsign = base->sign;
+    if( bsize > msize ) { /* The base is larger than the module. Reduce it. */
+       /* Allocate (BSIZE + 1) with space for remainder and quotient.
+        * (The quotient is (bsize - msize + 1) limbs.)  */
+       bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
+       MPN_COPY( bp, base->d, bsize );
+       /* We don't care about the quotient, store it above the remainder,
+        * at BP + MSIZE.  */
+       mpihelp_divrem( bp + msize, 0, bp, bsize, mp, msize );
+       bsize = msize;
+       /* Canonicalize the base, since we are going to multiply with it
+        * quite a few times.  */
+       MPN_NORMALIZE( bp, bsize );
+    }
+    else
+       bp = base->d;
+
+    if( !bsize ) {
+       res->nlimbs = 0;
+       res->sign = 0;
+       goto leave;
+    }
+
+    if( res->alloced < size ) {
+       /* We have to allocate more space for RES.  If any of the input
+        * parameters are identical to RES, defer deallocation of the old
+        * space.  */
+       if( rp == ep || rp == mp || rp == bp ) {
+           rp = mpi_alloc_limb_space( size, rsec );
+           assign_rp = 1;
+       }
+       else {
+           mpi_resize( res, size );
+           rp = res->d;
+       }
+    }
+    else { /* Make BASE, EXP and MOD not overlap with RES.  */
+       if( rp == bp ) {
+           /* RES and BASE are identical.  Allocate temp. space for BASE.  */
+           assert( !bp_marker );
+           bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
+           MPN_COPY(bp, rp, bsize);
+       }
+       if( rp == ep ) {
+           /* RES and EXP are identical.  Allocate temp. space for EXP.  */
+           ep = ep_marker = mpi_alloc_limb_space( esize, esec );
+           MPN_COPY(ep, rp, esize);
+       }
+       if( rp == mp ) {
+           /* RES and MOD are identical.  Allocate temporary space for MOD.*/
+           assert( !mp_marker );
+           mp = mp_marker = mpi_alloc_limb_space( msize, msec );
+           MPN_COPY(mp, rp, msize);
+       }
+    }
+
+    MPN_COPY( rp, bp, bsize );
+    rsize = bsize;
+    rsign = bsign;
+
+    {
+       mpi_size_t i;
+       mpi_ptr_t xp = xp_marker = mpi_alloc_limb_space( 2 * (msize + 1), msec );
+       int c;
+       mpi_limb_t e;
+       mpi_limb_t carry_limb;
+       struct karatsuba_ctx karactx;
+
+       memset( &karactx, 0, sizeof karactx );
+       negative_result = (ep[0] & 1) && base->sign;
+
+       i = esize - 1;
+       e = ep[i];
+       count_leading_zeros (c, e);
+       e = (e << c) << 1;     /* shift the exp bits to the left, lose msb */
+       c = BITS_PER_MPI_LIMB - 1 - c;
+
+       /* Main loop.
+        *
+        * Make the result be pointed to alternately by XP and RP.  This
+        * helps us avoid block copying, which would otherwise be necessary
+        * with the overlap restrictions of mpihelp_divmod. With 50% probability
+        * the result after this loop will be in the area originally pointed
+        * by RP (==RES->d), and with 50% probability in the area originally
+        * pointed to by XP.
+        */
+
+       for(;;) {
+           while( c ) {
+               mpi_ptr_t tp;
+               mpi_size_t xsize;
+
+               /*mpihelp_mul_n(xp, rp, rp, rsize);*/
+               if( rsize < KARATSUBA_THRESHOLD )
+                   mpih_sqr_n_basecase( xp, rp, rsize );
+               else {
+                   if( !tspace ) {
+                       tsize = 2 * rsize;
+                       tspace = mpi_alloc_limb_space( tsize, 0 );
+                   }
+                   else if( tsize < (2*rsize) ) {
+                       mpi_free_limb_space( tspace );
+                       tsize = 2 * rsize;
+                       tspace = mpi_alloc_limb_space( tsize, 0 );
+                   }
+                   mpih_sqr_n( xp, rp, rsize, tspace );
+               }
+
+               xsize = 2 * rsize;
+               if( xsize > msize ) {
+                   mpihelp_divrem(xp + msize, 0, xp, xsize, mp, msize);
+                   xsize = msize;
+               }
+
+               tp = rp; rp = xp; xp = tp;
+               rsize = xsize;
+
+               if( (mpi_limb_signed_t)e < 0 ) {
+                   /*mpihelp_mul( xp, rp, rsize, bp, bsize );*/
+                   if( bsize < KARATSUBA_THRESHOLD ) {
+                       mpihelp_mul( xp, rp, rsize, bp, bsize );
+                   }
+                   else {
+                       mpihelp_mul_karatsuba_case(
+                                    xp, rp, rsize, bp, bsize, &karactx );
+                   }
+
+                   xsize = rsize + bsize;
+                   if( xsize > msize ) {
+                       mpihelp_divrem(xp + msize, 0, xp, xsize, mp, msize);
+                       xsize = msize;
+                   }
+
+                   tp = rp; rp = xp; xp = tp;
+                   rsize = xsize;
+               }
+               e <<= 1;
+               c--;
+           }
+
+           i--;
+           if( i < 0 )
+               break;
+           e = ep[i];
+           c = BITS_PER_MPI_LIMB;
+       }
+
+       /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
+        * steps.  Adjust the result by reducing it with the original MOD.
+        *
+        * Also make sure the result is put in RES->d (where it already
+        * might be, see above).
+        */
+       if( mod_shift_cnt ) {
+           carry_limb = mpihelp_lshift( res->d, rp, rsize, mod_shift_cnt);
+           rp = res->d;
+           if( carry_limb ) {
+               rp[rsize] = carry_limb;
+               rsize++;
+           }
+       }
+       else {
+           MPN_COPY( res->d, rp, rsize);
+           rp = res->d;
+       }
+
+       if( rsize >= msize ) {
+           mpihelp_divrem(rp + msize, 0, rp, rsize, mp, msize);
+           rsize = msize;
+       }
+
+       /* Remove any leading zero words from the result.  */
+       if( mod_shift_cnt )
+           mpihelp_rshift( rp, rp, rsize, mod_shift_cnt);
+       MPN_NORMALIZE (rp, rsize);
+
+       mpihelp_release_karatsuba_ctx( &karactx );
+    }
+
+    if( negative_result && rsize ) {
+       if( mod_shift_cnt )
+           mpihelp_rshift( mp, mp, msize, mod_shift_cnt);
+       mpihelp_sub( rp, mp, msize, rp, rsize);
+       rsize = msize;
+       rsign = msign;
+       MPN_NORMALIZE(rp, rsize);
+    }
+    res->nlimbs = rsize;
+    res->sign = rsign;
+
+  leave:
+    if( assign_rp ) mpi_assign_limb_space( res, rp, size );
+    if( mp_marker ) mpi_free_limb_space( mp_marker );
+    if( bp_marker ) mpi_free_limb_space( bp_marker );
+    if( ep_marker ) mpi_free_limb_space( ep_marker );
+    if( xp_marker ) mpi_free_limb_space( xp_marker );
+    if( tspace )    mpi_free_limb_space( tspace );
+}
+
diff --git a/security/rsa/gnupg_mpi_mpi-scan.c b/security/rsa/gnupg_mpi_mpi-scan.c
new file mode 100644 (file)
index 0000000..a38a5b9
--- /dev/null
@@ -0,0 +1,129 @@
+/* mpi-scan.c  -  MPI functions
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+/****************
+ * Scan through an mpi and return byte for byte. a -1 is returned to indicate
+ * the end of the mpi. Scanning is done from the lsb to the msb, returned
+ * values are in the range of 0 .. 255.
+ *
+ * FIXME: This code is VERY ugly!
+ */
+int
+mpi_getbyte( MPI a, unsigned idx )
+{
+    int i, j;
+    unsigned n;
+    mpi_ptr_t ap;
+    mpi_limb_t limb;
+
+    ap = a->d;
+    for(n=0,i=0; i < a->nlimbs; i++ ) {
+       limb = ap[i];
+       for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+           if( n == idx )
+               return (limb >> j*8) & 0xff;
+    }
+    return -1;
+}
+
+
+/****************
+ * Put a value at position IDX into A. idx counts from lsb to msb
+ */
+void
+mpi_putbyte( MPI a, unsigned idx, int xc )
+{
+    int i, j;
+    unsigned n;
+    mpi_ptr_t ap;
+    mpi_limb_t limb, c;
+
+    c = xc & 0xff;
+    ap = a->d;
+    for(n=0,i=0; i < a->alloced; i++ ) {
+       limb = ap[i];
+       for( j=0; j < BYTES_PER_MPI_LIMB; j++, n++ )
+           if( n == idx ) {
+             #if BYTES_PER_MPI_LIMB == 4
+               if( j == 0 )
+                   limb = (limb & 0xffffff00) | c;
+               else if( j == 1 )
+                   limb = (limb & 0xffff00ff) | (c<<8);
+               else if( j == 2 )
+                   limb = (limb & 0xff00ffff) | (c<<16);
+               else
+                   limb = (limb & 0x00ffffff) | (c<<24);
+             #elif BYTES_PER_MPI_LIMB == 8
+               if( j == 0 )
+                   limb = (limb & 0xffffffffffffff00) | c;
+               else if( j == 1 )
+                   limb = (limb & 0xffffffffffff00ff) | (c<<8);
+               else if( j == 2 )
+                   limb = (limb & 0xffffffffff00ffff) | (c<<16);
+               else if( j == 3 )
+                   limb = (limb & 0xffffffff00ffffff) | (c<<24);
+               else if( j == 4 )
+                   limb = (limb & 0xffffff00ffffffff) | (c<<32);
+               else if( j == 5 )
+                   limb = (limb & 0xffff00ffffffffff) | (c<<40);
+               else if( j == 6 )
+                   limb = (limb & 0xff00ffffffffffff) | (c<<48);
+               else
+                   limb = (limb & 0x00ffffffffffffff) | (c<<56);
+             #else
+                #error please enhance this function, its ugly - i know.
+             #endif
+               if( a->nlimbs <= i )
+                   a->nlimbs = i+1;
+               ap[i] = limb;
+               return;
+           }
+    }
+    log_bug("index out of range\n");
+}
+
+
+/****************
+ * Count the number of zerobits at the low end of A
+ */
+unsigned
+mpi_trailing_zeros( MPI a )
+{
+    unsigned n, count = 0;
+
+    for(n=0; n < a->nlimbs; n++ ) {
+       if( a->d[n] ) {
+           unsigned nn;
+           mpi_limb_t alimb = a->d[n];
+
+           count_trailing_zeros( nn, alimb );
+           count += nn;
+           break;
+       }
+       count += BITS_PER_MPI_LIMB;
+    }
+    return count;
+
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpi.h b/security/rsa/gnupg_mpi_mpi.h
new file mode 100644 (file)
index 0000000..e64b2ee
--- /dev/null
@@ -0,0 +1,185 @@
+/* mpi.h  -  Multi Precision Integers
+ *     Copyright (C) 1994, 1996, 1998, 1999,
+ *                    2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#ifndef G10_MPI_H
+#define G10_MPI_H
+
+#include "gnupg_mpi_config.h"
+#include "gnupg_mpi_types.h"
+#include "gnupg_mpi_memory.h" 
+
+/* DSI defines */
+
+#define SHA1_DIGEST_LENGTH   20
+
+/*end of DSI defines */
+
+#define log_debug printk
+#define log_bug printk
+
+#define assert(x) do { \
+               if (!x) log_bug("failed assertion\n"); \
+              } while(0);
+
+#if BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_INT
+  typedef unsigned int mpi_limb_t;
+  typedef   signed int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG
+  typedef unsigned long int mpi_limb_t;
+  typedef   signed long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_LONG_LONG
+  typedef unsigned long long int mpi_limb_t;
+  typedef   signed long long int mpi_limb_signed_t;
+#elif BYTES_PER_MPI_LIMB == SIZEOF_UNSIGNED_SHORT
+  typedef unsigned short int mpi_limb_t;
+  typedef   signed short int mpi_limb_signed_t;
+#else
+  #error BYTES_PER_MPI_LIMB does not match any C type
+#endif
+#define BITS_PER_MPI_LIMB    (8*BYTES_PER_MPI_LIMB)
+
+#ifndef EXTERN_UNLESS_MAIN_MODULE
+ #if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
+  #define EXTERN_UNLESS_MAIN_MODULE extern
+ #else
+  #define EXTERN_UNLESS_MAIN_MODULE 
+ #endif
+#endif
+
+#define DBG_MPI     mpi_debug_mode
+EXTERN_UNLESS_MAIN_MODULE int mpi_debug_mode;
+
+
+struct gcry_mpi {
+    int alloced;    /* array size (# of allocated limbs) */
+    int nlimbs;     /* number of valid limbs */
+    int nbits;     /* the real number of valid bits (info only) */
+    int sign;      /* indicates a negative number */
+    unsigned flags; /* bit 0: array must be allocated in secure memory space */
+                   /* bit 1: not used */
+                   /* bit 2: the limb is a pointer to some m_alloced data */
+    mpi_limb_t *d;  /* array with the limbs */
+};
+
+typedef struct gcry_mpi *MPI;
+
+#define MPI_NULL NULL
+
+#define mpi_get_nlimbs(a)     ((a)->nlimbs)
+#define mpi_is_neg(a)        ((a)->sign)
+
+/*-- mpiutil.c --*/
+
+MPI mpi_alloc( unsigned nlimbs );
+MPI mpi_alloc_secure( unsigned nlimbs );
+MPI mpi_alloc_like( MPI a );
+void mpi_free( MPI a );
+void mpi_resize( MPI a, unsigned nlimbs );
+MPI  mpi_copy( MPI a );
+#define mpi_is_opaque(a) ((a) && ((a)->flags&4))
+MPI mpi_set_opaque( MPI a, void *p, int len );
+void *mpi_get_opaque( MPI a, int *len );
+#define mpi_is_secure(a) ((a) && ((a)->flags&1))
+void mpi_set_secure( MPI a );
+void mpi_clear( MPI a );
+void mpi_set( MPI w, MPI u);
+void mpi_set_ui( MPI w, ulong u);
+MPI  mpi_alloc_set_ui( unsigned long u);
+void mpi_m_check( MPI a );
+void mpi_swap( MPI a, MPI b);
+
+/*-- mpicoder.c --*/
+MPI do_encode_md(const byte *sha_buffer, unsigned nbits);
+MPI mpi_read_from_buffer(byte *buffer, unsigned *ret_nread, int secure);
+int mpi_fromstr(MPI val, const char *str);
+u32 mpi_get_keyid( MPI a, u32 *keyid );
+byte *mpi_get_buffer( MPI a, unsigned *nbytes, int *sign );
+byte *mpi_get_secure_buffer( MPI a, unsigned *nbytes, int *sign );
+void  mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign );
+
+#define log_mpidump g10_log_mpidump
+
+/*-- mpi-add.c --*/
+void mpi_add_ui(MPI w, MPI u, ulong v );
+void mpi_add(MPI w, MPI u, MPI v);
+void mpi_addm(MPI w, MPI u, MPI v, MPI m);
+void mpi_sub_ui(MPI w, MPI u, ulong v );
+void mpi_sub( MPI w, MPI u, MPI v);
+void mpi_subm( MPI w, MPI u, MPI v, MPI m);
+
+/*-- mpi-mul.c --*/
+void mpi_mul_ui(MPI w, MPI u, ulong v );
+void mpi_mul_2exp( MPI w, MPI u, ulong cnt);
+void mpi_mul( MPI w, MPI u, MPI v);
+void mpi_mulm( MPI w, MPI u, MPI v, MPI m);
+
+/*-- mpi-div.c --*/
+ulong mpi_fdiv_r_ui( MPI rem, MPI dividend, ulong divisor );
+void  mpi_fdiv_r( MPI rem, MPI dividend, MPI divisor );
+void  mpi_fdiv_q( MPI quot, MPI dividend, MPI divisor );
+void  mpi_fdiv_qr( MPI quot, MPI rem, MPI dividend, MPI divisor );
+void  mpi_tdiv_r( MPI rem, MPI num, MPI den);
+void  mpi_tdiv_qr( MPI quot, MPI rem, MPI num, MPI den);
+void  mpi_tdiv_q_2exp( MPI w, MPI u, unsigned count );
+int   mpi_divisible_ui(MPI dividend, ulong divisor );
+
+/*-- mpi-gcd.c --*/
+int mpi_gcd( MPI g, MPI a, MPI b );
+
+/*-- mpi-pow.c --*/
+void mpi_pow( MPI w, MPI u, MPI v);
+void mpi_powm( MPI res, MPI base, MPI exp, MPI mod);
+
+/*-- mpi-mpow.c --*/
+void mpi_mulpowm( MPI res, MPI *basearray, MPI *exparray, MPI mod);
+
+/*-- mpi-cmp.c --*/
+int mpi_cmp_ui( MPI u, ulong v );
+int mpi_cmp( MPI u, MPI v );
+
+/*-- mpi-scan.c --*/
+int mpi_getbyte( MPI a, unsigned idx );
+void mpi_putbyte( MPI a, unsigned idx, int value );
+unsigned mpi_trailing_zeros( MPI a );
+
+/*-- mpi-bit.c --*/
+void mpi_normalize( MPI a );
+unsigned mpi_get_nbits( MPI a );
+int  mpi_test_bit( MPI a, unsigned n );
+void mpi_set_bit( MPI a, unsigned n );
+void mpi_set_highbit( MPI a, unsigned n );
+void mpi_clear_highbit( MPI a, unsigned n );
+void mpi_clear_bit( MPI a, unsigned n );
+void mpi_rshift( MPI x, MPI a, unsigned n );
+
+/*-- mpi-inv.c --*/
+void mpi_invm( MPI x, MPI u, MPI v );
+
+
+#endif /*G10_MPI_H*/
diff --git a/security/rsa/gnupg_mpi_mpicoder.c b/security/rsa/gnupg_mpi_mpicoder.c
new file mode 100644 (file)
index 0000000..207d3ee
--- /dev/null
@@ -0,0 +1,363 @@
+/* mpicoder.c  -  Coder for the external representation of MPIs
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/string.h>
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_mpi_mpi-internal.h"
+
+#define DIM(v) (sizeof(v)/sizeof((v)[0]))
+#define MAX_EXTERN_MPI_BITS 16384
+
+
+static byte asn[15] = /* Object ID is 1.3.14.3.2.26 */
+  { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+    0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+
+
+MPI
+do_encode_md(const byte *sha_buffer, unsigned nbits)
+{
+  int nframe = (nbits+7) / 8;
+  byte *frame, *fr_pt;
+  int i = 0, n;
+  size_t asnlen = DIM(asn);
+  MPI a = MPI_NULL;
+
+  if(SHA1_DIGEST_LENGTH + asnlen + 4  > nframe )
+    log_bug("can't encode a %d bit MD into a %d bits frame\n",
+           (int)(SHA1_DIGEST_LENGTH*8), (int)nbits);
+
+  /* We encode the MD in this way:
+   *
+   *      0  A PAD(n bytes)   0  ASN(asnlen bytes)  MD(len bytes)
+   *
+   * PAD consists of FF bytes.
+   */
+  frame = (byte*)m_alloc(nframe);
+  n = 0;
+  frame[n++] = 0;
+  frame[n++] = 1; /* block type */
+  i = nframe - SHA1_DIGEST_LENGTH - asnlen -3 ;
+  
+  if(i <= 1) {
+    log_bug("message digest encoding failed\n");
+    m_free(frame);
+    return a;
+  }
+
+  memset( frame+n, 0xff, i ); n += i;
+  frame[n++] = 0;
+  memcpy( frame+n, &asn, asnlen ); n += asnlen;
+  memcpy( frame+n, sha_buffer, SHA1_DIGEST_LENGTH ); n += SHA1_DIGEST_LENGTH;
+  
+  i = nframe;
+  fr_pt = frame;
+
+  if (n != nframe) {
+    log_bug("message digest encoding failed, frame length is wrong\n");
+    m_free(frame);
+    return a;
+  }
+  
+  a = mpi_alloc( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+  mpi_set_buffer( a, frame, nframe, 0 );
+  m_free(frame);
+
+  return a;
+}
+
+
+MPI
+mpi_read_from_buffer(byte *buffer, unsigned *ret_nread, int secure)
+{
+  int i, j;
+  unsigned nbits, nbytes, nlimbs, nread=0;
+  mpi_limb_t a;
+  MPI val = MPI_NULL;
+
+  if( *ret_nread < 2 )
+    goto leave;
+  nbits = buffer[0] << 8 | buffer[1];
+
+  if( nbits > MAX_EXTERN_MPI_BITS ) {
+    log_bug("mpi too large (%u bits)\n", nbits);
+    goto leave;
+  }
+  buffer += 2;
+  nread = 2;
+
+  nbytes = (nbits+7) / 8;
+  nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+  val = secure? mpi_alloc_secure( nlimbs )
+    : mpi_alloc( nlimbs );
+  i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+  i %= BYTES_PER_MPI_LIMB;
+  val->nbits = nbits;
+  j= val->nlimbs = nlimbs;
+  val->sign = 0;
+  for( ; j > 0; j-- ) {
+    a = 0;
+    for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+      if( ++nread > *ret_nread ) {
+       log_bug("mpi larger than buffer nread=%d ret_nread=%d\n", nread, *ret_nread);
+       goto leave;
+      }
+      a <<= 8;
+      a |= *buffer++;
+    }
+    i = 0;
+    val->d[j-1] = a;
+  }
+
+ leave:
+  *ret_nread = nread;
+  return val;
+}
+
+
+/****************
+ * Make an mpi from a character string.
+ */
+int
+mpi_fromstr(MPI val, const char *str)
+{
+    int hexmode=0, sign=0, prepend_zero=0, i, j, c, c1, c2;
+    unsigned nbits, nbytes, nlimbs;
+    mpi_limb_t a;
+
+    if( *str == '-' ) {
+       sign = 1;
+       str++;
+    }
+    if( *str == '0' && str[1] == 'x' )
+       hexmode = 1;
+    else
+       return 1; /* other bases are not yet supported */
+    str += 2;
+
+    nbits = strlen(str)*4;
+    if( nbits % 8 )
+       prepend_zero = 1;
+    nbytes = (nbits+7) / 8;
+    nlimbs = (nbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB;
+    if( val->alloced < nlimbs )
+       mpi_resize(val, nlimbs );
+    i = BYTES_PER_MPI_LIMB - nbytes % BYTES_PER_MPI_LIMB;
+    i %= BYTES_PER_MPI_LIMB;
+    j= val->nlimbs = nlimbs;
+    val->sign = sign;
+    for( ; j > 0; j-- ) {
+       a = 0;
+       for(; i < BYTES_PER_MPI_LIMB; i++ ) {
+           if( prepend_zero ) {
+               c1 = '0';
+               prepend_zero = 0;
+           }
+           else
+               c1 = *str++;
+           assert(c1);
+           c2 = *str++;
+           assert(c2);
+           if( c1 >= '0' && c1 <= '9' )
+               c = c1 - '0';
+           else if( c1 >= 'a' && c1 <= 'f' )
+               c = c1 - 'a' + 10;
+           else if( c1 >= 'A' && c1 <= 'F' )
+               c = c1 - 'A' + 10;
+           else {
+               mpi_clear(val);
+               return 1;
+           }
+           c <<= 4;
+           if( c2 >= '0' && c2 <= '9' )
+               c |= c2 - '0';
+           else if( c2 >= 'a' && c2 <= 'f' )
+               c |= c2 - 'a' + 10;
+           else if( c2 >= 'A' && c2 <= 'F' )
+               c |= c2 - 'A' + 10;
+           else {
+               mpi_clear(val);
+               return 1;
+           }
+           a <<= 8;
+           a |= c;
+       }
+       i = 0;
+
+       val->d[j-1] = a;
+    }
+
+    return 0;
+}
+
+
+/****************
+ * Special function to get the low 8 bytes from an mpi.
+ * This can be used as a keyid; KEYID is an 2 element array.
+ * Return the low 4 bytes.
+ */
+u32
+mpi_get_keyid( MPI a, u32 *keyid )
+{
+#if BYTES_PER_MPI_LIMB == 4
+    if( keyid ) {
+       keyid[0] = a->nlimbs >= 2? a->d[1] : 0;
+       keyid[1] = a->nlimbs >= 1? a->d[0] : 0;
+    }
+    return a->nlimbs >= 1? a->d[0] : 0;
+#elif BYTES_PER_MPI_LIMB == 8
+    if( keyid ) {
+       keyid[0] = a->nlimbs? (u32)(a->d[0] >> 32) : 0;
+       keyid[1] = a->nlimbs? (u32)(a->d[0] & 0xffffffff) : 0;
+    }
+    return a->nlimbs? (u32)(a->d[0] & 0xffffffff) : 0;
+#else
+  #error Make this function work with other LIMB sizes
+#endif
+}
+
+
+/****************
+ * Return an m_alloced buffer with the MPI (msb first).
+ * NBYTES receives the length of this buffer. Caller must free the
+ * return string (This function does return a 0 byte buffer with NBYTES
+ * set to zero if the value of A is zero. If sign is not NULL, it will
+ * be set to the sign of the A.
+ */
+static byte *
+do_get_buffer( MPI a, unsigned *nbytes, int *sign, int force_secure )
+{
+    byte *p, *buffer;
+    mpi_limb_t alimb;
+    int i;
+    unsigned int n;
+
+    if( sign )
+       *sign = a->sign;
+    *nbytes = n = a->nlimbs * BYTES_PER_MPI_LIMB;
+    if (!n)
+      n++; /* avoid zero length allocation */
+    p = buffer = force_secure || mpi_is_secure(a) ? m_alloc_secure(n)
+                                                 : m_alloc(n);
+
+    for(i=a->nlimbs-1; i >= 0; i-- ) {
+       alimb = a->d[i];
+#if BYTES_PER_MPI_LIMB == 4
+       *p++ = alimb >> 24;
+       *p++ = alimb >> 16;
+       *p++ = alimb >>  8;
+       *p++ = alimb      ;
+#elif BYTES_PER_MPI_LIMB == 8
+       *p++ = alimb >> 56;
+       *p++ = alimb >> 48;
+       *p++ = alimb >> 40;
+       *p++ = alimb >> 32;
+       *p++ = alimb >> 24;
+       *p++ = alimb >> 16;
+       *p++ = alimb >>  8;
+       *p++ = alimb      ;
+#else
+#error please implement for this limb size.
+#endif
+    }
+
+    /* this is sub-optimal but we need to do the shift operation
+     * because the caller has to free the returned buffer */
+    for(p=buffer; !*p && *nbytes; p++, --*nbytes )
+      ;
+    if( p != buffer )
+      memmove(buffer,p, *nbytes);
+
+    return buffer;
+}
+
+
+byte *
+mpi_get_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+    return do_get_buffer( a, nbytes, sign, 0 );
+}
+
+byte *
+mpi_get_secure_buffer( MPI a, unsigned *nbytes, int *sign )
+{
+    return do_get_buffer( a, nbytes, sign, 1 );
+}
+
+/****************
+ * Use BUFFER to update MPI.
+ */
+void
+mpi_set_buffer( MPI a, const byte *buffer, unsigned nbytes, int sign )
+{
+    const byte *p;
+    mpi_limb_t alimb;
+    int nlimbs;
+    int i;
+
+    nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+    RESIZE_IF_NEEDED(a, nlimbs);
+    a->sign = sign;
+
+    for(i=0, p = buffer+nbytes-1; p >= buffer+BYTES_PER_MPI_LIMB; ) {
+      #if BYTES_PER_MPI_LIMB == 4
+       alimb  = (mpi_limb_t)*p-- ;
+       alimb |= (mpi_limb_t)*p-- <<  8 ;
+       alimb |= (mpi_limb_t)*p-- << 16 ;
+       alimb |= (mpi_limb_t)*p-- << 24 ;
+      #elif BYTES_PER_MPI_LIMB == 8
+       alimb  = (mpi_limb_t)*p--       ;
+       alimb |= (mpi_limb_t)*p-- <<  8 ;
+       alimb |= (mpi_limb_t)*p-- << 16 ;
+       alimb |= (mpi_limb_t)*p-- << 24 ;
+       alimb |= (mpi_limb_t)*p-- << 32 ;
+       alimb |= (mpi_limb_t)*p-- << 40 ;
+       alimb |= (mpi_limb_t)*p-- << 48 ;
+       alimb |= (mpi_limb_t)*p-- << 56 ;
+      #else
+       #error please implement for this limb size.
+      #endif
+       a->d[i++] = alimb;
+    }
+    if( p >= buffer ) {
+      #if BYTES_PER_MPI_LIMB == 4
+       alimb  = *p--       ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- <<  8 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 16 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 24 ;
+      #elif BYTES_PER_MPI_LIMB == 8
+       alimb  = (mpi_limb_t)*p-- ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- <<  8 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 16 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 24 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 32 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 40 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 48 ;
+       if( p >= buffer ) alimb |= (mpi_limb_t)*p-- << 56 ;
+      #else
+       #error please implement for this limb size.
+      #endif
+       a->d[i++] = alimb;
+    }
+    a->nlimbs = i;
+    assert( i == nlimbs );
+}
+
diff --git a/security/rsa/gnupg_mpi_mpih-cmp.c b/security/rsa/gnupg_mpi_mpih-cmp.c
new file mode 100644 (file)
index 0000000..891371c
--- /dev/null
@@ -0,0 +1,58 @@
+/* mpihelp-sub.c  -  MPI helper functions
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+
+/****************
+ * Compare OP1_PTR/OP1_SIZE with OP2_PTR/OP2_SIZE.
+ * There are no restrictions on the relative sizes of
+ * the two arguments.
+ * Return 1 if OP1 > OP2, 0 if they are equal, and -1 if OP1 < OP2.
+ */
+int
+mpihelp_cmp( mpi_ptr_t op1_ptr, mpi_ptr_t op2_ptr, mpi_size_t size )
+{
+    mpi_size_t i;
+    mpi_limb_t op1_word, op2_word;
+
+    for( i = size - 1; i >= 0 ; i--) {
+       op1_word = op1_ptr[i];
+       op2_word = op2_ptr[i];
+       if( op1_word != op2_word )
+           goto diff;
+    }
+    return 0;
+
+  diff:
+    /* This can *not* be simplified to
+     *  op2_word - op2_word
+     * since that expression might give signed overflow.  */
+    return (op1_word > op2_word) ? 1 : -1;
+}
+
diff --git a/security/rsa/gnupg_mpi_mpih-div.c b/security/rsa/gnupg_mpi_mpih-div.c
new file mode 100644 (file)
index 0000000..393d90b
--- /dev/null
@@ -0,0 +1,534 @@
+/* mpihelp-div.c  -  MPI helper functions
+ *     Copyright (C) 1994, 1996 Free Software Foundation, Inc.
+ *     Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+#ifndef UMUL_TIME
+  #define UMUL_TIME 1
+#endif
+#ifndef UDIV_TIME
+  #define UDIV_TIME UMUL_TIME
+#endif
+
+/* FIXME: We should be using invert_limb (or invert_normalized_limb)
+ * here (not udiv_qrnnd).
+ */
+
+mpi_limb_t
+mpihelp_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                                     mpi_limb_t divisor_limb)
+{
+    mpi_size_t i;
+    mpi_limb_t n1, n0, r;
+    int dummy;
+
+    /* Botch: Should this be handled at all?  Rely on callers? */
+    if( !dividend_size )
+       return 0;
+
+    /* If multiplication is much faster than division, and the
+     * dividend is large, pre-invert the divisor, and use
+     * only multiplications in the inner loop.
+     *
+     * This test should be read:
+     *  Does it ever help to use udiv_qrnnd_preinv?
+     *    && Does what we save compensate for the inversion overhead?
+     */
+    if( UDIV_TIME > (2 * UMUL_TIME + 6)
+       && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+       int normalization_steps;
+
+       count_leading_zeros( normalization_steps, divisor_limb );
+       if( normalization_steps ) {
+           mpi_limb_t divisor_limb_inverted;
+
+           divisor_limb <<= normalization_steps;
+
+           /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+            * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+            * most significant bit (with weight 2**N) implicit.
+            *
+            * Special case for DIVISOR_LIMB == 100...000.
+            */
+           if( !(divisor_limb << 1) )
+               divisor_limb_inverted = ~(mpi_limb_t)0;
+           else
+               udiv_qrnnd(divisor_limb_inverted, dummy,
+                          -divisor_limb, 0, divisor_limb);
+
+           n1 = dividend_ptr[dividend_size - 1];
+           r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+           /* Possible optimization:
+            * if (r == 0
+            * && divisor_limb > ((n1 << normalization_steps)
+            *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+            * ...one division less...
+            */
+           for( i = dividend_size - 2; i >= 0; i--) {
+               n0 = dividend_ptr[i];
+               UDIV_QRNND_PREINV(dummy, r, r,
+                                  ((n1 << normalization_steps)
+                         | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                         divisor_limb, divisor_limb_inverted);
+               n1 = n0;
+           }
+           UDIV_QRNND_PREINV(dummy, r, r,
+                             n1 << normalization_steps,
+                             divisor_limb, divisor_limb_inverted);
+           return r >> normalization_steps;
+       }
+       else {
+           mpi_limb_t divisor_limb_inverted;
+
+           /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+            * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+            * most significant bit (with weight 2**N) implicit.
+            *
+            * Special case for DIVISOR_LIMB == 100...000.
+            */
+           if( !(divisor_limb << 1) )
+               divisor_limb_inverted = ~(mpi_limb_t)0;
+           else
+               udiv_qrnnd(divisor_limb_inverted, dummy,
+                           -divisor_limb, 0, divisor_limb);
+
+           i = dividend_size - 1;
+           r = dividend_ptr[i];
+
+           if( r >= divisor_limb )
+               r = 0;
+           else
+               i--;
+
+           for( ; i >= 0; i--) {
+               n0 = dividend_ptr[i];
+               UDIV_QRNND_PREINV(dummy, r, r,
+                                 n0, divisor_limb, divisor_limb_inverted);
+           }
+           return r;
+       }
+    }
+    else {
+       if( UDIV_NEEDS_NORMALIZATION ) {
+           int normalization_steps;
+
+           count_leading_zeros(normalization_steps, divisor_limb);
+           if( normalization_steps ) {
+               divisor_limb <<= normalization_steps;
+
+               n1 = dividend_ptr[dividend_size - 1];
+               r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+               /* Possible optimization:
+                * if (r == 0
+                * && divisor_limb > ((n1 << normalization_steps)
+                *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+                * ...one division less...
+                */
+               for(i = dividend_size - 2; i >= 0; i--) {
+                   n0 = dividend_ptr[i];
+                   udiv_qrnnd (dummy, r, r,
+                               ((n1 << normalization_steps)
+                        | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                        divisor_limb);
+                   n1 = n0;
+               }
+               udiv_qrnnd (dummy, r, r,
+                           n1 << normalization_steps,
+                           divisor_limb);
+               return r >> normalization_steps;
+           }
+       }
+       /* No normalization needed, either because udiv_qrnnd doesn't require
+        * it, or because DIVISOR_LIMB is already normalized.  */
+       i = dividend_size - 1;
+       r = dividend_ptr[i];
+
+       if(r >= divisor_limb)
+           r = 0;
+       else
+           i--;
+
+       for(; i >= 0; i--) {
+           n0 = dividend_ptr[i];
+           udiv_qrnnd (dummy, r, r, n0, divisor_limb);
+       }
+       return r;
+    }
+}
+
+/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
+ * the NSIZE-DSIZE least significant quotient limbs at QP
+ * and the DSIZE long remainder at NP. If QEXTRA_LIMBS is
+ * non-zero, generate that many fraction bits and append them after the
+ * other quotient limbs.
+ * Return the most significant limb of the quotient, this is always 0 or 1.
+ *
+ * Preconditions:
+ * 0. NSIZE >= DSIZE.
+ * 1. The most significant bit of the divisor must be set.
+ * 2. QP must either not overlap with the input operands at all, or
+ *    QP + DSIZE >= NP must hold true. (This means that it's
+ *    possible to put the quotient in the high part of NUM, right after the
+ *    remainder in NUM.
+ * 3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.
+ */
+
+mpi_limb_t
+mpihelp_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+               mpi_ptr_t np, mpi_size_t nsize,
+               mpi_ptr_t dp, mpi_size_t dsize)
+{
+    mpi_limb_t most_significant_q_limb = 0;
+
+    switch(dsize) {
+      case 0:
+       /* We are asked to divide by zero, so go ahead and do it!  (To make
+          the compiler not remove this statement, return the value.)  */
+       return 1 / dsize;
+
+      case 1:
+       {
+           mpi_size_t i;
+           mpi_limb_t n1;
+           mpi_limb_t d;
+
+           d = dp[0];
+           n1 = np[nsize - 1];
+
+           if( n1 >= d ) {
+               n1 -= d;
+               most_significant_q_limb = 1;
+           }
+
+           qp += qextra_limbs;
+           for( i = nsize - 2; i >= 0; i--)
+               udiv_qrnnd( qp[i], n1, n1, np[i], d );
+           qp -= qextra_limbs;
+
+           for( i = qextra_limbs - 1; i >= 0; i-- )
+               udiv_qrnnd (qp[i], n1, n1, 0, d);
+
+           np[0] = n1;
+       }
+       break;
+
+      case 2:
+       {
+           mpi_size_t i;
+           mpi_limb_t n1, n0, n2;
+           mpi_limb_t d1, d0;
+
+           np += nsize - 2;
+           d1 = dp[1];
+           d0 = dp[0];
+           n1 = np[1];
+           n0 = np[0];
+
+           if( n1 >= d1 && (n1 > d1 || n0 >= d0) ) {
+               sub_ddmmss (n1, n0, n1, n0, d1, d0);
+               most_significant_q_limb = 1;
+           }
+
+           for( i = qextra_limbs + nsize - 2 - 1; i >= 0; i-- ) {
+               mpi_limb_t q;
+               mpi_limb_t r;
+
+               if( i >= qextra_limbs )
+                   np--;
+               else
+                   np[0] = 0;
+
+               if( n1 == d1 ) {
+                   /* Q should be either 111..111 or 111..110.  Need special
+                    * treatment of this rare case as normal division would
+                    * give overflow.  */
+                   q = ~(mpi_limb_t)0;
+
+                   r = n0 + d1;
+                   if( r < d1 ) {   /* Carry in the addition? */
+                       add_ssaaaa( n1, n0, r - d0, np[0], 0, d0 );
+                       qp[i] = q;
+                       continue;
+                   }
+                   n1 = d0 - (d0 != 0?1:0);
+                   n0 = -d0;
+               }
+               else {
+                   udiv_qrnnd (q, r, n1, n0, d1);
+                   umul_ppmm (n1, n0, d0, q);
+               }
+
+               n2 = np[0];
+             q_test:
+               if( n1 > r || (n1 == r && n0 > n2) ) {
+                   /* The estimated Q was too large.  */
+                   q--;
+                   sub_ddmmss (n1, n0, n1, n0, 0, d0);
+                   r += d1;
+                   if( r >= d1 )    /* If not carry, test Q again.  */
+                       goto q_test;
+               }
+
+               qp[i] = q;
+               sub_ddmmss (n1, n0, r, n2, n1, n0);
+           }
+           np[1] = n1;
+           np[0] = n0;
+       }
+       break;
+
+      default:
+       {
+           mpi_size_t i;
+           mpi_limb_t dX, d1, n0;
+
+           np += nsize - dsize;
+           dX = dp[dsize - 1];
+           d1 = dp[dsize - 2];
+           n0 = np[dsize - 1];
+
+           if( n0 >= dX ) {
+               if(n0 > dX || mpihelp_cmp(np, dp, dsize - 1) >= 0 ) {
+                   mpihelp_sub_n(np, np, dp, dsize);
+                   n0 = np[dsize - 1];
+                   most_significant_q_limb = 1;
+               }
+           }
+
+           for( i = qextra_limbs + nsize - dsize - 1; i >= 0; i--) {
+               mpi_limb_t q;
+               mpi_limb_t n1, n2;
+               mpi_limb_t cy_limb;
+
+               if( i >= qextra_limbs ) {
+                   np--;
+                   n2 = np[dsize];
+               }
+               else {
+                   n2 = np[dsize - 1];
+                   MPN_COPY_DECR (np + 1, np, dsize - 1);
+                   np[0] = 0;
+               }
+
+               if( n0 == dX ) {
+                   /* This might over-estimate q, but it's probably not worth
+                    * the extra code here to find out.  */
+                   q = ~(mpi_limb_t)0;
+               }
+               else {
+                   mpi_limb_t r;
+
+                   udiv_qrnnd(q, r, n0, np[dsize - 1], dX);
+                   umul_ppmm(n1, n0, d1, q);
+
+                   while( n1 > r || (n1 == r && n0 > np[dsize - 2])) {
+                       q--;
+                       r += dX;
+                       if( r < dX ) /* I.e. "carry in previous addition?" */
+                           break;
+                       n1 -= n0 < d1;
+                       n0 -= d1;
+                   }
+               }
+
+               /* Possible optimization: We already have (q * n0) and (1 * n1)
+                * after the calculation of q.  Taking advantage of that, we
+                * could make this loop make two iterations less.  */
+               cy_limb = mpihelp_submul_1(np, dp, dsize, q);
+
+               if( n2 != cy_limb ) {
+                   mpihelp_add_n(np, np, dp, dsize);
+                   q--;
+               }
+
+               qp[i] = q;
+               n0 = np[dsize - 1];
+           }
+       }
+    }
+
+    return most_significant_q_limb;
+}
+
+
+/****************
+ * Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
+ * Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
+ * Return the single-limb remainder.
+ * There are no constraints on the value of the divisor.
+ *
+ * QUOT_PTR and DIVIDEND_PTR might point to the same limb.
+ */
+
+mpi_limb_t
+mpihelp_divmod_1( mpi_ptr_t quot_ptr,
+                 mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                 mpi_limb_t divisor_limb)
+{
+    mpi_size_t i;
+    mpi_limb_t n1, n0, r;
+    int dummy;
+
+    if( !dividend_size )
+       return 0;
+
+    /* If multiplication is much faster than division, and the
+     * dividend is large, pre-invert the divisor, and use
+     * only multiplications in the inner loop.
+     *
+     * This test should be read:
+     * Does it ever help to use udiv_qrnnd_preinv?
+     * && Does what we save compensate for the inversion overhead?
+     */
+    if( UDIV_TIME > (2 * UMUL_TIME + 6)
+       && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+       int normalization_steps;
+
+       count_leading_zeros( normalization_steps, divisor_limb );
+       if( normalization_steps ) {
+           mpi_limb_t divisor_limb_inverted;
+
+           divisor_limb <<= normalization_steps;
+
+           /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+            * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+            * most significant bit (with weight 2**N) implicit.
+            */
+           /* Special case for DIVISOR_LIMB == 100...000.  */
+           if( !(divisor_limb << 1) )
+               divisor_limb_inverted = ~(mpi_limb_t)0;
+           else
+               udiv_qrnnd(divisor_limb_inverted, dummy,
+                          -divisor_limb, 0, divisor_limb);
+
+           n1 = dividend_ptr[dividend_size - 1];
+           r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+           /* Possible optimization:
+            * if (r == 0
+            * && divisor_limb > ((n1 << normalization_steps)
+            *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+            * ...one division less...
+            */
+           for( i = dividend_size - 2; i >= 0; i--) {
+               n0 = dividend_ptr[i];
+               UDIV_QRNND_PREINV( quot_ptr[i + 1], r, r,
+                                  ((n1 << normalization_steps)
+                        | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                             divisor_limb, divisor_limb_inverted);
+               n1 = n0;
+           }
+           UDIV_QRNND_PREINV( quot_ptr[0], r, r,
+                              n1 << normalization_steps,
+                              divisor_limb, divisor_limb_inverted);
+           return r >> normalization_steps;
+       }
+       else {
+           mpi_limb_t divisor_limb_inverted;
+
+           /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+            * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+            * most significant bit (with weight 2**N) implicit.
+            */
+           /* Special case for DIVISOR_LIMB == 100...000.  */
+           if( !(divisor_limb << 1) )
+               divisor_limb_inverted = ~(mpi_limb_t) 0;
+           else
+               udiv_qrnnd(divisor_limb_inverted, dummy,
+                          -divisor_limb, 0, divisor_limb);
+
+           i = dividend_size - 1;
+           r = dividend_ptr[i];
+
+           if( r >= divisor_limb )
+               r = 0;
+           else
+               quot_ptr[i--] = 0;
+
+           for( ; i >= 0; i-- ) {
+               n0 = dividend_ptr[i];
+               UDIV_QRNND_PREINV( quot_ptr[i], r, r,
+                                  n0, divisor_limb, divisor_limb_inverted);
+           }
+           return r;
+       }
+    }
+    else {
+       if(UDIV_NEEDS_NORMALIZATION) {
+           int normalization_steps;
+
+           count_leading_zeros (normalization_steps, divisor_limb);
+           if( normalization_steps ) {
+               divisor_limb <<= normalization_steps;
+
+               n1 = dividend_ptr[dividend_size - 1];
+               r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+               /* Possible optimization:
+                * if (r == 0
+                * && divisor_limb > ((n1 << normalization_steps)
+                *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+                * ...one division less...
+                */
+               for( i = dividend_size - 2; i >= 0; i--) {
+                   n0 = dividend_ptr[i];
+                   udiv_qrnnd (quot_ptr[i + 1], r, r,
+                            ((n1 << normalization_steps)
+                        | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                               divisor_limb);
+                   n1 = n0;
+               }
+               udiv_qrnnd (quot_ptr[0], r, r,
+                           n1 << normalization_steps,
+                           divisor_limb);
+               return r >> normalization_steps;
+           }
+       }
+       /* No normalization needed, either because udiv_qrnnd doesn't require
+        * it, or because DIVISOR_LIMB is already normalized.  */
+       i = dividend_size - 1;
+       r = dividend_ptr[i];
+
+       if(r >= divisor_limb)
+           r = 0;
+       else
+           quot_ptr[i--] = 0;
+
+       for(; i >= 0; i--) {
+           n0 = dividend_ptr[i];
+           udiv_qrnnd( quot_ptr[i], r, r, n0, divisor_limb );
+       }
+       return r;
+    }
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpih-mul.c b/security/rsa/gnupg_mpi_mpih-mul.c
new file mode 100644 (file)
index 0000000..18487ba
--- /dev/null
@@ -0,0 +1,525 @@
+/* mpihelp-mul.c  -  MPI helper functions
+ * Copyright (C) 1994, 1996, 1998, 1999,
+ *               2000 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * Note: This code is heavily based on the GNU MP Library.
+ *      Actually it's the same code with only minor changes in the
+ *      way the data is stored; this is to support the abstraction
+ *      of an optional secure memory allocation which may be used
+ *      to avoid revealing of sensitive data due to paging etc.
+ *      The GNU MP Library itself is published under the LGPL;
+ *      however I decided to publish this code under the plain GPL.
+ */
+
+#include <linux/string.h>
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_longlong.h"
+
+
+
+#define MPN_MUL_N_RECURSE(prodp, up, vp, size, tspace) \
+    do {                                               \
+       if( (size) < KARATSUBA_THRESHOLD )              \
+           mul_n_basecase (prodp, up, vp, size);       \
+       else                                            \
+           mul_n (prodp, up, vp, size, tspace);        \
+    } while (0);
+
+#define MPN_SQR_N_RECURSE(prodp, up, size, tspace) \
+    do {                                           \
+       if ((size) < KARATSUBA_THRESHOLD)           \
+           mpih_sqr_n_basecase (prodp, up, size);       \
+       else                                        \
+           mpih_sqr_n (prodp, up, size, tspace);        \
+    } while (0);
+
+
+
+
+/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
+ * both with SIZE limbs, and store the result at PRODP.  2 * SIZE limbs are
+ * always stored.  Return the most significant limb.
+ *
+ * Argument constraints:
+ * 1. PRODP != UP and PRODP != VP, i.e. the destination
+ *    must be distinct from the multiplier and the multiplicand.
+ *
+ *
+ * Handle simple cases with traditional multiplication.
+ *
+ * This is the most critical code of multiplication.  All multiplies rely
+ * on this, both small and huge.  Small ones arrive here immediately.  Huge
+ * ones arrive here as this is the base case for Karatsuba's recursive
+ * algorithm below.
+ */
+
+static mpi_limb_t
+mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
+                                mpi_ptr_t vp, mpi_size_t size)
+{
+    mpi_size_t i;
+    mpi_limb_t cy;
+    mpi_limb_t v_limb;
+
+    /* Multiply by the first limb in V separately, as the result can be
+     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+    v_limb = vp[0];
+    if( v_limb <= 1 ) {
+       if( v_limb == 1 )
+           MPN_COPY( prodp, up, size );
+       else
+           MPN_ZERO( prodp, size );
+       cy = 0;
+    }
+    else
+       cy = mpihelp_mul_1( prodp, up, size, v_limb );
+
+    prodp[size] = cy;
+    prodp++;
+
+    /* For each iteration in the outer loop, multiply one limb from
+     * U with one limb from V, and add it to PROD.  */
+    for( i = 1; i < size; i++ ) {
+       v_limb = vp[i];
+       if( v_limb <= 1 ) {
+           cy = 0;
+           if( v_limb == 1 )
+              cy = mpihelp_add_n(prodp, prodp, up, size);
+       }
+       else
+           cy = mpihelp_addmul_1(prodp, up, size, v_limb);
+
+       prodp[size] = cy;
+       prodp++;
+    }
+
+    return cy;
+}
+
+
+static void
+mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+                       mpi_size_t size, mpi_ptr_t tspace )
+{
+    if( size & 1 ) {
+      /* The size is odd, and the code below doesn't handle that.
+       * Multiply the least significant (size - 1) limbs with a recursive
+       * call, and handle the most significant limb of S1 and S2
+       * separately.
+       * A slightly faster way to do this would be to make the Karatsuba
+       * code below behave as if the size were even, and let it check for
+       * odd size in the end.  I.e., in essence move this code to the end.
+       * Doing so would save us a recursive call, and potentially make the
+       * stack grow a lot less.
+       */
+      mpi_size_t esize = size - 1;      /* even size */
+      mpi_limb_t cy_limb;
+
+      MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
+      cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, vp[esize] );
+      prodp[esize + esize] = cy_limb;
+      cy_limb = mpihelp_addmul_1( prodp + esize, vp, size, up[esize] );
+      prodp[esize + size] = cy_limb;
+    }
+    else {
+       /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
+        *
+        * Split U in two pieces, U1 and U0, such that
+        * U = U0 + U1*(B**n),
+        * and V in V1 and V0, such that
+        * V = V0 + V1*(B**n).
+        *
+        * UV is then computed recursively using the identity
+        *
+        *        2n   n          n                     n
+        * UV = (B  + B )U V  +  B (U -U )(V -V )  +  (B + 1)U V
+        *                1 1        1  0   0  1              0 0
+        *
+        * Where B = 2**BITS_PER_MP_LIMB.
+        */
+       mpi_size_t hsize = size >> 1;
+       mpi_limb_t cy;
+       int negflg;
+
+       /* Product H.      ________________  ________________
+        *                |_____U1 x V1____||____U0 x V0_____|
+        * Put result in upper part of PROD and pass low part of TSPACE
+        * as new TSPACE.
+        */
+       MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
+
+       /* Product M.      ________________
+        *                |_(U1-U0)(V0-V1)_|
+        */
+       if( mpihelp_cmp(up + hsize, up, hsize) >= 0 ) {
+           mpihelp_sub_n(prodp, up + hsize, up, hsize);
+           negflg = 0;
+       }
+       else {
+           mpihelp_sub_n(prodp, up, up + hsize, hsize);
+           negflg = 1;
+       }
+       if( mpihelp_cmp(vp + hsize, vp, hsize) >= 0 ) {
+           mpihelp_sub_n(prodp + hsize, vp + hsize, vp, hsize);
+           negflg ^= 1;
+       }
+       else {
+           mpihelp_sub_n(prodp + hsize, vp, vp + hsize, hsize);
+           /* No change of NEGFLG.  */
+       }
+       /* Read temporary operands from low part of PROD.
+        * Put result in low part of TSPACE using upper part of TSPACE
+        * as new TSPACE.
+        */
+       MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
+
+       /* Add/copy product H. */
+       MPN_COPY (prodp + hsize, prodp + size, hsize);
+       cy = mpihelp_add_n( prodp + size, prodp + size,
+                           prodp + size + hsize, hsize);
+
+       /* Add product M (if NEGFLG M is a negative number) */
+       if(negflg)
+           cy -= mpihelp_sub_n(prodp + hsize, prodp + hsize, tspace, size);
+       else
+           cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
+
+       /* Product L.      ________________  ________________
+        *                |________________||____U0 x V0_____|
+        * Read temporary operands from low part of PROD.
+        * Put result in low part of TSPACE using upper part of TSPACE
+        * as new TSPACE.
+        */
+       MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
+
+       /* Add/copy Product L (twice) */
+
+       cy += mpihelp_add_n(prodp + hsize, prodp + hsize, tspace, size);
+       if( cy )
+         mpihelp_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
+
+       MPN_COPY(prodp, tspace, hsize);
+       cy = mpihelp_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+       if( cy )
+           mpihelp_add_1(prodp + size, prodp + size, size, 1);
+    }
+}
+
+
+void
+mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
+{
+    mpi_size_t i;
+    mpi_limb_t cy_limb;
+    mpi_limb_t v_limb;
+
+    /* Multiply by the first limb in V separately, as the result can be
+     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+    v_limb = up[0];
+    if( v_limb <= 1 ) {
+       if( v_limb == 1 )
+           MPN_COPY( prodp, up, size );
+       else
+           MPN_ZERO(prodp, size);
+       cy_limb = 0;
+    }
+    else
+       cy_limb = mpihelp_mul_1( prodp, up, size, v_limb );
+
+    prodp[size] = cy_limb;
+    prodp++;
+
+    /* For each iteration in the outer loop, multiply one limb from
+     * U with one limb from V, and add it to PROD.  */
+    for( i=1; i < size; i++) {
+       v_limb = up[i];
+       if( v_limb <= 1 ) {
+           cy_limb = 0;
+           if( v_limb == 1 )
+               cy_limb = mpihelp_add_n(prodp, prodp, up, size);
+       }
+       else
+           cy_limb = mpihelp_addmul_1(prodp, up, size, v_limb);
+
+       prodp[size] = cy_limb;
+       prodp++;
+    }
+}
+
+
+void
+mpih_sqr_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
+{
+    if( size & 1 ) {
+       /* The size is odd, and the code below doesn't handle that.
+        * Multiply the least significant (size - 1) limbs with a recursive
+        * call, and handle the most significant limb of S1 and S2
+        * separately.
+        * A slightly faster way to do this would be to make the Karatsuba
+        * code below behave as if the size were even, and let it check for
+        * odd size in the end.  I.e., in essence move this code to the end.
+        * Doing so would save us a recursive call, and potentially make the
+        * stack grow a lot less.
+        */
+       mpi_size_t esize = size - 1;       /* even size */
+       mpi_limb_t cy_limb;
+
+       MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
+       cy_limb = mpihelp_addmul_1( prodp + esize, up, esize, up[esize] );
+       prodp[esize + esize] = cy_limb;
+       cy_limb = mpihelp_addmul_1( prodp + esize, up, size, up[esize] );
+
+       prodp[esize + size] = cy_limb;
+    }
+    else {
+       mpi_size_t hsize = size >> 1;
+       mpi_limb_t cy;
+
+       /* Product H.      ________________  ________________
+        *                |_____U1 x U1____||____U0 x U0_____|
+        * Put result in upper part of PROD and pass low part of TSPACE
+        * as new TSPACE.
+        */
+       MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
+
+       /* Product M.      ________________
+        *                |_(U1-U0)(U0-U1)_|
+        */
+       if( mpihelp_cmp( up + hsize, up, hsize) >= 0 )
+           mpihelp_sub_n( prodp, up + hsize, up, hsize);
+       else
+           mpihelp_sub_n (prodp, up, up + hsize, hsize);
+
+       /* Read temporary operands from low part of PROD.
+        * Put result in low part of TSPACE using upper part of TSPACE
+        * as new TSPACE.  */
+       MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
+
+       /* Add/copy product H  */
+       MPN_COPY(prodp + hsize, prodp + size, hsize);
+       cy = mpihelp_add_n(prodp + size, prodp + size,
+                          prodp + size + hsize, hsize);
+
+       /* Add product M (if NEGFLG M is a negative number).  */
+       cy -= mpihelp_sub_n (prodp + hsize, prodp + hsize, tspace, size);
+
+       /* Product L.      ________________  ________________
+        *                |________________||____U0 x U0_____|
+        * Read temporary operands from low part of PROD.
+        * Put result in low part of TSPACE using upper part of TSPACE
+        * as new TSPACE.  */
+       MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
+
+       /* Add/copy Product L (twice).  */
+       cy += mpihelp_add_n (prodp + hsize, prodp + hsize, tspace, size);
+       if( cy )
+           mpihelp_add_1(prodp + hsize + size, prodp + hsize + size,
+                                                           hsize, cy);
+
+       MPN_COPY(prodp, tspace, hsize);
+       cy = mpihelp_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+       if( cy )
+           mpihelp_add_1 (prodp + size, prodp + size, size, 1);
+    }
+}
+
+
+/* This should be made into an inline function in gmp.h.  */
+void
+mpihelp_mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp, mpi_size_t size)
+{
+    int secure;
+
+    if( up == vp ) {
+       if( size < KARATSUBA_THRESHOLD )
+           mpih_sqr_n_basecase( prodp, up, size );
+       else {
+           mpi_ptr_t tspace;
+           secure = m_is_secure( up );
+           tspace = mpi_alloc_limb_space( 2 * size, secure );
+           mpih_sqr_n( prodp, up, size, tspace );
+           mpi_free_limb_space( tspace );
+       }
+    }
+    else {
+       if( size < KARATSUBA_THRESHOLD )
+           mul_n_basecase( prodp, up, vp, size );
+       else {
+           mpi_ptr_t tspace;
+           secure = m_is_secure( up ) || m_is_secure( vp );
+           tspace = mpi_alloc_limb_space( 2 * size, secure );
+           mul_n (prodp, up, vp, size, tspace);
+           mpi_free_limb_space( tspace );
+       }
+    }
+}
+
+
+
+void
+mpihelp_mul_karatsuba_case( mpi_ptr_t prodp,
+                           mpi_ptr_t up, mpi_size_t usize,
+                           mpi_ptr_t vp, mpi_size_t vsize,
+                           struct karatsuba_ctx *ctx )
+{
+    mpi_limb_t cy;
+
+    if( !ctx->tspace || ctx->tspace_size < vsize ) {
+       if( ctx->tspace )
+           mpi_free_limb_space( ctx->tspace );
+       ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
+                                      m_is_secure( up ) || m_is_secure( vp ) );
+       ctx->tspace_size = vsize;
+    }
+
+    MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
+
+    prodp += vsize;
+    up += vsize;
+    usize -= vsize;
+    if( usize >= vsize ) {
+       if( !ctx->tp || ctx->tp_size < vsize ) {
+           if( ctx->tp )
+               mpi_free_limb_space( ctx->tp );
+           ctx->tp = mpi_alloc_limb_space( 2 * vsize, m_is_secure( up )
+                                                     || m_is_secure( vp ) );
+           ctx->tp_size = vsize;
+       }
+
+       do {
+           MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
+           cy = mpihelp_add_n( prodp, prodp, ctx->tp, vsize );
+           mpihelp_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
+           prodp += vsize;
+           up += vsize;
+           usize -= vsize;
+       } while( usize >= vsize );
+    }
+
+    if( usize ) {
+       if( usize < KARATSUBA_THRESHOLD ) {
+           mpihelp_mul( ctx->tspace, vp, vsize, up, usize );
+       }
+       else {
+           if( !ctx->next ) {
+               ctx->next = m_alloc_clear( sizeof *ctx );
+           }
+           mpihelp_mul_karatsuba_case( ctx->tspace,
+                                       vp, vsize,
+                                       up, usize,
+                                       ctx->next );
+       }
+
+       cy = mpihelp_add_n( prodp, prodp, ctx->tspace, vsize);
+       mpihelp_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
+    }
+}
+
+
+void
+mpihelp_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
+{
+    struct karatsuba_ctx *ctx2;
+
+    if( ctx->tp )
+       mpi_free_limb_space( ctx->tp );
+    if( ctx->tspace )
+       mpi_free_limb_space( ctx->tspace );
+    for( ctx=ctx->next; ctx; ctx = ctx2 ) {
+       ctx2 = ctx->next;
+       if( ctx->tp )
+           mpi_free_limb_space( ctx->tp );
+       if( ctx->tspace )
+           mpi_free_limb_space( ctx->tspace );
+       m_free( ctx );
+    }
+}
+
+/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
+ * and v (pointed to by VP, with VSIZE limbs), and store the result at
+ * PRODP.  USIZE + VSIZE limbs are always stored, but if the input
+ * operands are normalized.  Return the most significant limb of the
+ * result.
+ *
+ * NOTE: The space pointed to by PRODP is overwritten before finished
+ * with U and V, so overlap is an error.
+ *
+ * Argument constraints:
+ * 1. USIZE >= VSIZE.
+ * 2. PRODP != UP and PRODP != VP, i.e. the destination
+ *    must be distinct from the multiplier and the multiplicand.
+ */
+
+mpi_limb_t
+mpihelp_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+                             mpi_ptr_t vp, mpi_size_t vsize)
+{
+    mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
+    mpi_limb_t cy;
+    struct karatsuba_ctx ctx;
+
+    if( vsize < KARATSUBA_THRESHOLD ) {
+       mpi_size_t i;
+       mpi_limb_t v_limb;
+
+       if( !vsize )
+           return 0;
+
+       /* Multiply by the first limb in V separately, as the result can be
+        * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+       v_limb = vp[0];
+       if( v_limb <= 1 ) {
+           if( v_limb == 1 )
+               MPN_COPY( prodp, up, usize );
+           else
+               MPN_ZERO( prodp, usize );
+           cy = 0;
+       }
+       else
+           cy = mpihelp_mul_1( prodp, up, usize, v_limb );
+
+       prodp[usize] = cy;
+       prodp++;
+
+       /* For each iteration in the outer loop, multiply one limb from
+        * U with one limb from V, and add it to PROD.  */
+       for( i = 1; i < vsize; i++ ) {
+           v_limb = vp[i];
+           if( v_limb <= 1 ) {
+               cy = 0;
+               if( v_limb == 1 )
+                  cy = mpihelp_add_n(prodp, prodp, up, usize);
+           }
+           else
+               cy = mpihelp_addmul_1(prodp, up, usize, v_limb);
+
+           prodp[usize] = cy;
+           prodp++;
+       }
+
+       return cy;
+    }
+
+    memset( &ctx, 0, sizeof ctx );
+    mpihelp_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
+    mpihelp_release_karatsuba_ctx( &ctx );
+    return *prod_endp;
+}
+
+
diff --git a/security/rsa/gnupg_mpi_mpiutil.c b/security/rsa/gnupg_mpi_mpiutil.c
new file mode 100644 (file)
index 0000000..4dd0c3e
--- /dev/null
@@ -0,0 +1,468 @@
+/* mpiutil.ac  -  Utility functions for MPI
+ * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG 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.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <linux/string.h>
+
+#include "gnupg_mpi_mpi.h"
+#include "gnupg_mpi_mpi-internal.h"
+#include "gnupg_mpi_memory.h"
+
+
+int DBG_MEMORY = 0; 
+
+#ifdef M_DEBUG
+  #undef mpi_alloc
+  #undef mpi_alloc_secure
+  #undef mpi_free
+#endif
+
+/****************
+ * Note:  It was a bad idea to use the number of limbs to allocate
+ *       because on a alpha the limbs are large but we normally need
+ *       integers of n bits - So we should chnage this to bits (or bytes).
+ *
+ *       But mpi_alloc is used in a lot of places :-)
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc( unsigned nlimbs, const char *info )
+#else
+mpi_alloc( unsigned nlimbs )
+#endif
+{
+    MPI a;
+
+    if( DBG_MEMORY )
+       log_debug("mpi_alloc(%u)\n", nlimbs*BITS_PER_MPI_LIMB );
+  #ifdef M_DEBUG
+    a = m_debug_alloc( sizeof *a, info );
+    a->d = nlimbs? mpi_debug_alloc_limb_space( nlimbs, 0, info ) : NULL;
+  #else
+    a = m_alloc( sizeof *a );
+    a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 0 ) : NULL;
+  #endif
+    a->alloced = nlimbs;
+    a->nlimbs = 0;
+    a->sign = 0;
+    a->flags = 0;
+    a->nbits = 0;
+    return a;
+}
+
+void
+mpi_m_check( MPI a )
+{
+    m_check(a);
+    m_check(a->d);
+}
+
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc_secure( unsigned nlimbs, const char *info )
+#else
+mpi_alloc_secure( unsigned nlimbs )
+#endif
+{
+    MPI a;
+
+    if( DBG_MEMORY )
+       log_debug("mpi_alloc_secure(%u)\n", nlimbs*BITS_PER_MPI_LIMB );
+  #ifdef M_DEBUG
+    a = m_debug_alloc( sizeof *a, info );
+    a->d = nlimbs? mpi_debug_alloc_limb_space( nlimbs, 1, info ) : NULL;
+  #else
+    a = m_alloc( sizeof *a );
+    a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 1 ) : NULL;
+  #endif
+    a->alloced = nlimbs;
+    a->flags = 1;
+    a->nlimbs = 0;
+    a->sign = 0;
+    a->nbits = 0;
+    return a;
+}
+
+
+#if 0
+static void *unused_limbs_5;
+static void *unused_limbs_32;
+static void *unused_limbs_64;
+#endif
+
+mpi_ptr_t
+#ifdef M_DEBUG
+mpi_debug_alloc_limb_space( unsigned nlimbs, int secure, const char *info )
+#else
+mpi_alloc_limb_space( unsigned nlimbs, int secure )
+#endif
+{
+    size_t len = nlimbs * sizeof(mpi_limb_t);
+    mpi_ptr_t p;
+
+    if( DBG_MEMORY )
+       log_debug("mpi_alloc_limb_space(%u)\n", (unsigned)len*8 );
+  #if 0
+    if( !secure ) {
+       if( nlimbs == 5 && unused_limbs_5 ) {  /* DSA 160 bits */
+           p = unused_limbs_5;
+           unused_limbs_5 = *p;
+           return p;
+       }
+       else if( nlimbs == 32 && unused_limbs_32 ) {  /* DSA 1024 bits */
+           p = unused_limbs_32;
+           unused_limbs_32 = *p;
+           return p;
+       }
+       else if( nlimbs == 64 && unused_limbs_64 ) {  /* DSA 2*1024 bits */
+           p = unused_limbs_64;
+           unused_limbs_64 = *p;
+           return p;
+       }
+    }
+  #endif
+
+  #ifdef M_DEBUG
+    p = secure? m_debug_alloc_secure(len, info):m_debug_alloc( len, info );
+  #else
+    p = secure? m_alloc_secure( len ):m_alloc( len );
+  #endif
+
+    return p;
+}
+
+void
+#ifdef M_DEBUG
+mpi_debug_free_limb_space( mpi_ptr_t a, const char *info )
+#else
+mpi_free_limb_space( mpi_ptr_t a )
+#endif
+{
+    if( !a )
+       return;
+    if( DBG_MEMORY )
+       log_debug("mpi_free_limb_space of size %lu\n", (ulong)m_size(a)*8 );
+
+  #if 0
+    if( !m_is_secure(a) ) {
+       size_t nlimbs = m_size(a) / 4 ;
+       void *p = a;
+
+       if( nlimbs == 5 ) {  /* DSA 160 bits */
+           *a = unused_limbs_5;
+           unused_limbs_5 = a;
+           return;
+       }
+       else if( nlimbs == 32 ) {  /* DSA 1024 bits */
+           *a = unused_limbs_32;
+           unused_limbs_32 = a;
+           return;
+       }
+       else if( nlimbs == 64 ) {  /* DSA 2*1024 bits */
+           *a = unused_limbs_64;
+           unused_limbs_64 = a;
+           return;
+       }
+    }
+  #endif
+
+
+    m_free(a);
+}
+
+
+void
+mpi_assign_limb_space( MPI a, mpi_ptr_t ap, unsigned nlimbs )
+{
+    mpi_free_limb_space(a->d);
+    a->d = ap;
+    a->alloced = nlimbs;
+}
+
+
+
+/****************
+ * Resize the array of A to NLIMBS. the additional space is cleared
+ * (set to 0) [done by m_realloc()]
+ */
+void
+#ifdef M_DEBUG
+mpi_debug_resize( MPI a, unsigned nlimbs, const char *info )
+#else
+mpi_resize( MPI a, unsigned nlimbs )
+#endif
+{
+    if( nlimbs <= a->alloced )
+       return; /* no need to do it */
+    /* Note: a->secure is not used - instead the realloc functions
+     * take care of it. Maybe we should drop a->secure completely
+     * and rely on a mpi_is_secure function, which would be
+     * a wrapper around m_is_secure
+     */
+  #ifdef M_DEBUG
+    if( a->d )
+       a->d = m_debug_realloc(a->d, nlimbs * sizeof(mpi_limb_t), info );
+    else
+       a->d = m_debug_alloc_clear( nlimbs * sizeof(mpi_limb_t), info );
+  #else
+    if( a->d )
+       a->d = m_realloc(a->d, nlimbs * sizeof(mpi_limb_t) );
+    else
+       a->d = m_alloc_clear( nlimbs * sizeof(mpi_limb_t) );
+  #endif
+    a->alloced = nlimbs;
+}
+
+void
+mpi_clear( MPI a )
+{
+    a->nlimbs = 0;
+    a->nbits = 0;
+    a->flags = 0;
+}
+
+
+void
+#ifdef M_DEBUG
+mpi_debug_free( MPI a, const char *info )
+#else
+mpi_free( MPI a )
+#endif
+{
+    if( !a )
+       return;
+    if( DBG_MEMORY )
+       log_debug("mpi_free\n" );
+    if( a->flags & 4 )
+       m_free( a->d );
+    else {
+      #ifdef M_DEBUG
+       mpi_debug_free_limb_space(a->d, info);
+      #else
+       mpi_free_limb_space(a->d);
+      #endif
+    }
+    if( a->flags & ~7 )
+       log_bug("invalid flag value in mpi\n");
+    m_free(a);
+}
+
+
+void
+mpi_set_secure( MPI a )
+{
+    mpi_ptr_t ap, bp;
+
+    if( (a->flags & 1) )
+       return;
+    a->flags |= 1;
+    ap = a->d;
+    if( !a->nlimbs ) {
+       assert(!ap);
+       return;
+    }
+  #ifdef M_DEBUG
+    bp = mpi_debug_alloc_limb_space( a->nlimbs, 1, "set_secure" );
+  #else
+    bp = mpi_alloc_limb_space( a->nlimbs, 1 );
+  #endif
+    MPN_COPY( bp, ap, a->nlimbs );
+    a->d = bp;
+  #ifdef M_DEBUG
+    mpi_debug_free_limb_space(ap, "set_secure");
+  #else
+    mpi_free_limb_space(ap);
+  #endif
+}
+
+
+MPI
+mpi_set_opaque( MPI a, void *p, int len )
+{
+    if( !a ) {
+      #ifdef M_DEBUG
+       a = mpi_debug_alloc(0,"alloc_opaque");
+      #else
+       a = mpi_alloc(0);
+      #endif
+    }
+
+    if( a->flags & 4 )
+       m_free( a->d );
+    else {
+      #ifdef M_DEBUG
+       mpi_debug_free_limb_space(a->d, "alloc_opaque");
+      #else
+       mpi_free_limb_space(a->d);
+      #endif
+    }
+
+    a->d = p;
+    a->alloced = 0;
+    a->nlimbs = 0;
+    a->nbits = len;
+    a->flags = 4;
+    return a;
+}
+
+
+void *
+mpi_get_opaque( MPI a, int *len )
+{
+    if( !(a->flags & 4) )
+       log_bug("mpi_get_opaque on normal mpi\n");
+    if( len )
+       *len = a->nbits;
+    return a->d;
+}
+
+
+/****************
+ * Note: This copy function should not interpret the MPI
+ *      but copy it transparently.
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_copy( MPI a, const char *info )
+#else
+mpi_copy( MPI a )
+#endif
+{
+    int i;
+    MPI b;
+
+    if( a && (a->flags & 4) ) {
+       void *p = m_is_secure(a->d)? m_alloc_secure( a->nbits )
+                                  : m_alloc( a->nbits );
+       memcpy( p, a->d, a->nbits );
+       b = mpi_set_opaque( NULL, p, a->nbits );
+    }
+    else if( a ) {
+      #ifdef M_DEBUG
+       b = mpi_is_secure(a)? mpi_debug_alloc_secure( a->nlimbs, info )
+                           : mpi_debug_alloc( a->nlimbs, info );
+      #else
+       b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+                           : mpi_alloc( a->nlimbs );
+      #endif
+       b->nlimbs = a->nlimbs;
+       b->sign = a->sign;
+       b->flags  = a->flags;
+       b->nbits = a->nbits;
+       for(i=0; i < b->nlimbs; i++ )
+           b->d[i] = a->d[i];
+    }
+    else
+       b = NULL;
+    return b;
+}
+
+
+/****************
+ * This function allocates an MPI which is optimized to hold
+ * a value as large as the one given in the arhgument and allocates it
+ * with the same flags as A.
+ */
+MPI
+#ifdef M_DEBUG
+mpi_debug_alloc_like( MPI a, const char *info )
+#else
+mpi_alloc_like( MPI a )
+#endif
+{
+    MPI b;
+
+    if( a && (a->flags & 4) ) {
+       void *p = m_is_secure(a->d)? m_alloc_secure( a->nbits )
+                                  : m_alloc( a->nbits );
+       memcpy( p, a->d, a->nbits );
+       b = mpi_set_opaque( NULL, p, a->nbits );
+    }
+    else if( a ) {
+      #ifdef M_DEBUG
+       b = mpi_is_secure(a)? mpi_debug_alloc_secure( a->nlimbs, info )
+                           : mpi_debug_alloc( a->nlimbs, info );
+      #else
+       b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+                           : mpi_alloc( a->nlimbs );
+      #endif
+       b->nlimbs = 0;
+       b->sign = 0;
+       b->flags = a->flags;
+       b->nbits = 0;
+    }
+    else
+       b = NULL;
+    return b;
+}
+
+
+void
+mpi_set( MPI w, MPI u)
+{
+    mpi_ptr_t wp, up;
+    mpi_size_t usize = u->nlimbs;
+    int usign = u->sign;
+
+    RESIZE_IF_NEEDED(w, usize);
+    wp = w->d;
+    up = u->d;
+    MPN_COPY( wp, up, usize );
+    w->nlimbs = usize;
+    w->nbits = u->nbits;
+    w->flags = u->flags;
+    w->sign = usign;
+}
+
+
+void
+mpi_set_ui( MPI w, unsigned long u)
+{
+    RESIZE_IF_NEEDED(w, 1);
+    w->d[0] = u;
+    w->nlimbs = u? 1:0;
+    w->sign = 0;
+    w->nbits = 0;
+    w->flags = 0;
+}
+
+
+MPI
+mpi_alloc_set_ui( unsigned long u)
+{
+  #ifdef M_DEBUG
+    MPI w = mpi_debug_alloc(1,"alloc_set_ui");
+  #else
+    MPI w = mpi_alloc(1);
+  #endif
+    w->d[0] = u;
+    w->nlimbs = u? 1:0;
+    w->sign = 0;
+    return w;
+}
+
+
+void
+mpi_swap( MPI a, MPI b)
+{
+    struct gcry_mpi tmp;
+
+    tmp = *a; *a = *b; *b = tmp;
+}
+
diff --git a/security/rsa/gnupg_mpi_types.h b/security/rsa/gnupg_mpi_types.h
new file mode 100644 (file)
index 0000000..e74e48e
--- /dev/null
@@ -0,0 +1,88 @@
+/* types.h - some common typedefs
+ *     Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG 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.
+ *
+ * GNUPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef G10_TYPES_H
+#define G10_TYPES_H
+
+
+/* The AC_CHECK_SIZEOF() in configure fails for some machines.
+ * we provide some fallback values here */
+#if !SIZEOF_UNSIGNED_SHORT
+  #undef SIZEOF_UNSIGNED_SHORT
+  #define SIZEOF_UNSIGNED_SHORT 2
+#endif
+#if !SIZEOF_UNSIGNED_INT
+  #undef SIZEOF_UNSIGNED_INT
+  #define SIZEOF_UNSIGNED_INT 4
+#endif
+#if !SIZEOF_UNSIGNED_LONG
+  #undef SIZEOF_UNSIGNED_LONG
+  #define SIZEOF_UNSIGNED_LONG 4
+#endif
+
+
+#include <linux/types.h>
+
+
+#ifndef HAVE_BYTE_TYPEDEF
+  #undef byte      /* maybe there is a macro with this name */
+  #ifndef __riscos__
+    typedef unsigned char byte;
+  #else 
+    /* Norcroft treats char  = unsigned char  as legal assignment
+                   but char* = unsigned char* as illegal assignment
+       and the same applies to the signed variants as well  */
+    typedef char byte;
+  #endif 
+  #define HAVE_BYTE_TYPEDEF
+#endif
+
+#ifndef HAVE_USHORT_TYPEDEF
+  #undef ushort     /* maybe there is a macro with this name */
+  typedef unsigned short ushort;
+  #define HAVE_USHORT_TYPEDEF
+#endif
+
+#ifndef HAVE_ULONG_TYPEDEF
+  #undef ulong     /* maybe there is a macro with this name */
+  typedef unsigned long ulong;
+  #define HAVE_ULONG_TYPEDEF
+#endif
+
+typedef union {
+    int a;
+    short b;
+    char c[1];
+    long d;
+  #ifdef HAVE_U64_TYPEDEF
+    u64 e;
+  #endif
+    float f;
+    double g;
+} PROPERLY_ALIGNED_TYPE;
+
+typedef struct string_list {
+    struct string_list *next;
+    unsigned int flags;
+    char d[1];
+} *STRLIST;
+
+
+#endif /*G10_TYPES_H*/
diff --git a/security/rsa/mpi.c b/security/rsa/mpi.c
new file mode 100644 (file)
index 0000000..bf77953
--- /dev/null
@@ -0,0 +1,1933 @@
+//#include <stdlib.h>
+//#include <string.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include "foo.h"
+
+mpi_limb_t _gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize, mpi_ptr_t vp, mpi_size_t vsize);
+
+const unsigned char __clz_tab[] = {
+  0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+  6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+  8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+};
+
+//#define gcry_xmalloc(X)      malloc(X)
+//#define gcry_xrealloc(X, Y)  realloc(X, Y)
+//#define gcry_xcalloc(X, Y)   calloc(X, Y)
+//#define gcry_free(X)         free(X)
+
+static __inline__ void * kcalloc(size_t nmemb, size_t size)
+{
+       void *foo;
+       foo = kmalloc(nmemb*size, GFP_KERNEL);
+       if (!foo)
+               return NULL;
+       memset(foo, 0x00, nmemb*size);
+       return foo;
+}
+
+// yeah, it's a hack, sue me...
+static __inline__ void * krealloc(void *old, size_t size)
+{
+       void *new;
+
+       new = kmalloc(size, GFP_KERNEL);
+       if (old == NULL)
+               return new;
+       if (!new)
+               return NULL;
+       memcpy(new, old, size);
+       return new;
+}
+
+
+#define gcry_xmalloc(X)                kmalloc(X, GFP_KERNEL)
+#define gcry_xrealloc(X, Y)    krealloc(X, Y)
+#define gcry_xcalloc(X, Y)     kcalloc(X, Y)
+#define gcry_free(X)           kfree(X)
+
+
+#define gcry_is_secure(X)      (0)
+#define mpi_alloc(X)           mpi_alloc_secure(X)
+
+#define mpi_free_limb_space(X) gcry_free(X)
+
+/* Divide the two-limb number in (NH,,NL) by D, with DI being the largest
+ * limb not larger than (2**(2*BITS_PER_MP_LIMB))/D - (2**BITS_PER_MP_LIMB).
+ * If this would yield overflow, DI should be the largest possible number
+ * (i.e., only ones).  For correct operation, the most significant bit of D
+ * has to be set.  Put the quotient in Q and the remainder in R.
+ */
+#define UDIV_QRNND_PREINV(q, r, nh, nl, d, di) \
+    do {                                                            \
+        mpi_limb_t _q, _ql, _r;                                     \
+        mpi_limb_t _xh, _xl;                                        \
+        umul_ppmm (_q, _ql, (nh), (di));                            \
+        _q += (nh);     /* DI is 2**BITS_PER_MPI_LIMB too small */  \
+        umul_ppmm (_xh, _xl, _q, (d));                              \
+        sub_ddmmss (_xh, _r, (nh), (nl), _xh, _xl);                 \
+        if( _xh ) {                                                 \
+            sub_ddmmss (_xh, _r, _xh, _r, 0, (d));                  \
+            _q++;                                                   \
+            if( _xh) {                                              \
+                sub_ddmmss (_xh, _r, _xh, _r, 0, (d));              \
+                _q++;                                               \
+            }                                                       \
+        }                                                           \
+        if( _r >= (d) ) {                                           \
+            _r -= (d);                                              \
+            _q++;                                                   \
+        }                                                           \
+        (r) = _r;                                                   \
+        (q) = _q;                                                   \
+    } while (0)
+
+
+/* Define this unconditionally, so it can be used for debugging.  */
+#define __udiv_qrnnd_c(q, r, n1, n0, d) \
+  do {                                                                  \
+    UWtype __d1, __d0, __q1, __q0, __r1, __r0, __m;                     \
+    __d1 = __ll_highpart (d);                                           \
+    __d0 = __ll_lowpart (d);                                            \
+                                                                        \
+    __r1 = (n1) % __d1;                                                 \
+    __q1 = (n1) / __d1;                                                 \
+    __m = (UWtype) __q1 * __d0;                                         \
+    __r1 = __r1 * __ll_B | __ll_highpart (n0);                          \
+    if (__r1 < __m)                                                     \
+      {                                                                 \
+        __q1--, __r1 += (d);                                            \
+        if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\
+          if (__r1 < __m)                                               \
+            __q1--, __r1 += (d);                                        \
+      }                                                                 \
+    __r1 -= __m;                                                        \
+                                                                        \
+    __r0 = __r1 % __d1;                                                 \
+    __q0 = __r1 / __d1;                                                 \
+    __m = (UWtype) __q0 * __d0;                                         \
+    __r0 = __r0 * __ll_B | __ll_lowpart (n0);                           \
+    if (__r0 < __m)                                                     \
+      {                                                                 \
+        __q0--, __r0 += (d);                                            \
+        if (__r0 >= (d))                                                \
+          if (__r0 < __m)                                               \
+            __q0--, __r0 += (d);                                        \
+      }                                                                 \
+    __r0 -= __m;                                                        \
+                                                                        \
+    (q) = (UWtype) __q1 * __ll_B | __q0;                                \
+    (r) = __r0;                                                         \
+  } while (0)
+
+
+struct gcry_mpi *mpi_alloc_secure( unsigned nlimbs )
+{
+    struct gcry_mpi *a;
+
+    a = gcry_xmalloc( sizeof *a );
+    a->d = nlimbs? mpi_alloc_limb_space( nlimbs, 1 ) : NULL;
+    a->alloced = nlimbs;
+    a->flags = 1;
+    a->nlimbs = 0;
+    a->sign = 0;
+    return a;
+}
+
+
+mpi_ptr_t mpi_alloc_limb_space( unsigned nlimbs, int secure )
+{
+    size_t len = nlimbs * sizeof(mpi_limb_t);
+    mpi_ptr_t p;
+
+    p = gcry_xmalloc( len );
+
+    return p;
+}
+
+void mpi_assign_limb_space( struct gcry_mpi *a, mpi_ptr_t ap, unsigned nlimbs )
+{
+    mpi_free_limb_space(a->d);
+    a->d = ap;
+    a->alloced = nlimbs;
+}
+
+
+
+struct gcry_mpi * gcry_mpi_set_opaque( struct gcry_mpi * a, void *p, unsigned int nbits )
+{
+    if( !a ) {
+        a = mpi_alloc(0);
+    }
+
+    if( a->flags & 4 )
+        gcry_free( a->d );
+    else {
+        //mpi_free_limb_space(a->d);
+        gcry_free(a->d);
+    }
+
+    a->d = p;
+    a->alloced = 0;
+    a->nlimbs = 0;
+    a->sign  = nbits;
+    a->flags = 4;
+    return a;
+}
+
+
+/****************
+ * Note: This copy function should not interpret the MPI
+ *       but copy it transparently.
+ */
+struct gcry_mpi *mpi_copy( struct gcry_mpi * a )
+{
+    int i; 
+    struct gcry_mpi *b;
+
+    if( a && (a->flags & 4) ) {
+        void *p = gcry_xmalloc( (a->sign+7)/8 );
+        memcpy( p, a->d, (a->sign+7)/8 );
+        b = gcry_mpi_set_opaque( NULL, p, a->sign );
+    }
+    else if( a ) {
+        b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs )
+                            : mpi_alloc( a->nlimbs );
+        b->nlimbs = a->nlimbs;
+        b->sign = a->sign;
+        b->flags  = a->flags;
+        for(i=0; i < b->nlimbs; i++ )
+            b->d[i] = a->d[i];
+    }
+    else
+        b = NULL;
+    return b;
+}
+
+
+
+
+mpi_limb_t _gcry_mpih_sub_1(mpi_ptr_t res_ptr,  mpi_ptr_t s1_ptr, size_t s1_size, mpi_limb_t s2_limb )
+{
+    mpi_limb_t x;
+
+    x = *s1_ptr++;
+    s2_limb = x - s2_limb;
+    *res_ptr++ = s2_limb;
+    if( s2_limb > x ) {
+        while( --s1_size ) {
+            x = *s1_ptr++;
+            *res_ptr++ = x - 1;
+            if( x )
+                goto leave;
+        }
+        return 1;
+    }
+
+  leave:
+    if( res_ptr != s1_ptr ) {
+        size_t i;
+        for( i=0; i < s1_size-1; i++ )
+            res_ptr[i] = s1_ptr[i];
+    }
+    return 0;
+}
+
+
+/****************
+ * Resize the array of A to NLIMBS. the additional space is cleared
+ * (set to 0) [done by gcry_realloc()]
+ */
+static void mpi_resize(struct gcry_mpi *a, unsigned nlimbs )
+{
+    if( nlimbs <= a->alloced )
+        return; /* no need to do it */
+    /* Note: a->secure is not used - instead the realloc functions
+     * take care of it. Maybe we should drop a->secure completely
+     * and rely on a mpi_is_secure function, which would be
+     * a wrapper around gcry_is_secure
+     */
+    if( a->d )
+        a->d = gcry_xrealloc(a->d, nlimbs * sizeof(mpi_limb_t) );
+    else  /* FIXME: It may not be allocted in secure memory */
+        a->d = gcry_xcalloc( nlimbs , sizeof(mpi_limb_t) );
+    a->alloced = nlimbs;
+}
+
+
+/****************
+ * Subtract the unsigned integer V from the mpi-integer U and store the
+ * result in W.
+ */
+void mpi_sub_ui(struct gcry_mpi *w, struct gcry_mpi *u, unsigned long v )
+{
+    mpi_ptr_t wp, up;
+    size_t usize, wsize;
+    int usign, wsign;
+
+    usize = u->nlimbs;
+    usign = u->sign;
+    wsign = 0;
+
+    /* If not space for W (and possible carry), increase space.  */
+    wsize = usize + 1;
+    if( w->alloced < wsize )
+        mpi_resize(w, wsize);
+
+    /* These must be after realloc (U may be the same as W).  */
+    up = u->d;
+    wp = w->d;
+
+    if( !usize ) {  /* simple */
+        wp[0] = v;
+        wsize = v? 1:0;
+        wsign = 1;
+    }
+    else if( usign ) {  /* mpi and v are negative */
+        mpi_limb_t cy;
+        cy = _gcry_mpih_add_1(wp, up, usize, v);
+        wp[usize] = cy;
+        wsize = usize + cy;
+    }
+    else {  /* The signs are different.  Need exact comparison to determine
+             * which operand to subtract from which.  */
+        if( usize == 1 && up[0] < v ) {
+            wp[0] = v - up[0];
+            wsize = 1;
+            wsign = 1;
+        }
+        else {
+            _gcry_mpih_sub_1(wp, up, usize, v);
+            /* Size can decrease with at most one limb. */
+            wsize = usize - (wp[usize-1]==0);
+        }
+    }
+
+    w->nlimbs = wsize;
+    w->sign   = wsign;
+}
+
+void mpi_free(struct gcry_mpi *a )
+{
+    if( !a )
+        return;
+    if( a->flags & 4 )
+        gcry_free( a->d );
+    else {
+       //mpi_free_limb_space(a->d);
+        gcry_free(a->d);
+    }
+//    if( a->flags & ~7 )
+//        log_bug("invalid flag value in mpi\n");
+    gcry_free(a);
+}
+
+void mpi_add(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v)
+{
+    mpi_ptr_t wp, up, vp;
+    size_t usize, vsize, wsize;
+    int usign, vsign, wsign;
+
+    if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+        usize = v->nlimbs;
+        usign = v->sign;
+        vsize = u->nlimbs;
+        vsign = u->sign;
+        wsize = usize + 1;
+        RESIZE_IF_NEEDED(w, wsize);
+        /* These must be after realloc (u or v may be the same as w).  */
+        up    = v->d;
+        vp    = u->d;
+    }
+    else {
+        usize = u->nlimbs;
+        usign = u->sign;
+        vsize = v->nlimbs;
+        vsign = v->sign;
+        wsize = usize + 1;
+        RESIZE_IF_NEEDED(w, wsize);
+        /* These must be after realloc (u or v may be the same as w).  */
+        up    = u->d;
+        vp    = v->d;
+    }
+    wp = w->d;
+    wsign = 0;
+
+    if( !vsize ) {  /* simple */
+        MPN_COPY(wp, up, usize );
+        wsize = usize;
+        wsign = usign;
+    }
+    else if( usign != vsign ) { /* different sign */
+        /* This test is right since USIZE >= VSIZE */
+        if( usize != vsize ) {
+            _gcry_mpih_sub(wp, up, usize, vp, vsize);
+            wsize = usize;
+            MPN_NORMALIZE(wp, wsize);
+            wsign = usign;
+        }
+        else if( _gcry_mpih_cmp(up, vp, usize) < 0 ) {
+            _gcry_mpih_sub_n(wp, vp, up, usize);
+            wsize = usize;
+            MPN_NORMALIZE(wp, wsize);
+            if( !usign )
+                wsign = 1;
+        }
+        else {
+            _gcry_mpih_sub_n(wp, up, vp, usize);
+            wsize = usize;
+            MPN_NORMALIZE(wp, wsize);
+            if( usign )
+                wsign = 1;
+        }
+    }
+    else { /* U and V have same sign. Add them. */
+        mpi_limb_t cy = _gcry_mpih_add(wp, up, usize, vp, vsize);
+        wp[usize] = cy;
+        wsize = usize + cy;
+        if( usign )
+            wsign = 1;
+    }
+
+    w->nlimbs = wsize;
+    w->sign = wsign;
+}
+
+
+/****************
+ * Divide (DIVIDEND_PTR,,DIVIDEND_SIZE) by DIVISOR_LIMB.
+ * Write DIVIDEND_SIZE limbs of quotient at QUOT_PTR.
+ * Return the single-limb remainder. 
+ * There are no constraints on the value of the divisor.
+ *              
+ * QUOT_PTR and DIVIDEND_PTR might point to the same limb.
+ */                      
+                              
+mpi_limb_t      
+_gcry_mpih_divmod_1( mpi_ptr_t quot_ptr,
+                        mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                        mpi_limb_t divisor_limb)
+{                              
+    mpi_size_t i;
+    mpi_limb_t n1, n0, r;
+    int dummy;
+            
+    if( !dividend_size )
+        return 0;
+
+    /* If multiplication is much faster than division, and the
+     * dividend is large, pre-invert the divisor, and use
+     * only multiplications in the inner loop.
+     *      
+     * This test should be read:
+     * Does it ever help to use udiv_qrnnd_preinv?
+     * && Does what we save compensate for the inversion overhead?
+     */                    
+    if( UDIV_TIME > (2 * UMUL_TIME + 6)
+        && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+        int normalization_steps;
+
+        count_leading_zeros( normalization_steps, divisor_limb );
+        if( normalization_steps ) {
+            mpi_limb_t divisor_limb_inverted;
+                
+            divisor_limb <<= normalization_steps;
+            
+            /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+             * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+             * most significant bit (with weight 2**N) implicit.
+             */
+            /* Special case for DIVISOR_LIMB == 100...000.  */
+            if( !(divisor_limb << 1) )
+                divisor_limb_inverted = ~(mpi_limb_t)0;
+            else
+                udiv_qrnnd(divisor_limb_inverted, dummy,
+                           -divisor_limb, 0, divisor_limb);
+
+            n1 = dividend_ptr[dividend_size - 1];
+            r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+            /* Possible optimization:
+             * if (r == 0
+             * && divisor_limb > ((n1 << normalization_steps)
+             *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+             * ...one division less...
+             */
+            for( i = dividend_size - 2; i >= 0; i--) {
+                n0 = dividend_ptr[i];
+                UDIV_QRNND_PREINV( quot_ptr[i + 1], r, r,
+                                   ((n1 << normalization_steps)
+                         | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                              divisor_limb, divisor_limb_inverted);
+                n1 = n0;
+            }
+            UDIV_QRNND_PREINV( quot_ptr[0], r, r,
+                               n1 << normalization_steps,
+                               divisor_limb, divisor_limb_inverted);
+            return r >> normalization_steps;
+        }
+        else {
+            mpi_limb_t divisor_limb_inverted;
+
+            /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+             * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+             * most significant bit (with weight 2**N) implicit.
+             */
+            /* Special case for DIVISOR_LIMB == 100...000.  */
+            if( !(divisor_limb << 1) )
+                divisor_limb_inverted = ~(mpi_limb_t) 0;
+            else
+                udiv_qrnnd(divisor_limb_inverted, dummy,
+                           -divisor_limb, 0, divisor_limb);
+
+            i = dividend_size - 1;
+            r = dividend_ptr[i];
+
+            if( r >= divisor_limb )
+                r = 0;
+            else
+                quot_ptr[i--] = 0;
+
+            for( ; i >= 0; i-- ) {
+                n0 = dividend_ptr[i];
+                UDIV_QRNND_PREINV( quot_ptr[i], r, r,
+                                   n0, divisor_limb, divisor_limb_inverted);
+            }
+            return r;
+        }
+    }
+    else {
+        if(UDIV_NEEDS_NORMALIZATION) {
+            int normalization_steps;
+
+            count_leading_zeros (normalization_steps, divisor_limb);
+            if( normalization_steps ) {
+                divisor_limb <<= normalization_steps;
+                n1 = dividend_ptr[dividend_size - 1];
+                r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+                /* Possible optimization:
+                 * if (r == 0
+                 * && divisor_limb > ((n1 << normalization_steps)
+                 *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+                 * ...one division less...
+                 */
+                for( i = dividend_size - 2; i >= 0; i--) {
+                    n0 = dividend_ptr[i];
+                    udiv_qrnnd (quot_ptr[i + 1], r, r,
+                             ((n1 << normalization_steps)
+                         | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                                divisor_limb);
+                    n1 = n0;
+                }
+                udiv_qrnnd (quot_ptr[0], r, r,
+                            n1 << normalization_steps,
+                            divisor_limb);
+                return r >> normalization_steps;
+            }
+        }
+        /* No normalization needed, either because udiv_qrnnd doesn't require
+         * it, or because DIVISOR_LIMB is already normalized.  */
+        i = dividend_size - 1;
+        r = dividend_ptr[i];
+        if(r >= divisor_limb)
+            r = 0;
+        else
+            quot_ptr[i--] = 0;
+        for(; i >= 0; i--) {
+            n0 = dividend_ptr[i];
+            udiv_qrnnd( quot_ptr[i], r, r, n0, divisor_limb );
+        }
+        return r;
+    }
+}
+
+
+mpi_limb_t
+_gcry_mpih_mod_1(mpi_ptr_t dividend_ptr, mpi_size_t dividend_size,
+                                      mpi_limb_t divisor_limb)
+{                                     
+    mpi_size_t i;
+    mpi_limb_t n1, n0, r;
+    int dummy; 
+    
+    /* Botch: Should this be handled at all?  Rely on callers?  */
+    if( !dividend_size )
+        return 0;
+        
+    /* If multiplication is much faster than division, and the
+     * dividend is large, pre-invert the divisor, and use
+     * only multiplications in the inner loop.
+     *
+     * This test should be read:
+     *   Does it ever help to use udiv_qrnnd_preinv?
+     *     && Does what we save compensate for the inversion overhead?
+     */
+    if( UDIV_TIME > (2 * UMUL_TIME + 6)
+        && (UDIV_TIME - (2 * UMUL_TIME + 6)) * dividend_size > UDIV_TIME ) {
+        int normalization_steps;
+        
+        count_leading_zeros( normalization_steps, divisor_limb );
+        if( normalization_steps ) {
+            mpi_limb_t divisor_limb_inverted;
+            
+            divisor_limb <<= normalization_steps;
+            
+            /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+             * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+             * most significant bit (with weight 2**N) implicit.
+             *
+             * Special case for DIVISOR_LIMB == 100...000.
+             */
+            if( !(divisor_limb << 1) )
+                divisor_limb_inverted = ~(mpi_limb_t)0;
+            else
+                udiv_qrnnd(divisor_limb_inverted, dummy,
+                           -divisor_limb, 0, divisor_limb);
+
+            n1 = dividend_ptr[dividend_size - 1];
+            r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+            /* Possible optimization:
+             * if (r == 0
+             * && divisor_limb > ((n1 << normalization_steps)
+             *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+             * ...one division less...
+             */
+            for( i = dividend_size - 2; i >= 0; i--) {
+                n0 = dividend_ptr[i];
+                UDIV_QRNND_PREINV(dummy, r, r,
+                                   ((n1 << normalization_steps)
+                          | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                          divisor_limb, divisor_limb_inverted);
+                n1 = n0;
+            }
+            UDIV_QRNND_PREINV(dummy, r, r,
+                              n1 << normalization_steps,
+                              divisor_limb, divisor_limb_inverted);
+            return r >> normalization_steps;
+        }
+        else {
+            mpi_limb_t divisor_limb_inverted;
+
+            /* Compute (2**2N - 2**N * DIVISOR_LIMB) / DIVISOR_LIMB.  The
+             * result is a (N+1)-bit approximation to 1/DIVISOR_LIMB, with the
+             * most significant bit (with weight 2**N) implicit.
+             *
+             * Special case for DIVISOR_LIMB == 100...000.
+             */
+            if( !(divisor_limb << 1) )
+                divisor_limb_inverted = ~(mpi_limb_t)0;
+            else
+                udiv_qrnnd(divisor_limb_inverted, dummy,
+                            -divisor_limb, 0, divisor_limb);
+
+            i = dividend_size - 1;
+            r = dividend_ptr[i];
+
+            if( r >= divisor_limb )
+                r = 0;
+            else
+                i--;
+
+            for( ; i >= 0; i--) {
+                n0 = dividend_ptr[i];
+                UDIV_QRNND_PREINV(dummy, r, r,
+                                  n0, divisor_limb, divisor_limb_inverted);
+            }
+            return r;
+        }
+    }
+    else {
+        if( UDIV_NEEDS_NORMALIZATION ) {
+            int normalization_steps;
+
+            count_leading_zeros(normalization_steps, divisor_limb);
+            if( normalization_steps ) {
+                divisor_limb <<= normalization_steps;
+
+                n1 = dividend_ptr[dividend_size - 1];
+                r = n1 >> (BITS_PER_MPI_LIMB - normalization_steps);
+
+                /* Possible optimization:
+                 * if (r == 0
+                 * && divisor_limb > ((n1 << normalization_steps)
+                 *                 | (dividend_ptr[dividend_size - 2] >> ...)))
+                 * ...one division less...
+                 */
+                for(i = dividend_size - 2; i >= 0; i--) {
+                    n0 = dividend_ptr[i];
+                    udiv_qrnnd (dummy, r, r,
+                                ((n1 << normalization_steps)
+                         | (n0 >> (BITS_PER_MPI_LIMB - normalization_steps))),
+                         divisor_limb);
+                    n1 = n0;
+                }
+                udiv_qrnnd (dummy, r, r,
+                            n1 << normalization_steps,
+                            divisor_limb);
+                return r >> normalization_steps;
+            }
+        }
+        /* No normalization needed, either because udiv_qrnnd doesn't require
+         * it, or because DIVISOR_LIMB is already normalized.  */
+        i = dividend_size - 1;
+        r = dividend_ptr[i];
+
+        if(r >= divisor_limb)
+            r = 0;
+        else
+            i--;
+
+        for(; i >= 0; i--) {
+            n0 = dividend_ptr[i];
+            udiv_qrnnd (dummy, r, r, n0, divisor_limb);
+        }
+        return r;
+    }
+}
+
+
+/* Divide num (NP/NSIZE) by den (DP/DSIZE) and write
+ * the NSIZE-DSIZE least significant quotient limbs at QP
+ * and the DSIZE long remainder at NP.  If QEXTRA_LIMBS is
+ * non-zero, generate that many fraction bits and append them after the
+ * other quotient limbs.
+ * Return the most significant limb of the quotient, this is always 0 or 1.
+ *
+ * Preconditions:
+ * 0. NSIZE >= DSIZE.
+ * 1. The most significant bit of the divisor must be set.
+ * 2. QP must either not overlap with the input operands at all, or
+ *    QP + DSIZE >= NP must hold true.  (This means that it's
+ *    possible to put the quotient in the high part of NUM, right after the
+ *    remainder in NUM.
+ * 3. NSIZE >= DSIZE, even if QEXTRA_LIMBS is non-zero.
+ */
+
+mpi_limb_t
+_gcry_mpih_divrem( mpi_ptr_t qp, mpi_size_t qextra_limbs,
+                      mpi_ptr_t np, mpi_size_t nsize,
+                      mpi_ptr_t dp, mpi_size_t dsize)
+{                     
+    mpi_limb_t most_significant_q_limb = 0;
+    
+    switch(dsize) {
+      case 0:
+        /* We are asked to divide by zero, so go ahead and do it!  (To make
+           the compiler not remove this statement, return the value.)  */
+        return 1 / dsize;
+
+      case 1:
+        {
+            mpi_size_t i;
+            mpi_limb_t n1;
+            mpi_limb_t d;
+
+            d = dp[0];
+            n1 = np[nsize - 1];
+
+            if( n1 >= d ) {
+                n1 -= d;
+                most_significant_q_limb = 1;
+            }
+
+            qp += qextra_limbs;
+            for( i = nsize - 2; i >= 0; i--)
+                udiv_qrnnd( qp[i], n1, n1, np[i], d );
+            qp -= qextra_limbs;
+
+            for( i = qextra_limbs - 1; i >= 0; i-- )
+                udiv_qrnnd (qp[i], n1, n1, 0, d);
+
+            np[0] = n1;
+        }
+        break;
+
+      case 2:
+        {
+            mpi_size_t i;
+            mpi_limb_t n1, n0, n2;
+            mpi_limb_t d1, d0;
+
+            np += nsize - 2;
+            d1 = dp[1];
+            d0 = dp[0];
+            n1 = np[1];
+            n0 = np[0];
+
+            if( n1 >= d1 && (n1 > d1 || n0 >= d0) ) {
+                sub_ddmmss (n1, n0, n1, n0, d1, d0);
+                most_significant_q_limb = 1;
+            }
+
+            for( i = qextra_limbs + nsize - 2 - 1; i >= 0; i-- ) {
+                mpi_limb_t q;
+                mpi_limb_t r;
+
+                if( i >= qextra_limbs )
+                    np--;
+                else
+                    np[0] = 0;
+
+                if( n1 == d1 ) {
+                    /* Q should be either 111..111 or 111..110.  Need special
+                     * treatment of this rare case as normal division would
+                     * give overflow.  */
+                    q = ~(mpi_limb_t)0;
+
+                    r = n0 + d1;
+                    if( r < d1 ) {   /* Carry in the addition? */
+                        add_ssaaaa( n1, n0, r - d0, np[0], 0, d0 );
+                        qp[i] = q;
+                        continue;
+                    }
+                    n1 = d0 - (d0 != 0?1:0);
+                    n0 = -d0;
+                }
+                else {
+                    udiv_qrnnd (q, r, n1, n0, d1);
+                    umul_ppmm (n1, n0, d0, q);
+                }
+
+                n2 = np[0];
+              q_test:
+                if( n1 > r || (n1 == r && n0 > n2) ) {
+                    /* The estimated Q was too large.  */
+                    q--;
+                    sub_ddmmss (n1, n0, n1, n0, 0, d0);
+                    r += d1;
+                    if( r >= d1 )    /* If not carry, test Q again.  */
+                        goto q_test;
+                }
+
+                qp[i] = q;
+                sub_ddmmss (n1, n0, r, n2, n1, n0);
+            }
+            np[1] = n1;
+            np[0] = n0;
+        }
+        break;
+
+      default:
+        {
+            mpi_size_t i;
+            mpi_limb_t dX, d1, n0;
+
+            np += nsize - dsize;
+            dX = dp[dsize - 1];
+            d1 = dp[dsize - 2];
+            n0 = np[dsize - 1];
+
+            if( n0 >= dX ) {
+                if(n0 > dX || _gcry_mpih_cmp(np, dp, dsize - 1) >= 0 ) {
+                    _gcry_mpih_sub_n(np, np, dp, dsize);
+                    n0 = np[dsize - 1];
+                    most_significant_q_limb = 1;
+                }
+            }
+
+            for( i = qextra_limbs + nsize - dsize - 1; i >= 0; i--) {
+                mpi_limb_t q;
+                mpi_limb_t n1, n2;
+                mpi_limb_t cy_limb;
+
+                if( i >= qextra_limbs ) {
+                    np--;
+                    n2 = np[dsize];
+                }
+                else {
+                    n2 = np[dsize - 1];
+                    MPN_COPY_DECR (np + 1, np, dsize - 1);
+                    np[0] = 0;
+                }
+
+                if( n0 == dX ) {
+                    /* This might over-estimate q, but it's probably not worth
+                     * the extra code here to find out.  */
+                    q = ~(mpi_limb_t)0;
+                }
+                else {
+                    mpi_limb_t r;
+
+                    udiv_qrnnd(q, r, n0, np[dsize - 1], dX);
+                    umul_ppmm(n1, n0, d1, q);
+
+                    while( n1 > r || (n1 == r && n0 > np[dsize - 2])) {
+                        q--;
+                        r += dX;
+                        if( r < dX ) /* I.e. "carry in previous addition?" */
+                            break;
+                        n1 -= n0 < d1;
+                        n0 -= d1;
+                    }
+                }
+
+                /* Possible optimization: We already have (q * n0) and (1 * n1)
+                 * after the calculation of q.  Taking advantage of that, we
+                 * could make this loop make two iterations less.  */
+                cy_limb = _gcry_mpih_submul_1(np, dp, dsize, q);
+
+                if( n2 != cy_limb ) {
+                    _gcry_mpih_add_n(np, np, dp, dsize);
+                    q--;
+                }
+
+                qp[i] = q;
+                n0 = np[dsize - 1];
+            }
+        }
+    }
+
+    return most_significant_q_limb;
+}
+
+
+
+
+static void _gcry_mpi_tdiv_qr(struct gcry_mpi *quot, struct gcry_mpi *rem, struct gcry_mpi *num, struct gcry_mpi *den)
+{
+    mpi_ptr_t np, dp;
+    mpi_ptr_t qp, rp;
+    mpi_size_t nsize = num->nlimbs;
+    mpi_size_t dsize = den->nlimbs;
+    mpi_size_t qsize, rsize;
+    mpi_size_t sign_remainder = num->sign;
+    mpi_size_t sign_quotient = num->sign ^ den->sign;
+    unsigned normalization_steps;
+    mpi_limb_t q_limb;
+    mpi_ptr_t marker[5];
+    int markidx=0;
+
+    /* Ensure space is enough for quotient and remainder.
+     * We need space for an extra limb in the remainder, because it's
+     * up-shifted (normalized) below.  */
+    rsize = nsize + 1;
+    mpi_resize( rem, rsize);
+
+    qsize = rsize - dsize;        /* qsize cannot be bigger than this.  */
+    if( qsize <= 0 ) {
+        if( num != rem ) {
+            rem->nlimbs = num->nlimbs;
+            rem->sign = num->sign;
+            MPN_COPY(rem->d, num->d, nsize);
+        }
+        if( quot ) {
+            /* This needs to follow the assignment to rem, in case the
+             * numerator and quotient are the same.  */
+            quot->nlimbs = 0;
+            quot->sign = 0;
+        }
+        return;
+    }
+
+    if( quot )
+        mpi_resize( quot, qsize);
+
+    /* Read pointers here, when reallocation is finished.  */
+    np = num->d;
+    dp = den->d;
+    rp = rem->d;
+
+    /* Optimize division by a single-limb divisor.  */
+    if( dsize == 1 ) {
+        mpi_limb_t rlimb;
+        if( quot ) {
+            qp = quot->d;
+            rlimb = _gcry_mpih_divmod_1( qp, np, nsize, dp[0] );
+            qsize -= qp[qsize - 1] == 0;
+            quot->nlimbs = qsize;
+            quot->sign = sign_quotient;
+        }
+        else
+            rlimb = _gcry_mpih_mod_1( np, nsize, dp[0] );
+        rp[0] = rlimb;
+        rsize = rlimb != 0?1:0;
+        rem->nlimbs = rsize;
+        rem->sign = sign_remainder;
+        return;
+    }
+
+
+    if( quot ) {
+        qp = quot->d;
+        /* Make sure QP and NP point to different objects.  Otherwise the
+         * numerator would be gradually overwritten by the quotient limbs.  */
+        if(qp == np) { /* Copy NP object to temporary space.  */
+            np = marker[markidx++] = mpi_alloc_limb_space(nsize,
+                                                          mpi_is_secure(quot));
+            MPN_COPY(np, qp, nsize);
+        }
+    }
+    else /* Put quotient at top of remainder. */
+        qp = rp + dsize;
+
+    count_leading_zeros( normalization_steps, dp[dsize - 1] );
+
+    /* Normalize the denominator, i.e. make its most significant bit set by
+     * shifting it NORMALIZATION_STEPS bits to the left.  Also shift the
+     * numerator the same number of steps (to keep the quotient the same!).
+     */
+    if( normalization_steps ) {
+        mpi_ptr_t tp;
+        mpi_limb_t nlimb;
+
+        /* Shift up the denominator setting the most significant bit of
+         * the most significant word.  Use temporary storage not to clobber
+         * the original contents of the denominator.  */
+        tp = marker[markidx++] = mpi_alloc_limb_space(dsize,mpi_is_secure(den));
+        _gcry_mpih_lshift( tp, dp, dsize, normalization_steps );
+        dp = tp;
+
+        /* Shift up the numerator, possibly introducing a new most
+         * significant word.  Move the shifted numerator in the remainder
+         * meanwhile.  */
+        nlimb = _gcry_mpih_lshift(rp, np, nsize, normalization_steps);
+        if( nlimb ) {
+            rp[nsize] = nlimb;
+            rsize = nsize + 1;
+        }
+        else
+            rsize = nsize;
+    }
+    else {
+        /* The denominator is already normalized, as required.  Copy it to
+         * temporary space if it overlaps with the quotient or remainder.  */
+        if( dp == rp || (quot && (dp == qp))) {
+            mpi_ptr_t tp;
+
+            tp = marker[markidx++] = mpi_alloc_limb_space(dsize, mpi_is_secure(den));
+            MPN_COPY( tp, dp, dsize );
+            dp = tp;
+        }
+
+        /* Move the numerator to the remainder.  */
+        if( rp != np )
+            MPN_COPY(rp, np, nsize);
+
+        rsize = nsize;
+    }
+
+    q_limb = _gcry_mpih_divrem( qp, 0, rp, rsize, dp, dsize );
+
+    if( quot ) {
+        qsize = rsize - dsize;
+        if(q_limb) {
+            qp[qsize] = q_limb;
+            qsize += 1;
+        }
+
+        quot->nlimbs = qsize;
+        quot->sign = sign_quotient;
+    }
+
+    rsize = dsize;
+    MPN_NORMALIZE (rp, rsize);
+
+    if( normalization_steps && rsize ) {
+        _gcry_mpih_rshift(rp, rp, rsize, normalization_steps);
+        rsize -= rp[rsize - 1] == 0?1:0;
+    }
+
+    rem->nlimbs = rsize;
+    rem->sign   = sign_remainder;
+    while( markidx )
+//        mpi_free_limb_space(marker[--markidx]);
+        gcry_free(marker[--markidx]);
+}
+
+/* If den == quot, den needs temporary storage.
+ * If den == rem, den needs temporary storage.
+ * If num == quot, num needs temporary storage.
+ * If den has temporary storage, it can be normalized while being copied,
+ *   i.e no extra storage should be allocated.
+ */
+
+static void _gcry_mpi_tdiv_r(struct gcry_mpi *rem, struct gcry_mpi *num, struct gcry_mpi *den)
+{
+    _gcry_mpi_tdiv_qr(NULL, rem, num, den );
+}
+
+
+
+void mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor )
+{
+    int divisor_sign = divisor->sign;
+    struct gcry_mpi *temp_divisor = NULL;
+
+    /* We need the original value of the divisor after the remainder has been
+     * preliminary calculated.  We have to copy it to temporary space if it's
+     * the same variable as REM.  */
+    if( rem == divisor ) {
+        temp_divisor = mpi_copy( divisor );
+        divisor = temp_divisor;
+    }
+
+    _gcry_mpi_tdiv_r( rem, dividend, divisor );
+
+    if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+        mpi_add( rem, rem, divisor);
+
+    if( temp_divisor )
+        mpi_free(temp_divisor);
+}
+
+
+
+void
+_gcry_mpih_sqr_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t size )
+{
+    mpi_size_t i;
+    mpi_limb_t cy_limb;
+    mpi_limb_t v_limb;
+    
+    /* Multiply by the first limb in V separately, as the result can be
+     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+    v_limb = up[0];
+    if( v_limb <= 1 ) {
+        if( v_limb == 1 )
+            MPN_COPY( prodp, up, size );
+        else
+            MPN_ZERO(prodp, size);
+        cy_limb = 0;
+    }   
+    else
+        cy_limb = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+        
+    prodp[size] = cy_limb;
+    prodp++;
+
+    /* For each iteration in the outer loop, multiply one limb from
+     * U with one limb from V, and add it to PROD.  */
+    for( i=1; i < size; i++) {
+        v_limb = up[i];
+        if( v_limb <= 1 ) {
+            cy_limb = 0;
+            if( v_limb == 1 )
+                cy_limb = _gcry_mpih_add_n(prodp, prodp, up, size);
+        }
+        else
+            cy_limb = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+        prodp[size] = cy_limb;
+        prodp++;
+    }
+}
+
+
+/* Multiply the natural numbers u (pointed to by UP) and v (pointed to by VP),
+ * both with SIZE limbs, and store the result at PRODP.  2 * SIZE limbs are
+ * always stored.  Return the most significant limb.
+ *
+ * Argument constraints:
+ * 1. PRODP != UP and PRODP != VP, i.e. the destination
+ *    must be distinct from the multiplier and the multiplicand.
+ *
+ *
+ * Handle simple cases with traditional multiplication.
+ *
+ * This is the most critical code of multiplication.  All multiplies rely
+ * on this, both small and huge.  Small ones arrive here immediately.  Huge
+ * ones arrive here as this is the base case for Karatsuba's recursive
+ * algorithm below.
+ */
+
+static mpi_limb_t
+mul_n_basecase( mpi_ptr_t prodp, mpi_ptr_t up,
+                                 mpi_ptr_t vp, mpi_size_t size)
+{
+    mpi_size_t i;
+    mpi_limb_t cy;
+    mpi_limb_t v_limb;
+
+    /* Multiply by the first limb in V separately, as the result can be
+     * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+    v_limb = vp[0];
+    if( v_limb <= 1 ) {
+        if( v_limb == 1 )
+            MPN_COPY( prodp, up, size );
+        else
+            MPN_ZERO( prodp, size );
+        cy = 0;
+    }
+    else
+        cy = _gcry_mpih_mul_1( prodp, up, size, v_limb );
+
+    prodp[size] = cy;
+    prodp++;
+
+    /* For each iteration in the outer loop, multiply one limb from
+     * U with one limb from V, and add it to PROD.  */
+    for( i = 1; i < size; i++ ) {
+        v_limb = vp[i];
+        if( v_limb <= 1 ) {
+            cy = 0;
+            if( v_limb == 1 )
+               cy = _gcry_mpih_add_n(prodp, prodp, up, size);
+        }
+        else
+            cy = _gcry_mpih_addmul_1(prodp, up, size, v_limb);
+
+        prodp[size] = cy;
+        prodp++;
+    }
+
+    return cy;
+}
+
+
+
+void
+_gcry_mpih_sqr_n( mpi_ptr_t prodp,
+                  mpi_ptr_t up, mpi_size_t size, mpi_ptr_t tspace)
+{
+    if( size & 1 ) {
+        /* The size is odd, and the code below doesn't handle that.
+         * Multiply the least significant (size - 1) limbs with a recursive
+         * call, and handle the most significant limb of S1 and S2
+         * separately.
+         * A slightly faster way to do this would be to make the Karatsuba
+         * code below behave as if the size were even, and let it check for
+         * odd size in the end.  I.e., in essence move this code to the end.
+         * Doing so would save us a recursive call, and potentially make the
+         * stack grow a lot less.
+         */
+        mpi_size_t esize = size - 1;       /* even size */
+        mpi_limb_t cy_limb;
+
+        MPN_SQR_N_RECURSE( prodp, up, esize, tspace );
+        cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, up[esize] );
+        prodp[esize + esize] = cy_limb;
+        cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, size, up[esize] );
+
+        prodp[esize + size] = cy_limb;
+    }
+    else {
+        mpi_size_t hsize = size >> 1;
+        mpi_limb_t cy;
+
+        /* Product H.      ________________  ________________
+         *                |_____U1 x U1____||____U0 x U0_____|
+         * Put result in upper part of PROD and pass low part of TSPACE
+         * as new TSPACE.
+         */
+        MPN_SQR_N_RECURSE(prodp + size, up + hsize, hsize, tspace);
+
+        /* Product M.      ________________
+         *                |_(U1-U0)(U0-U1)_|
+         */
+        if( _gcry_mpih_cmp( up + hsize, up, hsize) >= 0 )
+            _gcry_mpih_sub_n( prodp, up + hsize, up, hsize);
+        else
+            _gcry_mpih_sub_n (prodp, up, up + hsize, hsize);
+
+        /* Read temporary operands from low part of PROD.
+         * Put result in low part of TSPACE using upper part of TSPACE
+         * as new TSPACE.  */
+        MPN_SQR_N_RECURSE(tspace, prodp, hsize, tspace + size);
+
+        /* Add/copy product H  */
+        MPN_COPY(prodp + hsize, prodp + size, hsize);
+        cy = _gcry_mpih_add_n(prodp + size, prodp + size,
+                           prodp + size + hsize, hsize);
+
+        /* Add product M (if NEGFLG M is a negative number).  */
+        cy -= _gcry_mpih_sub_n (prodp + hsize, prodp + hsize, tspace, size);
+
+        /* Product L.      ________________  ________________
+         *                |________________||____U0 x U0_____|
+         * Read temporary operands from low part of PROD.
+         * Put result in low part of TSPACE using upper part of TSPACE
+         * as new TSPACE.  */
+        MPN_SQR_N_RECURSE (tspace, up, hsize, tspace + size);
+
+        /* Add/copy Product L (twice).  */
+        cy += _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace, size);
+        if( cy )
+            _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size,
+                                                            hsize, cy);
+
+        MPN_COPY(prodp, tspace, hsize);
+        cy = _gcry_mpih_add_n (prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+        if( cy )
+            _gcry_mpih_add_1 (prodp + size, prodp + size, size, 1);
+    }
+}
+
+
+static void
+mul_n( mpi_ptr_t prodp, mpi_ptr_t up, mpi_ptr_t vp,
+                        mpi_size_t size, mpi_ptr_t tspace )
+{
+    if( size & 1 ) {
+      /* The size is odd, and the code below doesn't handle that.
+       * Multiply the least significant (size - 1) limbs with a recursive
+       * call, and handle the most significant limb of S1 and S2
+       * separately.
+       * A slightly faster way to do this would be to make the Karatsuba
+       * code below behave as if the size were even, and let it check for
+       * odd size in the end.  I.e., in essence move this code to the end.
+       * Doing so would save us a recursive call, and potentially make the
+       * stack grow a lot less.
+       */
+      mpi_size_t esize = size - 1;       /* even size */
+      mpi_limb_t cy_limb;
+
+      MPN_MUL_N_RECURSE( prodp, up, vp, esize, tspace );
+      cy_limb = _gcry_mpih_addmul_1( prodp + esize, up, esize, vp[esize] );
+      prodp[esize + esize] = cy_limb;
+      cy_limb = _gcry_mpih_addmul_1( prodp + esize, vp, size, up[esize] );
+      prodp[esize + size] = cy_limb;
+    }
+    else {
+        /* Anatolij Alekseevich Karatsuba's divide-and-conquer algorithm.
+         *
+         * Split U in two pieces, U1 and U0, such that
+         * U = U0 + U1*(B**n),
+         * and V in V1 and V0, such that
+         * V = V0 + V1*(B**n).
+         *
+         * UV is then computed recursively using the identity
+         *
+         *        2n   n          n                     n
+         * UV = (B  + B )U V  +  B (U -U )(V -V )  +  (B + 1)U V
+         *                1 1        1  0   0  1              0 0
+         *
+         * Where B = 2**BITS_PER_MP_LIMB.
+         */
+        mpi_size_t hsize = size >> 1;
+        mpi_limb_t cy;
+        int negflg;
+
+        /* Product H.      ________________  ________________
+         *                |_____U1 x V1____||____U0 x V0_____|
+         * Put result in upper part of PROD and pass low part of TSPACE
+         * as new TSPACE.
+         */
+        MPN_MUL_N_RECURSE(prodp + size, up + hsize, vp + hsize, hsize, tspace);
+
+        /* Product M.      ________________
+         *                |_(U1-U0)(V0-V1)_|
+         */
+        if( _gcry_mpih_cmp(up + hsize, up, hsize) >= 0 ) {
+            _gcry_mpih_sub_n(prodp, up + hsize, up, hsize);
+            negflg = 0;
+        }
+        else {
+            _gcry_mpih_sub_n(prodp, up, up + hsize, hsize);
+            negflg = 1;
+        }
+        if( _gcry_mpih_cmp(vp + hsize, vp, hsize) >= 0 ) {
+            _gcry_mpih_sub_n(prodp + hsize, vp + hsize, vp, hsize);
+            negflg ^= 1;
+        }
+        else {
+            _gcry_mpih_sub_n(prodp + hsize, vp, vp + hsize, hsize);
+            /* No change of NEGFLG.  */
+        }
+        /* Read temporary operands from low part of PROD.
+         * Put result in low part of TSPACE using upper part of TSPACE
+         * as new TSPACE.
+         */
+        MPN_MUL_N_RECURSE(tspace, prodp, prodp + hsize, hsize, tspace + size);
+
+        /* Add/copy product H. */
+        MPN_COPY (prodp + hsize, prodp + size, hsize);
+        cy = _gcry_mpih_add_n( prodp + size, prodp + size,
+                            prodp + size + hsize, hsize);
+
+        /* Add product M (if NEGFLG M is a negative number) */
+        if(negflg)
+            cy -= _gcry_mpih_sub_n(prodp + hsize, prodp + hsize, tspace, size);
+        else
+            cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+
+        /* Product L.      ________________  ________________
+         *                |________________||____U0 x V0_____|
+         * Read temporary operands from low part of PROD.
+         * Put result in low part of TSPACE using upper part of TSPACE
+         * as new TSPACE.
+         */
+        MPN_MUL_N_RECURSE(tspace, up, vp, hsize, tspace + size);
+
+        /* Add/copy Product L (twice) */
+
+        cy += _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace, size);
+        if( cy )
+          _gcry_mpih_add_1(prodp + hsize + size, prodp + hsize + size, hsize, cy);
+
+        MPN_COPY(prodp, tspace, hsize);
+        cy = _gcry_mpih_add_n(prodp + hsize, prodp + hsize, tspace + hsize, hsize);
+        if( cy )
+            _gcry_mpih_add_1(prodp + size, prodp + size, size, 1);
+    }
+}
+
+
+void
+_gcry_mpih_mul_karatsuba_case( mpi_ptr_t prodp,
+                                  mpi_ptr_t up, mpi_size_t usize,
+                                  mpi_ptr_t vp, mpi_size_t vsize,
+                                  struct karatsuba_ctx *ctx )
+{
+    mpi_limb_t cy;
+
+    if( !ctx->tspace || ctx->tspace_size < vsize ) {
+        if( ctx->tspace )
+            mpi_free_limb_space( ctx->tspace );
+        ctx->tspace = mpi_alloc_limb_space( 2 * vsize,
+                                       gcry_is_secure( up ) || gcry_is_secure( vp ) );
+        ctx->tspace_size = vsize;
+    }
+
+    MPN_MUL_N_RECURSE( prodp, up, vp, vsize, ctx->tspace );
+
+    prodp += vsize;
+    up += vsize;
+    usize -= vsize;
+    if( usize >= vsize ) {
+        if( !ctx->tp || ctx->tp_size < vsize ) {
+            if( ctx->tp )
+                mpi_free_limb_space( ctx->tp );
+            ctx->tp = mpi_alloc_limb_space( 2 * vsize, gcry_is_secure( up )
+                                                      || gcry_is_secure( vp ) );
+            ctx->tp_size = vsize;
+        }
+
+        do {
+            MPN_MUL_N_RECURSE( ctx->tp, up, vp, vsize, ctx->tspace );
+            cy = _gcry_mpih_add_n( prodp, prodp, ctx->tp, vsize );
+            _gcry_mpih_add_1( prodp + vsize, ctx->tp + vsize, vsize, cy );
+            prodp += vsize;
+            up += vsize;
+            usize -= vsize;
+        } while( usize >= vsize );
+    }
+
+    if( usize ) {
+        if( usize < KARATSUBA_THRESHOLD ) {
+            _gcry_mpih_mul( ctx->tspace, vp, vsize, up, usize );
+        }
+        else {
+            if( !ctx->next ) {
+                ctx->next = gcry_xcalloc( 1, sizeof *ctx );
+            }
+            _gcry_mpih_mul_karatsuba_case( ctx->tspace,
+                                        vp, vsize,
+                                        up, usize,
+                                        ctx->next );
+        }
+
+        cy = _gcry_mpih_add_n( prodp, prodp, ctx->tspace, vsize);
+        _gcry_mpih_add_1( prodp + vsize, ctx->tspace + vsize, usize, cy );
+    }
+}
+
+
+void
+_gcry_mpih_release_karatsuba_ctx( struct karatsuba_ctx *ctx )
+{
+    struct karatsuba_ctx *ctx2;
+
+    if( ctx->tp )
+        mpi_free_limb_space( ctx->tp );
+    if( ctx->tspace )
+        mpi_free_limb_space( ctx->tspace );
+    for( ctx=ctx->next; ctx; ctx = ctx2 ) {
+        ctx2 = ctx->next;
+        if( ctx->tp )
+            mpi_free_limb_space( ctx->tp );
+        if( ctx->tspace )
+            mpi_free_limb_space( ctx->tspace );
+        gcry_free( ctx );
+    }
+}
+
+/* Multiply the natural numbers u (pointed to by UP, with USIZE limbs)
+ * and v (pointed to by VP, with VSIZE limbs), and store the result at
+ * PRODP.  USIZE + VSIZE limbs are always stored, but if the input
+ * operands are normalized.  Return the most significant limb of the
+ * result.
+ *
+ * NOTE: The space pointed to by PRODP is overwritten before finished
+ * with U and V, so overlap is an error.
+ *
+ * Argument constraints:
+ * 1. USIZE >= VSIZE.
+ * 2. PRODP != UP and PRODP != VP, i.e. the destination
+ *    must be distinct from the multiplier and the multiplicand.
+ */
+
+mpi_limb_t
+_gcry_mpih_mul( mpi_ptr_t prodp, mpi_ptr_t up, mpi_size_t usize,
+                   mpi_ptr_t vp, mpi_size_t vsize)
+{
+    mpi_ptr_t prod_endp = prodp + usize + vsize - 1;
+    mpi_limb_t cy;
+    struct karatsuba_ctx ctx;
+
+    if( vsize < KARATSUBA_THRESHOLD ) {
+        mpi_size_t i;
+        mpi_limb_t v_limb;
+
+        if( !vsize )
+            return 0;
+
+        /* Multiply by the first limb in V separately, as the result can be
+         * stored (not added) to PROD.  We also avoid a loop for zeroing.  */
+        v_limb = vp[0];
+        if( v_limb <= 1 ) {
+            if( v_limb == 1 )
+                MPN_COPY( prodp, up, usize );
+            else
+                MPN_ZERO( prodp, usize );
+            cy = 0;
+        }
+        else
+            cy = _gcry_mpih_mul_1( prodp, up, usize, v_limb );
+
+        prodp[usize] = cy;
+        prodp++;
+
+        /* For each iteration in the outer loop, multiply one limb from
+         * U with one limb from V, and add it to PROD.  */
+        for( i = 1; i < vsize; i++ ) {
+            v_limb = vp[i];
+            if( v_limb <= 1 ) {
+                cy = 0;
+                if( v_limb == 1 )
+                   cy = _gcry_mpih_add_n(prodp, prodp, up, usize);
+            }
+            else
+                cy = _gcry_mpih_addmul_1(prodp, up, usize, v_limb);
+
+            prodp[usize] = cy;
+            prodp++;
+        }
+
+        return cy;
+    }
+
+    memset( &ctx, 0, sizeof ctx );
+    _gcry_mpih_mul_karatsuba_case( prodp, up, usize, vp, vsize, &ctx );
+    _gcry_mpih_release_karatsuba_ctx( &ctx );
+    return *prod_endp;
+}
+
+
+
+
+
+
+
+
+/****************
+ * RES = BASE ^ EXP mod MOD
+ */
+void mpi_powm(struct gcry_mpi *res, struct gcry_mpi *base, struct gcry_mpi *exp, struct gcry_mpi *mod)
+{
+    mpi_ptr_t  rp, ep, mp, bp;
+    mpi_size_t esize, msize, bsize, rsize;
+    int        esign, msign, bsign, rsign;
+    int        esec,  msec,  bsec,  rsec;
+    mpi_size_t size;
+    int mod_shift_cnt;
+    int negative_result;
+    mpi_ptr_t mp_marker=NULL, bp_marker=NULL, ep_marker=NULL;
+    mpi_ptr_t xp_marker=NULL;
+    int assign_rp=0;
+    mpi_ptr_t tspace = NULL;
+    mpi_size_t tsize=0;   /* to avoid compiler warning */
+                         /* fixme: we should check that the warning is void*/
+
+    esize = exp->nlimbs;
+    msize = mod->nlimbs;
+    size = 2 * msize;
+    esign = exp->sign;
+    msign = mod->sign;
+
+    esec = mpi_is_secure(exp);
+    msec = mpi_is_secure(mod);
+    bsec = mpi_is_secure(base);
+    rsec = mpi_is_secure(res);
+
+    rp = res->d;
+    ep = exp->d;
+
+    if( !msize )
+       msize = 1 / msize;          /* provoke a signal */
+
+    if( !esize ) {
+       /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
+        * depending on if MOD equals 1.  */
+       rp[0] = 1;
+       res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
+       res->sign = 0;
+       goto leave;
+    }
+
+    /* Normalize MOD (i.e. make its most significant bit set) as required by
+     * mpn_divrem.  This will make the intermediate values in the calculation
+     * slightly larger, but the correct result is obtained after a final
+     * reduction using the original MOD value. */
+    mp = mp_marker = mpi_alloc_limb_space(msize, msec);
+    count_leading_zeros( mod_shift_cnt, mod->d[msize-1] );
+    if( mod_shift_cnt )
+       _gcry_mpih_lshift( mp, mod->d, msize, mod_shift_cnt );
+    else
+       MPN_COPY( mp, mod->d, msize );
+
+    bsize = base->nlimbs;
+    bsign = base->sign;
+    if( bsize > msize ) { /* The base is larger than the module. Reduce it. */
+       /* Allocate (BSIZE + 1) with space for remainder and quotient.
+        * (The quotient is (bsize - msize + 1) limbs.)  */
+       bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
+       MPN_COPY( bp, base->d, bsize );
+       /* We don't care about the quotient, store it above the remainder,
+        * at BP + MSIZE.  */
+       _gcry_mpih_divrem( bp + msize, 0, bp, bsize, mp, msize );
+       bsize = msize;
+       /* Canonicalize the base, since we are going to multiply with it
+        * quite a few times.  */
+       MPN_NORMALIZE( bp, bsize );
+    }
+    else
+       bp = base->d;
+
+    if( !bsize ) {
+       res->nlimbs = 0;
+       res->sign = 0;
+       goto leave;
+    }
+
+    if( res->alloced < size ) {
+       /* We have to allocate more space for RES.  If any of the input
+        * parameters are identical to RES, defer deallocation of the old
+        * space.  */
+       if( rp == ep || rp == mp || rp == bp ) {
+           rp = mpi_alloc_limb_space( size, rsec );
+           assign_rp = 1;
+       }
+       else {
+           mpi_resize( res, size );
+           rp = res->d;
+       }
+    }
+    else { /* Make BASE, EXP and MOD not overlap with RES.  */
+       if( rp == bp ) {
+           /* RES and BASE are identical.  Allocate temp. space for BASE.  */
+           assert( !bp_marker );
+           bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
+           MPN_COPY(bp, rp, bsize);
+       }
+       if( rp == ep ) {
+           /* RES and EXP are identical.  Allocate temp. space for EXP.  */
+           ep = ep_marker = mpi_alloc_limb_space( esize, esec );
+           MPN_COPY(ep, rp, esize);
+       }
+       if( rp == mp ) {
+           /* RES and MOD are identical.  Allocate temporary space for MOD.*/
+           assert( !mp_marker );
+           mp = mp_marker = mpi_alloc_limb_space( msize, msec );
+           MPN_COPY(mp, rp, msize);
+       }
+    }
+
+    MPN_COPY( rp, bp, bsize );
+    rsize = bsize;
+    rsign = bsign;
+
+    {
+       mpi_size_t i;
+       mpi_ptr_t xp = xp_marker = mpi_alloc_limb_space( 2 * (msize + 1), msec );
+       int c;
+       mpi_limb_t e;
+       mpi_limb_t carry_limb;
+       struct karatsuba_ctx karactx;
+
+       memset( &karactx, 0, sizeof karactx );
+       negative_result = (ep[0] & 1) && base->sign;
+
+       i = esize - 1;
+       e = ep[i];
+       count_leading_zeros (c, e);
+       e = (e << c) << 1;     /* shift the exp bits to the left, lose msb */
+       c = BITS_PER_MPI_LIMB - 1 - c;
+
+       /* Main loop.
+        *
+        * Make the result be pointed to alternately by XP and RP.  This
+        * helps us avoid block copying, which would otherwise be necessary
+        * with the overlap restrictions of _gcry_mpih_divmod. With 50% probability
+        * the result after this loop will be in the area originally pointed
+        * by RP (==RES->d), and with 50% probability in the area originally
+        * pointed to by XP.
+        */
+
+       for(;;) {
+           while( c ) {
+               mpi_ptr_t tp;
+               mpi_size_t xsize;
+
+               /*mpih_mul_n(xp, rp, rp, rsize);*/
+               if( rsize < KARATSUBA_THRESHOLD )
+                   _gcry_mpih_sqr_n_basecase( xp, rp, rsize );
+               else {
+                   if( !tspace ) {
+                       tsize = 2 * rsize;
+                       tspace = mpi_alloc_limb_space( tsize, 0 );
+                   }
+                   else if( tsize < (2*rsize) ) {
+                       mpi_free_limb_space( tspace );
+                       tsize = 2 * rsize;
+                       tspace = mpi_alloc_limb_space( tsize, 0 );
+                   }
+                   _gcry_mpih_sqr_n( xp, rp, rsize, tspace );
+               }
+
+               xsize = 2 * rsize;
+               if( xsize > msize ) {
+                   _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+                   xsize = msize;
+               }
+
+               tp = rp; rp = xp; xp = tp;
+               rsize = xsize;
+
+               if( (mpi_limb_signed_t)e < 0 ) {
+                   /*mpih_mul( xp, rp, rsize, bp, bsize );*/
+                   if( bsize < KARATSUBA_THRESHOLD ) {
+                       _gcry_mpih_mul( xp, rp, rsize, bp, bsize );
+                   }
+                   else {
+                       _gcry_mpih_mul_karatsuba_case(
+                                    xp, rp, rsize, bp, bsize, &karactx );
+                   }
+
+                   xsize = rsize + bsize;
+                   if( xsize > msize ) {
+                       _gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
+                       xsize = msize;
+                   }
+
+                   tp = rp; rp = xp; xp = tp;
+                   rsize = xsize;
+               }
+               e <<= 1;
+               c--;
+           }
+
+           i--;
+           if( i < 0 )
+               break;
+           e = ep[i];
+           c = BITS_PER_MPI_LIMB;
+       }
+
+       /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
+        * steps.  Adjust the result by reducing it with the original MOD.
+        *
+        * Also make sure the result is put in RES->d (where it already
+        * might be, see above).
+        */
+       if( mod_shift_cnt ) {
+           carry_limb = _gcry_mpih_lshift( res->d, rp, rsize, mod_shift_cnt);
+           rp = res->d;
+           if( carry_limb ) {
+               rp[rsize] = carry_limb;
+               rsize++;
+           }
+       }
+       else {
+           MPN_COPY( res->d, rp, rsize);
+           rp = res->d;
+       }
+
+       if( rsize >= msize ) {
+           _gcry_mpih_divrem(rp + msize, 0, rp, rsize, mp, msize);
+           rsize = msize;
+       }
+
+       /* Remove any leading zero words from the result.  */
+       if( mod_shift_cnt )
+           _gcry_mpih_rshift( rp, rp, rsize, mod_shift_cnt);
+       MPN_NORMALIZE (rp, rsize);
+
+       _gcry_mpih_release_karatsuba_ctx( &karactx );
+    }
+
+    if( negative_result && rsize ) {
+       if( mod_shift_cnt )
+           _gcry_mpih_rshift( mp, mp, msize, mod_shift_cnt);
+       _gcry_mpih_sub( rp, mp, msize, rp, rsize);
+       rsize = msize;
+       rsign = msign;
+       MPN_NORMALIZE(rp, rsize);
+    }
+    res->nlimbs = rsize;
+    res->sign = rsign;
+
+  leave:
+    if( assign_rp ) mpi_assign_limb_space( res, rp, size );
+    if( mp_marker ) mpi_free_limb_space( mp_marker );
+    if( bp_marker ) mpi_free_limb_space( bp_marker );
+    if( ep_marker ) mpi_free_limb_space( ep_marker );
+    if( xp_marker ) mpi_free_limb_space( xp_marker );
+    if( tspace )    mpi_free_limb_space( tspace );
+}
+
+
+void mpi_sub(struct gcry_mpi *w, struct gcry_mpi *u, struct gcry_mpi *v)
+{
+    if( w == v ) {
+        struct gcry_mpi *vv = mpi_copy(v);
+        vv->sign = !vv->sign;
+        mpi_add( w, u, vv );
+        mpi_free(vv);
+    }
+    else {
+        /* fixme: this is not thread-save (we temp. modify v) */
+        v->sign = !v->sign;
+        mpi_add( w, u, v );
+        v->sign = !v->sign;
+    }
+}
+
+void _gcry_mpi_fdiv_r(struct gcry_mpi *rem, struct gcry_mpi *dividend, struct gcry_mpi *divisor )
+{
+    int divisor_sign = divisor->sign;
+    struct gcry_mpi * temp_divisor = NULL;
+
+    /* We need the original value of the divisor after the remainder has been
+     * preliminary calculated.  We have to copy it to temporary space if it's
+     * the same variable as REM.  */
+    if( rem == divisor ) {
+        temp_divisor = mpi_copy( divisor );
+        divisor = temp_divisor;
+    }
+
+    _gcry_mpi_tdiv_r( rem, dividend, divisor );
+
+    if( ((divisor_sign?1:0) ^ (dividend->sign?1:0)) && rem->nlimbs )
+        mpi_add( rem, rem, divisor);
+
+    if( temp_divisor )
+        mpi_free(temp_divisor);
+}
+
+
+
+
+void mpi_mul(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v)
+{           
+    mpi_size_t usize, vsize, wsize;
+    mpi_ptr_t up, vp, wp;
+    mpi_limb_t cy;
+    int usign, vsign, usecure, vsecure, sign_product;
+    int assign_wp=0;
+    mpi_ptr_t tmp_limb=NULL;
+    
+        
+    if( u->nlimbs < v->nlimbs ) { /* Swap U and V. */
+        usize = v->nlimbs;
+        usign = v->sign;
+        usecure = mpi_is_secure(v);
+        up    = v->d;
+        vsize = u->nlimbs;
+        vsign = u->sign;
+        vsecure = mpi_is_secure(u);
+        vp    = u->d;
+    }
+    else {
+        usize = u->nlimbs;
+        usign = u->sign;
+        usecure = mpi_is_secure(u);
+        up    = u->d;
+        vsize = v->nlimbs;
+        vsign = v->sign;
+        vsecure = mpi_is_secure(v);
+        vp    = v->d;
+    }
+    sign_product = usign ^ vsign;
+    wp = w->d;
+
+    /* Ensure W has space enough to store the result.  */
+    wsize = usize + vsize;
+    if ( !mpi_is_secure (w) && (mpi_is_secure (u) || mpi_is_secure (v)) ) {
+        /* w is not allocated in secure space but u or v is.  To make sure
+         * that no temporray results are stored in w, we temporary use 
+         * a newly allocated limb space for w */
+        wp = mpi_alloc_limb_space( wsize, 1 );
+        assign_wp = 2; /* mark it as 2 so that we can later copy it back to
+                        * mormal memory */
+    }
+    else if( w->alloced < wsize ) {
+        if( wp == up || wp == vp ) {
+            wp = mpi_alloc_limb_space( wsize, mpi_is_secure(w) );
+            assign_wp = 1;
+        }
+        else {
+            mpi_resize(w, wsize );
+            wp = w->d;
+        }
+    }
+    else { /* Make U and V not overlap with W.  */
+        if( wp == up ) {
+            /* W and U are identical.  Allocate temporary space for U.  */
+            up = tmp_limb = mpi_alloc_limb_space( usize, usecure  );
+            /* Is V identical too?  Keep it identical with U.  */
+            if( wp == vp )
+                vp = up;
+            /* Copy to the temporary space.  */
+            MPN_COPY( up, wp, usize );
+        }
+        else if( wp == vp ) {
+            /* W and V are identical.  Allocate temporary space for V.  */
+            vp = tmp_limb = mpi_alloc_limb_space( vsize, vsecure );
+            /* Copy to the temporary space.  */
+            MPN_COPY( vp, wp, vsize );
+        }
+    }
+
+    if( !vsize )
+        wsize = 0;
+    else {
+        cy = _gcry_mpih_mul( wp, up, usize, vp, vsize );
+        wsize -= cy? 0:1;
+    }
+
+    if( assign_wp ) {
+        if (assign_wp == 2) {
+            /* copy the temp wp from secure memory back to normal memory */
+            mpi_ptr_t tmp_wp = mpi_alloc_limb_space (wsize, 0);
+            MPN_COPY (tmp_wp, wp, wsize);
+            mpi_free_limb_space (wp);
+            wp = tmp_wp;
+        }
+        mpi_assign_limb_space( w, wp, wsize );
+    }
+    w->nlimbs = wsize;
+    w->sign = sign_product;
+    if( tmp_limb )
+        mpi_free_limb_space( tmp_limb );
+}
+
+
+void mpi_mulm(struct gcry_mpi *w, struct gcry_mpi *u,struct gcry_mpi *v, struct gcry_mpi *m)
+{
+    mpi_mul(w, u, v);
+    _gcry_mpi_fdiv_r( w, w, m );
+}
+
diff --git a/security/rsa/mpi_generic.c b/security/rsa/mpi_generic.c
new file mode 100644 (file)
index 0000000..fe6e243
--- /dev/null
@@ -0,0 +1,227 @@
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include "foo.h"
+
+
+mpi_limb_t
+_gcry_mpih_sub_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                                  mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+    mpi_limb_t x, y, cy;
+    mpi_size_t j;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+       the loop becomes faster.  */
+    j = -size;
+
+    /* Offset the base pointers to compensate for the negative indices.  */
+    s1_ptr -= j;
+    s2_ptr -= j;
+    res_ptr -= j;
+
+    cy = 0;
+    do {
+        y = s2_ptr[j];
+        x = s1_ptr[j];
+        y += cy;                  /* add previous carry to subtrahend */
+        cy = y < cy;              /* get out carry from that addition */
+        y = x - y;                /* main subtract */
+        cy += y > x;              /* get out carry from the subtract, combine */
+        res_ptr[j] = y;
+    } while( ++j );
+
+    return cy;
+}
+
+
+mpi_limb_t
+_gcry_mpih_add_n( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+               mpi_ptr_t s2_ptr, mpi_size_t size)
+{
+    mpi_limb_t x, y, cy;
+    mpi_size_t j;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+       the loop becomes faster.  */
+    j = -size;
+
+    /* Offset the base pointers to compensate for the negative indices. */
+    s1_ptr -= j;
+    s2_ptr -= j;
+    res_ptr -= j;
+
+    cy = 0;
+    do {
+        y = s2_ptr[j];
+        x = s1_ptr[j];
+        y += cy;                  /* add previous carry to one addend */
+        cy = y < cy;              /* get out carry from that addition */
+        y += x;                   /* add other addend */
+        cy += y < x;              /* get out carry from that add, combine */
+        res_ptr[j] = y;
+    } while( ++j );
+
+    return cy;
+}
+
+/* Shift U (pointed to by UP and USIZE digits long) CNT bits to the left
+ * and store the USIZE least significant digits of the result at WP.
+ * Return the bits shifted out from the most significant digit.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be >= UP.
+ */
+
+mpi_limb_t
+_gcry_mpih_lshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize,
+                                            unsigned int cnt)
+{
+    mpi_limb_t high_limb, low_limb;
+    unsigned sh_1, sh_2;
+    mpi_size_t i;
+    mpi_limb_t retval;
+
+    sh_1 = cnt;
+    wp += 1;
+    sh_2 = BITS_PER_MPI_LIMB - sh_1;
+    i = usize - 1;
+    low_limb = up[i];
+    retval = low_limb >> sh_2;
+    high_limb = low_limb;
+    while( --i >= 0 ) {
+        low_limb = up[i];
+        wp[i] = (high_limb << sh_1) | (low_limb >> sh_2);
+        high_limb = low_limb;
+    }
+    wp[i] = high_limb << sh_1;
+
+    return retval;
+}
+
+
+/* Shift U (pointed to by UP and USIZE limbs long) CNT bits to the right
+ * and store the USIZE least significant limbs of the result at WP.
+ * The bits shifted out to the right are returned.
+ *
+ * Argument constraints:
+ * 1. 0 < CNT < BITS_PER_MP_LIMB
+ * 2. If the result is to be written over the input, WP must be <= UP.
+ */
+mpi_limb_t
+_gcry_mpih_rshift( mpi_ptr_t wp, mpi_ptr_t up, mpi_size_t usize, unsigned cnt)
+{
+    mpi_limb_t high_limb, low_limb;
+    unsigned sh_1, sh_2;
+    mpi_size_t i;
+    mpi_limb_t retval;
+
+    sh_1 = cnt;
+    wp -= 1;
+    sh_2 = BITS_PER_MPI_LIMB - sh_1;
+    high_limb = up[0];
+    retval = high_limb << sh_2;
+    low_limb = high_limb;
+    for( i=1; i < usize; i++) {
+        high_limb = up[i];
+        wp[i] = (low_limb >> sh_1) | (high_limb << sh_2);
+        low_limb = high_limb;
+    }
+    wp[i] = low_limb >> sh_1;
+
+    return retval;
+}
+
+mpi_limb_t
+_gcry_mpih_submul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                  mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+    mpi_limb_t x;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+    res_ptr -= j;
+    s1_ptr -= j;
+
+    cy_limb = 0;
+    do {
+        umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb);
+
+        prod_low += cy_limb;
+        cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+        x = res_ptr[j];
+        prod_low = x - prod_low;
+        cy_limb += prod_low > x?1:0;
+        res_ptr[j] = prod_low;
+    } while( ++j );
+
+    return cy_limb;
+}
+
+
+mpi_limb_t
+_gcry_mpih_mul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr, mpi_size_t s1_size,
+                                                    mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+
+    /* The loop counter and index J goes from -S1_SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+
+    /* Offset the base pointers to compensate for the negative indices.  */
+    s1_ptr -= j;
+    res_ptr -= j;
+
+    cy_limb = 0;
+    do {
+        umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+        prod_low += cy_limb;
+        cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+        res_ptr[j] = prod_low;
+    } while( ++j );
+
+    return cy_limb;
+}
+
+
+mpi_limb_t
+_gcry_mpih_addmul_1( mpi_ptr_t res_ptr, mpi_ptr_t s1_ptr,
+                  mpi_size_t s1_size, mpi_limb_t s2_limb)
+{
+    mpi_limb_t cy_limb;
+    mpi_size_t j;
+    mpi_limb_t prod_high, prod_low;
+    mpi_limb_t x;
+
+    /* The loop counter and index J goes from -SIZE to -1.  This way
+     * the loop becomes faster.  */
+    j = -s1_size;
+    res_ptr -= j;
+    s1_ptr -= j;
+
+    cy_limb = 0;
+    do {
+        umul_ppmm( prod_high, prod_low, s1_ptr[j], s2_limb );
+
+        prod_low += cy_limb;
+        cy_limb = (prod_low < cy_limb?1:0) + prod_high;
+
+        x = res_ptr[j];
+        prod_low = x + prod_low;
+        cy_limb += prod_low < x?1:0;
+        res_ptr[j] = prod_low;
+    } while ( ++j );
+    return cy_limb;
+}
+
+
diff --git a/security/rsa/rsa.c b/security/rsa/rsa.c
new file mode 100644 (file)
index 0000000..014d8e9
--- /dev/null
@@ -0,0 +1,32 @@
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include "gnupg_mpi_mpi.h"
+
+extern char rsa_key_n[];
+extern char rsa_key_e[];
+extern const int rsa_key_n_size;
+extern const int rsa_key_e_size;
+
+static MPI public_key[2];
+
+int rsa_check_sig(char *sig, u8 *sha1)
+{
+       int nread;
+
+       /* initialize our keys */
+       nread = rsa_key_n_size;
+       public_key[0] = mpi_read_from_buffer(&rsa_key_n[0], &nread, 0);
+       nread = rsa_key_e_size;
+       public_key[1] = mpi_read_from_buffer(&rsa_key_e[0], &nread, 0);
+
+
+       return 0;
+}
+EXPORT_SYMBOL(rsa_check_sig);
+
+
diff --git a/security/rsa/rsa.h b/security/rsa/rsa.h
new file mode 100644 (file)
index 0000000..51f0c85
--- /dev/null
@@ -0,0 +1,36 @@
+/* rsa.h
+ *     Copyright (C) 1997,1998 by Werner Koch (dd9jn)
+ *     Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+#ifndef G10_RSA_H
+#define G10_RSA_H
+
+int _gcry_rsa_generate( int algo, unsigned nbits, struct gcry_mpi * *skey, struct gcry_mpi * **retfactors );
+int _gcry_rsa_check_secret_key( int algo, struct gcry_mpi * *skey );
+int _gcry_rsa_encrypt( int algo, struct gcry_mpi * *resarr, struct gcry_mpi * data, struct gcry_mpi * *pkey );
+int _gcry_rsa_decrypt( int algo, struct gcry_mpi * *result, struct gcry_mpi * *data, struct gcry_mpi * *skey );
+int _gcry_rsa_sign( int algo, struct gcry_mpi * *resarr, struct gcry_mpi * data, struct gcry_mpi * *skey );
+int _gcry_rsa_verify( int algo, struct gcry_mpi * hash, struct gcry_mpi * *data, struct gcry_mpi * *pkey,
+                   int (*cmp)(void *, struct gcry_mpi *), void *opaquev );
+unsigned _gcry_rsa_get_nbits( int algo, struct gcry_mpi * *pkey );
+const char *_gcry_rsa_get_info( int algo, int *npkey, int *nskey,
+                                   int *nenc, int *nsig, int *use );
+
+
+#endif /*G10_RSA_H*/
diff --git a/security/rsa/rsa_key.c b/security/rsa/rsa_key.c
new file mode 100644 (file)
index 0000000..1d1c511
--- /dev/null
@@ -0,0 +1,51 @@
+const char rsa_key_n[] = {
+0x04, 0x00, 0xb6, 0x3a, 0x9f, 0x93, 0x4c, 0x3e, 0xec, 0xb9, 0x0a, 0xd7, 0x87, 0x4b, 0x37, 0xd9, 0x2e, 0xcc, 0xfb, 0x2a, 0xad, 0x51, 0x6b, 0x67, 0x61, 0x23, 0x96, 0x5c, 0x3d, 0xdb, 0x86, 0x9b, 0x5a, 0x67, 0xfe, 0x44, 0x3d, 0x38, 0x50, 0xe5, 0x8d, 0x10, 0xe3, 0x8c, 0x1e, 0x4a, 0xfb, 0xd4, 0x37, 0x4a, 0x81, 0x36, 0xc7, 0x12, 0x0f, 0x22, 0x3d, 0x7d, 0x9e, 0x35, 0x99, 0xb1, 0xbd, 0x24, 0x97, 0x98, 0x4c, 0xb5, 0xc4, 0x5c, 0x7b, 0x44, 0xe7, 0x96, 0x4d, 0x43, 0xb3, 0x7b, 0x2d, 0xb7, 0x13, 0x7c, 0xb7, 0x9a, 0x54, 0xe8, 0xc4, 0x3e, 0xc7, 0x72, 0xba, 0x2e, 0xa3, 0xa6, 0x98, 0xc8, 0x25, 0xd6, 0x9e, 0x45, 0x1b, 0xea, 0xc3, 0x73, 0xc6, 0xad, 0x02, 0xb7, 0x6a, 0x3f, 0xb5, 0x83, 0x38, 0x49, 0x2a, 0x29, 0x1c, 0x1b, 0x52, 0x48, 0x7b, 0x49, 0x40, 0xee, 0x95, 0x3f, 0x2d, 0x78, 0x02, 0xed, };
+
+const int rsa_key_n_size = 130;
+const char rsa_key_e[] = {
+0x00, 0x06, 0x29, };
+const int rsa_key_e_size = 3;
+const char rsa_key[] =
+       "\x98\x8b\x04\x3f\x84\x97\x99\x01\x04\x00\xb6\x3a\x9f\x93\x4c\x3e"
+       "\xec\xb9\x0a\xd7\x87\x4b\x37\xd9\x2e\xcc\xfb\x2a\xad\x51\x6b\x67"
+       "\x61\x23\x96\x5c\x3d\xdb\x86\x9b\x5a\x67\xfe\x44\x3d\x38\x50\xe5"
+       "\x8d\x10\xe3\x8c\x1e\x4a\xfb\xd4\x37\x4a\x81\x36\xc7\x12\x0f\x22"
+       "\x3d\x7d\x9e\x35\x99\xb1\xbd\x24\x97\x98\x4c\xb5\xc4\x5c\x7b\x44"
+       "\xe7\x96\x4d\x43\xb3\x7b\x2d\xb7\x13\x7c\xb7\x9a\x54\xe8\xc4\x3e"
+       "\xc7\x72\xba\x2e\xa3\xa6\x98\xc8\x25\xd6\x9e\x45\x1b\xea\xc3\x73"
+       "\xc6\xad\x02\xb7\x6a\x3f\xb5\x83\x38\x49\x2a\x29\x1c\x1b\x52\x48"
+       "\x7b\x49\x40\xee\x95\x3f\x2d\x78\x02\xed\x00\x06\x29\xb4\x07\x74"
+       "\x65\x73\x74\x6b\x65\x79\x88\xb2\x04\x13\x01\x02\x00\x1c\x05\x02"
+       "\x3f\x84\x97\x99\x02\x1b\x03\x04\x0b\x07\x03\x02\x03\x15\x02\x03"
+       "\x03\x16\x02\x01\x02\x1e\x01\x02\x17\x80\x00\x0a\x09\x10\x82\xbd"
+       "\x9b\x6d\xe6\x83\xcc\x5a\x77\x23\x03\xfe\x2e\x67\x4d\x03\x87\x36"
+       "\x05\x56\xdc\x31\x5f\xa7\x0a\x93\x3b\x74\x1e\x87\xfd\x95\x98\x1f"
+       "\x75\x1d\x20\x77\x1a\x07\xa8\x85\x95\x94\x6c\x9e\x5b\xab\x96\xeb"
+       "\x4d\x46\xbe\xea\xcb\xff\xd8\xac\xa7\xdd\x32\x0e\xb5\xfb\x1d\x10"
+       "\xf7\xb6\xf7\x54\x4c\x4e\xfc\x12\x28\x47\x1c\xd9\x1d\x06\x66\xdb"
+       "\x94\xe3\xde\x62\xfd\xe7\xeb\x2d\xc5\xfb\x11\xa1\x3d\xf1\x4e\xf4"
+       "\x46\x42\x8f\x8b\x86\x78\x08\xb7\x9e\xef\xbf\x88\x72\x7e\x73\x73"
+       "\x30\x92\xa6\xa1\x59\x30\x5e\x94\x51\x39\x92\xab\xd5\xce\xf8\x67"
+       "\x8d\x9c\x37\x85\xdc\x6e\x25\x3c\xc1\x81\xb8\x8b\x04\x3f\x84\x97"
+       "\xb8\x01\x04\x00\xe7\x16\x95\xed\x53\xcc\x3d\x03\xc3\x49\xa9\xf2"
+       "\xa6\x4e\x6c\x74\x5f\xc3\xe1\x53\x14\x4e\x29\xa2\x54\x36\xd6\xc9"
+       "\x47\x66\x40\xb9\xd9\x71\xae\x53\x15\xab\x97\x37\x60\x68\x58\x1c"
+       "\x7b\xe9\xc8\xe1\xf1\x81\xa4\xb0\x06\xd8\x71\x9e\x75\x82\x5e\xe6"
+       "\x1b\x7e\xbd\xb6\xa1\x4f\x4c\x9d\xee\xaf\x83\x32\x8a\xa9\x87\xf6"
+       "\x40\x99\x2c\xe5\x80\x42\x10\x29\xe6\xaf\x72\xcc\xd5\x2b\xf1\x83"
+       "\xe9\xa7\xee\xd1\x07\xc7\xd6\x05\xdd\xdd\x37\x84\x06\xf9\x18\x50"
+       "\x0c\xd1\x83\x76\xe0\xe4\x7d\x31\xdf\x8e\x4a\x2b\xed\x56\x4e\x82"
+       "\xb3\x0f\xf1\x0b\x00\x06\x29\x88\x9f\x04\x18\x01\x02\x00\x09\x05"
+       "\x02\x3f\x84\x97\xb8\x02\x1b\x0c\x00\x0a\x09\x10\x82\xbd\x9b\x6d"
+       "\xe6\x83\xcc\x5a\x61\xcb\x03\xff\x54\x94\xa1\x6f\x90\x56\x32\x12"
+       "\xcf\x2a\xd9\xf6\x3f\xec\xbe\xcc\x47\x6c\xac\x09\x42\xef\x14\xec"
+       "\xe6\xf7\xed\x8c\xf6\x98\x20\xd2\xe1\xb9\x47\xc1\x47\xd4\xcb\xd0"
+       "\x1b\xb1\x6b\x38\xf1\x35\xc1\xfd\x36\xc8\x3f\xfc\xbc\x81\xc6\xa5"
+       "\x14\xcc\x41\x2b\x7c\x69\x78\x9a\x7e\x08\x8c\x3b\x5d\x2b\x86\x50"
+       "\xea\x4c\x7b\x4c\x34\x01\x87\xae\xe6\x04\x77\x93\x7d\xe5\x54\xcc"
+       "\x70\x30\x56\x11\x1b\xaf\x8c\xcf\x4b\x99\x2c\x5a\xa8\x70\x32\xfc"
+       "\xd9\xef\x6f\x3e\x76\x4c\xd5\x97\xdf\x1c\x49\x85\xcf\x24\x01\xed"
+       "\x3c\x8a\x56\x03\x8f\xad\x50\x35"
+       ;
+
+const int rsa_key_size = 632;
diff --git a/security/rsa/userspace/Makefile b/security/rsa/userspace/Makefile
new file mode 100644 (file)
index 0000000..7fe8be8
--- /dev/null
@@ -0,0 +1,30 @@
+# Set the following to `true' to make a debuggable build.
+# Leave this set to `false' for production use.
+DEBUG = true
+
+
+ROOT =         mod
+VERSION =      0.1
+INSTALL_DIR =  /usr/local/bin
+RELEASE_NAME = $(ROOT)-$(VERSION)
+
+CC = gcc
+
+INCLUDES = 
+CFLAGS = -g -O -Wall
+
+OBJS = mod.o
+
+all: extract_pkey $(ROOT)
+
+$(ROOT): $(OBJS)
+       $(CC) $(LDFLAGS) -o $(ROOT) $(OBJS) -lbfd -liberty $(LIB_OBJS) $(ARCH_LIB_OBJS)
+
+.c.o:
+       $(CC) $(INCLUDES) $(CFLAGS) -c $< -o $@
+
+extract_pkey: extract_pkey.c
+       gcc -g extract_pkey.c -o extract_pkey
+
+clean:
+       -rm $(OBJS) $(ROOT) extract_pkey
diff --git a/security/rsa/userspace/extract_pkey.c b/security/rsa/userspace/extract_pkey.c
new file mode 100644 (file)
index 0000000..10631da
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Distributed Security Module (DSM)
+ *
+ * Simple extractor of MPIs for e and n in gpg exported keys (or pubrings
+ * with only one key apparently)
+ * Exported keys come from gpg --export.
+ * Output is meant to be copy pasted into kernel code until better mechanism.
+ *     
+ * Copyright (C) 2002-2003 Ericsson, Inc
+ *
+ *      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.
+ * 
+ * Author: David Gordon Aug 2003 
+ * Modifs: Vincent Roy Sept 2003 
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define DSI_ELF_SIG_SIZE 512               /* this is a redefinition */
+#define DSI_PKEY_N_OFFSET        8         /* offset for pkey->n */
+#define DSI_MPI_MAX_SIZE_N 1024 
+/* pkey->e MPI follows pkey->n MPI */
+
+
+void usage();
+
+
+int main(int argc, char **argv)
+{
+       int pkey_file, module_file, nread, count, i;
+       unsigned int num;
+       unsigned int len;
+       unsigned char c;
+       unsigned char *key;
+       unsigned char *temp;
+       int key_offset = 0;
+
+       if (argc <= 1)
+               usage();
+
+       /* Get pkey MPIs, one for 'n' and the other for 'e' */
+       pkey_file = open(argv[1], O_RDONLY);
+       if (!pkey_file) {
+               printf ("Unable to open pkey_file %s %s\n", argv[1], strerror(errno));
+               return -1;
+       }
+//     module_file = open ("/dev/Digsig", O_WRONLY);
+//     if (!module_file) {
+//             printf ("Unable to open module char device %s\n", strerror(errno));
+//             return -1;
+//     }
+  
+       key = (unsigned char *)malloc (DSI_MPI_MAX_SIZE_N+1);
+//     key[key_offset++] = 'n';
+       /*
+        * Format of an MPI:
+        * - 2 bytes: length of MPI in BITS
+        * - MPI
+        */
+
+       lseek(pkey_file, DSI_PKEY_N_OFFSET, SEEK_SET);
+       read(pkey_file, &c, 1);
+       key[key_offset++] = c;
+       len = c << 8;
+
+       read(pkey_file, &c, 1);
+       key[key_offset++] = c;
+       len |= c;
+       len = (len + 7) / 8;   /* round up */
+
+       if (len > DSI_ELF_SIG_SIZE) {
+               printf("\nLength of 'n' MPI is too large %#x\n", len);
+               return -1;
+       }
+
+       for (i = 0; i < len; i++) {
+               read(pkey_file, &c, 1);
+               key[key_offset++] = c;
+               if (key_offset == DSI_MPI_MAX_SIZE_N+2) {
+                       temp = (unsigned char *)malloc (DSI_MPI_MAX_SIZE_N*2+1);
+                       memcpy (temp, key, key_offset-1);
+                       free (key);
+                       key = temp;
+               }
+       }
+       printf("const char rsa_key_n[] = {\n");
+       for (i = 0; i < key_offset; i++  ) {
+               printf("0x%02x, ", key[i] & 0xff);
+       }
+       printf("};\n\n");
+       printf("const int rsa_key_n_size = %d;\n", key_offset);
+//     write (module_file, key, key_offset);
+
+       key_offset = 0;
+//     key[key_offset++] = 'e';
+       read(pkey_file, &c, 1);
+       key[key_offset++] = c;
+       len = c << 8;
+
+       read(pkey_file, &c, 1);
+       key[key_offset++] = c;
+       len |= c;
+       len = (len + 7) / 8;   /* round up */
+
+       if (len > DSI_ELF_SIG_SIZE) {
+               printf("\nLength of 'e' MPI is too large %#x\n", len);
+               return -1;
+       }
+
+       for (i = 0; i < len; i++) {
+               read(pkey_file, &c, 1);
+               key[key_offset++] = c;
+       }
+
+       printf("const char rsa_key_e[] = {\n");
+       for (i = 0; i < key_offset; i++  ) {
+               printf("0x%02x, ", key[i] & 0xff);
+       }
+       printf("};\n");
+       printf("const int rsa_key_e_size = %d;\n", key_offset);
+//     write (module_file, key, key_offset);
+
+       return 0;
+}
+
+
+void usage() 
+{
+       printf("Usage: extract_pkey gpgkey\nYou can export a key with gpg --export\n");
+}
+
+
diff --git a/security/rsa/userspace/mod.c b/security/rsa/userspace/mod.c
new file mode 100644 (file)
index 0000000..1ec8033
--- /dev/null
@@ -0,0 +1,101 @@
+
+
+
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <bfd.h>
+
+/* This should be set in a Makefile somehow */
+#define TARGET "i686-pc-linux-gnu"
+
+static FILE *out;
+
+static void dump_data(bfd *abfd)
+{
+       asection *section;
+       bfd_byte *data = 0;
+       bfd_size_type addr_offset;
+       bfd_size_type stop_offset;
+       unsigned int opb = bfd_octets_per_byte(abfd);
+
+       for (section = abfd->sections; section != NULL; section = section->next) {
+               if (section->flags & SEC_HAS_CONTENTS) {
+                       if (bfd_section_size(abfd, section) == 0)
+                               continue;
+
+                       /* We only care about sections with "text" or "data" in their names */
+                       if ((strstr(section->name, "text") == NULL) &&
+                           (strstr(section->name, "data") == NULL))
+                               continue;
+
+                       printf ("Contents of section %s size %d:\n", section->name, (int)bfd_section_size(abfd, section));
+
+                       data = (bfd_byte *) malloc((size_t) bfd_section_size (abfd, section));
+
+                       bfd_get_section_contents(abfd, section, (PTR) data, 0, bfd_section_size (abfd, section));
+
+                       stop_offset = bfd_section_size(abfd, section) / opb;
+
+                       for (addr_offset = 0; addr_offset < stop_offset; ++addr_offset) {
+                               fprintf (out, "%c", (data[addr_offset]));
+//                             fprintf (out, "%02x", (unsigned)(data[addr_offset]));
+                       }
+                       free (data);
+//                     printf('\n');
+               }
+       }
+}
+
+void set_default_bfd_target(void)
+{
+       const char *target = TARGET;
+
+       if (!bfd_set_default_target(target))
+               fprintf(stderr, "can't set BFD default target to `%s': %s", target, bfd_errmsg (bfd_get_error ()));
+}
+
+int main (int argc, char *argv[])
+{
+       char *in_file;
+       char *out_file;
+       bfd *file;
+       char **matching;
+
+       if (argc != 3) {
+               fprintf(stderr, "%s [infile] [outfile]\n", argv[0]);
+               exit(1);
+       }
+
+       in_file = argv[1];
+       out_file = argv[2];
+
+       bfd_init();
+       set_default_bfd_target();
+
+
+//     file = bfd_openr(in_file, "elf32-i386");
+       file = bfd_openr(in_file, NULL);
+       if (file == NULL) {
+               fprintf(stderr, "error \"%s\" trying to open %s\n", strerror(errno), in_file);
+               exit(1);
+       }
+
+       out = fopen(out_file, "w");
+       if (out == NULL) {
+               fprintf(stderr, "error \"%s\" trying to create %s\n", strerror(errno), out_file);
+               exit(1);
+       }
+
+       if (bfd_check_format_matches(file, bfd_object, &matching)) {
+               dump_data (file);
+       }
+
+       fclose(out);
+
+       return 0;
+}
+
diff --git a/security/rsa/userspace/mod_elf.c b/security/rsa/userspace/mod_elf.c
new file mode 100644 (file)
index 0000000..8757cef
--- /dev/null
@@ -0,0 +1,139 @@
+#include <argp.h>
+#include <assert.h>
+#include <dwarf.h>
+#include <errno.h>
+#include <error.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <inttypes.h>
+#include <langinfo.h>
+//#include <libdwarf.h>
+#include <libebl.h>
+//#include <libintl.h>
+#include <locale.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+size_t section_num;
+
+/* Print the section headers.  */
+static void print_shdr (Elf *elf, GElf_Ehdr *ehdr)
+{
+       size_t cnt;
+       size_t shstrndx;
+
+       /* Get the section header string table index.  */
+       if (elf_getshstrndx(elf, &shstrndx) < 0) {
+               fprintf(stderr, "cannot get section header string table index");
+               return;
+       }
+
+       for (cnt = 0; cnt < section_num; ++cnt) {
+               char flagbuf[20];
+               char *cp;
+               Elf_Scn *scn = elf_getscn(elf, cnt);
+               GElf_Shdr shdr_mem;
+               GElf_Shdr *shdr;
+               char *name;
+               char *buf;
+               int i;
+
+               if (scn == NULL)
+                       error (EXIT_FAILURE, 0, "cannot get section: %s", elf_errmsg (-1));
+
+               /* Get the section header.  */
+               shdr = gelf_getshdr (scn, &shdr_mem);
+               if (shdr == NULL)
+                       error (EXIT_FAILURE, 0, "cannot get section header: %s", elf_errmsg (-1));
+
+               name = elf_strptr(elf, shstrndx, shdr->sh_name);
+               buf = elf_strptr(elf, shstrndx, shdr->sh_addr);
+               printf("%s - ", name);
+               for (i = 0; i < 200; ++i) {
+                       printf("%c", buf[i]);
+               }
+               printf("\n");
+
+               //if (shdr->sh_flags & SHF_ALLOC)
+      printf ("[%2zu] %-20s %-12s %0*" PRIx64 " %0*" PRIx64 " %0*" PRIx64
+             " %2" PRId64 " %-5s %2" PRId32 " %3" PRId32
+             " %2" PRId64 "\n",
+             cnt,
+             elf_strptr(elf, shstrndx, shdr->sh_name)
+             ?: "<corrupt>",
+             //ebl_section_type_name (ebl, shdr->sh_type, buf, sizeof (buf)),
+             "foobar",
+             ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 8 : 16, shdr->sh_addr,
+             ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_offset,
+             ehdr->e_ident[EI_CLASS] == ELFCLASS32 ? 6 : 8, shdr->sh_size,
+             shdr->sh_entsize, flagbuf, shdr->sh_link, shdr->sh_info,
+             shdr->sh_addralign);
+    }
+
+  fputc_unlocked ('\n', stdout);
+}
+
+
+
+
+
+int main (int argc, char *argv[])
+{
+       char *filename = "foo";
+       int fd;
+       Elf *elf;
+
+       elf_version(EV_CURRENT);
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               fprintf(stderr, "error \"%s\" trying to open %s\n", strerror(errno), filename);
+               exit(1);
+       }
+
+       elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
+       if (elf == NULL) {
+               fprintf(stderr, "Can't get elf descriptor for %s\n", filename);
+               goto exit;
+       }
+
+       Elf_Kind kind = elf_kind(elf);
+       switch(kind) {
+               case ELF_K_ELF:
+                               printf("ELF_K_ELF\n");
+                               break;
+               default:
+                               fprintf(stderr, "Not a proper elf file for us to process.\n");
+                               goto end;
+       }
+
+       GElf_Ehdr ehdr_mem;
+       GElf_Ehdr *ehdr = gelf_getehdr(elf, &ehdr_mem);
+       if (ehdr == NULL) {
+               fprintf(stderr, "Can not read elf header.\n");
+               goto end;
+       }
+
+       if (elf_getshnum(elf, &section_num) < 0) {
+               fprintf(stderr, "Can not determine number of sections.\n");
+               goto end;
+       }
+
+       printf("section_num = %d\n", section_num);
+
+       print_shdr(elf, ehdr);
+
+
+end:   
+
+       elf_end(elf);
+exit:
+       close(fd);
+
+       return 0;
+}
+
diff --git a/security/rsa/userspace/sign b/security/rsa/userspace/sign
new file mode 100644 (file)
index 0000000..8d216a9
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+
+if [ $# = "0" ] ; then
+       echo
+#      echo "usage: $0 <module_to_sign> <key_name>"
+       echo "usage: $0 <module_to_sign>"
+       echo
+       exit 1
+fi
+
+module=$1
+#key=$2
+
+# strip out only the sections that we care about
+./mod $module $module.out
+
+# sign the sections
+gpg --no-greeting -sb $module.out
+## sha1 the sections
+#sha1sum $module.out | awk "{print \$1}" > $module.sha1
+#
+## encrypt the sections
+#gpg --no-greeting -e -o - -r $key $module.sha1 > $module.crypt
+
+# add the encrypted data to the module
+#objcopy --add-section module_sig=$module.crypt $module $module.sign
+objcopy --add-section module_sig=$module.out.sig $module $module.sign
+
index fae5886..dfc8aab 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/sound.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/minors.h>
@@ -387,6 +388,24 @@ static int snd_hwdep_dev_free(snd_device_t *device)
        return snd_hwdep_free(hwdep);
 }
 
+static void snd_hwdep_dev_release(snd_hwdep_t *hwdep)
+{
+       return;
+}
+
+#define to_hwdep_dev(hwdep) container_of(hwdep, snd_hwdep_t, class_dev)
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       int minor = -1;
+       snd_hwdep_t *hwdep = to_hwdep_dev(class_dev);
+       minor = SNDRV_MINOR(hwdep->card->number, SNDRV_DEVICE_TYPE_HWDEP + hwdep->device);
+
+       return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
 static int snd_hwdep_dev_register(snd_device_t *device)
 {
        snd_hwdep_t *hwdep = snd_magic_cast(snd_hwdep_t, device->device_data, return -ENXIO);
@@ -399,6 +418,7 @@ static int snd_hwdep_dev_register(snd_device_t *device)
                up(&register_mutex);
                return -EBUSY;
        }
+       hwdep->release = snd_hwdep_dev_release;
        snd_hwdep_devices[idx] = hwdep;
        sprintf(name, "hwC%iD%i", hwdep->card->number, hwdep->device);
        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_HWDEP,
@@ -410,6 +430,8 @@ static int snd_hwdep_dev_register(snd_device_t *device)
                up(&register_mutex);
                return err;
        }
+       sound_add_class_device(name, &hwdep->class_dev, hwdep->dev);
+       class_device_create_file(&hwdep->class_dev, &class_device_attr_dev);
 #ifdef CONFIG_SND_OSSEMUL
        hwdep->ossreg = 0;
        if (hwdep->oss_type >= 0) {
@@ -446,6 +468,7 @@ static int snd_hwdep_dev_unregister(snd_device_t *device)
        if (hwdep->ossreg)
                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 #endif
+       sound_remove_class_device(&hwdep->class_dev);
        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
        snd_hwdep_devices[idx] = NULL;
        up(&register_mutex);
index 10bd538..85e937f 100644 (file)
@@ -22,6 +22,7 @@
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/sound.h>
 #include <linux/time.h>
 #include <sound/core.h>
 #include <sound/minors.h>
@@ -825,6 +826,32 @@ void snd_pcm_release_substream(snd_pcm_substream_t *substream)
        substream->pstr->substream_opened--;
 }
 
+static void snd_pcm_dev_release(snd_pcm_t *pcm)
+{
+       return;
+}
+
+#define to_pcm_dev0(pcm) container_of(pcm, snd_pcm_t, class_dev[0])
+#define to_pcm_dev1(pcm) container_of(pcm, snd_pcm_t, class_dev[1])
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       int minor = -1;
+       snd_pcm_t *pcm;
+       if (!strcmp((class_dev->class_id + strlen(class_dev->class_id) - 1), "p")) {
+               pcm = to_pcm_dev0(class_dev);
+               minor = SNDRV_MINOR(pcm->card->number, SNDRV_DEVICE_TYPE_PCM_PLAYBACK + pcm->device);
+       }
+       else
+       {
+               pcm = to_pcm_dev1(class_dev);
+               minor = SNDRV_MINOR(pcm->card->number, SNDRV_DEVICE_TYPE_PCM_CAPTURE + pcm->device);
+       }
+       return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
 static int snd_pcm_dev_register(snd_device_t *device)
 {
        int idx, cidx, err;
@@ -844,6 +871,7 @@ static int snd_pcm_dev_register(snd_device_t *device)
        snd_pcm_devices[idx] = pcm;
        for (cidx = 0; cidx < 2; cidx++) {
                int devtype = -1;
+               pcm->release = snd_pcm_dev_release;
                if (pcm->streams[cidx].substream == NULL)
                        continue;
                switch (cidx) {
@@ -863,6 +891,8 @@ static int snd_pcm_dev_register(snd_device_t *device)
                        snd_pcm_lock(1);
                        return err;
                }
+               sound_add_class_device(str, &pcm->class_dev[cidx], pcm->dev);
+               class_device_create_file(&pcm->class_dev[cidx], &class_device_attr_dev);
                for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
                        snd_pcm_timer_init(substream);
        }
@@ -912,8 +942,10 @@ static int snd_pcm_dev_unregister(snd_device_t *device)
                        break;
                case SNDRV_PCM_STREAM_CAPTURE:
                        devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
+                       pcm->class_dev[0] = pcm->class_dev[cidx];
                        break;
                }
+               sound_remove_class_device(&pcm->class_dev[0]);
                snd_unregister_device(devtype, pcm->card, pcm->device);
                for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
                        snd_pcm_timer_done(substream);
index ed008eb..d03c17f 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/wait.h>
+#include <linux/sound.h>
 #include <sound/rawmidi.h>
 #include <sound/info.h>
 #include <sound/control.h>
@@ -1449,6 +1450,22 @@ static void snd_rawmidi_dev_seq_free(snd_seq_device_t *device)
 }
 #endif
 
+static void snd_rawmidi_dev_release(snd_rawmidi_t *rmidi)
+{
+       return;
+}
+
+#define to_rawmidi_dev(rmidi) container_of(rmidi, snd_rawmidi_t, class_dev)
+
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       snd_rawmidi_t *rmidi = to_rawmidi_dev(class_dev);
+       int minor = SNDRV_MINOR(rmidi->card->number, SNDRV_DEVICE_TYPE_RAWMIDI + rmidi->device);
+       return sprintf(buf, "%d:%d\n", CONFIG_SND_MAJOR, minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
 static int snd_rawmidi_dev_register(snd_device_t *device)
 {
        int idx, err;
@@ -1465,6 +1482,7 @@ static int snd_rawmidi_dev_register(snd_device_t *device)
                return -EBUSY;
        }
        snd_rawmidi_devices[idx] = rmidi;
+       rmidi->release = snd_rawmidi_dev_release;
        sprintf(name, "midiC%iD%i", rmidi->card->number, rmidi->device);
        if ((err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
                                       rmidi->card, rmidi->device,
@@ -1474,10 +1492,13 @@ static int snd_rawmidi_dev_register(snd_device_t *device)
                up(&register_mutex);
                return err;
        }
+       sound_add_class_device(name, &rmidi->class_dev, rmidi->dev_ptr);
+       class_device_create_file(&rmidi->class_dev, &class_device_attr_dev);
        if (rmidi->ops && rmidi->ops->dev_register &&
            (err = rmidi->ops->dev_register(rmidi)) < 0) {
                snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
                snd_rawmidi_devices[idx] = NULL;
+               sound_remove_class_device(&rmidi->class_dev);
                up(&register_mutex);
                return err;
        }
@@ -1570,6 +1591,7 @@ static int snd_rawmidi_dev_unregister(snd_device_t *device)
 #endif /* CONFIG_SND_OSSEMUL */
        if (rmidi->ops && rmidi->ops->dev_unregister)
                rmidi->ops->dev_unregister(rmidi);
+       sound_remove_class_device(&rmidi->class_dev);
        snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
        up(&register_mutex);
 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
index e0cc44a..0017a66 100644 (file)
@@ -121,11 +121,11 @@ int snd_register_oss_device(int type, snd_card_t * card, int dev, snd_minor_t *
                track2 = SNDRV_MINOR_OSS(cidx, SNDRV_MINOR_OSS_DMMIDI1);
                break;
        }
-       register1 = register_sound_special(reg->f_ops, minor);
+       register1 = register_sound_special(reg->f_ops, minor, NULL);
        if (register1 != minor)
                goto __end;
        if (track2 >= 0) {
-               register2 = register_sound_special(reg->f_ops, track2);
+               register2 = register_sound_special(reg->f_ops, track2, NULL);
                if (register2 != track2)
                        goto __end;
        }
index c9b1557..0a83a3c 100644 (file)
@@ -510,6 +510,7 @@ int snd_opl3_hwdep_new(opl3_t * opl3,
                return err;
        }
        hw->private_data = opl3;
+       hw->dev = opl3->dev;
 #ifdef CONFIG_SND_OSSEMUL
        if (device == 0) {
                hw->oss_type = SNDRV_OSS_DEVICE_TYPE_DMFM;
index f37ee7a..fb405a6 100644 (file)
@@ -857,7 +857,7 @@ static int ad1889_ac97_init(ad1889_dev_t *dev, int id)
 
        dev->ac97_features = eid;
 
-       if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1)) < 0) {
+       if ((ac97->dev_mixer = register_sound_mixer(&ad1889_mixer_fops, -1, &dev->pci->dev)) < 0) {
                printk(KERN_ERR DEVNAME ": cannot register mixer\n");
                goto out_free;
        }
@@ -1008,7 +1008,7 @@ static int __devinit ad1889_probe(struct pci_dev *pcidev, const struct pci_devic
                goto err_free_mem;
 
        /* register /dev/dsp */
-       if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1)) < 0) {
+       if ((dev->dev_audio = register_sound_dsp(&ad1889_fops, -1, &pcidev->dev)) < 0) {
                printk(KERN_ERR DEVNAME ": cannot register /dev/dsp\n");
                goto err_free_irq;
        }
index 8a905b3..599128d 100644 (file)
@@ -3321,7 +3321,7 @@ static int __devinit ali_ac97_init(struct ali_card *card)
 
                if ((eid & 0xc000) == 0)        /* primary codec */
                        total_channels += 2;
-               if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1)) < 0) {
+               if ((codec->dev_mixer = register_sound_mixer(&ali_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
                        printk(KERN_ERR "ali_audio: couldn't register mixer!\n");
                        kfree(codec);
                        break;
@@ -3492,7 +3492,7 @@ static int __devinit ali_probe(struct pci_dev *pci_dev,
        }
 
        /* register /dev/dsp */
-       if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1)) < 0) {
+       if ((card->dev_audio = register_sound_dsp(&ali_audio_fops, -1, &pci_dev->dev)) < 0) {
                int i;
                printk(KERN_ERR"ali_audio: couldn't register DSP device!\n");
                release_region(card->iobase, 256);
index c8866da..8b7f28c 100644 (file)
@@ -971,7 +971,7 @@ static int __devinit btaudio_probe(struct pci_dev *pci_dev,
        /* register devices */
        if (digital) {
                rc = bta->dsp_digital =
-                       register_sound_dsp(&btaudio_digital_dsp_fops,dsp1);
+                       register_sound_dsp(&btaudio_digital_dsp_fops,dsp1, &pci_dev->dev);
                if (rc < 0) {
                        printk(KERN_WARNING
                               "btaudio: can't register digital dsp (rc=%d)\n",rc);
@@ -982,7 +982,7 @@ static int __devinit btaudio_probe(struct pci_dev *pci_dev,
        }
        if (analog) {
                rc = bta->dsp_analog =
-                       register_sound_dsp(&btaudio_analog_dsp_fops,dsp2);
+                       register_sound_dsp(&btaudio_analog_dsp_fops,dsp2, &pci_dev->dev);
                if (rc < 0) {
                        printk(KERN_WARNING
                               "btaudio: can't register analog dsp (rc=%d)\n",rc);
@@ -990,7 +990,7 @@ static int __devinit btaudio_probe(struct pci_dev *pci_dev,
                }
                printk(KERN_INFO "btaudio: registered device dsp%d [analog]\n",
                       bta->dsp_analog >> 4);
-               rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer);
+               rc = bta->mixer_dev = register_sound_mixer(&btaudio_mixer_fops,mixer, &pci_dev->dev);
                if (rc < 0) {
                        printk(KERN_WARNING
                               "btaudio: can't register mixer (rc=%d)\n",rc);
index 599245f..a31cbae 100644 (file)
@@ -3024,16 +3024,16 @@ void initialize_chip(struct pci_dev *pcidev)
                printk(KERN_INFO "cmpci: found %s adapter at io %#06x irq %u\n",
                       devicename, s->iobase, s->irq);
                /* register devices */
-               if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1)) < 0)
+               if ((s->dev_audio = register_sound_dsp(&cm_audio_fops, -1, &pcidev->dev)) < 0)
                        goto err_dev1;
-               if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1)) < 0)
+               if ((s->dev_mixer = register_sound_mixer(&cm_mixer_fops, -1, &pcidev->dev)) < 0)
                        goto err_dev2;
 #ifdef CONFIG_SOUND_CMPCI_MIDI
-               if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1)) < 0)
+               if ((s->dev_midi = register_sound_midi(&cm_midi_fops, -1, &pcidev->dev)) < 0)
                        goto err_dev3;
 #endif
 #ifdef CONFIG_SOUND_CMPCI_FM
-               if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15 /* ?? */)) < 0)
+               if ((s->dev_dmfm = register_sound_special(&cm_dmfm_fops, 15, &pcidev->dev /* ?? */)) < 0)
                        goto err_dev4;
 #endif
                pci_set_master(pcidev); /* enable bus mastering */
index f0f1758..d6c337a 100644 (file)
@@ -4360,19 +4360,19 @@ static int __devinit cs4281_probe(struct pci_dev *pcidev,
                          printk(KERN_ERR "cs4281: irq %u in use\n", s->irq));
                goto err_irq;
        }
-       if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1)) <
+       if ((s->dev_audio = register_sound_dsp(&cs4281_audio_fops, -1, &pcidev->dev)) <
            0) {
                CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
                         "cs4281: probe() register_sound_dsp() failed.\n"));
                goto err_dev1;
        }
-       if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1)) <
+       if ((s->dev_mixer = register_sound_mixer(&cs4281_mixer_fops, -1, &pcidev->dev)) <
            0) {
                CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
                         "cs4281: probe() register_sound_mixer() failed.\n"));
                goto err_dev2;
        }
-       if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1)) < 0) {
+       if ((s->dev_midi = register_sound_midi(&cs4281_midi_fops, -1, &pcidev->dev)) < 0) {
                CS_DBGOUT(CS_INIT | CS_ERROR, 1, printk(KERN_ERR
                         "cs4281: probe() register_sound_midi() failed.\n"));
                goto err_dev3;
index 4b40e66..8d5167b 100644 (file)
@@ -4286,7 +4286,7 @@ static int __init cs_ac97_init(struct cs_card *card)
                
                card->ac97_features = eid;
                        
-               if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1)) < 0) {
+               if ((codec->dev_mixer = register_sound_mixer(&cs_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
                        printk(KERN_ERR "cs46xx: couldn't register mixer!\n");
                        ac97_release_codec(codec);
                        break;
@@ -5504,13 +5504,13 @@ static int __devinit cs46xx_probe(struct pci_dev *pci_dev,
                goto fail2;
        }
        /* register /dev/dsp */
-       if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1)) < 0) {
+       if ((card->dev_audio = register_sound_dsp(&cs461x_fops, -1, &card->pci_dev->dev)) < 0) {
                printk(KERN_ERR "cs46xx: unable to register dsp\n");
                goto fail;
        }
 
         /* register /dev/midi */
-        if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1)) < 0)
+        if((card->dev_midi = register_sound_midi(&cs_midi_fops, -1, &card->pci_dev->dev)) < 0)
                 printk(KERN_ERR "cs46xx: unable to register midi\n");
                 
        card->pm.flags |= CS46XX_PM_IDLE;
index 4658e62..b999aa1 100644 (file)
@@ -123,7 +123,7 @@ void sound_unload_audiodev(int dev)
 
 int sound_alloc_audiodev(void)
 { 
-       int i = register_sound_dsp(&oss_sound_fops, -1);
+       int i = register_sound_dsp(&oss_sound_fops, -1, NULL);
        if(i==-1)
                return i;
        i>>=4;
@@ -134,7 +134,7 @@ int sound_alloc_audiodev(void)
 
 int sound_alloc_mididev(void)
 {
-       int i = register_sound_midi(&oss_sound_fops, -1);
+       int i = register_sound_midi(&oss_sound_fops, -1, NULL);
        if(i==-1)
                return i;
        i>>=4;
@@ -159,7 +159,7 @@ int sound_alloc_synthdev(void)
 
 int sound_alloc_mixerdev(void)
 {
-       int i = register_sound_mixer(&oss_sound_fops, -1);
+       int i = register_sound_mixer(&oss_sound_fops, -1, NULL);
        if(i==-1)
                return -1;
        i>>=4;
index f899b8d..2146cfc 100644 (file)
@@ -231,25 +231,25 @@ static void emu10k1_audio_cleanup(struct emu10k1_card *card)
 
 static int __devinit emu10k1_register_devices(struct emu10k1_card *card)
 {
-       card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1);
+       card->audio_dev = register_sound_dsp(&emu10k1_audio_fops, -1, &card->pci_dev->dev);
        if (card->audio_dev < 0) {
                printk(KERN_ERR "emu10k1: cannot register first audio device!\n");
                goto err_dev;
        }
 
-       card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1);
+       card->audio_dev1 = register_sound_dsp(&emu10k1_audio_fops, -1, &card->pci_dev->dev);
        if (card->audio_dev1 < 0) {
                printk(KERN_ERR "emu10k1: cannot register second audio device!\n");
                goto err_dev1;
        }
 
-       card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1);
+       card->ac97->dev_mixer = register_sound_mixer(&emu10k1_mixer_fops, -1, &card->pci_dev->dev);
        if (card->ac97->dev_mixer < 0) {
                printk(KERN_ERR "emu10k1: cannot register mixer device\n");
                goto err_mixer;
         }
 
-       card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1);
+       card->midi_dev = register_sound_midi(&emu10k1_midi_fops, -1, &card->pci_dev->dev);
        if (card->midi_dev < 0) {
                 printk(KERN_ERR "emu10k1: cannot register midi device!\n");
                goto err_midi;
index 6f00a93..b37c728 100644 (file)
@@ -2624,19 +2624,19 @@ static int __devinit es1370_probe(struct pci_dev *pcidev, const struct pci_devic
               (s->ctrl & CTRL_XCTL0) ? "out" : "in",
                       (s->ctrl & CTRL_XCTL1) ? "1" : "0");
        /* register devices */
-       if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1)) < 0) {
+       if ((s->dev_audio = register_sound_dsp(&es1370_audio_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_audio;
                goto err_dev1;
        }
-       if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1)) < 0) {
+       if ((s->dev_mixer = register_sound_mixer(&es1370_mixer_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_mixer;
                goto err_dev2;
        }
-       if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1)) < 0) {
+       if ((s->dev_dac = register_sound_dsp(&es1370_dac_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_dac;
                goto err_dev3;
        }
-       if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1)) < 0) {
+       if ((s->dev_midi = register_sound_midi(&es1370_midi_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_midi;
                goto err_dev4;
        }
index 043ed79..40d3ba9 100644 (file)
@@ -2855,13 +2855,13 @@ static int __devinit es1371_probe(struct pci_dev *pcidev, const struct pci_devic
        printk(KERN_INFO PFX "found es1371 rev %d at io %#lx irq %u joystick %#x\n",
               s->rev, s->io, s->irq, s->gameport.io);
        /* register devices */
-       if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1)))<0)
+       if ((res=(s->dev_audio = register_sound_dsp(&es1371_audio_fops,-1, &pcidev->dev)))<0)
                goto err_dev1;
-       if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1))) < 0)
+       if ((res=(s->codec->dev_mixer = register_sound_mixer(&es1371_mixer_fops, -1, &pcidev->dev))) < 0)
                goto err_dev2;
-       if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1))) < 0)
+       if ((res=(s->dev_dac = register_sound_dsp(&es1371_dac_fops, -1, &pcidev->dev))) < 0)
                goto err_dev3;
-       if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1)))<0 )
+       if ((res=(s->dev_midi = register_sound_midi(&es1371_midi_fops, -1, &pcidev->dev)))<0 )
                goto err_dev4;
 #ifdef ES1371_DEBUG
        /* initialize the debug proc device */
index 0ab2e4b..1e5c0d8 100644 (file)
@@ -2355,19 +2355,19 @@ static int __devinit solo1_probe(struct pci_dev *pcidev, const struct pci_device
        }
        printk(KERN_INFO "solo1: joystick port at %#x\n", s->gameport.io+1);
        /* register devices */
-       if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1)) < 0) {
+       if ((s->dev_audio = register_sound_dsp(&solo1_audio_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_audio;
                goto err_dev1;
        }
-       if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1)) < 0) {
+       if ((s->dev_mixer = register_sound_mixer(&solo1_mixer_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_mixer;
                goto err_dev2;
        }
-       if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1)) < 0) {
+       if ((s->dev_midi = register_sound_midi(&solo1_midi_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_midi;
                goto err_dev3;
        }
-       if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15 /* ?? */)) < 0) {
+       if ((s->dev_dmfm = register_sound_special(&solo1_dmfm_fops, 15, &pcidev->dev /* ?? */)) < 0) {
                ret = s->dev_dmfm;
                goto err_dev4;
        }
index 06a31b7..1b7c9da 100644 (file)
@@ -1954,7 +1954,7 @@ forte_chip_init (struct forte_chip *chip)
 
        /* Register mixer */
        if ((codec->dev_mixer = 
-            register_sound_mixer (&forte_mixer_fops, -1)) < 0) {
+            register_sound_mixer (&forte_mixer_fops, -1, &chip->pci_dev->dev)) < 0) {
                printk (KERN_ERR PFX "couldn't register mixer!\n");
                ac97_release_codec(codec);
                return -1;
@@ -1963,7 +1963,7 @@ forte_chip_init (struct forte_chip *chip)
        chip->ac97 = codec;
 
        /* Register DSP */
-       if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1) ) < 0) {
+       if ((chip->dsp = register_sound_dsp (&forte_dsp_fops, -1, &chip->pci_dev->dev) ) < 0) {
                printk (KERN_ERR PFX "couldn't register dsp!\n");
                return -1;
        }
index ed4101e..bea49bc 100644 (file)
@@ -2996,7 +2996,7 @@ static int __devinit i810_ac97_init(struct i810_card *card)
                        printk("i810_audio: AC'97 codec %d Unable to map surround DAC's (or DAC's not present), total channels = %d\n", ac97_id, total_channels);
                }
 
-               if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1)) < 0) {
+               if ((codec->dev_mixer = register_sound_mixer(&i810_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
                        printk(KERN_ERR "i810_audio: couldn't register mixer!\n");
                        ac97_release_codec(codec);
                        break;
@@ -3225,7 +3225,7 @@ static int __devinit i810_probe(struct pci_dev *pci_dev, const struct pci_device
        }
 
        /* register /dev/dsp */
-       if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1)) < 0) {
+       if ((card->dev_audio = register_sound_dsp(&i810_audio_fops, -1, &pci_dev->dev)) < 0) {
                int i;
                printk(KERN_ERR "i810_audio: couldn't register DSP device!\n");
                free_irq(card->irq, card);
index 2b17c06..2ef71d4 100644 (file)
@@ -3486,7 +3486,7 @@ maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid)
                if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
                        printk("maestro: BOTCH!\n");
                /* register devices */
-               if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1)) < 0)
+               if ((s->dev_audio = register_sound_dsp(&ess_audio_fops, -1, NULL)) < 0)
                        break;
        }
        
@@ -3543,7 +3543,7 @@ maestro_probe(struct pci_dev *pcidev,const struct pci_device_id *pdid)
                maestro_ac97_init(card);
        }
 
-       if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1)) < 0) {
+       if ((card->dev_mixer = register_sound_mixer(&ess_mixer_fops, -1, NULL)) < 0) {
                printk("maestro: couldn't register mixer!\n");
        } else {
                memcpy(card->mix.mixer_state,mixer_defaults,sizeof(card->mix.mixer_state));
index 137cb11..e6caceb 100644 (file)
@@ -2316,7 +2316,7 @@ static int __devinit m3_codec_install(struct m3_card *card)
         return -1;
     }
 
-    if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1)) < 0) {
+    if ((codec->dev_mixer = register_sound_mixer(&m3_mixer_fops, -1, &card->pcidev->dev)) < 0) {
         printk(KERN_ERR PFX "couldn't register mixer!\n");
         ac97_release_codec(codec);
         return -1;
@@ -2687,7 +2687,7 @@ static int __devinit m3_probe(struct pci_dev *pci_dev, const struct pci_device_i
         if(s->dma_adc.ready || s->dma_dac.ready || s->dma_adc.rawbuf)
             printk(KERN_WARNING PFX "initing a dsp device that is already in use?\n");
         /* register devices */
-        if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) {
+        if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1, &pci_dev->dev)) < 0) {
             break;
         }
 
index aea4a65..a998253 100644 (file)
@@ -1388,7 +1388,7 @@ static int __init attach_multisound(void)
                return err;
        }
 
-       if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1)) < 0) {
+       if ((dev.dsp_minor = register_sound_dsp(&dev_fileops, -1, NULL)) < 0) {
                printk(KERN_ERR LOGNAME ": Unable to register DSP operations\n");
                msnd_unregister(&dev);
                release_region(dev.io, dev.numio);
@@ -1396,7 +1396,7 @@ static int __init attach_multisound(void)
                return dev.dsp_minor;
        }
 
-       if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1)) < 0) {
+       if ((dev.mixer_minor = register_sound_mixer(&dev_fileops, -1, NULL)) < 0) {
                printk(KERN_ERR LOGNAME ": Unable to register mixer operations\n");
                unregister_sound_mixer(dev.mixer_minor);
                msnd_unregister(&dev);
index c684ba9..ae75cd2 100644 (file)
@@ -1007,11 +1007,11 @@ static int __devinit rme96xx_probe(struct pci_dev *pcidev, const struct pci_devi
                goto err_devices;
        }
        for (i=0;i<devices;i++) {
-               if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1)) < 0)
+               if ((s->dspnum[i] = register_sound_dsp(&rme96xx_audio_fops, -1, &pcidev->dev)) < 0)
                        goto err_devices;
        }
 
-       if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1)) < 0)
+       if ((s->mixer = register_sound_mixer(&rme96xx_mixer_fops, -1, &pcidev->dev)) < 0)
                goto err_devices;
 
        pci_set_drvdata(pcidev, s);
index 904eaf1..3842d7f 100644 (file)
@@ -2614,19 +2614,19 @@ static int __devinit sv_probe(struct pci_dev *pcidev, const struct pci_device_id
        printk(KERN_INFO "sv: found adapter at io %#lx irq %u dmaa %#06x dmac %#06x revision %u\n",
               s->ioenh, s->irq, s->iodmaa, s->iodmac, rdindir(s, SV_CIREVISION));
        /* register devices */
-       if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1)) < 0) {
+       if ((s->dev_audio = register_sound_dsp(&sv_audio_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_audio;
                goto err_dev1;
        }
-       if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1)) < 0) {
+       if ((s->dev_mixer = register_sound_mixer(&sv_mixer_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_mixer;
                goto err_dev2;
        }
-       if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1)) < 0) {
+       if ((s->dev_midi = register_sound_midi(&sv_midi_fops, -1, &pcidev->dev)) < 0) {
                ret = s->dev_midi;
                goto err_dev3;
        }
-       if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15 /* ?? */)) < 0) {
+       if ((s->dev_dmfm = register_sound_special(&sv_dmfm_fops, 15, &pcidev->dev/* ?? */)) < 0) {
                ret = s->dev_dmfm;
                goto err_dev4;
        }
index 3b023a9..85ff55d 100644 (file)
@@ -503,10 +503,10 @@ struct file_operations oss_sound_fops = {
 static int create_special_devices(void)
 {
        int seq1,seq2;
-       seq1=register_sound_special(&oss_sound_fops, 1);
+       seq1=register_sound_special(&oss_sound_fops, 1, NULL);
        if(seq1==-1)
                goto bad;
-       seq2=register_sound_special(&oss_sound_fops, 8);
+       seq2=register_sound_special(&oss_sound_fops, 8, NULL);
        if(seq2!=-1)
                return 0;
        unregister_sound_special(1);
index c06dbee..e92e4a9 100644 (file)
@@ -4045,7 +4045,7 @@ static int __devinit trident_ac97_init(struct trident_card *card)
                if (ac97_probe_codec(codec) == 0)
                        break;
 
-               if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1)) < 0) {
+               if ((codec->dev_mixer = register_sound_mixer(&trident_mixer_fops, -1, &card->pci_dev->dev)) < 0) {
                        printk(KERN_ERR "trident: couldn't register mixer!\n");
                        ac97_release_codec(codec);
                        break;
@@ -4261,7 +4261,7 @@ static int __devinit trident_probe(struct pci_dev *pci_dev, const struct pci_dev
                goto out_proc_fs;
        }
        /* register /dev/dsp */
-       if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1)) < 0) {
+       if ((card->dev_audio = register_sound_dsp(&trident_audio_fops, -1, &pci_dev->dev)) < 0) {
                printk(KERN_ERR "trident: couldn't register DSP device!\n");
                goto out_free_irq;
        }
index 68b8e39..d0423a6 100644 (file)
@@ -1770,7 +1770,7 @@ static int __devinit via_ac97_init (struct via_info *card)
        card->ac97->codec_write = via_ac97_write_reg;
        card->ac97->codec_wait = via_ac97_codec_wait;
 
-       card->ac97->dev_mixer = register_sound_mixer (&via_mixer_fops, -1);
+       card->ac97->dev_mixer = register_sound_mixer (&via_mixer_fops, -1, &card->pdev->dev);
        if (card->ac97->dev_mixer < 0) {
                printk (KERN_ERR PFX "unable to register AC97 mixer, aborting\n");
                DPRINTK ("EXIT, returning -EIO\n");
@@ -2090,7 +2090,7 @@ static int __devinit via_dsp_init (struct via_info *card)
 
        via_stop_everything (card);
 
-       card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1);
+       card->dev_dsp = register_sound_dsp (&via_dsp_fops, -1, &card->pdev->dev);
        if (card->dev_dsp < 0) {
                DPRINTK ("EXIT, returning -ENODEV\n");
                return -ENODEV;
index a79d87c..9090bca 100644 (file)
@@ -2798,7 +2798,7 @@ static int __init wavefront_init (int atboot)
 static int __init install_wavefront (void)
 
 {
-       if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1)) < 0) {
+       if ((dev.synth_dev = register_sound_synth (&wavefront_fops, -1, NULL)) < 0) {
                printk (KERN_ERR LOGNAME "cannot register raw synth\n");
                return -1;
        }
index c6183e7..18f063f 100644 (file)
@@ -2487,7 +2487,7 @@ static int ymf_ac97_init(ymfpci_t *unit, int num_ac97)
 
        unit->ac97_features = eid;
 
-       if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1)) < 0) {
+       if ((codec->dev_mixer = register_sound_mixer(&ymf_mixer_fops, -1, &unit->pci->dev)) < 0) {
                printk(KERN_ERR "ymfpci: couldn't register mixer!\n");
                goto out_kfree;
        }
@@ -2584,7 +2584,7 @@ static int __devinit ymf_probe_one(struct pci_dev *pcidev, const struct pci_devi
        }
 
        /* register /dev/dsp */
-       if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1)) < 0) {
+       if ((codec->dev_audio = register_sound_dsp(&ymf_fops, -1, &pcidev->dev)) < 0) {
                printk(KERN_ERR "ymfpci: unable to register dsp\n");
                goto out_free_irq;
        }
index 4a3dfc7..3835bbc 100644 (file)
@@ -1746,6 +1746,7 @@ static int __devinit snd_ali_pcm(ali_t * codec, int device, snd_pcm_t ** rpcm)
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "ALI 5451");
+       pcm->dev = &codec->pci->dev;
        codec->pcm = pcm;
        if (rpcm) *rpcm = pcm;
        return 0;
index 320a141..e29596d 100644 (file)
@@ -510,6 +510,7 @@ static int __devinit snd_als4000_pcm(sb_t *chip, int device)
                return err;
        pcm->private_free = snd_als4000_pcm_free;
        pcm->private_data = chip;
+       pcm->dev = &chip->pci->dev;
        pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX;
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_als4000_playback_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_als4000_capture_ops);
@@ -658,6 +659,8 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
                snd_card_free(card);
                printk(KERN_ERR "als4000: no MPU-401device at 0x%lx ?\n", gcr+0x30);
                return err;
+       } else {
+               chip->rmidi->dev_ptr = &pci->dev; 
        }
 
        if ((err = snd_als4000_pcm(chip, 0)) < 0) {
@@ -674,6 +677,7 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
                printk(KERN_ERR "als4000: no OPL device at 0x%lx-0x%lx ?\n",
                           gcr+0x10, gcr+0x12 );
        } else {
+               opl3->dev = &pci->dev;
                if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                        snd_card_free(card);
                        return err;
index a28d984..3105235 100644 (file)
@@ -1239,6 +1239,7 @@ static int __devinit snd_azf3328_pcm(azf3328_t *chip, int device)
        pcm->private_free = snd_azf3328_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
@@ -1502,6 +1503,8 @@ static int __devinit snd_azf3328_probe(struct pci_dev *pci,
                snd_printk("azf3328: no MPU-401 device at 0x%lx?\n", chip->mpu_port);
                snd_card_free(card);
                return err;
+       } else {
+               chip->rmidi->dev_ptr = &pci->dev;
        }
 
        if ((err = snd_azf3328_pcm(chip, 0)) < 0) {
@@ -1514,6 +1517,7 @@ static int __devinit snd_azf3328_probe(struct pci_dev *pci,
                snd_printk("azf3328: no OPL3 device at 0x%lx-0x%lx?\n",
                           chip->synth_port, chip->synth_port+2 );
        } else {
+               opl3->dev = &pci->dev;
                if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                        snd_card_free(card);
                        return err;
index 40d4862..1880727 100644 (file)
@@ -2063,6 +2063,7 @@ static int __devinit snd_cmipci_pcm_new(cmipci_t *cm, int device)
        pcm->private_free = snd_cmipci_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "C-Media PCI DAC/ADC");
+       pcm->dev = &cm->pci->dev;
        cm->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
@@ -2085,6 +2086,7 @@ static int __devinit snd_cmipci_pcm2_new(cmipci_t *cm, int device)
        pcm->private_free = snd_cmipci_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "C-Media PCI 2nd DAC");
+       pcm->dev = &cm->pci->dev;
        cm->pcm2 = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
@@ -2115,6 +2117,7 @@ static int __devinit snd_cmipci_pcm_spdif_new(cmipci_t *cm, int device)
        pcm->private_free = snd_cmipci_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "C-Media PCI IEC958");
+       pcm->dev = &cm->pci->dev;
        cm->pcm_spdif = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(cm->pci, pcm, 64*1024, 128*1024);
@@ -3051,6 +3054,7 @@ static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci,
                        printk(KERN_ERR "cmipci: no OPL device at 0x%lx, skipping...\n", iosynth);
                        iosynth = 0;
                } else {
+                       cm->opl3->dev = &pci->dev;
                        if ((err = snd_opl3_hwdep_new(cm->opl3, 0, 1, &cm->opl3hwdep)) < 0) {
                                printk(KERN_ERR "cmipci: cannot create OPL3 hwdep\n");
                                return err;
@@ -3093,6 +3097,8 @@ static int __devinit snd_cmipci_create(snd_card_t *card, struct pci_dev *pci,
                                               iomidi, 0,
                                               cm->irq, 0, &cm->rmidi)) < 0) {
                        printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
+               } else {
+                       cm->rmidi->dev_ptr = &pci->dev;
                }
        }
 
index facc28d..2269416 100644 (file)
@@ -1036,6 +1036,7 @@ static int __devinit snd_cs4281_pcm(cs4281_t * chip, int device, snd_pcm_t ** rp
        pcm->private_free = snd_cs4281_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS4281");
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 512*1024);
@@ -1865,6 +1866,7 @@ static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t
        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
        rmidi->private_data = chip;
        chip->rmidi = rmidi;
+       chip->rmidi->dev_ptr = &chip->pci->dev;
        if (rrawmidi)
                *rrawmidi = rmidi;
        return 0;
@@ -1989,6 +1991,7 @@ static int __devinit snd_cs4281_probe(struct pci_dev *pci,
                snd_card_free(card);
                return err;
        }
+       opl3->dev = &pci->dev;
        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                snd_card_free(card);
                return err;
index db0a73f..7b5c399 100644 (file)
@@ -1701,6 +1701,7 @@ int __devinit snd_cs46xx_pcm(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS46xx");
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1732,6 +1733,7 @@ int __devinit snd_cs46xx_pcm_rear(cs46xx_t *chip, int device, snd_pcm_t ** rpcm)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS46xx - Rear");
+       pcm->dev = &chip->pci->dev;
        chip->pcm_rear = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1761,6 +1763,7 @@ int __devinit snd_cs46xx_pcm_center_lfe(cs46xx_t *chip, int device, snd_pcm_t **
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS46xx - Center LFE");
+       pcm->dev = &chip->pci->dev;
        chip->pcm_center_lfe = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1790,6 +1793,7 @@ int __devinit snd_cs46xx_pcm_iec958(cs46xx_t *chip, int device, snd_pcm_t ** rpc
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS46xx - IEC958");
+       pcm->dev = &chip->pci->dev;
        chip->pcm_rear = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -2732,6 +2736,7 @@ int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmi
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_cs46xx_midi_input);
        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
        rmidi->private_data = chip;
+       rmidi->dev_ptr = &chip->pci->dev;
        chip->rmidi = rmidi;
        if (rrawmidi)
                *rrawmidi = NULL;
index d66b6ec..5261486 100644 (file)
@@ -758,6 +758,7 @@ int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
 
        pcm->info_flags = 0;
        strcpy(pcm->name, "EMU10K1 FX8010");
+       pcm->dev = &emu->pci->dev;
        emu->pcm_fx8010 = pcm;
        
        snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 0);
@@ -2424,6 +2425,7 @@ int __devinit snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t **
        hw->ops.ioctl = snd_emu10k1_fx8010_ioctl;
        hw->ops.release = snd_emu10k1_fx8010_release;
        hw->private_data = emu;
+       hw->dev = &emu->pci->dev;
        if (rhwdep)
                *rhwdep = hw;
        return 0;
index 5c4cfc8..ce5154f 100644 (file)
@@ -325,6 +325,7 @@ static int __devinit emu10k1_midi_init(emu10k1_t *emu, emu10k1_midi_t *midi, int
                             SNDRV_RAWMIDI_INFO_DUPLEX;
        rmidi->private_data = midi;
        rmidi->private_free = snd_emu10k1_midi_free;
+       rmidi->dev_ptr = &emu->pci->dev;
        midi->rmidi = rmidi;
        return 0;
 }
index c92c58a..a58f8a4 100644 (file)
@@ -995,6 +995,7 @@ int __devinit snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm)
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "EMU10K1");
+       pcm->dev = &emu->pci->dev;
        emu->pcm = pcm;
 
        for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next)
@@ -1046,6 +1047,7 @@ int __devinit snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm
 
        pcm->info_flags = 0;
        strcpy(pcm->name, "EMU10K1 MIC");
+       pcm->dev = &emu->pci->dev;
        emu->pcm_mic = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(emu->pci, pcm, 64*1024, 64*1024);
@@ -1149,6 +1151,7 @@ int __devinit snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm
 
        pcm->info_flags = 0;
        strcpy(pcm->name, "EMU10K1 EFX");
+       pcm->dev = &emu->pci->dev;
        emu->pcm_efx = pcm;
        if (rpcm)
                *rpcm = pcm;
index 1220a2d..3e61b08 100644 (file)
@@ -1221,6 +1221,7 @@ static int __devinit snd_ensoniq_pcm(ensoniq_t * ensoniq, int device, snd_pcm_t
 
        pcm->private_data = ensoniq;
        pcm->private_free = snd_ensoniq_pcm_free;
+       pcm->dev = &ensoniq->pci->dev;
        pcm->info_flags = 0;
 #ifdef CHIP1370
        strcpy(pcm->name, "ES1370 DAC2/ADC");
@@ -1265,6 +1266,7 @@ static int __devinit snd_ensoniq_pcm2(ensoniq_t * ensoniq, int device, snd_pcm_t
 #endif
        pcm->private_data = ensoniq;
        pcm->private_free = snd_ensoniq_pcm_free2;
+       pcm->dev = &ensoniq->pci->dev;
        pcm->info_flags = 0;
 #ifdef CHIP1370
        strcpy(pcm->name, "ES1370 DAC1");
@@ -2237,6 +2239,7 @@ static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmi
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_ensoniq_midi_input);
        rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT | SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
        rmidi->private_data = ensoniq;
+       rmidi->dev_ptr = &ensoniq->pci->dev;
        ensoniq->rmidi = rmidi;
        if (rrawmidi)
                *rrawmidi = rmidi;
index 5811228..23da346 100644 (file)
@@ -1017,6 +1017,7 @@ static int __devinit snd_es1938_new_pcm(es1938_t *chip, int device, snd_pcm_t **
        pcm->private_free = snd_es1938_free_pcm;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ESS Solo-1");
+       pcm->dev = &chip->pci->dev;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
 
@@ -1652,6 +1653,7 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
                        snd_card_free(card);
                        return err;
                }
+               opl3->dev = &pci->dev;
                if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                        snd_card_free(card);
                        return err;
@@ -1660,6 +1662,8 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
        if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
                                chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
                printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
+       } else {
+               chip->rmidi->dev_ptr = &pci->dev;
        } /*else
            snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0x40);*/
 
index e4155a7..5717b3e 100644 (file)
@@ -1895,6 +1895,7 @@ snd_es1968_pcm(es1968_t *chip, int device)
 
        strcpy(pcm->name, "ESS Maestro");
 
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        return 0;
@@ -2755,6 +2756,7 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
                        printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
                }
        }
+       chip->rmidi->dev_ptr = &pci->dev;
 
        /* card switches */
        for (i = 0; i < num_controls(snd_es1968_control_switches); i++) {
index 45b2131..f23fb7f 100644 (file)
@@ -663,6 +663,7 @@ static int __devinit snd_fm801_pcm(fm801_t *chip, int device, snd_pcm_t ** rpcm)
        pcm->private_free = snd_fm801_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "FM801");
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, chip->multichannel ? 128*1024 : 64*1024, 128*1024);
@@ -1080,6 +1081,8 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
                                       chip->irq, 0, &chip->rmidi)) < 0) {
                snd_card_free(card);
                return err;
+       } else {
+               chip->rmidi->dev_ptr = &pci->dev;
        }
        if ((err = snd_opl3_create(card, FM801_REG(chip, OPL3_BANK0),
                                   FM801_REG(chip, OPL3_BANK1),
@@ -1087,6 +1090,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
                snd_card_free(card);
                return err;
        }
+       opl3->dev = &pci->dev;
        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                snd_card_free(card);
                return err;
index 71c69d6..ecb0a01 100644 (file)
@@ -884,6 +884,7 @@ static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** r
        pcm->private_free = snd_ice1712_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ICE1712 consumer");
+       pcm->dev = &ice->pci->dev;
        ice->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
@@ -920,6 +921,7 @@ static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t *
        pcm->private_free = snd_ice1712_pcm_free_ds;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ICE1712 consumer (DS)");
+       pcm->dev = &ice->pci->dev;
        ice->pcm_ds = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
@@ -1265,6 +1267,7 @@ static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_
        pcm->private_free = snd_ice1712_pcm_profi_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ICE1712 multi");
+       pcm->dev = &ice->pci->dev;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
 
@@ -2541,16 +2544,21 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
                                               &ice->rmidi[0])) < 0) {
                        snd_card_free(card);
                        return err;
+               } else {
+                       ice->rmidi[0]->dev_ptr = &pci->dev;
                }
 
-               if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401)
+               if (ice->eeprom.data[ICE_EEP1_CODEC] & ICE1712_CFG_2xMPU401) {
                        if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
                                                       ICEREG(ice, MPU2_CTRL), 1,
                                                       ice->irq, 0,
                                                       &ice->rmidi[1])) < 0) {
                                snd_card_free(card);
                                return err;
+                       } else {
+                               ice->rmidi[1]->dev_ptr = &pci->dev;
                        }
+               }
        }
 
        sprintf(card->longname, "%s at 0x%lx, irq %i",
index efc03aa..f44d6f2 100644 (file)
@@ -719,6 +719,7 @@ static int __devinit snd_vt1724_pcm_profi(ice1712_t * ice, int device)
        pcm->private_data = ice;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ICE1724");
+       pcm->dev = &ice->pci->dev;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
 
@@ -890,6 +891,7 @@ static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device)
        pcm->private_data = ice;
        pcm->info_flags = 0;
        strcpy(pcm->name, "ICE1724 IEC958");
+       pcm->dev = &ice->pci->dev;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
 
@@ -1955,6 +1957,8 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
                                                       &ice->rmidi[0])) < 0) {
                                snd_card_free(card);
                                return err;
+                       } else {
+                               ice->rmidi[0]->dev_ptr = &pci->dev;
                        }
                }
        }
index e0226de..516efa2 100644 (file)
@@ -1380,6 +1380,7 @@ static int __devinit snd_intel8x0_pcm1(intel8x0_t *chip, int device, struct ich_
 
        pcm->private_data = chip;
        pcm->info_flags = 0;
+       pcm->dev = &chip->pci->dev;
        if (rec->suffix)
                sprintf(pcm->name, "%s - %s", chip->card->shortname, rec->suffix);
        else
@@ -2612,6 +2613,8 @@ static int __devinit snd_intel8x0_probe(struct pci_dev *pci,
                                               -1, 0, &chip->rmidi)) < 0) {
                        printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", mpu_port[dev]);
                        mpu_port[dev] = 0;
+               } else {
+                       chip->rmidi->dev_ptr = &pci->dev;
                }
        } else
                mpu_port[dev] = 0;
index cfebb51..fe1c71d 100644 (file)
@@ -2438,6 +2438,7 @@ static int __devinit snd_korg1212_create(snd_card_t * card, struct pci_dev *pci,
        korg1212->pcm->private_data = korg1212;
         korg1212->pcm->private_free = snd_korg1212_free_pcm;
         strcpy(korg1212->pcm->name, "korg1212");
+       korg1212->pcm->dev = &pci->dev;
 
         snd_pcm_set_ops(korg1212->pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_korg1212_playback_ops);
         
index 54adb86..791c5d0 100644 (file)
@@ -1814,6 +1814,7 @@ snd_m3_pcm(m3_t * chip, int device)
        pcm->private_data = chip;
        pcm->info_flags = 0;
        strcpy(pcm->name, chip->card->driver);
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
        
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 64*1024);
index 4d6b72a..a58dac8 100644 (file)
@@ -915,6 +915,7 @@ snd_nm256_pcm(nm256_t *chip, int device)
 
        pcm->private_data = chip;
        pcm->info_flags = 0;
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        return 0;
index ee6feb9..98a433f 100644 (file)
@@ -1370,6 +1370,7 @@ static int __devinit snd_rme32_create(rme32_t * rme32)
        rme32->spdif_pcm->private_data = rme32;
        rme32->spdif_pcm->private_free = snd_rme32_free_spdif_pcm;
        strcpy(rme32->spdif_pcm->name, "Digi32 IEC958");
+       rme32->spdif_pcm->dev = &rme32->pci->dev;
        snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK,
                        &snd_rme32_playback_spdif_ops);
        snd_pcm_set_ops(rme32->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE,
@@ -1397,6 +1398,7 @@ static int __devinit snd_rme32_create(rme32_t * rme32)
                rme32->adat_pcm->private_data = rme32;
                rme32->adat_pcm->private_free = snd_rme32_free_adat_pcm;
                strcpy(rme32->adat_pcm->name, "Digi32 ADAT");
+               rme32->adat_pcm->dev = &rme32->pci->dev;
                snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, 
                                &snd_rme32_playback_adat_ops);
                snd_pcm_set_ops(rme32->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, 
index 0d006c9..9f85c56 100644 (file)
@@ -1713,6 +1713,7 @@ snd_rme96_create(rme96_t *rme96)
        rme96->spdif_pcm->private_data = rme96;
        rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
        strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
+       rme96->spdif_pcm->dev = &rme96->pci->dev;
        snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
        snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
 
@@ -1733,6 +1734,7 @@ snd_rme96_create(rme96_t *rme96)
                rme96->adat_pcm->private_data = rme96;
                rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
                strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
+               rme96->adat_pcm->dev = &rme96->pci->dev;
                snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
                snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
                
index de4cca6..f1a6edb 100644 (file)
@@ -1329,6 +1329,7 @@ static int __devinit snd_hdsp_create_midi (snd_card_t *card, hdsp_t *hdsp, int i
 
        sprintf (hdsp->midi[id].rmidi->name, "%s MIDI %d", card->id, id+1);
        hdsp->midi[id].rmidi->private_data = &hdsp->midi[id];
+       hdsp->midi[id].rmidi->dev_ptr = &hdsp->pci->dev;
 
        snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_hdsp_midi_output);
        snd_rawmidi_set_ops (hdsp->midi[id].rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_hdsp_midi_input);
@@ -3963,6 +3964,7 @@ static int __devinit snd_hdsp_create_hwdep(snd_card_t *card,
        hdsp->hwdep = hw;
        hw->private_data = hdsp;
        strcpy(hw->name, "HDSP hwdep interface");
+       hw->dev = &hdsp->pci->dev;
 
        hw->ops.open = snd_hdsp_hwdep_dummy_op;
        hw->ops.ioctl = snd_hdsp_hwdep_ioctl;
@@ -3983,6 +3985,7 @@ static int __devinit snd_hdsp_create_pcm(snd_card_t *card,
        hdsp->pcm = pcm;
        pcm->private_data = hdsp;
        strcpy(pcm->name, hdsp->card_name);
+       pcm->dev = &hdsp->pci->dev;
 
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_hdsp_playback_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_hdsp_capture_ops);
index 0cbd3ef..f73b4c7 100644 (file)
@@ -2528,6 +2528,7 @@ static int __devinit snd_rme9652_create_pcm(snd_card_t *card,
 
        rme9652->pcm = pcm;
        pcm->private_data = rme9652;
+       pcm->dev = &rme9652->pci->dev;
        strcpy(pcm->name, rme9652->card_name);
 
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme9652_playback_ops);
index 5b43f8e..c779019 100644 (file)
@@ -884,6 +884,7 @@ static int __devinit snd_sonicvibes_pcm(sonicvibes_t * sonic, int device, snd_pc
        pcm->private_free = snd_sonicvibes_pcm_free;
        pcm->info_flags = 0;
        strcpy(pcm->name, "S3 SonicVibes");
+       pcm->dev = &sonic->pci->dev;
        sonic->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(sonic->pci, pcm, 64*1024, 128*1024);
@@ -1481,6 +1482,8 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
                                       &midi_uart)) < 0) {
                snd_card_free(card);
                return err;
+       } else {
+               midi_uart->dev_ptr = &pci->dev;
        }
        snd_sonicvibes_midi(sonic, midi_uart);
        if ((err = snd_opl3_create(card, sonic->synth_port,
@@ -1489,6 +1492,7 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
                snd_card_free(card);
                return err;
        }
+       opl3->dev = &pci->dev;
        if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                snd_card_free(card);
                return err;
index cf5b5a9..1be3476 100644 (file)
@@ -130,6 +130,8 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
                                       trident->irq, 0, &trident->rmidi)) < 0) {
                snd_card_free(card);
                return err;
+       } else {
+               trident->rmidi->dev_ptr = &pci->dev;
        }
 
 #if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
index 072f4cb..9f94cf1 100644 (file)
@@ -2184,6 +2184,7 @@ int __devinit snd_trident_pcm(trident_t * trident, int device, snd_pcm_t ** rpcm
        pcm->info_flags = 0;
        pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
        strcpy(pcm->name, "Trident 4DWave");
+       pcm->dev = &trident->pci->dev;
        trident->pcm = pcm;
 
        if (trident->tlb.entries) {
@@ -2227,6 +2228,7 @@ int __devinit snd_trident_foldback_pcm(trident_t * trident, int device, snd_pcm_
 
        foldback->private_data = trident;
        foldback->private_free = snd_trident_foldback_pcm_free;
+       foldback->dev = &trident->pci->dev;
        if (trident->tlb.entries)
                snd_pcm_set_ops(foldback, SNDRV_PCM_STREAM_CAPTURE, &snd_trident_nx_foldback_ops);
        else
@@ -2278,6 +2280,7 @@ int __devinit snd_trident_spdif_pcm(trident_t * trident, int device, snd_pcm_t *
 
        spdif->private_data = trident;
        spdif->private_free = snd_trident_spdif_pcm_free;
+       spdif->dev = &trident->pci->dev;
        if (trident->device != TRIDENT_DEVICE_ID_SI7018) {
                snd_pcm_set_ops(spdif, SNDRV_PCM_STREAM_PLAYBACK, &snd_trident_spdif_ops);
        } else {
index 0d35c40..fc1775e 100644 (file)
@@ -1257,6 +1257,7 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
        pcm->private_data = chip;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        /* set up playbacks */
        for (i = 0; i < 4; i++)
                init_viadev(chip, i, 0x10 * i, 0);
@@ -1274,6 +1275,7 @@ static int __devinit snd_via8233_pcm_new(via82xx_t *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
        pcm->private_data = chip;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        /* set up playback */
        init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
        /* set up capture */
@@ -1307,6 +1309,7 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
        pcm->private_data = chip;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        /* set up playback */
        init_viadev(chip, chip->multi_devno, VIA_REG_MULTPLAY_STATUS, 0);
        /* capture */
@@ -1322,6 +1325,7 @@ static int __devinit snd_via8233a_pcm_new(via82xx_t *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
        pcm->private_data = chip;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        /* set up playback */
        init_viadev(chip, chip->playback_devno, 0x30, 0);
 
@@ -1351,6 +1355,7 @@ static int __devinit snd_via686_pcm_new(via82xx_t *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686_capture_ops);
        pcm->private_data = chip;
        strcpy(pcm->name, chip->card->shortname);
+       pcm->dev = &chip->pci->dev;
        init_viadev(chip, 0, VIA_REG_PLAYBACK_STATUS, 0);
        init_viadev(chip, 1, VIA_REG_CAPTURE_STATUS, 1);
 
index 02e7cbc..ff0127e 100644 (file)
@@ -243,6 +243,8 @@ static int __devinit snd_vx222_probe(struct pci_dev *pci,
        if ((err = snd_vx_hwdep_new(&vx->core)) < 0) {
                snd_card_free(card);
                return err;
+       } else {
+               (vx->core).hwdep->dev = &pci->dev;
        }
 
        if ((err = snd_card_register(card)) < 0) {
index 2fd1a5d..4a34998 100644 (file)
@@ -213,6 +213,8 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
                        printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
                        legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
                        pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
+               } else {
+                       chip->rawmidi->dev_ptr = &pci->dev;
                }
        }
        if (chip->fm_res) {
@@ -223,10 +225,13 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
                        printk(KERN_WARNING "ymfpci: cannot initialize FM OPL3 at 0x%lx, skipping...\n", fm_port[dev]);
                        legacy_ctrl &= ~YMFPCI_LEGACY_FMEN;
                        pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
-               } else if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
+               } else {
+                       opl3->dev = &pci->dev;
+                       if ((err = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
                        snd_card_free(card);
                        snd_printk("cannot create opl3 hwdep\n");
                        return err;
+                       }
                }
        }
 #if defined(CONFIG_GAMEPORT) || (defined(MODULE) && defined(CONFIG_GAMEPORT_MODULE))
index 81f17fd..937fe2c 100644 (file)
@@ -1101,6 +1101,7 @@ int __devinit snd_ymfpci_pcm(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "YMFPCI");
+       pcm->dev = &chip->pci->dev;
        chip->pcm = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1145,6 +1146,7 @@ int __devinit snd_ymfpci_pcm2(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "YMFPCI - AC'97");
+       pcm->dev = &chip->pci->dev;
        chip->pcm2 = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1189,6 +1191,7 @@ int __devinit snd_ymfpci_pcm_spdif(ymfpci_t *chip, int device, snd_pcm_t ** rpcm
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "YMFPCI - IEC958");
+       pcm->dev = &chip->pci->dev;
        chip->pcm_spdif = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
@@ -1233,6 +1236,7 @@ int __devinit snd_ymfpci_pcm_4ch(ymfpci_t *chip, int device, snd_pcm_t ** rpcm)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "YMFPCI - Rear PCM");
+       pcm->dev = &chip->pci->dev;
        chip->pcm_4ch = pcm;
 
        snd_pcm_lib_preallocate_pci_pages_for_all(chip->pci, pcm, 64*1024, 256*1024);
index 6b675d2..fedc49d 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/major.h>
 #include <linux/kmod.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/device.h>
 
 #define SOUND_STEP 16
 
@@ -55,8 +56,41 @@ struct sound_unit
        struct file_operations *unit_fops;
        struct sound_unit *next;
        char name[32];
+       void (*release)(struct sound_unit *su);
+       struct class_device class_dev;
 };
 
+#define to_sound_unit(su) container_of(su, struct sound_unit, class_dev)
+
+static void release_sound_unit(struct sound_unit *su)
+{
+       kfree(su);
+}
+
+static void release_sound(struct class_device *class_dev)
+{
+       struct sound_unit *su = to_sound_unit(class_dev);
+       if (!su->release){
+               printk (KERN_INFO "Please create your release method.\n");
+               return;
+       }
+       su->release(su);
+}
+static ssize_t show_dev(struct class_device *class_dev, char *buf)
+{
+       struct sound_unit *su = to_sound_unit(class_dev);
+       return sprintf(buf, "%d:%d\n", SOUND_MAJOR, su->unit_minor);
+}
+
+static CLASS_DEVICE_ATTR(dev, S_IRUGO, show_dev, NULL);
+
+struct class sound_class = {
+       .name = "sound",
+       .release = &release_sound,
+};
+
+EXPORT_SYMBOL(sound_class);
+
 #ifdef CONFIG_SOUND_MSNDCLAS
 extern int msnd_classic_init(void);
 #endif
@@ -149,10 +183,10 @@ static spinlock_t sound_loader_lock = SPIN_LOCK_UNLOCKED;
  *     list. Acquires locks as needed
  */
 
-static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode)
+static int sound_insert_unit(struct sound_unit **list, struct file_operations *fops, int index, int low, int top, const char *name, umode_t mode, struct device *dev)
 {
        struct sound_unit *s = kmalloc(sizeof(*s), GFP_KERNEL);
-       int r;
+       int r, rc;
 
        if (!s)
                return -ENOMEM;
@@ -168,8 +202,14 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f
        else
                sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
 
-       devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor),
-                       S_IFCHR | mode, s->name);
+       devfs_mk_cdev(MKDEV(SOUND_MAJOR, s->unit_minor), S_IFCHR | mode, s->name);      
+       s->release = release_sound_unit;
+       rc = sound_add_class_device(s->name+6, &s->class_dev, dev);
+       if (rc) {
+               r = rc;
+               goto fail;
+       }
+       class_device_create_file(&s->class_dev, &class_device_attr_dev);
        return r;
 
  fail:
@@ -191,11 +231,28 @@ static void sound_remove_unit(struct sound_unit **list, int unit)
        p = __sound_remove_unit(list, unit);
        spin_unlock(&sound_loader_lock);
        if (p) {
+               sound_remove_class_device(&p->class_dev);
                devfs_remove(p->name);
-               kfree(p);
+               /* kfree(p) now handled in release_sound_unit()*/
+               //kfree(p);
        }
 }
 
+int sound_add_class_device(char *name, struct class_device *class_dev, struct device *dev)
+{
+       memset(class_dev, 0x00, sizeof(struct class_device));
+       class_dev->dev = dev;
+       class_dev->class = &sound_class;
+       sprintf(class_dev->class_id, "%s", name);
+       return class_device_register(class_dev);
+}
+EXPORT_SYMBOL(sound_add_class_device);
+
+void sound_remove_class_device(struct class_device *class_dev)
+{
+       class_device_unregister(class_dev);
+}
+EXPORT_SYMBOL(sound_remove_class_device);
 /*
  *     Allocations
  *
@@ -229,7 +286,7 @@ static struct sound_unit *chains[SOUND_STEP];
  *     a negative error code is returned.
  */
  
-int register_sound_special(struct file_operations *fops, int unit)
+int register_sound_special(struct file_operations *fops, int unit, struct device *device)
 {
        const int chain = unit % SOUND_STEP;
        int max_unit = 128 + chain;
@@ -286,8 +343,7 @@ int register_sound_special(struct file_operations *fops, int unit)
                }
                break;
        }
-       return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit,
-                                name, S_IRUSR | S_IWUSR);
+       return sound_insert_unit(&chains[chain], fops, -1, unit, max_unit, name, S_IRUSR | S_IWUSR, device);
 }
  
 EXPORT_SYMBOL(register_sound_special);
@@ -302,10 +358,10 @@ EXPORT_SYMBOL(register_sound_special);
  *     number is returned, on failure a negative error code is returned.
  */
 
-int register_sound_mixer(struct file_operations *fops, int dev)
+int register_sound_mixer(struct file_operations *fops, int dev, struct device *device)
 {
        return sound_insert_unit(&chains[0], fops, dev, 0, 128,
-                                "mixer", S_IRUSR | S_IWUSR);
+                                "mixer", S_IRUSR | S_IWUSR, device);
 }
 
 EXPORT_SYMBOL(register_sound_mixer);
@@ -320,10 +376,10 @@ EXPORT_SYMBOL(register_sound_mixer);
  *     number is returned, on failure a negative error code is returned.
  */
 
-int register_sound_midi(struct file_operations *fops, int dev)
+int register_sound_midi(struct file_operations *fops, int dev, struct device *device)
 {
        return sound_insert_unit(&chains[2], fops, dev, 2, 130,
-                                "midi", S_IRUSR | S_IWUSR);
+                                "midi", S_IRUSR | S_IWUSR, device);
 }
 
 EXPORT_SYMBOL(register_sound_midi);
@@ -346,10 +402,10 @@ EXPORT_SYMBOL(register_sound_midi);
  *     and will always allocate them as a matching pair - eg dsp3/audio3
  */
 
-int register_sound_dsp(struct file_operations *fops, int dev)
+int register_sound_dsp(struct file_operations *fops, int dev, struct device *device)
 {
        return sound_insert_unit(&chains[3], fops, dev, 3, 131,
-                                "dsp", S_IWUSR | S_IRUSR);
+                                "dsp", S_IWUSR | S_IRUSR, device);
 }
 
 EXPORT_SYMBOL(register_sound_dsp);
@@ -365,10 +421,10 @@ EXPORT_SYMBOL(register_sound_dsp);
  */
 
 
-int register_sound_synth(struct file_operations *fops, int dev)
+int register_sound_synth(struct file_operations *fops, int dev, struct device *device)
 {
        return sound_insert_unit(&chains[9], fops, dev, 9, 137,
-                                "synth", S_IRUSR | S_IWUSR);
+                                "synth", S_IRUSR | S_IWUSR, device);
 }
 
 EXPORT_SYMBOL(register_sound_synth);
@@ -552,6 +608,7 @@ static void __exit cleanup_soundcore(void)
 {
        /* We have nothing to really do here - we know the lists must be
           empty */
+       class_unregister(&sound_class);
        unregister_chrdev(SOUND_MAJOR, "sound");
        devfs_remove("sound");
 }
@@ -564,6 +621,7 @@ static int __init init_soundcore(void)
        }
        devfs_mk_dir ("sound");
 
+       class_register(&sound_class);
        return 0;
 }
 
index 2420cab..1706a1b 100644 (file)
 #define USB_SUBCLASS_MIDI_STREAMING    0x03
 #define USB_SUBCLASS_VENDOR_SPEC       0xff
 
-#define USB_DT_CS_DEVICE                0x21
-#define USB_DT_CS_CONFIG                0x22
-#define USB_DT_CS_STRING                0x23
-#define USB_DT_CS_INTERFACE             0x24
-#define USB_DT_CS_ENDPOINT              0x25
-
 #define CS_AUDIO_UNDEFINED             0x20
 #define CS_AUDIO_DEVICE                        0x21
 #define CS_AUDIO_CONFIGURATION         0x22
index 8690659..56a47e4 100644 (file)
@@ -1019,6 +1019,7 @@ static int snd_usbmidi_create_rawmidi(snd_usb_midi_t* umidi,
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &snd_usbmidi_output_ops);
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &snd_usbmidi_input_ops);
 
+       rmidi->dev_ptr = &umidi->iface->dev;
        umidi->rmidi = rmidi;
        return 0;
 }