This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / drivers / ide / ide.c
index 4e2b993..46855a9 100644 (file)
@@ -175,6 +175,7 @@ static int system_bus_speed;        /* holds what we think is VESA/PCI bus speed */
 static int initializing;       /* set while initializing built-in drivers */
 
 DECLARE_MUTEX(ide_cfg_sem);
+EXPORT_SYMBOL_GPL(ide_cfg_sem);
 spinlock_t ide_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 
 #ifdef CONFIG_BLK_DEV_IDEPCI
@@ -188,7 +189,6 @@ int noautodma = 1;
 #endif
 
 EXPORT_SYMBOL(noautodma);
-EXPORT_SYMBOL(ide_bus_type);
 
 /*
  * This is declared extern in ide.h, for access by other IDE modules:
@@ -206,11 +206,13 @@ static void setup_driver_defaults(ide_driver_t *driver);
 static void init_hwif_data(ide_hwif_t *hwif, unsigned int index)
 {
        unsigned int unit;
+       unsigned int key = hwif->key;
 
        /* bulk initialize hwif & drive info with zeros */
        memset(hwif, 0, sizeof(ide_hwif_t));
 
        /* fill in any non-zero initial values */
+       hwif->key       = key + 1;      /* Protected by ide_cfg_sem */
        hwif->index     = index;
        hwif->major     = ide_hwif_to_major[index];
 
@@ -272,6 +274,8 @@ static void init_hwif_default(ide_hwif_t *hwif, unsigned int index)
 #endif
 }
 
+extern void ide_arm_init(void);
+
 /*
  * init_ide_data() sets reasonable default values into all fields
  * of all instances of the hwifs and drives, but only on the first call.
@@ -312,23 +316,112 @@ static void __init init_ide_data (void)
                        ide_init_default_irq(hwif->io_ports[IDE_DATA_OFFSET]);
 #endif
        }
-
-/* OBSOLETE: still needed on arm26 and arm */
-#ifdef CONFIG_ARM
-       /* Add default hw interfaces */
+#ifdef CONFIG_IDE_ARM
        initializing = 1;
-       ide_init_default_hwifs();
+       ide_arm_init();
        initializing = 0;
 #endif
 }
 
 /*
- * ide_system_bus_speed() returns what we think is the system VESA/PCI
- * bus speed (in MHz).  This is used for calculating interface PIO timings.
- * The default is 40 for known PCI systems, 50 otherwise.
- * The "idebus=xx" parameter can be used to override this value.
- * The actual value to be used is computed/displayed the first time through.
+ *     ide_drive_from_key      -       turn key into drive
+ *     @kval: persistent key
+ *
+ *     Convert a key into a drive. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+ide_drive_t *ide_drive_from_key(void *kval)
+{
+       unsigned long key = (unsigned long) kval;
+       int idx = (key >> 8) & 0xFF;
+       int drive = key & 3;
+       ide_hwif_t *hwif = &ide_hwifs[idx];
+       ide_drive_t *ret;
+
+       key >>= 16;
+
+       if(hwif->configured == 0 || hwif->present == 0 || hwif->drives[drive].dead || hwif->key != key)
+               ret = NULL;
+       else
+               ret = &ide_hwifs[idx].drives[drive];
+
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(ide_drive_from_key);
+
+/*
+ *     ide_drive_to_key        -       turn drive to persistent key
+ *     @drive: drive to use
+ *
+ *     Convert drive into a key. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
  */
+
+void *ide_drive_to_key(ide_drive_t *drive)
+{
+       ide_hwif_t *hwif = HWIF(drive);
+       unsigned long val;
+
+       val = (hwif->index << 8) | (hwif->key << 16) | drive->select.b.unit;
+       return (void *)val;
+}
+
+/*
+ *     ide_hwif_from_key       -       turn key into hwif
+ *     @kval: persistent key
+ *
+ *     Convert a key into a drive. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+ide_hwif_t *ide_hwif_from_key(void *kval)
+{
+       unsigned long key = (unsigned long) kval;
+       int idx = (key >> 8) & 0xFF;
+       ide_hwif_t *hwif = &ide_hwifs[idx];
+
+       key >>= 16;
+
+       if(hwif->configured == 0 || hwif->present == 0 || hwif->key != key)
+               return NULL;
+       return hwif;
+}
+
+/*
+ *     ide_hwif_to_key -       turn drive to persistent key
+ *     @hwif: hwif to use
+ *
+ *     Convert drive into a key. Currently the key is packed as
+ *     [keyval] << 16 | hwif << 8 | drive_num. Caller must hold 
+ *     ide_settings_sem for the duration of the returned reference
+ */
+
+void *ide_hwif_to_key(ide_hwif_t *hwif)
+{
+       unsigned long val;
+
+       val = (hwif->index << 8) | (hwif->key << 16);
+       return (void *)val;
+}
+
+/**
+ *     ide_system_bus_speed    -       guess bus speed
+ *
+ *     ide_system_bus_speed() returns what we think is the system VESA/PCI
+ *     bus speed (in MHz). This is used for calculating interface PIO timings.
+ *     The default is 40 for known PCI systems, 50 otherwise.
+ *     The "idebus=xx" parameter can be used to override this value.
+ *     The actual value to be used is computed/displayed the first time
+ *     through. Drivers should only use this as a last resort.
+ *
+ *     Returns a guessed speed in MHz.
+ */
+
 int ide_system_bus_speed (void)
 {
        if (!system_bus_speed) {
@@ -349,10 +442,15 @@ int ide_system_bus_speed (void)
        return system_bus_speed;
 }
 
-/*
- * current_capacity() returns the capacity (in sectors) of a drive
- * according to its current geometry/LBA settings.
+/**
+ *     current_capacity        -       drive capacity
+ *     @drive: drive to query
+ *
+ *     Return the current capacity (in sectors) of a drive according to
+ *     its current geometry/LBA settings. Empty removables are reported
+ *     as size zero.
  */
+
 sector_t current_capacity (ide_drive_t *drive)
 {
        if (!drive->present)
@@ -362,9 +460,17 @@ sector_t current_capacity (ide_drive_t *drive)
 
 EXPORT_SYMBOL(current_capacity);
 
-/*
- * Error reporting, in human readable form (luxurious, but a memory hog).
+/**
+ *     ide_dump_status         -       translate ATA error
+ *     @drive: drive the error occured on
+ *     @msg: information string
+ *     @stat: status byte
+ *
+ *     Error reporting, in human readable form (luxurious, but a memory hog).
+ *     Combines the drive name, message and status byte to provide a
+ *     user understandable explanation of the device error.
  */
+
 u8 ide_dump_status (ide_drive_t *drive, const char *msg, u8 stat)
 {
        ide_hwif_t *hwif = HWIF(drive);
@@ -373,7 +479,6 @@ u8 ide_dump_status (ide_drive_t *drive, const char *msg, u8 stat)
 
        local_irq_set(flags);
        printk(KERN_WARNING "%s: %s: status=0x%02x", drive->name, msg, stat);
-#if FANCY_STATUS_DUMPS
        printk(" { ");
        if (stat & BUSY_STAT) {
                printk("Busy ");
@@ -387,12 +492,10 @@ u8 ide_dump_status (ide_drive_t *drive, const char *msg, u8 stat)
                if (stat & ERR_STAT)    printk("Error ");
        }
        printk("}");
-#endif /* FANCY_STATUS_DUMPS */
        printk("\n");
        if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
                err = hwif->INB(IDE_ERROR_REG);
                printk("%s: %s: error=0x%02x", drive->name, msg, err);
-#if FANCY_STATUS_DUMPS
                if (drive->media == ide_disk) {
                        printk(" { ");
                        if (err & ABRT_ERR)     printk("DriveStatusError ");
@@ -436,9 +539,34 @@ u8 ide_dump_status (ide_drive_t *drive, const char *msg, u8 stat)
                                        printk(", sector=%llu", (unsigned long long)HWGROUP(drive)->rq->sector);
                        }
                }
-#endif /* FANCY_STATUS_DUMPS */
                printk("\n");
        }
+       {
+               struct request *rq;
+               int opcode = 0x100;
+
+               spin_lock(&ide_lock);
+               rq = NULL;
+               if (HWGROUP(drive))
+                       rq = HWGROUP(drive)->rq;
+               spin_unlock(&ide_lock);
+               if (!rq)
+                       goto out;
+               if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) {
+                       char *args = rq->buffer;
+                       if (args)
+                               opcode = args[0];
+               } else if (rq->flags & REQ_DRIVE_TASKFILE) {
+                       ide_task_t *args = rq->special;
+                       if (args) {
+                               task_struct_t *tf = (task_struct_t *) args->tfRegister;
+                               opcode = tf->command;
+                       }
+               }
+
+               printk("ide: failed opcode was %x\n", opcode);
+       }
+out:
        local_irq_restore(flags);
        return err;
 }
@@ -450,37 +578,52 @@ static int ide_open (struct inode * inode, struct file * filp)
        return -ENXIO;
 }
 
+/*
+ *     drives_lock protects the list of drives, drivers lock the
+ *     list of drivers. Currently nobody takes both at once.
+ *     drivers_sem guards the drivers_list for readers that may
+ *     sleep. It must be taken before drivers_lock. Take drivers_sem
+ *     before ide_setting_sem and idecfg_sem before either of the
+ *     others.
+ */
+
 static spinlock_t drives_lock = SPIN_LOCK_UNLOCKED;
+static DECLARE_MUTEX(drivers_sem);
 static spinlock_t drivers_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(drivers);
 
-/* Iterator */
+/* Iterator for the driver list. */
+
 static void *m_start(struct seq_file *m, loff_t *pos)
 {
        struct list_head *p;
        loff_t l = *pos;
-       spin_lock(&drivers_lock);
+       down(&drivers_sem);
        list_for_each(p, &drivers)
                if (!l--)
                        return list_entry(p, ide_driver_t, drivers);
        return NULL;
 }
+
 static void *m_next(struct seq_file *m, void *v, loff_t *pos)
 {
        struct list_head *p = ((ide_driver_t *)v)->drivers.next;
        (*pos)++;
        return p==&drivers ? NULL : list_entry(p, ide_driver_t, drivers);
 }
+
 static void m_stop(struct seq_file *m, void *v)
 {
-       spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 }
+
 static int show_driver(struct seq_file *m, void *v)
 {
        ide_driver_t *driver = v;
        seq_printf(m, "%s version %s\n", driver->name, driver->version);
        return 0;
 }
+
 struct seq_operations ide_drivers_op = {
        .start  = m_start,
        .next   = m_next,
@@ -517,6 +660,7 @@ static struct resource* hwif_request_region(ide_hwif_t *hwif,
  *     MMIO leaves it to the controller driver,
  *     PIO will migrate this way over time.
  */
+
 int ide_hwif_request_regions(ide_hwif_t *hwif)
 {
        unsigned long addr;
@@ -555,8 +699,6 @@ control_region_busy:
        return -EBUSY;
 }
 
-EXPORT_SYMBOL(ide_hwif_request_regions);
-
 /**
  *     ide_hwif_release_regions - free IDE resources
  *
@@ -568,6 +710,7 @@ EXPORT_SYMBOL(ide_hwif_request_regions);
  *     importantly our caller should be doing this so we need to 
  *     restructure this as a helper function for drivers.
  */
+
 void ide_hwif_release_regions(ide_hwif_t *hwif)
 {
        u32 i = 0;
@@ -585,17 +728,122 @@ void ide_hwif_release_regions(ide_hwif_t *hwif)
                        release_region(hwif->io_ports[i], 1);
 }
 
-EXPORT_SYMBOL(ide_hwif_release_regions);
+/**
+ *     ide_hwif_restore        -       restore hwif to template
+ *     @hwif: hwif to update
+ *     @tmp_hwif: template
+ *
+ *     Restore hwif to a previous state by copying most settngs
+ *     from the template.
+ */
+
+static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
+{
+       hwif->hwgroup                   = tmp_hwif->hwgroup;
+
+       hwif->gendev.parent             = tmp_hwif->gendev.parent;
+
+       hwif->proc                      = tmp_hwif->proc;
+
+       hwif->major                     = tmp_hwif->major;
+       hwif->straight8                 = tmp_hwif->straight8;
+       hwif->bus_state                 = tmp_hwif->bus_state;
+
+       hwif->atapi_dma                 = tmp_hwif->atapi_dma;
+       hwif->ultra_mask                = tmp_hwif->ultra_mask;
+       hwif->mwdma_mask                = tmp_hwif->mwdma_mask;
+       hwif->swdma_mask                = tmp_hwif->swdma_mask;
+
+       hwif->chipset                   = tmp_hwif->chipset;
+       hwif->hold                      = tmp_hwif->hold;
+
+#ifdef CONFIG_BLK_DEV_IDEPCI
+       hwif->pci_dev                   = tmp_hwif->pci_dev;
+       hwif->cds                       = tmp_hwif->cds;
+#endif
+
+       hwif->identify                  = tmp_hwif->identify;
+       hwif->tuneproc                  = tmp_hwif->tuneproc;
+       hwif->speedproc                 = tmp_hwif->speedproc;
+       hwif->selectproc                = tmp_hwif->selectproc;
+       hwif->reset_poll                = tmp_hwif->reset_poll;
+       hwif->pre_reset                 = tmp_hwif->pre_reset;
+       hwif->resetproc                 = tmp_hwif->resetproc;
+       hwif->intrproc                  = tmp_hwif->intrproc;
+       hwif->maskproc                  = tmp_hwif->maskproc;
+       hwif->quirkproc                 = tmp_hwif->quirkproc;
+       hwif->busproc                   = tmp_hwif->busproc;
+       hwif->raw_taskfile              = tmp_hwif->raw_taskfile;
+
+       hwif->ata_input_data            = tmp_hwif->ata_input_data;
+       hwif->ata_output_data           = tmp_hwif->ata_output_data;
+       hwif->atapi_input_bytes         = tmp_hwif->atapi_input_bytes;
+       hwif->atapi_output_bytes        = tmp_hwif->atapi_output_bytes;
+
+       hwif->ide_dma_read              = tmp_hwif->ide_dma_read;
+       hwif->ide_dma_write             = tmp_hwif->ide_dma_write;
+       hwif->ide_dma_begin             = tmp_hwif->ide_dma_begin;
+       hwif->ide_dma_end               = tmp_hwif->ide_dma_end;
+       hwif->ide_dma_check             = tmp_hwif->ide_dma_check;
+       hwif->ide_dma_on                = tmp_hwif->ide_dma_on;
+       hwif->ide_dma_off_quietly       = tmp_hwif->ide_dma_off_quietly;
+       hwif->ide_dma_test_irq          = tmp_hwif->ide_dma_test_irq;
+       hwif->ide_dma_host_on           = tmp_hwif->ide_dma_host_on;
+       hwif->ide_dma_host_off          = tmp_hwif->ide_dma_host_off;
+       hwif->ide_dma_verbose           = tmp_hwif->ide_dma_verbose;
+       hwif->ide_dma_lostirq           = tmp_hwif->ide_dma_lostirq;
+       hwif->ide_dma_timeout           = tmp_hwif->ide_dma_timeout;
+
+       hwif->OUTB                      = tmp_hwif->OUTB;
+       hwif->OUTBSYNC                  = tmp_hwif->OUTBSYNC;
+       hwif->OUTW                      = tmp_hwif->OUTW;
+       hwif->OUTL                      = tmp_hwif->OUTL;
+       hwif->OUTSW                     = tmp_hwif->OUTSW;
+       hwif->OUTSL                     = tmp_hwif->OUTSL;
+
+       hwif->INB                       = tmp_hwif->INB;
+       hwif->INW                       = tmp_hwif->INW;
+       hwif->INL                       = tmp_hwif->INL;
+       hwif->INSW                      = tmp_hwif->INSW;
+       hwif->INSL                      = tmp_hwif->INSL;
+
+       hwif->mmio                      = tmp_hwif->mmio;
+       hwif->rqsize                    = tmp_hwif->rqsize;
+       hwif->no_lba48                  = tmp_hwif->no_lba48;
+
+#ifndef CONFIG_BLK_DEV_IDECS
+       hwif->irq                       = tmp_hwif->irq;
+#endif
+
+       hwif->dma_base                  = tmp_hwif->dma_base;
+       hwif->dma_master                = tmp_hwif->dma_master;
+       hwif->dma_command               = tmp_hwif->dma_command;
+       hwif->dma_vendor1               = tmp_hwif->dma_vendor1;
+       hwif->dma_status                = tmp_hwif->dma_status;
+       hwif->dma_vendor3               = tmp_hwif->dma_vendor3;
+       hwif->dma_prdtable              = tmp_hwif->dma_prdtable;
+
+       hwif->dma_extra                 = tmp_hwif->dma_extra;
+       hwif->config_data               = tmp_hwif->config_data;
+       hwif->select_data               = tmp_hwif->select_data;
+       hwif->autodma                   = tmp_hwif->autodma;
+       hwif->udma_four                 = tmp_hwif->udma_four;
+       hwif->no_dsc                    = tmp_hwif->no_dsc;
+
+       hwif->hwif_data                 = tmp_hwif->hwif_data;
+}
 
 /**
- *     ide_unregister          -       free an ide interface
- *     @index: index of interface (will change soon to a pointer)
+ *     __ide_unregister_hwif   -       free an ide interface
+ *     @hwif: interface to unregister
  *
  *     Perform the final unregister of an IDE interface. At the moment
  *     we don't refcount interfaces so this will also get split up.
  *
  *     Locking:
- *     The caller must not hold the IDE locks
+ *     The caller must not hold the IDE locks except for ide_cfg_sem
+ *     which must be held.
+ *
  *     The drive present/vanishing is not yet properly locked
  *     Take care with the callbacks. These have been split to avoid
  *     deadlocking the IDE layer. The shutdown callback is called
@@ -605,27 +853,39 @@ EXPORT_SYMBOL(ide_hwif_release_regions);
  *     isnt yet done btw). After we commit to the final kill we
  *     call the cleanup callback with the ide locks held.
  *
+ *     An interface can be in four states we care about
+ *     - It can be busy (drive or driver thinks its active). No unload
+ *     - It can be unconfigured - which means its already gone
+ *     - It can be configured and present - a full interface
+ *     - It can be configured and not present - pci configured but no drives
+ *                                              so logically absent.
+ *
  *     Unregister restores the hwif structures to the default state.
- *     This is raving bonkers.
  */
-void ide_unregister (unsigned int index)
+
+int __ide_unregister_hwif(ide_hwif_t *hwif)
 {
-       ide_drive_t *drive;
-       ide_hwif_t *hwif, *g;
+       ide_drive_t *drive = NULL; /* keep compiler happy */
+       ide_hwif_t *g;
+       static ide_hwif_t tmp_hwif; /* protected by ide_cfg_sem */
        ide_hwgroup_t *hwgroup;
        int irq_count = 0, unit, i;
-       ide_hwif_t old_hwif;
+       int was_present;
+       int ret = 0;
+       int index = hwif->index;
 
-       if (index >= MAX_HWIFS)
-               BUG();
-               
        BUG_ON(in_interrupt());
        BUG_ON(irqs_disabled());
-       down(&ide_cfg_sem);
+
+       /* Make sure nobody sneaks in via the proc interface */ 
+       down(&ide_setting_sem);
+       
+       /* Now ensure nobody gets in for I/O while we clean up and
+          do the busy check. If busy is set then the device is still
+          open and we must stop */
        spin_lock_irq(&ide_lock);
-       hwif = &ide_hwifs[index];
-       if (!hwif->present)
+       
+       if (!hwif->configured)
                goto abort;
        for (unit = 0; unit < MAX_DRIVES; ++unit) {
                drive = &hwif->drives[unit];
@@ -635,9 +895,17 @@ void ide_unregister (unsigned int index)
                        goto abort;
                drive->dead = 1;
        }
+       /* 
+        * Protect against new users. From this point the hwif
+        * is not present so cannot be opened by a new I/O source.
+        * This also invalidates key driven access from procfs
+        */
+
+       was_present = hwif->present;     
        hwif->present = 0;
-       
+
        spin_unlock_irq(&ide_lock);
+       up(&ide_setting_sem);
 
        for (unit = 0; unit < MAX_DRIVES; ++unit) {
                drive = &hwif->drives[unit];
@@ -645,31 +913,41 @@ void ide_unregister (unsigned int index)
                        continue;
                DRIVER(drive)->cleanup(drive);
        }
-       
+
 #ifdef CONFIG_PROC_FS
        destroy_proc_ide_drives(hwif);
+       destroy_proc_ide_interface(hwif);
 #endif
 
+       spin_lock_irq(&ide_lock);
        hwgroup = hwif->hwgroup;
-       /*
-        * free the irq if we were the only hwif using it
-        */
-       g = hwgroup->hwif;
-       do {
-               if (g->irq == hwif->irq)
-                       ++irq_count;
-               g = g->next;
-       } while (g != hwgroup->hwif);
-       if (irq_count == 1)
+       if(hwgroup)
+       {
+               /*
+                * free the irq if we were the only hwif using it
+                */
+               g = hwgroup->hwif;
+               do {
+                       if (g->irq == hwif->irq)
+                               ++irq_count;
+                       g = g->next;
+               } while (g != hwgroup->hwif);
+       }
+       spin_unlock_irq(&ide_lock);
+       
+       if (irq_count == 1 && hwgroup)
                free_irq(hwif->irq, hwgroup);
 
-       spin_lock_irq(&ide_lock);
        /*
         * Note that we only release the standard ports,
         * and do not even try to handle any extra ports
         * allocated for weird IDE interface chipsets.
+        *
+        * FIXME: should defer this I think
         */
-       ide_hwif_release_regions(hwif);
+
+       if(was_present)
+               ide_hwif_release_regions(hwif);
 
        /*
         * Remove us from the hwgroup, and free
@@ -683,6 +961,14 @@ void ide_unregister (unsigned int index)
                }
                if (!drive->present)
                        continue;
+                       
+               /*
+                * The hwgroup chain is IRQ touched. We must protect
+                * walking this from an IDE event for another device
+                * in the chain 
+                */
+                  
+               spin_lock_irq(&ide_lock);
                if (drive == drive->next) {
                        /* special case: last drive from hwgroup. */
                        BUG_ON(hwgroup->drive != drive);
@@ -699,47 +985,67 @@ void ide_unregister (unsigned int index)
                                hwgroup->hwif = HWIF(hwgroup->drive);
                        }
                }
+               spin_unlock_irq(&ide_lock);
+
+               /*
+                * The rest of the cleanup is private
+                */
+
                BUG_ON(hwgroup->drive == drive);
                if (drive->id != NULL) {
                        kfree(drive->id);
                        drive->id = NULL;
                }
                drive->present = 0;
-               /* Messed up locking ... */
-               spin_unlock_irq(&ide_lock);
                blk_cleanup_queue(drive->queue);
                device_unregister(&drive->gendev);
                down(&drive->gendev_rel_sem);
-               spin_lock_irq(&ide_lock);
                drive->queue = NULL;
        }
-       if (hwif->next == hwif) {
-               BUG_ON(hwgroup->hwif != hwif);
-               kfree(hwgroup);
-       } else {
-               /* There is another interface in hwgroup.
-                * Unlink us, and set hwgroup->drive and ->hwif to
-                * something sane.
-                */
-               g = hwgroup->hwif;
-               while (g->next != hwif)
-                       g = g->next;
-               g->next = hwif->next;
-               if (hwgroup->hwif == hwif) {
-                       /* Chose a random hwif for hwgroup->hwif.
-                        * It's guaranteed that there are no drives
-                        * left in the hwgroup.
+       /*
+        * Lock against hwgroup walkers including interrupts off other
+        * IDE devices wile we unhook ourselves.
+        */
+        
+       spin_lock_irq(&ide_lock);
+       
+       if (hwgroup)
+       {
+               if (hwif->next == hwif) {
+                       BUG_ON(hwgroup->hwif != hwif);
+                       kfree(hwgroup);
+               } else {
+                       /* There is another interface in hwgroup.
+                        * Unlink us, and set hwgroup->drive and ->hwif to
+                        * something sane.
                         */
-                       BUG_ON(hwgroup->drive != NULL);
-                       hwgroup->hwif = g;
+                       g = hwgroup->hwif;
+                       while (g->next != hwif)
+                               g = g->next;
+                       g->next = hwif->next;
+                       if (hwgroup->hwif == hwif) {
+                               /* Chose a random hwif for hwgroup->hwif.
+                                * It's guaranteed that there are no drives
+                                * left in the hwgroup.
+                                */
+                               BUG_ON(hwgroup->drive != NULL);
+                               hwgroup->hwif = g;
+                       }
+                       BUG_ON(hwgroup->hwif == hwif);
                }
-               BUG_ON(hwgroup->hwif == hwif);
        }
-
-       /* More messed up locking ... */
        spin_unlock_irq(&ide_lock);
-       device_unregister(&hwif->gendev);
-       down(&hwif->gendev_rel_sem);
+
+       /*
+        * PCI interfaces with no devices don't exist in the device
+        * tree so don't unregister them.
+        */
+        
+       if(was_present)
+       {
+               device_unregister(&hwif->gendev);
+               down(&hwif->gendev_rel_sem);
+       }
 
        /*
         * Remove us from the kernel's knowledge
@@ -753,6 +1059,13 @@ void ide_unregister (unsigned int index)
        unregister_blkdev(hwif->major, hwif->name);
        spin_lock_irq(&ide_lock);
 
+       /*
+        * Let the driver free up private objects
+        */
+
+       if(hwif->remove)
+               hwif->remove(hwif);
+
        if (hwif->dma_base) {
                (void) ide_release_dma(hwif);
 
@@ -764,127 +1077,67 @@ void ide_unregister (unsigned int index)
                hwif->dma_vendor3 = 0;
                hwif->dma_prdtable = 0;
        }
+       hwif->chipset = ide_unknown;
 
-       old_hwif                        = *hwif;
+       /* copy original settings */
+       tmp_hwif = *hwif;
 
-       init_hwif_data(hwif, index);    /* restore hwif data to pristine status */
+       /* restore hwif data to pristine status */
+       init_hwif_data(hwif, index);
        init_hwif_default(hwif, index);
 
-       hwif->hwgroup                   = old_hwif.hwgroup;
+       hwif->configured = 0;
 
-       hwif->gendev.parent             = old_hwif.gendev.parent;
-
-       hwif->proc                      = old_hwif.proc;
-
-       hwif->major                     = old_hwif.major;
-//     hwif->index                     = old_hwif.index;
-//     hwif->channel                   = old_hwif.channel;
-       hwif->straight8                 = old_hwif.straight8;
-       hwif->bus_state                 = old_hwif.bus_state;
-
-       hwif->atapi_dma                 = old_hwif.atapi_dma;
-       hwif->ultra_mask                = old_hwif.ultra_mask;
-       hwif->mwdma_mask                = old_hwif.mwdma_mask;
-       hwif->swdma_mask                = old_hwif.swdma_mask;
-
-       hwif->chipset                   = old_hwif.chipset;
-       hwif->hold                      = old_hwif.hold;
-
-#ifdef CONFIG_BLK_DEV_IDEPCI
-       hwif->pci_dev                   = old_hwif.pci_dev;
-       hwif->cds                       = old_hwif.cds;
-#endif /* CONFIG_BLK_DEV_IDEPCI */
-
-#if 0
-       hwif->hwifops                   = old_hwif.hwifops;
-#else
-       hwif->identify                  = old_hwif.identify;
-       hwif->tuneproc                  = old_hwif.tuneproc;
-       hwif->speedproc                 = old_hwif.speedproc;
-       hwif->selectproc                = old_hwif.selectproc;
-       hwif->reset_poll                = old_hwif.reset_poll;
-       hwif->pre_reset                 = old_hwif.pre_reset;
-       hwif->resetproc                 = old_hwif.resetproc;
-       hwif->intrproc                  = old_hwif.intrproc;
-       hwif->maskproc                  = old_hwif.maskproc;
-       hwif->quirkproc                 = old_hwif.quirkproc;
-       hwif->busproc                   = old_hwif.busproc;
-#endif
+       ide_hwif_restore(hwif, &tmp_hwif);
+       
+       spin_unlock_irq(&ide_lock);
+       return 0;
 
-#if 0
-       hwif->pioops                    = old_hwif.pioops;
-#else
-       hwif->ata_input_data            = old_hwif.ata_input_data;
-       hwif->ata_output_data           = old_hwif.ata_output_data;
-       hwif->atapi_input_bytes         = old_hwif.atapi_input_bytes;
-       hwif->atapi_output_bytes        = old_hwif.atapi_output_bytes;
-#endif
+abort:
+       if(hwif->configured)
+       {
+               printk("Unregister %d fail %d %d\n", index, drive->usage, DRIVER(drive)->busy);
+               ret = -EBUSY;
+       }
+       else
+       {
+               printk("No such hwif!\n");
+               ret = -ENOENT;
+       }
+       spin_unlock_irq(&ide_lock);
+       up(&ide_setting_sem);
+       return ret;
+}
 
-#if 0
-       hwif->dmaops                    = old_hwif.dmaops;
-#else
-       hwif->ide_dma_read              = old_hwif.ide_dma_read;
-       hwif->ide_dma_write             = old_hwif.ide_dma_write;
-       hwif->ide_dma_begin             = old_hwif.ide_dma_begin;
-       hwif->ide_dma_end               = old_hwif.ide_dma_end;
-       hwif->ide_dma_check             = old_hwif.ide_dma_check;
-       hwif->ide_dma_on                = old_hwif.ide_dma_on;
-       hwif->ide_dma_off_quietly       = old_hwif.ide_dma_off_quietly;
-       hwif->ide_dma_test_irq          = old_hwif.ide_dma_test_irq;
-       hwif->ide_dma_host_on           = old_hwif.ide_dma_host_on;
-       hwif->ide_dma_host_off          = old_hwif.ide_dma_host_off;
-       hwif->ide_dma_verbose           = old_hwif.ide_dma_verbose;
-       hwif->ide_dma_lostirq           = old_hwif.ide_dma_lostirq;
-       hwif->ide_dma_timeout           = old_hwif.ide_dma_timeout;
-#endif
+EXPORT_SYMBOL_GPL(__ide_unregister_hwif);
 
-#if 0
-       hwif->iops                      = old_hwif.iops;
-#else
-       hwif->OUTB              = old_hwif.OUTB;
-       hwif->OUTBSYNC          = old_hwif.OUTBSYNC;
-       hwif->OUTW              = old_hwif.OUTW;
-       hwif->OUTL              = old_hwif.OUTL;
-       hwif->OUTSW             = old_hwif.OUTSW;
-       hwif->OUTSL             = old_hwif.OUTSL;
-
-       hwif->INB               = old_hwif.INB;
-       hwif->INW               = old_hwif.INW;
-       hwif->INL               = old_hwif.INL;
-       hwif->INSW              = old_hwif.INSW;
-       hwif->INSL              = old_hwif.INSL;
-#endif
+/**
+ *     ide_unregister_hwif     -       free an ide interface
+ *     @hwif: interface to unregister
+ *
+ *     Perform the final unregister of an IDE interface. At the moment
+ *     we don't refcount interfaces so this will also get split up.
+ *     Unregister restores the hwif structures to the default state.
+ *
+ *     No locks should be held on entry. When an unregister must
+ *     be done atomically with a register see __ide_unregister_hwif
+ *     and hold the ide_cfg_sem yourself.
+ */
 
-       hwif->mmio                      = old_hwif.mmio;
-       hwif->rqsize                    = old_hwif.rqsize;
-       hwif->no_lba48                  = old_hwif.no_lba48;
-#ifndef CONFIG_BLK_DEV_IDECS
-       hwif->irq                       = old_hwif.irq;
-#endif /* CONFIG_BLK_DEV_IDECS */
-
-       hwif->dma_base                  = old_hwif.dma_base;
-       hwif->dma_master                = old_hwif.dma_master;
-       hwif->dma_command               = old_hwif.dma_command;
-       hwif->dma_vendor1               = old_hwif.dma_vendor1;
-       hwif->dma_status                = old_hwif.dma_status;
-       hwif->dma_vendor3               = old_hwif.dma_vendor3;
-       hwif->dma_prdtable              = old_hwif.dma_prdtable;
-
-       hwif->dma_extra                 = old_hwif.dma_extra;
-       hwif->config_data               = old_hwif.config_data;
-       hwif->select_data               = old_hwif.select_data;
-       hwif->autodma                   = old_hwif.autodma;
-       hwif->udma_four                 = old_hwif.udma_four;
-       hwif->no_dsc                    = old_hwif.no_dsc;
-
-       hwif->hwif_data                 = old_hwif.hwif_data;
-abort:
-       spin_unlock_irq(&ide_lock);
+int ide_unregister_hwif(ide_hwif_t *hwif)
+{
+       int ret;
+       
+       /* This protects two things. Firstly it serializes the 
+          shutdown sequence, secondly it protects us from
+          races while we are killing off a device */
+       down(&ide_cfg_sem);
+       ret = __ide_unregister_hwif(hwif);
        up(&ide_cfg_sem);
+       return ret;
 }
 
-EXPORT_SYMBOL(ide_unregister);
-
+EXPORT_SYMBOL_GPL(ide_unregister_hwif);
 
 /**
  *     ide_setup_ports         -       set up IDE interface ports
@@ -939,17 +1192,30 @@ void ide_setup_ports (   hw_regs_t *hw,
  */
 }
 
-EXPORT_SYMBOL(ide_setup_ports);
-
-/*
- * Register an IDE interface, specifying exactly the registers etc
- * Set init=1 iff calling before probes have taken place.
+/**
+ *     ide_register_hw_with_fixup      -       register IDE interface
+ *     @hw: hardware registers
+ *     @hwifp: pointer to returned hwif
+ *     @fixup function to call
+ *
+ *     Register an IDE interface, specifying exactly the registers etc.
+ *     Set init=1 iff calling before probes have taken place. The
+ *     ide_cfg_sem protects this against races.
+ *
+ *     Invokes a fixup function after the probe and before device attachment
+ *     that can be used by the driver to amend settings or to work around
+ *     hardware funnies.
+ *
+ *     Returns -1 on error.
  */
-int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp)
+
+int ide_register_hw_with_fixup(hw_regs_t *hw, ide_hwif_t **hwifp, void (*fixup)(ide_hwif_t *hwif))
 {
        int index, retry = 1;
        ide_hwif_t *hwif;
 
+       down(&ide_cfg_sem);
+
        do {
                for (index = 0; index < MAX_HWIFS; ++index) {
                        hwif = &ide_hwifs[index];
@@ -960,31 +1226,40 @@ int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp)
                        hwif = &ide_hwifs[index];
                        if (hwif->hold)
                                continue;
-                       if ((!hwif->present && !hwif->mate && !initializing) ||
+                       if ((!hwif->configured && !hwif->mate && !initializing) ||
                            (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing))
                                goto found;
                }
+               /* FIXME- this check should die as should the retry loop */
                for (index = 0; index < MAX_HWIFS; index++)
-                       ide_unregister(index);
+               {
+                       hwif = &ide_hwifs[index];
+                       __ide_unregister_hwif(hwif);
+               }
        } while (retry--);
+
+       up(&ide_cfg_sem);
        return -1;
 found:
-       if (hwif->present)
-               ide_unregister(index);
+       /* FIXME: do we really need this case */
+       if (hwif->configured)
+               __ide_unregister_hwif(hwif);
        else if (!hwif->hold) {
                init_hwif_data(hwif, index);
                init_hwif_default(hwif, index);
        }
-       if (hwif->present)
+       if (hwif->configured)
                return -1;
+       hwif->configured = 1;
        memcpy(&hwif->hw, hw, sizeof(*hw));
        memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports));
        hwif->irq = hw->irq;
        hwif->noprobe = 0;
        hwif->chipset = hw->chipset;
+       up(&ide_cfg_sem);
 
        if (!initializing) {
-               probe_hwif_init(hwif);
+               probe_hwif_init_with_fixup(hwif, fixup);
                create_proc_ide_interfaces();
        }
 
@@ -994,6 +1269,25 @@ found:
        return (initializing || hwif->present) ? index : -1;
 }
 
+EXPORT_SYMBOL(ide_register_hw_with_fixup);
+
+/**
+ *     ide_register_hw         -       register new IDE hardware
+ *     @hw: hardware registers
+ *     @hwifp: pointer to returned hwif
+ *
+ *     Register an IDE interface, specifying exactly the registers etc.
+ *     Set init=1 iff calling before probes have taken place. The
+ *     ide_cfg_sem protects this against races.
+ *
+ *     Returns -1 on error.
+ */
+int ide_register_hw(hw_regs_t *hw, ide_hwif_t **hwif)
+{
+       return ide_register_hw_with_fixup(hw, hwif, NULL);
+}
+
 EXPORT_SYMBOL(ide_register_hw);
 
 /*
@@ -1001,7 +1295,6 @@ EXPORT_SYMBOL(ide_register_hw);
  */
 
 DECLARE_MUTEX(ide_setting_sem);
-EXPORT_SYMBOL(ide_setting_sem);
 
 /**
  *     ide_add_setting -       add an ide setting option
@@ -1019,21 +1312,21 @@ EXPORT_SYMBOL(ide_setting_sem);
  *     @set: setting
  *
  *     Removes the setting named from the device if it is present.
- *     The function takes the settings_lock to protect against 
- *     parallel changes. This function must not be called from IRQ
- *     context. Returns 0 on success or -1 on failure.
+ *     This function must not be called from IRQ context. Returns 0
+ *     on success or -1 on failure.
  *
  *     BUGS: This code is seriously over-engineered. There is also
  *     magic about how the driver specific features are setup. If
  *     a driver is attached we assume the driver settings are auto
  *     remove.
+ *
+ *     The caller must hold settings_lock
  */
  
 int ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
 {
        ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
 
-       down(&ide_setting_sem);
        while ((*p) && strcmp((*p)->name, name) < 0)
                p = &((*p)->next);
        if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
@@ -1057,10 +1350,8 @@ int ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioct
        if (drive->driver != &idedefault_driver)
                setting->auto_remove = 1;
        *p = setting;
-       up(&ide_setting_sem);
        return 0;
 abort:
-       up(&ide_setting_sem);
        if (setting)
                kfree(setting);
        return -1;
@@ -1069,7 +1360,7 @@ abort:
 EXPORT_SYMBOL(ide_add_setting);
 
 /**
- *     __ide_remove_setting    -       remove an ide setting option
+ *     ide_remove_setting      -       remove an ide setting option
  *     @drive: drive to use
  *     @name: setting name
  *
@@ -1077,7 +1368,7 @@ EXPORT_SYMBOL(ide_add_setting);
  *     The caller must hold the setting semaphore.
  */
  
-static void __ide_remove_setting (ide_drive_t *drive, char *name)
+static void ide_remove_setting (ide_drive_t *drive, char *name)
 {
        ide_settings_t **p, *setting;
 
@@ -1094,26 +1385,6 @@ static void __ide_remove_setting (ide_drive_t *drive, char *name)
        kfree(setting);
 }
 
-/**
- *     ide_remove_setting      -       remove an ide setting option
- *     @drive: drive to use
- *     @name: setting name
- *
- *     Removes the setting named from the device if it is present.
- *     The function takes the settings_lock to protect against 
- *     parallel changes. This function must not be called from IRQ
- *     context.
- */
-void ide_remove_setting (ide_drive_t *drive, char *name)
-{
-       down(&ide_setting_sem);
-       __ide_remove_setting(drive, name);
-       up(&ide_setting_sem);
-}
-
-EXPORT_SYMBOL(ide_remove_setting);
-
 /**
  *     ide_find_setting_by_ioctl       -       find a drive specific ioctl
  *     @drive: drive to scan
@@ -1175,7 +1446,7 @@ repeat:
        setting = drive->settings;
        while (setting) {
                if (setting->auto_remove) {
-                       __ide_remove_setting(drive, setting->name);
+                       ide_remove_setting(drive, setting->name);
                        goto repeat;
                }
                setting = setting->next;
@@ -1219,6 +1490,15 @@ int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting)
        return val;
 }
 
+/**
+ *     ide_spin_wait_hwgroup   -       wait for group
+ *     @drive: drive in the group
+ *
+ *     Wait for an IDE device group to go non busy and then return
+ *     holding the ide_lock which guards the hwgroup->busy status
+ *     and right to use it.
+ */
+
 int ide_spin_wait_hwgroup (ide_drive_t *drive)
 {
        ide_hwgroup_t *hwgroup = HWGROUP(drive);
@@ -1260,6 +1540,7 @@ EXPORT_SYMBOL(ide_spin_wait_hwgroup);
  *     to the driver to change settings, and then wait on a sema for completion.
  *     The current scheme of polling is kludgy, though safe enough.
  */
+
 int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
 {
        int i;
@@ -1295,8 +1576,6 @@ int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val)
        return 0;
 }
 
-EXPORT_SYMBOL(ide_write_setting);
-
 static int set_io_32bit(ide_drive_t *drive, int arg)
 {
        drive->io_32bit = arg;
@@ -1355,25 +1634,18 @@ static int set_xfer_rate (ide_drive_t *drive, int arg)
        return err;
 }
 
-int ide_atapi_to_scsi (ide_drive_t *drive, int arg)
-{
-       if (drive->media == ide_disk) {
-               drive->scsi = 0;
-               return 0;
-       }
-
-       if (DRIVER(drive)->cleanup(drive)) {
-               drive->scsi = 0;
-               return 0;
-       }
-
-       drive->scsi = (u8) arg;
-       ata_attach(drive);
-       return 0;
-}
+/**
+ *     ide_add_generic_settings        -       generic ide settings
+ *     @drive: drive being configured
+ *
+ *     Add the generic parts of the system settings to the /proc files and
+ *     ioctls for this IDE device. The caller must not be holding the
+ *     ide_setting_sem.
+ */
 
 void ide_add_generic_settings (ide_drive_t *drive)
 {
+       down(&ide_setting_sem);
 /*
  *                     drive   setting name            read/write access                               read ioctl              write ioctl             data type       min     max                             mul_factor      div_factor      data pointer                    set function
  */
@@ -1386,29 +1658,16 @@ void ide_add_generic_settings (ide_drive_t *drive)
        ide_add_setting(drive,  "init_speed",           SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      70,                             1,              1,              &drive->init_speed,             NULL);
        ide_add_setting(drive,  "current_speed",        SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      70,                             1,              1,              &drive->current_speed,          set_xfer_rate);
        ide_add_setting(drive,  "number",               SETTING_RW,                                     -1,                     -1,                     TYPE_BYTE,      0,      3,                              1,              1,              &drive->dn,                     NULL);
-       if (drive->media != ide_disk)
-               ide_add_setting(drive,  "ide-scsi",             SETTING_RW,                                     -1,             HDIO_SET_IDE_SCSI,              TYPE_BYTE,      0,      1,                              1,              1,              &drive->scsi,                   ide_atapi_to_scsi);
+
+       up(&ide_setting_sem);
 }
 
-/*
- * Delay for *at least* 50ms.  As we don't know how much time is left
- * until the next tick occurs, we wait an extra tick to be safe.
- * This is used only during the probing/polling for drives at boot time.
+/**
+ *     system_bus_clock        -       clock guess
  *
- * However, its usefullness may be needed in other places, thus we export it now.
- * The future may change this to a millisecond setable delay.
+ *     External version of the bus clock guess used by very old IDE drivers
+ *     for things like VLB timings. Should not be used.
  */
-void ide_delay_50ms (void)
-{
-#ifndef CONFIG_BLK_DEV_IDECS
-       mdelay(50);
-#else
-       __set_current_state(TASK_UNINTERRUPTIBLE);
-       schedule_timeout(1+HZ/20);
-#endif /* CONFIG_BLK_DEV_IDECS */
-}
-
-EXPORT_SYMBOL(ide_delay_50ms);
 
 int system_bus_clock (void)
 {
@@ -1444,34 +1703,48 @@ abort:
        return 1;
 }
 
-EXPORT_SYMBOL(ide_replace_subdriver);
+/**
+ *     ata_attach              -       attach an ATA/ATAPI device
+ *     @drive: drive to attach
+ *
+ *     Takes a drive that is as yet not assigned to any midlayer IDE
+ *     driver (or is assigned to the default driver) and figures out
+ *     which driver would like to own it. If nobody claims the drive
+ *     then it is automatically attached to the default driver used for
+ *     unclaimed objects.
+ *
+ *     A return of zero indicates attachment to a driver, of one
+ *     attachment to the default driver.
+ *
+ *     Takes the driver list lock and the ide_settings semaphore.
+ */
 
 int ata_attach(ide_drive_t *drive)
 {
        struct list_head *p;
-       spin_lock(&drivers_lock);
+       down(&drivers_sem);
+       down(&ide_setting_sem);
        list_for_each(p, &drivers) {
                ide_driver_t *driver = list_entry(p, ide_driver_t, drivers);
                if (!try_module_get(driver->owner))
                        continue;
-               spin_unlock(&drivers_lock);
                if (driver->attach(drive) == 0) {
                        module_put(driver->owner);
                        drive->gendev.driver = &driver->gen_driver;
+                       up(&ide_setting_sem);
+                       up(&drivers_sem);
                        return 0;
                }
-               spin_lock(&drivers_lock);
                module_put(driver->owner);
        }
        drive->gendev.driver = &idedefault_driver.gen_driver;
-       spin_unlock(&drivers_lock);
+       up(&ide_setting_sem);
+       up(&drivers_sem);
        if(idedefault_driver.attach(drive) != 0)
                panic("ide: default attach failed");
        return 1;
 }
 
-EXPORT_SYMBOL(ata_attach);
-
 static int generic_ide_suspend(struct device *dev, u32 state)
 {
        ide_drive_t *drive = dev->driver_data;
@@ -1510,19 +1783,20 @@ static int generic_ide_resume(struct device *dev)
        return ide_do_drive_cmd(drive, &rq, ide_head_wait);
 }
 
-int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
-                       unsigned long arg)
+int generic_ide_ioctl(struct file *file, struct block_device *bdev,
+                       unsigned int cmd, unsigned long arg)
 {
        ide_drive_t *drive = bdev->bd_disk->private_data;
        ide_settings_t *setting;
        int err = 0;
+       void __user *p = (void __user *)arg;
 
        down(&ide_setting_sem);
        if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
                if (cmd == setting->read_ioctl) {
                        err = ide_read_setting(drive, setting);
                        up(&ide_setting_sem);
-                       return err >= 0 ? put_user(err, (long *) arg) : err;
+                       return err >= 0 ? put_user(err, (long __user *)arg) : err;
                } else {
                        if (bdev != bdev->bd_contains)
                                err = -EINVAL;
@@ -1537,14 +1811,14 @@ int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
        switch (cmd) {
                case HDIO_GETGEO:
                {
-                       struct hd_geometry *loc = (struct hd_geometry *) arg;
-                       u16 bios_cyl = drive->bios_cyl; /* truncate */
-                       if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
-                       if (put_user(drive->bios_head, (u8 *) &loc->heads)) return -EFAULT;
-                       if (put_user(drive->bios_sect, (u8 *) &loc->sectors)) return -EFAULT;
-                       if (put_user(bios_cyl, (u16 *) &loc->cylinders)) return -EFAULT;
-                       if (put_user((unsigned)get_start_sect(bdev),
-                               (unsigned long *) &loc->start)) return -EFAULT;
+                       struct hd_geometry geom;
+                       if (!p || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
+                       geom.heads = drive->bios_head;
+                       geom.sectors = drive->bios_sect;
+                       geom.cylinders = (u16)drive->bios_cyl; /* truncate */
+                       geom.start = get_start_sect(bdev);
+                       if (copy_to_user(p, &geom, sizeof(struct hd_geometry)))
+                               return -EFAULT;
                        return 0;
                }
 
@@ -1554,7 +1828,7 @@ int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
                                return -EINVAL;
                        if (drive->id_read == 0)
                                return -ENOMSG;
-                       if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
+                       if (copy_to_user(p, drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
                                return -EFAULT;
                        return 0;
 
@@ -1564,7 +1838,7 @@ int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
                                        drive->nice0            <<      IDE_NICE_0              |
                                        drive->nice1            <<      IDE_NICE_1              |
                                        drive->nice2            <<      IDE_NICE_2,
-                                       (long *) arg);
+                                       (long __user *) arg);
 
 #ifdef CONFIG_IDE_TASK_IOCTL
                case HDIO_DRIVE_TASKFILE:
@@ -1591,22 +1865,27 @@ int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
                case HDIO_SCAN_HWIF:
                {
                        hw_regs_t hw;
+                       ide_hwif_t *hwif;
                        int args[3];
                        if (!capable(CAP_SYS_RAWIO)) return -EACCES;
-                       if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
+                       if (copy_from_user(args, p, 3 * sizeof(int)))
                                return -EFAULT;
                        memset(&hw, 0, sizeof(hw));
                        ide_init_hwif_ports(&hw, (unsigned long) args[0],
                                            (unsigned long) args[1], NULL);
                        hw.irq = args[2];
-                       if (ide_register_hw(&hw, NULL) == -1)
+                       if (ide_register_hw(&hw, &hwif) == -1)
                                return -EIO;
+                       hwif->user_dev = 1;
                        return 0;
                }
                case HDIO_UNREGISTER_HWIF:
                        if (!capable(CAP_SYS_RAWIO)) return -EACCES;
-                       /* (arg > MAX_HWIFS) checked in function */
-                       ide_unregister(arg);
+                       if(arg > MAX_HWIFS || arg < 0)
+                               return -EINVAL;
+                       if(!ide_hwifs[arg].user_dev)
+                               return -EINVAL;
+                       return ide_unregister_hwif(&ide_hwifs[arg]);
                        return 0;
                case HDIO_SET_NICE:
                        if (!capable(CAP_SYS_ADMIN)) return -EACCES;
@@ -1661,12 +1940,12 @@ int generic_ide_ioctl(struct block_device *bdev, unsigned int cmd,
 
                case CDROMEJECT:
                case CDROMCLOSETRAY:
-                       return scsi_cmd_ioctl(bdev->bd_disk, cmd, arg);
+                       return scsi_cmd_ioctl(file, bdev->bd_disk, cmd, p);
 
                case HDIO_GET_BUSSTATE:
                        if (!capable(CAP_SYS_ADMIN))
                                return -EACCES;
-                       if (put_user(HWIF(drive)->bus_state, (long *)arg))
+                       if (put_user(HWIF(drive)->bus_state, (long __user *)arg))
                                return -EFAULT;
                        return 0;
 
@@ -1828,7 +2107,7 @@ int __init ide_setup (char *s)
        if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
                const char *hd_words[] = {
                        "none", "noprobe", "nowerr", "cdrom", "serialize",
-                       "autotune", "noautotune", "minus8", "swapdata", "bswap",
+                       "autotune", "noautotune", "stroke", "swapdata", "bswap",
                        "minus11", "remap", "remap63", "scsi", NULL };
                unit = s[2] - 'a';
                hw   = unit / MAX_DRIVES;
@@ -1862,6 +2141,9 @@ int __init ide_setup (char *s)
                        case -7: /* "noautotune" */
                                drive->autotune = IDE_TUNE_NOAUTO;
                                goto done;
+                       case -8: /* stroke */
+                               drive->stroke = 1;
+                               goto done;
                        case -9: /* "swapdata" */
                        case -10: /* "bswap" */
                                drive->bswap = 1;
@@ -2056,6 +2338,9 @@ done:
        return 1;
 }
 
+extern void pnpide_init(void);
+extern void h8300_ide_init(void);
+
 /*
  * probe_for_hwifs() finds/initializes "known" IDE interfaces
  */
@@ -2090,12 +2375,6 @@ static void __init probe_for_hwifs (void)
                pmac_ide_probe();
        }
 #endif /* CONFIG_BLK_DEV_IDE_PMAC */
-#ifdef CONFIG_BLK_DEV_IDE_SWARM
-       {
-               extern void swarm_ide_probe(void);
-               swarm_ide_probe();
-       }
-#endif /* CONFIG_BLK_DEV_IDE_SWARM */
 #ifdef CONFIG_BLK_DEV_GAYLE
        {
                extern void gayle_init(void);
@@ -2126,12 +2405,12 @@ static void __init probe_for_hwifs (void)
                buddha_init();
        }
 #endif /* CONFIG_BLK_DEV_BUDDHA */
-#if defined(CONFIG_BLK_DEV_IDEPNP) && defined(CONFIG_PNP)
-       {
-               extern void pnpide_init(int enable);
-               pnpide_init(1);
-       }
-#endif /* CONFIG_BLK_DEV_IDEPNP */
+#ifdef CONFIG_BLK_DEV_IDEPNP
+       pnpide_init();
+#endif
+#ifdef CONFIG_H8300
+       h8300_ide_init();
+#endif
 }
 
 /*
@@ -2246,8 +2525,8 @@ int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver)
        drive->suspend_reset = 0;
 #ifdef CONFIG_PROC_FS
        if (drive->driver != &idedefault_driver) {
-               ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive);
-               ide_add_proc_entries(drive->proc, driver->proc, drive);
+               ide_add_proc_entries(drive->proc, generic_subdriver_entries, ide_drive_to_key(drive));
+               ide_add_proc_entries(drive->proc, driver->proc, ide_drive_to_key(drive));
        }
 #endif
        return 0;
@@ -2255,9 +2534,24 @@ int ide_register_subdriver(ide_drive_t *drive, ide_driver_t *driver)
 
 EXPORT_SYMBOL(ide_register_subdriver);
 
+/**
+ *     ide_unregister_subdriver        -       disconnect drive from driver
+ *     @drive: drive to unplug
+ *
+ *     Disconnect a drive from the driver it was attached to and then
+ *     clean up the various proc files and other objects attached to it.
+ *
+ *     Takes ide_setting_sem, ide_lock and drives_lock.
+ *     Caller must hold none of the locks.
+ *
+ *     No locking versus subdriver unload because we are moving to the
+ *     default driver anyway. Wants double checking.
+ */
+
 int ide_unregister_subdriver (ide_drive_t *drive)
 {
        unsigned long flags;
+       ide_proc_entry_t *dir;
        
        down(&ide_setting_sem);
        spin_lock_irqsave(&ide_lock, flags);
@@ -2266,16 +2560,14 @@ int ide_unregister_subdriver (ide_drive_t *drive)
                up(&ide_setting_sem);
                return 1;
        }
-#if defined(CONFIG_BLK_DEV_IDEPNP) && defined(CONFIG_PNP) && defined(MODULE)
-       pnpide_init(0);
-#endif /* CONFIG_BLK_DEV_IDEPNP */
+       dir = DRIVER(drive)->proc;
+       drive->driver = &idedefault_driver;
+       spin_unlock_irqrestore(&ide_lock, flags);
 #ifdef CONFIG_PROC_FS
-       ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc);
+       ide_remove_proc_entries(drive->proc, dir);
        ide_remove_proc_entries(drive->proc, generic_subdriver_entries);
 #endif
        auto_remove_settings(drive);
-       drive->driver = &idedefault_driver;
-       spin_unlock_irqrestore(&ide_lock, flags);
        up(&ide_setting_sem);
        spin_lock(&drives_lock);
        list_del_init(&drive->list);
@@ -2293,6 +2585,18 @@ static int ide_drive_remove(struct device * dev)
        return 0;
 }
 
+/**
+ *     ide_register_driver     -       register IDE device driver
+ *     @driver: the IDE device driver
+ *
+ *     Register a new device driver and then scan the devices
+ *     on the IDE bus in case any should be attached to the
+ *     driver we have just registered.  If so attach them.
+ *
+ *     Takes the drivers and drives lock. Should take the
+ *     ide_sem but doesn't - FIXME ??
+ */
+
 int ide_register_driver(ide_driver_t *driver)
 {
        struct list_head list;
@@ -2301,9 +2605,11 @@ int ide_register_driver(ide_driver_t *driver)
 
        setup_driver_defaults(driver);
 
+       down(&drivers_sem);
        spin_lock(&drivers_lock);
        list_add(&driver->drivers, &drivers);
        spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 
        INIT_LIST_HEAD(&list);
        spin_lock(&drives_lock);
@@ -2324,13 +2630,26 @@ int ide_register_driver(ide_driver_t *driver)
 
 EXPORT_SYMBOL(ide_register_driver);
 
+/**
+ *     ide_unregister_driver   -       unregister IDE device driver
+ *     @driver: the IDE device driver
+ *
+ *     Called when a driver module is being unloaded. We reattach any
+ *     devices to whatever driver claims them next (typically the default
+ *     driver).
+ *
+ *     Takes drivers_lock and called functions will take ide_setting_sem.
+ */
+
 void ide_unregister_driver(ide_driver_t *driver)
 {
        ide_drive_t *drive;
 
+       down(&drivers_sem);
        spin_lock(&drivers_lock);
        list_del(&driver->drivers);
        spin_unlock(&drivers_lock);
+       up(&drivers_sem);
 
        driver_unregister(&driver->gen_driver);
 
@@ -2379,7 +2698,7 @@ int __init ide_init (void)
        init_ide_data();
 
 #ifdef CONFIG_PROC_FS
-       proc_ide_root = proc_mkdir("ide", 0);
+       proc_ide_root = proc_mkdir("ide", NULL);
 #endif
 
 #ifdef CONFIG_BLK_DEV_ALI14XX
@@ -2444,7 +2763,8 @@ void cleanup_module (void)
        int index;
 
        for (index = 0; index < MAX_HWIFS; ++index) {
-               ide_unregister(index);
+               if(ide_unregister_hwif(&ide_hwifs[index]))
+                       printk(KERN_ERR "ide: unload yet busy!\n");
                if (ide_hwifs[index].dma_base)
                        (void) ide_release_dma(&ide_hwifs[index]);
        }