patch-2.1.8 linux/drivers/block/ide.c

Next file: linux/drivers/block/ide.h
Previous file: linux/drivers/block/ide-tape.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.7/linux/drivers/block/ide.c linux/drivers/block/ide.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.52  Sep  24, 1996
+ *  linux/drivers/block/ide.c	Version 5.60  Nov   5, 1996
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -261,6 +261,14 @@
  *			change delay_10ms() to delay_50ms() to fix problems
  * Version 5.52		fix incorrect invalidation of removable devices
  *			add "hdx=slow" command line option
+ * Version 5.60		start to modularize the driver; the disk and ATAPI
+ *			 drivers can be compiled as loadable modules.
+ *			move IDE probe code to ide-probe.c
+ *			move IDE disk code to ide-disk.c
+ *			add support for generic IDE device subdrivers
+ *			add m68k code from Geert Uytterhoeven
+ *			probe all interfaces by default
+ *			add ioctl to (re)probe an interface
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -271,6 +279,7 @@
 #undef REALLY_SLOW_IO		/* most systems can safely undef this */
 
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/types.h>
 #include <linux/string.h>
 #include <linux/kernel.h>
@@ -290,6 +299,7 @@
 #include <asm/irq.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
+#include <asm/bitops.h>
 
 #ifdef CONFIG_PCI
 #include <linux/bios32.h>
@@ -299,18 +309,26 @@
 #include "ide.h"
 #include "ide_modes.h"
 
-#ifdef CONFIG_BLK_DEV_PROMISE
-#include "promise.h"
-#define IS_PROMISE_DRIVE (HWIF(drive)->chipset == ide_promise)
-#else
-#define IS_PROMISE_DRIVE (0)	/* auto-NULLs out Promise code */
-#endif /* CONFIG_BLK_DEV_PROMISE */
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif /* CONFIG_KERNELD */
+
+static const byte	ide_hwif_to_major[] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
 
-static const byte	ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
-static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
-static const byte	default_irqs[MAX_HWIFS]     = {14, 15, 11, 10};
 static int	idebus_parameter; /* holds the "idebus=" parameter */
 static int	system_bus_speed; /* holds what we think is VESA/PCI bus speed */
+static int	initializing;     /* set while initializing built-in drivers */
+
+/*
+ * ide_lock is used by the Atari code to obtain access to the IDE interrupt,
+ * which is shared between several drivers.
+ */
+static int	ide_lock = 0;
+
+/*
+ * ide_modules keeps track of the available IDE chipset/probe/driver modules.
+ */
+static ide_module_t *ide_modules = NULL;
 
 /*
  * This is declared extern in ide.h, for access by other IDE modules:
@@ -349,7 +367,6 @@
 
 #endif /* DISK_RECOVERY_TIME */
 
-
 /*
  * Do not even *think* about calling this!
  */
@@ -367,11 +384,10 @@
 
 	/* fill in any non-zero initial values */
 	hwif->index     = index;
-	hwif->noprobe	= (index > 1);
-	hwif->io_base	= default_io_base[index];
-	hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
+	ide_init_hwif_ports(hwif->io_ports, ide_default_io_base(index), &hwif->irq);
+	hwif->noprobe	= !hwif->io_ports[IDE_DATA_OFFSET];
 #ifdef CONFIG_BLK_DEV_HD
-	if (hwif->io_base == HD_DATA)
+	if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA)
 		hwif->noprobe = 1; /* may be overridden by ide_setup() */
 #endif /* CONFIG_BLK_DEV_HD */
 	hwif->major	= ide_hwif_to_major[index];
@@ -379,12 +395,10 @@
 	hwif->name[1]	= 'd';
 	hwif->name[2]	= 'e';
 	hwif->name[3]	= '0' + index;
-#ifdef CONFIG_BLK_DEV_IDETAPE
-	hwif->tape_drive = NULL;
-#endif /* CONFIG_BLK_DEV_IDETAPE */
 	for (unit = 0; unit < MAX_DRIVES; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
 
+		drive->media			= ide_disk;
 		drive->select.all		= (unit<<4)|0xa0;
 		drive->hwif			= hwif;
 		drive->ctl			= 0x08;
@@ -431,7 +445,7 @@
 
 /*
  * 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.
+ * 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.
@@ -447,7 +461,8 @@
 #endif /* CONFIG_PCI */
 		else
 			system_bus_speed = 50;	/* safe default value for VESA and PCI */
-		printk("ide: Assuming %dMhz system bus speed for PIO modes; override with idebus=xx\n", system_bus_speed);
+		printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed,
+			idebus_parameter ? "" : "; override with idebus=xx");
 	}
 	return system_bus_speed;
 }
@@ -460,7 +475,7 @@
  * of the sector count register location, with interrupts disabled
  * to ensure that the reads all happen together.
  */
-static inline void do_vlb_sync (unsigned short port) {
+static inline void do_vlb_sync (ide_ioreg_t port) {
 	(void) inb (port);
 	(void) inb (port);
 	(void) inb (port);
@@ -472,32 +487,30 @@
  */
 void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	unsigned short io_base  = HWIF(drive)->io_base;
-	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
 	byte io_32bit = drive->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			cli();
-			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
-			insl(data_reg, buffer, wcount);
+			do_vlb_sync(IDE_NSECTOR_REG);
+			insl(IDE_DATA_REG, buffer, wcount);
 			if (drive->unmask)
 				sti();
 		} else
 #endif /* SUPPORT_VLB_SYNC */
-			insl(data_reg, buffer, wcount);
+			insl(IDE_DATA_REG, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
 		if (drive->slow) {
 			unsigned short *ptr = (unsigned short *) buffer;
 			while (wcount--) {
-				*ptr++ = inw_p(data_reg);
-				*ptr++ = inw_p(data_reg);
+				*ptr++ = inw_p(IDE_DATA_REG);
+				*ptr++ = inw_p(IDE_DATA_REG);
 			}
 		} else
 #endif /* SUPPORT_SLOW_DATA_PORTS */
-			insw(data_reg, buffer, wcount<<1);
+			insw(IDE_DATA_REG, buffer, wcount<<1);
 	}
 }
 
@@ -506,33 +519,68 @@
  */
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
 {
-	unsigned short io_base  = HWIF(drive)->io_base;
-	unsigned short data_reg = io_base+IDE_DATA_OFFSET;
 	byte io_32bit = drive->io_32bit;
 
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			cli();
-			do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
-			outsl(data_reg, buffer, wcount);
+			do_vlb_sync(IDE_NSECTOR_REG);
+			outsl(IDE_DATA_REG, buffer, wcount);
 			if (drive->unmask)
 				sti();
 		} else
 #endif /* SUPPORT_VLB_SYNC */
-			outsl(data_reg, buffer, wcount);
+			outsl(IDE_DATA_REG, buffer, wcount);
 	} else {
 #if SUPPORT_SLOW_DATA_PORTS
 		if (drive->slow) {
 			unsigned short *ptr = (unsigned short *) buffer;
 			while (wcount--) {
-				outw_p(*ptr++, data_reg);
-				outw_p(*ptr++, data_reg);
+				outw_p(*ptr++, IDE_DATA_REG);
+				outw_p(*ptr++, IDE_DATA_REG);
 			}
 		} else
 #endif /* SUPPORT_SLOW_DATA_PORTS */
-			outsw(data_reg, buffer, wcount<<1);
+			outsw(IDE_DATA_REG, buffer, wcount<<1);
+	}
+}
+
+/*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+#ifdef CONFIG_ATARI
+	if (MACH_IS_ATARI) {
+		/* Atari has a byte-swapped IDE interface */
+		insw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+		return;
+	}
+#endif /* CONFIG_ATARI */
+	ide_input_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+#ifdef CONFIG_ATARI
+	if (MACH_IS_ATARI) {
+		/* Atari has a byte-swapped IDE interface */
+		outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2);
+		return;
 	}
+#endif /* CONFIG_ATARI */
+	ide_output_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
 }
 
 /*
@@ -540,7 +588,7 @@
  * wait for an interrupt response from a drive.  handler() points
  * at the appropriate code to handle the next interrupt, and a
  * timer is started to prevent us from waiting forever in case
- * something goes wrong (see the timer_expiry() handler later on).
+ * something goes wrong (see the ide_timer_expiry() handler later on).
  */
 void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
 {
@@ -557,131 +605,38 @@
 }
 
 /*
- * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
- * value for this drive (from its reported identification information).
- *
- * Returns:	1 if lba_capacity looks sensible
- *		0 otherwise
- */
-static int lba_capacity_is_ok (struct hd_driveid *id)
-{
-	unsigned long lba_sects   = id->lba_capacity;
-	unsigned long chs_sects   = id->cyls * id->heads * id->sectors;
-	unsigned long _10_percent = chs_sects / 10;
-
-	/* perform a rough sanity check on lba_sects:  within 10% is "okay" */
-	if ((lba_sects - chs_sects) < _10_percent)
-		return 1;	/* lba_capacity is good */
-
-	/* some drives have the word order reversed */
-	lba_sects = (lba_sects << 16) | (lba_sects >> 16);
-	if ((lba_sects - chs_sects) < _10_percent) {
-		id->lba_capacity = lba_sects;	/* fix it */
-		return 1;	/* lba_capacity is (now) good */
-	}
-	return 0;	/* lba_capacity value is bad */
-}
-
-/*
  * current_capacity() returns the capacity (in sectors) of a drive
  * according to its current geometry/LBA settings.
  */
-static unsigned long current_capacity (ide_drive_t  *drive)
+static unsigned long current_capacity (ide_drive_t *drive)
 {
-	struct hd_driveid *id = drive->id;
-	unsigned long capacity = drive->cyl * drive->head * drive->sect;
-
 	if (!drive->present)
 		return 0;
-	if (drive->media != ide_disk)
-		return 0x7fffffff;	/* cdrom or tape */
-	drive->select.b.lba = 0;
-	/* Determine capacity, and use LBA if the drive properly supports it */
-	if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
-		if (id->lba_capacity >= capacity) {
-			capacity = id->lba_capacity;
-			drive->select.b.lba = 1;
-		}
-	}
-	return (capacity - drive->sect0);
+	if (drive->driver != NULL)
+		return DRIVER(drive)->capacity(drive);
+	return 0;
 }
 
 /*
  * ide_geninit() is called exactly *once* for each major, from genhd.c,
  * at the beginning of the initial partition check for the drives.
  */
-static void ide_geninit (struct gendisk *gd)
+void ide_geninit (struct gendisk *gd)
 {
 	unsigned int unit;
 	ide_hwif_t *hwif = gd->real_devices;
 
 	for (unit = 0; unit < gd->nr_real; ++unit) {
 		ide_drive_t *drive = &hwif->drives[unit];
-#ifdef CONFIG_BLK_DEV_IDECD
-		if (drive->present && drive->media == ide_cdrom)
-			ide_cdrom_setup(drive);
-#endif /* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-		if (drive->present && drive->media == ide_tape)
-			idetape_setup(drive);
-#endif /* CONFIG_BLK_DEV_IDETAPE */
+
 		drive->part[0].nr_sects = current_capacity(drive);
-		if (!drive->present || drive->media != ide_disk) {
+		if (!drive->present || drive->media != ide_disk || drive->driver == NULL)
 			drive->part[0].start_sect = -1; /* skip partition check */
-		}
-	}
-}
-
-/*
- * init_gendisk() (as opposed to ide_geninit) is called for each major device,
- * after probing for drives, to allocate partition tables and other data
- * structures needed for the routines in genhd.c.  ide_geninit() gets called
- * somewhat later, during the partition check.
- */
-static void init_gendisk (ide_hwif_t *hwif)
-{
-	struct gendisk *gd, **gdp;
-	unsigned int unit, units, minors;
-	int *bs;
-
-	/* figure out maximum drive number on the interface */
-	for (units = MAX_DRIVES; units > 0; --units) {
-		if (hwif->drives[units-1].present)
-			break;
 	}
-	minors    = units * (1<<PARTN_BITS);
-	gd        = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
-	gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
-	gd->part  = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
-	bs        = kmalloc (minors*sizeof(int), GFP_KERNEL);
-
-	memset(gd->part, 0, minors * sizeof(struct hd_struct));
-
-	/* cdroms and msdos f/s are examples of non-1024 blocksizes */
-	blksize_size[hwif->major] = bs;
-	for (unit = 0; unit < minors; ++unit)
-		*bs++ = BLOCK_SIZE;
-
-	for (unit = 0; unit < units; ++unit)
-		hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
-
-	gd->major	= hwif->major;		/* our major device number */
-	gd->major_name	= IDE_MAJOR_NAME;	/* treated special in genhd.c */
-	gd->minor_shift	= PARTN_BITS;		/* num bits for partitions */
-	gd->max_p	= 1<<PARTN_BITS;	/* 1 + max partitions / drive */
-	gd->max_nr	= units;		/* max num real drives */
-	gd->nr_real	= units;		/* current num real drives */
-	gd->init	= ide_geninit;		/* initialization function */
-	gd->real_devices= hwif;			/* ptr to internal data */
-	gd->next	= NULL;			/* linked list of major devs */
-
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) ;
-	hwif->gd = *gdp = gd;			/* link onto tail of list */
 }
 
 static void do_reset1 (ide_drive_t *, int);		/* needed below */
 
-#ifdef CONFIG_BLK_DEV_IDEATAPI
 /*
  * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
  * during an atapi drive reset operation. If the drive has not yet responded,
@@ -710,7 +665,6 @@
 	}
 	hwgroup->poll_timeout = 0;	/* done polling */
 }
-#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
 /*
  * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
@@ -761,6 +715,20 @@
 	hwgroup->poll_timeout = 0;	/* done polling */
 }
 
+static void pre_reset (ide_drive_t *drive)
+{
+	if (!drive->keep_settings) {
+		drive->unmask = 0;
+		drive->io_32bit = 0;
+		if (drive->using_dma) {
+			drive->using_dma = 0;
+			printk("%s: disabled DMA\n", drive->name);
+		}
+	}
+	if (drive->driver != NULL)
+		DRIVER(drive)->pre_reset(drive);
+}
+
 /*
  * do_reset1() attempts to recover a confused drive by resetting it.
  * Unfortunately, resetting a disk drive actually resets all devices on
@@ -786,48 +754,24 @@
 	save_flags(flags);
 	cli();		/* Why ? */
 
-#ifdef CONFIG_BLK_DEV_IDEATAPI
 	/* For an ATAPI device, first try an ATAPI SRST. */
-	if (drive->media != ide_disk) {
-		if (!do_not_try_atapi) {
-			if (!drive->keep_settings) {
-				drive->unmask = 0;
-				drive->io_32bit = 0;
-			}
-			OUT_BYTE (drive->select.all, IDE_SELECT_REG);
-			udelay (20);
-			OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
-			hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
-			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
-			restore_flags (flags);
-			return;
-		}
+	if (drive->media != ide_disk && !do_not_try_atapi) {
+		pre_reset(drive);
+		OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+		udelay (20);
+		OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+		ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+		restore_flags (flags);
+		return;
 	}
-#endif /* CONFIG_BLK_DEV_IDEATAPI */
 
 	/*
 	 * First, reset any device state data we were maintaining
 	 * for any of the drives on this interface.
 	 */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *rdrive = &hwif->drives[unit];
-		rdrive->special.all = 0;
-		rdrive->special.b.set_geometry = 1;
-		rdrive->special.b.recalibrate  = 1;
-		if (OK_TO_RESET_CONTROLLER)
-			rdrive->mult_count = 0;
-		if (!rdrive->keep_settings) {
-			rdrive->mult_req = 0;
-			rdrive->unmask = 0;
-			rdrive->io_32bit = 0;
-			if (rdrive->using_dma) {
-				rdrive->using_dma = 0;
-				printk("%s: disabled DMA\n", rdrive->name);
-			}
-		}
-		if (rdrive->mult_req != rdrive->mult_count)
-			rdrive->special.b.set_multmode = 1;
-	}
+	for (unit = 0; unit < MAX_DRIVES; ++unit)
+		pre_reset(&hwif->drives[unit]);
 
 #if OK_TO_RESET_CONTROLLER
 	/*
@@ -855,10 +799,6 @@
 void ide_do_reset (ide_drive_t *drive)
 {
 	do_reset1 (drive, 0);
-#ifdef CONFIG_BLK_DEV_IDETAPE
-	if (drive->media == ide_tape)
-		drive->tape.reset_issued=1;
-#endif /* CONFIG_BLK_DEV_IDETAPE */
 }
 
 /*
@@ -897,7 +837,7 @@
 	byte err = 0;
 
 	save_flags (flags);
-	sti();
+	ide_sti();
 	printk("%s: %s: status=0x%02x", drive->name, msg, stat);
 #if FANCY_STATUS_DUMPS
 	if (drive->media == ide_disk) {
@@ -1009,16 +949,10 @@
 		rq->errors |= ERROR_RESET;	/* Mmmm.. timing problem */
 
 	if (rq->errors >= ERROR_MAX) {
-#ifdef CONFIG_BLK_DEV_IDETAPE
-		if (drive->media == ide_tape) {
-			rq->errors = 0;
-			idetape_end_request(0, HWGROUP(drive));
-		}
-		else
-#endif /* CONFIG_BLK_DEV_IDETAPE */
+		if (drive->driver != NULL)
+			DRIVER(drive)->end_request(0, HWGROUP(drive));
  		ide_end_request(0, HWGROUP(drive));
-	}
-	else {
+	} else {
 		if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
 			++rq->errors;
 			ide_do_reset(drive);
@@ -1030,153 +964,10 @@
 }
 
 /*
- * read_intr() is the handler for disk read/multread interrupts
- */
-static void read_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	unsigned int msect, nsect;
-	struct request *rq;
-
-	if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
-		ide_error(drive, "read_intr", stat);
-		return;
-	}
-	msect = drive->mult_count;
-read_next:
-	rq = HWGROUP(drive)->rq;
-	if (msect) {
-		if ((nsect = rq->current_nr_sectors) > msect)
-			nsect = msect;
-		msect -= nsect;
-	} else
-		nsect = 1;
-	ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
-#ifdef DEBUG
-	printk("%s:  read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
-		drive->name, rq->sector, rq->sector+nsect-1,
-		(unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
-#endif
-	rq->sector += nsect;
-	rq->buffer += nsect<<9;
-	rq->errors = 0;
-	i = (rq->nr_sectors -= nsect);
-	if ((rq->current_nr_sectors -= nsect) <= 0)
-		ide_end_request(1, HWGROUP(drive));
-	if (i > 0) {
-		if (msect)
-			goto read_next;
-		ide_set_handler (drive, &read_intr, WAIT_CMD);
-	}
-}
-
-/*
- * write_intr() is the handler for disk write interrupts
- */
-static void write_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = hwgroup->rq;
-
-	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
-#ifdef DEBUG
-		printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
-			drive->name, rq->sector, (unsigned long) rq->buffer,
-			rq->nr_sectors-1);
-#endif
-		if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
-			rq->sector++;
-			rq->buffer += 512;
-			rq->errors = 0;
-			i = --rq->nr_sectors;
-			--rq->current_nr_sectors;
-			if (rq->current_nr_sectors <= 0)
-				ide_end_request(1, hwgroup);
-			if (i > 0) {
-				ide_output_data (drive, rq->buffer, SECTOR_WORDS);
-				ide_set_handler (drive, &write_intr, WAIT_CMD);
-			}
-			return;
-		}
-	}
-	ide_error(drive, "write_intr", stat);
-}
-
-/*
- * ide_multwrite() transfers a block of up to mcount sectors of data
- * to a drive as part of a disk multiple-sector write operation.
- */
-void ide_multwrite (ide_drive_t *drive, unsigned int mcount)
-{
-	struct request *rq = &HWGROUP(drive)->wrq;
-
-	do {
-		unsigned int nsect = rq->current_nr_sectors;
-		if (nsect > mcount)
-			nsect = mcount;
-		mcount -= nsect;
-
-		ide_output_data(drive, rq->buffer, nsect<<7);
-#ifdef DEBUG
-		printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
-			drive->name, rq->sector, (unsigned long) rq->buffer,
-			nsect, rq->nr_sectors - nsect);
-#endif
-		if ((rq->nr_sectors -= nsect) <= 0)
-			break;
-		if ((rq->current_nr_sectors -= nsect) == 0) {
-			if ((rq->bh = rq->bh->b_reqnext) != NULL) {
-				rq->current_nr_sectors = rq->bh->b_size>>9;
-				rq->buffer             = rq->bh->b_data;
-			} else {
-				panic("%s: buffer list corrupted\n", drive->name);
-				break;
-			}
-		} else {
-			rq->buffer += nsect << 9;
-		}
-	} while (mcount);
-}
-
-/*
- * multwrite_intr() is the handler for disk multwrite interrupts
- */
-static void multwrite_intr (ide_drive_t *drive)
-{
-	byte stat;
-	int i;
-	ide_hwgroup_t *hwgroup = HWGROUP(drive);
-	struct request *rq = &hwgroup->wrq;
-
-	if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
-		if (stat & DRQ_STAT) {
-			if (rq->nr_sectors) {
-				ide_multwrite(drive, drive->mult_count);
-				ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-				return;
-			}
-		} else {
-			if (!rq->nr_sectors) {	/* all done? */
-				rq = hwgroup->rq;
-				for (i = rq->nr_sectors; i > 0;){
-					i -= rq->current_nr_sectors;
-					ide_end_request(1, hwgroup);
-				}
-				return;
-			}
-		}
-	}
-	ide_error(drive, "multwrite_intr", stat);
-}
-
-/*
  * Issue a simple drive command
  * The drive must be selected beforehand.
  */
-static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
 {
 	ide_set_handler (drive, handler, WAIT_CMD);
 	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
@@ -1185,47 +976,6 @@
 }
 
 /*
- * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
- */
-static void set_multmode_intr (ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	sti();
-	if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
-		drive->mult_count = drive->mult_req;
-	} else {
-		drive->mult_req = drive->mult_count = 0;
-		drive->special.b.recalibrate = 1;
-		(void) ide_dump_status(drive, "set_multmode", stat);
-	}
-}
-
-/*
- * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
- */
-static void set_geometry_intr (ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	sti();
-	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		ide_error(drive, "set_geometry_intr", stat);
-}
-
-/*
- * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
- */
-static void recal_intr (ide_drive_t *drive)
-{
-	byte stat = GET_STAT();
-
-	sti();
-	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
-		ide_error(drive, "recal_intr", stat);
-}
-
-/*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
 static void drive_cmd_intr (ide_drive_t *drive)
@@ -1234,7 +984,7 @@
 	byte *args = (byte *) rq->buffer;
 	byte stat = GET_STAT();
 
-	sti();
+	ide_sti();
 	if ((stat & DRQ_STAT) && args && args[3]) {
 		byte io_32bit = drive->io_32bit;
 		drive->io_32bit = 0;
@@ -1259,38 +1009,16 @@
 #ifdef DEBUG
 	printk("%s: do_special: 0x%02x\n", drive->name, s->all);
 #endif
-	if (s->b.set_geometry) {
-		s->b.set_geometry = 0;
-		if (drive->media == ide_disk) {
-			OUT_BYTE(drive->sect,IDE_SECTOR_REG);
-			OUT_BYTE(drive->cyl,IDE_LCYL_REG);
-			OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
-			OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
-			if (!IS_PROMISE_DRIVE)
-				ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
-		}
-	} else if (s->b.recalibrate) {
-		s->b.recalibrate = 0;
-		if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
-			ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
-	} else if (s->b.set_tune) {
+	if (s->b.set_tune) {
 		ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
 		s->b.set_tune = 0;
 		if (tuneproc != NULL)
 			tuneproc(drive, drive->tune_req);
-	} else if (s->b.set_multmode) {
-		s->b.set_multmode = 0;
-		if (drive->media == ide_disk) {
-			if (drive->id && drive->mult_req > drive->id->max_multsect)
-				drive->mult_req = drive->id->max_multsect;
-			if (!IS_PROMISE_DRIVE)
-				ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
-		} else
-			drive->mult_req = 0;
+	} else if (drive->driver != NULL) {
+		DRIVER(drive)->special(drive);
 	} else if (s->all) {
-		int special = s->all;
+		printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
 		s->all = 0;
-		printk("%s: bad special flag: 0x%02x\n", drive->name, special);
 	}
 }
 
@@ -1320,7 +1048,7 @@
 	}
 
 	save_flags(flags);
-	sti();
+	ide_sti();
 	timeout += jiffies;
 	do {
 		if (!((stat = GET_STAT()) & BUSY_STAT)) {
@@ -1335,98 +1063,6 @@
 }
 
 /*
- * do_rw_disk() issues READ and WRITE commands to a disk,
- * using LBA if supported, or CHS otherwise, to address sectors.
- * It also takes care of issuing special DRIVE_CMDs.
- */
-static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
-{
-	ide_hwif_t *hwif = HWIF(drive);
-	unsigned short io_base = hwif->io_base;
-#ifdef CONFIG_BLK_DEV_PROMISE
-	int use_promise_io = 0;
-#endif /* CONFIG_BLK_DEV_PROMISE */
-
-	OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
-	OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);
-#ifdef CONFIG_BLK_DEV_PROMISE
-	if (IS_PROMISE_DRIVE) {
-		if (hwif->is_promise2 || rq->cmd == READ) {
-			use_promise_io = 1;
-		}
-	}
-	if (drive->select.b.lba || use_promise_io) {
-#else /* !CONFIG_BLK_DEV_PROMISE */
-	if (drive->select.b.lba) {
-#endif /* CONFIG_BLK_DEV_PROMISE */
-#ifdef DEBUG
-		printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
-			drive->name, (rq->cmd==READ)?"read":"writ",
-			block, rq->nr_sectors, (unsigned long) rq->buffer);
-#endif
-		OUT_BYTE(block,io_base+IDE_SECTOR_OFFSET);
-		OUT_BYTE(block>>=8,io_base+IDE_LCYL_OFFSET);
-		OUT_BYTE(block>>=8,io_base+IDE_HCYL_OFFSET);
-		OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+IDE_SELECT_OFFSET);
-	} else {
-		unsigned int sect,head,cyl,track;
-		track = block / drive->sect;
-		sect  = block % drive->sect + 1;
-		OUT_BYTE(sect,io_base+IDE_SECTOR_OFFSET);
-		head  = track % drive->head;
-		cyl   = track / drive->head;
-		OUT_BYTE(cyl,io_base+IDE_LCYL_OFFSET);
-		OUT_BYTE(cyl>>8,io_base+IDE_HCYL_OFFSET);
-		OUT_BYTE(head|drive->select.all,io_base+IDE_SELECT_OFFSET);
-#ifdef DEBUG
-		printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
-			drive->name, (rq->cmd==READ)?"read":"writ", cyl,
-			head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
-#endif
-	}
-#ifdef CONFIG_BLK_DEV_PROMISE
-	if (use_promise_io) {
-		do_promise_io (drive, rq);
-		return;
-	}
-#endif /* CONFIG_BLK_DEV_PROMISE */
-	if (rq->cmd == READ) {
-#ifdef CONFIG_BLK_DEV_TRITON
-		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
-			return;
-#endif /* CONFIG_BLK_DEV_TRITON */
-		ide_set_handler(drive, &read_intr, WAIT_CMD);
-		OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
-		return;
-	}
-	if (rq->cmd == WRITE) {
-#ifdef CONFIG_BLK_DEV_TRITON
-		if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
-			return;
-#endif /* CONFIG_BLK_DEV_TRITON */
-		OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+IDE_COMMAND_OFFSET);
-		if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
-			printk("%s: no DRQ after issuing %s\n", drive->name,
-				drive->mult_count ? "MULTWRITE" : "WRITE");
-			return;
-		}
-		if (!drive->unmask)
-			cli();
-		if (drive->mult_count) {
-			HWGROUP(drive)->wrq = *rq; /* scratchpad */
-			ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
-			ide_multwrite(drive, drive->mult_count);
-		} else {
-			ide_set_handler (drive, &write_intr, WAIT_CMD);
-			ide_output_data(drive, rq->buffer, SECTOR_WORDS);
-		}
-		return;
-	}
-	printk("%s: bad command: %d\n", drive->name, rq->cmd);
-	ide_end_request(0, HWGROUP(drive));
-}
-
-/*
  * execute_drive_cmd() issues a special drive command,
  * usually initiated by ioctl() from the external hdparm program.
  */
@@ -1461,9 +1097,9 @@
 {
 	unsigned int minor, unit;
 	unsigned long block, blockend;
-	ide_drive_t *drive;
+	ide_drive_t *drive = NULL;
 
-	sti();
+	ide_sti();
 #ifdef DEBUG
 	printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
 #endif
@@ -1498,10 +1134,6 @@
 	while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
 #endif
 
-#ifdef CONFIG_BLK_DEV_IDETAPE
-	POLL_HWIF_TAPE_DRIVE;	/* macro from ide-tape.h */
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-
 	SELECT_DRIVE(hwif,drive);
 	if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
 		printk("%s: drive not ready for command\n", drive->name);
@@ -1513,36 +1145,20 @@
 			execute_drive_cmd(drive, rq);
 			return;
 		}
-#ifdef CONFIG_BLK_DEV_IDEATAPI
-		switch (drive->media) {
-			case ide_disk:
-				do_rw_disk (drive, rq, block);
-				return;
-#ifdef CONFIG_BLK_DEV_IDECD
-			case ide_cdrom:
-				ide_do_rw_cdrom (drive, block);
-				return;
-#endif /* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-			case ide_tape:
-				idetape_do_request (drive, rq, block);
-				return;
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-
-			default:
-				printk("%s: media type %d not supported\n",
-					drive->name, drive->media);
-				goto kill_rq;
+		if (drive->driver != NULL) {
+			DRIVER(drive)->do_request(drive, rq, block);
+			return;
 		}
-#else
-		do_rw_disk (drive, rq, block); /* simpler and faster */
-		return;
-#endif /* CONFIG_BLK_DEV_IDEATAPI */;
+		printk("%s: media type %d not supported\n", drive->name, drive->media);
+		goto kill_rq;
 	}
 	do_special(drive);
 	return;
 kill_rq:
-	ide_end_request(0, hwif->hwgroup);
+	if (drive != NULL && drive->driver != NULL)
+		DRIVER(drive)->end_request(0, HWGROUP(drive));
+	else
+		ide_end_request(0, hwif->hwgroup);
 }
 
 /*
@@ -1572,7 +1188,7 @@
 		struct request *rq;
 		if ((rq = hwgroup->rq) == NULL) {
 			if (hwif->sharing_irq && hwgroup->drive) /* set nIEN */
-				OUT_BYTE(hwgroup->drive->ctl|2,hwif->ctl_port);
+				OUT_BYTE(hwgroup->drive->ctl|2,hwif->io_ports[IDE_CONTROL_OFFSET]);
 			/*
 			 * hwgroup->next_hwif is different from hwgroup->hwif
 			 * only when a request is inserted using "ide_next".
@@ -1584,6 +1200,7 @@
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
 			} while ((hwif = hwif->next) != hwgroup->next_hwif);
+			ide_release_lock(&ide_lock);
 			return;		/* no work left for this hwgroup */
 		}
 	got_rq:	
@@ -1608,6 +1225,8 @@
 	if (hwgroup->handler == NULL) {
 		ide_hwif_t *hgif = hwgroup->hwif;
 		ide_hwif_t *hwif = hgif;
+
+		ide_get_lock(&ide_lock, ide_intr, hwgroup);
 		do {
 			disable_irq(hwif->irq);
 		} while ((hwif = hwif->next) != hgif);
@@ -1618,33 +1237,33 @@
 	}
 }
 
-static void do_ide0_request (void)	/* invoked with cli() */
+void do_ide0_request (void)	/* invoked with cli() */
 {
 	do_hwgroup_request (ide_hwifs[0].hwgroup);
 }
 
 #if MAX_HWIFS > 1
-static void do_ide1_request (void)	/* invoked with cli() */
+void do_ide1_request (void)	/* invoked with cli() */
 {
 	do_hwgroup_request (ide_hwifs[1].hwgroup);
 }
-#endif
+#endif /* MAX_HWIFS > 1 */
 
 #if MAX_HWIFS > 2
-static void do_ide2_request (void)	/* invoked with cli() */
+void do_ide2_request (void)	/* invoked with cli() */
 {
 	do_hwgroup_request (ide_hwifs[2].hwgroup);
 }
-#endif
+#endif /* MAX_HWIFS > 2 */
 
 #if MAX_HWIFS > 3
-static void do_ide3_request (void)	/* invoked with cli() */
+void do_ide3_request (void)	/* invoked with cli() */
 {
 	do_hwgroup_request (ide_hwifs[3].hwgroup);
 }
-#endif
+#endif /* MAX_HWIFS > 3 */
 
-static void timer_expiry (unsigned long data)
+void ide_timer_expiry (unsigned long data)
 {
 	ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
 	ide_drive_t   *drive   = hwgroup->drive;
@@ -1658,7 +1277,7 @@
 		hwgroup->handler = NULL;
 		handler(drive);
 	} else if (hwgroup->handler == NULL) {	 /* not waiting for anything? */
-		sti(); /* drive must have responded just as the timer expired */
+		ide_sti(); /* drive must have responded just as the timer expired */
 		printk("%s: marginal timeout\n", drive->name);
 	} else {
 		hwgroup->handler = NULL;	/* abort the operation */
@@ -1736,17 +1355,21 @@
 	ide_hwgroup_t *hwgroup = dev_id;
 	ide_handler_t *handler;
 
+	if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_DATA_OFFSET],
+			   hwgroup->hwif->io_ports[IDE_IRQ_OFFSET]))
+		return;
+
 	if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
 		ide_drive_t *drive = hwgroup->drive;
 		hwgroup->handler = NULL;
 		del_timer(&(hwgroup->timer));
 		if (drive->unmask)
-			sti();
+			ide_sti();
 		handler(drive);
 		cli();	/* this is necessary, as next rq may be different irq */
 		if (hwgroup->handler == NULL) {
 			SET_RECOVERY_TIMER(HWIF(drive));
- 			ide_do_request(hwgroup);
+			ide_do_request(hwgroup);
 		}
 	} else {
 		unexpected_intr(irq, hwgroup);
@@ -1795,12 +1418,6 @@
 	rq->bh = NULL;
 	rq->bhtail = NULL;
 	rq->next = NULL;
-
-#if 0	/* these are done each time through ide_do_drive_cmd() */
-	rq->errors = 0;
-	rq->rq_status = RQ_ACTIVE;
-	rq->rq_dev = ????;
-#endif
 }
 
 /*
@@ -1876,96 +1493,24 @@
 	return rq->errors ? -EIO : 0;	/* return -EIO if errors */
 }
 
-static int ide_open(struct inode * inode, struct file * filp)
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0.  If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int ide_revalidate_disk(kdev_t i_rdev)
 {
 	ide_drive_t *drive;
-	unsigned long flags;
+	unsigned int p, major, minor;
+	long flags;
 
-	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
-		return -ENXIO;
-	save_flags(flags);
-	cli();
-	while (drive->busy)
-		sleep_on(&drive->wqueue);
-	drive->usage++;
-	restore_flags(flags);
-#ifdef CONFIG_BLK_DEV_IDECD
-	if (drive->media == ide_cdrom)
-		return ide_cdrom_open (inode, filp, drive);
-#endif	/* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-	if (drive->media == ide_tape)
-		return idetape_blkdev_open (inode, filp, drive);
-#endif	/* CONFIG_BLK_DEV_IDETAPE */
-	if (drive->removable && drive->usage == 1) {
-		byte door_lock[] = {WIN_DOORLOCK,0,0,0};
-		struct request rq;
-		check_disk_change(inode->i_rdev);
-		ide_init_drive_cmd (&rq);
-		rq.buffer = door_lock;
-		/*
-		 * Ignore the return code from door_lock,
-		 * since the open() has already succeeded,
-		 * and the door_lock is irrelevant at this point.
-		 */
-		(void) ide_do_drive_cmd(drive, &rq, ide_wait);
-	}
-	return 0;
-}
-
-/*
- * Releasing a block device means we sync() it, so that it can safely
- * be forgotten about...
- */
-static void ide_release(struct inode * inode, struct file * file)
-{
-	ide_drive_t *drive;
-
-	if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
-		fsync_dev(inode->i_rdev);
-		drive->usage--;
-#ifdef CONFIG_BLK_DEV_IDECD
-		if (drive->media == ide_cdrom) {
-			ide_cdrom_release (inode, file, drive);
-			return;
-		}
-#endif	/* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-		if (drive->media == ide_tape) {
-			idetape_blkdev_release (inode, file, drive);
-			return;
-		}
-#endif	/* CONFIG_BLK_DEV_IDETAPE */
-		if (drive->removable && !drive->usage) {
-			byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
-			struct request rq;
-			invalidate_buffers(inode->i_rdev);
-			ide_init_drive_cmd (&rq);
-			rq.buffer = door_unlock;
-			(void) ide_do_drive_cmd(drive, &rq, ide_wait);
-		}
-	}
-}
-
-/*
- * This routine is called to flush all partitions and partition tables
- * for a changed disk, and then re-read the new partition table.
- * If we are revalidating a disk because of a media change, then we
- * enter with usage == 0.  If we are using an ioctl, we automatically have
- * usage == 1 (we need an open channel to use an ioctl :-), so this
- * is our limit.
- */
-static int revalidate_disk(kdev_t i_rdev)
-{
-	ide_drive_t *drive;
-	unsigned int p, major, minor;
-	long flags;
-
-	if ((drive = get_info_ptr(i_rdev)) == NULL)
-		return -ENODEV;
-
-	major = MAJOR(i_rdev);
-	minor = drive->select.b.unit << PARTN_BITS;
+	if ((drive = get_info_ptr(i_rdev)) == NULL)
+		return -ENODEV;
+	major = MAJOR(i_rdev);
+	minor = drive->select.b.unit << PARTN_BITS;
 	save_flags(flags);
 	cli();
 	if (drive->busy || (drive->usage > 1)) {
@@ -1973,6 +1518,7 @@
 		return -EBUSY;
 	};
 	drive->busy = 1;
+	MOD_INC_USE_COUNT;
 	restore_flags(flags);
 
 	for (p = 0; p < (1<<PARTN_BITS); ++p) {
@@ -1987,25 +1533,225 @@
 	};
 
 	drive->part[0].nr_sects = current_capacity(drive);
-	if (drive->media != ide_disk)
+	if (drive->media != ide_disk || drive->driver == NULL)
 		drive->part[0].start_sect = -1;
 	resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
 	drive->busy = 0;
 	wake_up(&drive->wqueue);
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
-static int write_fs_long (unsigned long useraddr, long value)
+static void revalidate_drives (void)
 {
-	int err;
+	ide_hwif_t *hwif;
+	ide_drive_t *drive;
+	int index, unit;
 
-	if (NULL == (long *)useraddr)
-		return -EINVAL;
-	if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
-		return err;
-	put_user((unsigned)value, (long *) useraddr);
-	return 0;
+	for (index = 0; index < MAX_HWIFS; ++index) {
+		hwif = &ide_hwifs[index];
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			drive = &ide_hwifs[index].drives[unit];
+			if (drive->revalidate) {
+				drive->revalidate = 0;
+				if (!initializing)
+					(void) ide_revalidate_disk(MKDEV(hwif->major, unit<<PARTN_BITS));
+			}
+		}
+	}
+}
+
+static void ide_init_module (int type)
+{
+	ide_module_t *module = ide_modules;
+	
+	while (module) {
+		if (module->type == type)
+			(void) module->init();
+		module = module->next;
+	}
+	revalidate_drives();
+}
+
+static int ide_open(struct inode * inode, struct file * filp)
+{
+	ide_drive_t *drive;
+	int rc;
+
+	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+		return -ENXIO;
+	MOD_INC_USE_COUNT;
+	if (drive->driver == NULL)
+		ide_init_module(IDE_DRIVER_MODULE);
+#ifdef CONFIG_KERNELD
+	if (drive->driver == NULL) {
+		if (drive->media == ide_disk)
+			(void) request_module("ide-disk");
+		if (drive->media == ide_cdrom)
+			(void) request_module("ide-cd");
+		if (drive->media == ide_tape)
+			(void) request_module("ide-tape");
+		if (drive->media == ide_floppy)
+			(void) request_module("ide-floppy");
+	}
+#endif /* CONFIG_KERNELD */
+	while (drive->busy)
+		sleep_on(&drive->wqueue);
+	drive->usage++;
+	if (drive->driver != NULL) {
+		if ((rc = DRIVER(drive)->open(inode, filp, drive)))
+			MOD_DEC_USE_COUNT;
+		return rc;
+	}
+	printk ("%s: driver not present\n", drive->name);
+	drive->usage--;
+	MOD_DEC_USE_COUNT;
+	return -ENXIO;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void ide_release(struct inode * inode, struct file * file)
+{
+	ide_drive_t *drive;
+
+	if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+		fsync_dev(inode->i_rdev);
+		drive->usage--;
+		if (drive->driver != NULL)
+			DRIVER(drive)->release(inode, file, drive);
+		MOD_DEC_USE_COUNT;
+	}
+}
+
+void ide_unregister (unsigned int index)
+{
+	struct gendisk *gd, **gdp;
+	ide_drive_t *drive;
+	ide_hwif_t *hwif, *g;
+	ide_hwgroup_t *hwgroup;
+	int irq_count = 0, unit;
+	unsigned long flags;
+
+	if (index >= MAX_HWIFS)
+		return;
+	save_flags(flags);
+	cli();
+	hwif = &ide_hwifs[index];
+	if (!hwif->present)
+		goto abort;
+	for (unit = 0; unit < MAX_DRIVES; ++unit) {
+		drive = &hwif->drives[unit];
+		if (!drive->present)
+			continue;
+		if (drive->busy || drive->usage)
+			goto abort;
+		if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+			goto abort;
+		if (drive->id != NULL) {
+			kfree(drive->id);
+			drive->id = NULL;
+		}
+		drive->present = 0;
+	}
+	hwif->present = 0;
+	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)
+		free_irq(hwif->irq, hwgroup);
+
+	/*
+	 * Note that we only release the standard ports,
+	 * and do not even try to handle any extra ports
+	 * allocated for weird IDE interface chipsets.
+	 */
+	ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8);
+	ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1);
+
+	/*
+	 * Remove us from the hwgroup, and free
+	 * the hwgroup if we were the only member
+	 */
+	while (hwgroup->hwif->next != hwif)
+		hwgroup->hwif = hwgroup->hwif->next;
+	hwgroup->hwif->next = hwif->next;
+	if (hwgroup->hwif == hwif)
+		hwgroup->hwif = hwif->next;
+	if (hwgroup->next_hwif == hwif)
+		hwgroup->next_hwif = hwif->next;
+	if (hwgroup->hwif == hwif)
+		kfree(hwgroup);
+
+	/*
+	 * Remove us from the kernel's knowledge
+	 */
+	unregister_blkdev(hwif->major, hwif->name);
+	kfree(blksize_size[hwif->major]);
+	blk_dev[hwif->major].request_fn = NULL;
+	blksize_size[hwif->major] = NULL;
+	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+		if (*gdp == hwif->gd)
+			break;
+	if (*gdp == NULL)
+		printk("gd not in disk chain!\n");
+	else {
+		gd = *gdp; *gdp = gd->next;
+		kfree(gd->sizes);
+		kfree(gd->part);
+		kfree(gd);
+	}
+	init_hwif_data (index);	/* restore hwif data to pristine status */
+abort:
+	restore_flags(flags);
+}
+
+int ide_register (int arg1, int arg2, int irq)
+{
+	int index, retry = 1;
+	ide_hwif_t *hwif;
+	ide_ioreg_t data_port = (ide_ioreg_t) arg1, ctl_port = (ide_ioreg_t) arg2;
+
+	do {
+		for (index = 0; index < MAX_HWIFS; ++index) {
+			hwif = &ide_hwifs[index];
+			if (hwif->io_ports[IDE_DATA_OFFSET] == data_port)
+				goto found;
+		}
+		for (index = 0; index < MAX_HWIFS; ++index) {
+			hwif = &ide_hwifs[index];
+			if (!hwif->present) {
+				ide_init_hwif_ports(hwif->io_ports, data_port, &hwif->irq);
+				if (ctl_port)
+					hwif->io_ports[IDE_CONTROL_OFFSET] = ctl_port;
+				hwif->irq = irq;
+				goto found;
+			}
+		}
+		for (index = 0; index < MAX_HWIFS; index++)
+			ide_unregister(index);
+	} while (retry--);
+	return -1;
+found:
+	if (hwif->present)
+		ide_unregister(index);
+	if (hwif->present)
+		return -1;
+	hwif->noprobe = 0;
+	ide_init_module(IDE_PROBE_MODULE);
+	ide_init_module(IDE_DRIVER_MODULE);
+	return hwif->present ? index : -1;
 }
 
 static int ide_ioctl (struct inode *inode, struct file *file,
@@ -2026,13 +1772,11 @@
 		{
 			struct hd_geometry *loc = (struct hd_geometry *) arg;
 			if (!loc || drive->media != ide_disk) return -EINVAL;
-			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
-			if (err) return err;
-			put_user(drive->bios_head, (byte *) &loc->heads);
-			put_user(drive->bios_sect, (byte *) &loc->sectors);
-			put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders);
-			put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
-				(unsigned long *) &loc->start);
+			if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT;
+			if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT;
+			if (put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT;
+			if (put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+				(unsigned long *) &loc->start)) return -EFAULT;
 			return 0;
 		}
 		case BLKFLSBUF:
@@ -2048,48 +1792,46 @@
 			return 0;
 
 		case BLKRAGET:
-			return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
+			return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
 
 	 	case BLKGETSIZE:   /* Return device size */
-			return write_fs_long(arg, drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects);
+			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
+
 		case BLKRRPART: /* Re-read partition tables */
 			if (!suser()) return -EACCES;
-			return revalidate_disk(inode->i_rdev);
+			return ide_revalidate_disk(inode->i_rdev);
 
 		case HDIO_GET_KEEPSETTINGS:
-			return write_fs_long(arg, drive->keep_settings);
+			return put_user(drive->keep_settings, (long *) arg);
 
 		case HDIO_GET_UNMASKINTR:
-			return write_fs_long(arg, drive->unmask);
+			return put_user(drive->unmask, (long *) arg);
 
 		case HDIO_GET_DMA:
-			return write_fs_long(arg, drive->using_dma);
+			return put_user(drive->using_dma, (long *) arg);
 
 		case HDIO_GET_32BIT:
-			return write_fs_long(arg, drive->io_32bit);
+			return put_user(drive->io_32bit, (long *) arg);
 
 		case HDIO_GET_MULTCOUNT:
-			return write_fs_long(arg, drive->mult_count);
+			return put_user(drive->mult_count, (long *) arg);
 
 		case HDIO_GET_IDENTITY:
-			if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
+			if (MINOR(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
 			if (drive->id == NULL)
 				return -ENOMSG;
-			err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
-			if (!err)
-				copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id));
-			return err;
+			if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id)))
+				return -EFAULT;
+			return 0;
 
-			case HDIO_GET_NOWERR:
-			return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
+		case HDIO_GET_NOWERR:
+			return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
 
 		case HDIO_SET_DMA:
 			if (!suser()) return -EACCES;
-#ifdef CONFIG_BLK_DEV_IDECD
-			if (drive->media == ide_cdrom)
+			if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
 				return -EPERM;
-#endif /* CONFIG_BLK_DEV_IDECD */
 			if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
 				return -EPERM;
 		case HDIO_SET_KEEPSETTINGS:
@@ -2166,28 +1908,23 @@
 			byte args[4], *argbuf = args;
 			int argsize = 4;
 			if (!suser()) return -EACCES;
-			if (NULL == (void *) arg) {
-				err = ide_do_drive_cmd(drive, &rq, ide_wait);
-			} else if (!(err = verify_area(VERIFY_READ,(void *)arg, 4))) {
-				copy_from_user(args, (void *)arg, 4);
-				if (args[3]) {
-					argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
-					argbuf = kmalloc(argsize, GFP_KERNEL);
-					if (argbuf == NULL)
-						return -ENOMEM;
-					argbuf[0] = args[0];
-					argbuf[1] = args[1];
-					argbuf[2] = args[2];
-					argbuf[3] = args[3];
-				}
-				if (!(err = verify_area(VERIFY_WRITE,(void *)arg, argsize))) {
-					rq.buffer = argbuf;
-					err = ide_do_drive_cmd(drive, &rq, ide_wait);
-					copy_to_user((void *)arg, argbuf, argsize);
-				}
-				if (argsize > 4)
-					kfree(argbuf);
-			}
+			if (NULL == (void *) arg)
+				return ide_do_drive_cmd(drive, &rq, ide_wait);
+			if (copy_from_user(args, (void *)arg, 4))
+				return -EFAULT;
+			if (args[3]) {
+				argsize = 4 + (SECTOR_WORDS * 4 * args[3]);
+				argbuf = kmalloc(argsize, GFP_KERNEL);
+				if (argbuf == NULL)
+					return -ENOMEM;
+				memcpy(argbuf, args, 4);
+			}
+			rq.buffer = argbuf;
+			err = ide_do_drive_cmd(drive, &rq, ide_wait);
+			if (copy_to_user((void *)arg, argbuf, argsize))
+				err = -EFAULT;
+			if (argsize > 4)
+				kfree(argbuf);
 			return err;
 		}
 		case HDIO_SET_PIO_MODE:
@@ -2208,17 +1945,22 @@
 			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
 			return 0;
 
+		case HDIO_SCAN_HWIF:
+		{
+			int args[3];
+			if (!suser()) return -EACCES;
+			if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
+				return -EFAULT;
+			if (ide_register(args[0], args[1], args[2]) == -1)
+				return -EIO;
+			return 0;
+		}
+
 		RO_IOCTLS(inode->i_rdev, arg);
 
 		default:
-#ifdef CONFIG_BLK_DEV_IDECD
-			if (drive->media == ide_cdrom)
-				return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
-#endif /* CONFIG_BLK_DEV_IDECD */
-#ifdef CONFIG_BLK_DEV_IDETAPE
-			if (drive->media == ide_tape)
-				return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
-#endif /* CONFIG_BLK_DEV_IDETAPE */
+			if (drive->driver != NULL)
+				return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg);
 			return -EPERM;
 	}
 }
@@ -2229,12 +1971,8 @@
 
 	if ((drive = get_info_ptr(i_rdev)) == NULL)
 		return -ENODEV;
-#ifdef CONFIG_BLK_DEV_IDECD
-	if (drive->media == ide_cdrom)
-		return ide_cdrom_check_media_change (drive);
-#endif	/* CONFIG_BLK_DEV_IDECD */
-	if (drive->removable) /* for disks */
-		return 1;	/* always assume it was changed */
+	if (drive->driver != NULL)
+		return DRIVER(drive)->media_change(drive);
 	return 0;
 }
 
@@ -2265,506 +2003,18 @@
 		*p++ = '\0';
 }
 
-static inline void do_identify (ide_drive_t *drive, byte cmd)
-{
-	int bswap;
-	struct hd_driveid *id;
-	unsigned long capacity, check;
-
-	id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
-	ide_input_data(drive, id, SECTOR_WORDS);/* read 512 bytes of id info */
-	sti();
-
-#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO)
-	/*
-	 * EATA SCSI controllers do a hardware ATA emulation:  
-	 * Ignore them if there is a driver for them available.
-	 */
-	if ((id->model[0] == 'P' && id->model[1] == 'M')
-	 || (id->model[0] == 'S' && id->model[1] == 'K')) {
-		printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
-		drive->present = 0;
-		return;
-	}
-#endif
-
-	/*
-	 *  WIN_IDENTIFY returns little-endian info,
-	 *  WIN_PIDENTIFY *usually* returns little-endian info.
-	 */
-	bswap = 1;
-	if (cmd == WIN_PIDENTIFY) {
-		if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
-		 || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
-		 || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
-			bswap = 0;	/* Vertos drives may still be weird */
-	}
-	ide_fixstring (id->model,     sizeof(id->model),     bswap);
-	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
-	ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
-
-#ifdef CONFIG_BLK_DEV_IDEATAPI
-	/*
-	 * Check for an ATAPI device
-	 */
-	if (cmd == WIN_PIDENTIFY) {
-		byte type = (id->config >> 8) & 0x1f;
-		printk("%s: %s, ATAPI ", drive->name, id->model);
-#ifdef CONFIG_BLK_DEV_PROMISE
-		if (HWIF(drive)->is_promise2) {
-			printk(" -- not supported on 2nd Promise port\n");
-			drive->present = 0;
-			return;
-		}
-#endif /* CONFIG_BLK_DEV_PROMISE */
-		switch (type) {
-			case 0:		/* Early cdrom models used zero */
-			case 5:
-#ifdef CONFIG_BLK_DEV_IDECD
-				printk ("CDROM drive\n");
-				drive->media = ide_cdrom;
- 				drive->present = 1;
-				drive->removable = 1;
-				return;
-#else
-				printk ("CDROM ");
-				break;
-#endif /* CONFIG_BLK_DEV_IDECD */
-			case 1:
-#ifdef CONFIG_BLK_DEV_IDETAPE
-				printk ("TAPE drive");
-				if (idetape_identify_device (drive,id)) {
-					drive->media = ide_tape;
-					drive->present = 1;
-					drive->removable = 1;
-					if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
-						if (!HWIF(drive)->dmaproc(ide_dma_check, drive))
-							printk(", DMA");
-					}
-					printk("\n");
-				}
-				else {
-					drive->present = 0;
-					printk ("\nide-tape: the tape is not supported by this version of the driver\n");
-				}
-				return;
-#else
-				printk ("TAPE ");
-				break;
-#endif /* CONFIG_BLK_DEV_IDETAPE */
-			default:
-				drive->present = 0;
-				printk("Type %d - Unknown device\n", type);
-				return;
-		}
-		drive->present = 0;
-		printk("- not supported by this kernel\n");
-		return;
-	}
-#endif /* CONFIG_BLK_DEV_IDEATAPI */
-
-	/* check for removable disks (eg. SYQUEST), ignore 'WD' drives */
-	if (id->config & (1<<7)) {	/* removable disk ? */
-		if (id->model[0] != 'W' || id->model[1] != 'D')
-			drive->removable = 1;
-	}
-
-	/* SunDisk drives: treat as non-removable, force one unit */
-	if (id->model[0] == 'S' && id->model[1] == 'u') {
-		drive->removable = 0;
-		if (drive->select.all & (1<<4)) {
-		    drive->present = 0;
-		    return;
-		}
-	}
-	
-	drive->media = ide_disk;
-	/* Extract geometry if we did not already have one for the drive */
-	if (!drive->present) {
-		drive->present = 1;
-		drive->cyl     = drive->bios_cyl  = id->cyls;
-		drive->head    = drive->bios_head = id->heads;
-		drive->sect    = drive->bios_sect = id->sectors;
-	}
-	/* Handle logical geometry translation by the drive */
-	if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
-	 && (id->cur_heads <= 16) && id->cur_sectors)
-	{
-		/*
-		 * Extract the physical drive geometry for our use.
-		 * Note that we purposely do *not* update the bios info.
-		 * This way, programs that use it (like fdisk) will
-		 * still have the same logical view as the BIOS does,
-		 * which keeps the partition table from being screwed.
-		 *
-		 * An exception to this is the cylinder count,
-		 * which we reexamine later on to correct for 1024 limitations.
-		 */
-		drive->cyl  = id->cur_cyls;
-		drive->head = id->cur_heads;
-		drive->sect = id->cur_sectors;
-
-		/* check for word-swapped "capacity" field in id information */
-		capacity = drive->cyl * drive->head * drive->sect;
-		check = (id->cur_capacity0 << 16) | id->cur_capacity1;
-		if (check == capacity) {	/* was it swapped? */
-			/* yes, bring it into little-endian order: */
-			id->cur_capacity0 = (capacity >>  0) & 0xffff;
-			id->cur_capacity1 = (capacity >> 16) & 0xffff;
-		}
-	}
-	/* Use physical geometry if what we have still makes no sense */
-	if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
-		drive->cyl  = id->cyls;
-		drive->head = id->heads;
-		drive->sect = id->sectors;
-	}
-	/* Correct the number of cyls if the bios value is too small */
-	if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
-		if (drive->cyl > drive->bios_cyl)
-			drive->bios_cyl = drive->cyl;
-	}
-
-	(void) current_capacity (drive); /* initialize LBA selection */
-
-	printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
-	 drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
-	 drive->select.b.lba ? "LBA, " : "",
-	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
-
-	drive->mult_count = 0;
-	if (id->max_multsect) {
-		drive->mult_req = INITIAL_MULT_COUNT;
-		if (drive->mult_req > id->max_multsect)
-			drive->mult_req = id->max_multsect;
-		if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
-			drive->special.b.set_multmode = 1;
-	}
-	if (drive->autotune != 2 && HWIF(drive)->dmaproc != NULL) {
-		if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
-			printk(", DMA");
-	}
-	printk("\n");
-}
-
 /*
- * 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.
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
  */
-static void delay_50ms (void)
+static int stridx (const char *s, char c)
 {
-	unsigned long timer = jiffies + ((HZ + 19)/20) + 1;
-	while (timer > jiffies);
+	char *i = strchr(s, c);
+	return (i && c) ? i - s : -1;
 }
 
 /*
- * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
- * and waits for a response.  It also monitors irqs while this is
- * happening, in hope of automatically determining which one is
- * being used by the interface.
- *
- * Returns:	0  device was identified
- *		1  device timed-out (no response to identify request)
- *		2  device aborted the command (refused to identify itself)
- */
-static int try_to_identify (ide_drive_t *drive, byte cmd)
-{
-	int hd_status, rc;
-	unsigned long timeout;
-	int irqs = 0;
-
-	if (!HWIF(drive)->irq) {		/* already got an IRQ? */
-		probe_irq_off(probe_irq_on());	/* clear dangling irqs */
-		irqs = probe_irq_on();		/* start monitoring irqs */
-		OUT_BYTE(drive->ctl,IDE_CONTROL_REG);	/* enable device irq */
-	}
-
-	delay_50ms();				/* take a deep breath */
-	if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
-		printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
-		hd_status = IDE_STATUS_REG;	/* ancient Seagate drives */
-	} else
-		hd_status = IDE_ALTSTATUS_REG;	/* use non-intrusive polling */
-
-#if CONFIG_BLK_DEV_PROMISE
-	if (IS_PROMISE_DRIVE) {
-		if (promise_cmd(drive,PROMISE_IDENTIFY)) {
-			if (irqs)
-				(void) probe_irq_off(irqs);
-			return 1;
-		}
-	} else
-#endif /* CONFIG_BLK_DEV_PROMISE */
-		OUT_BYTE(cmd,IDE_COMMAND_REG);		/* ask drive for ID */
-	timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
-	timeout += jiffies;
-	do {
-		if (jiffies > timeout) {
-			if (irqs)
-				(void) probe_irq_off(irqs);
-			return 1;	/* drive timed-out */
-		}
-		delay_50ms();		/* give drive a breather */
-	} while (IN_BYTE(hd_status) & BUSY_STAT);
-
-	delay_50ms();		/* wait for IRQ and DRQ_STAT */
-	if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
-		unsigned long flags;
-		save_flags(flags);
-		cli();			/* some systems need this */
-		do_identify(drive, cmd); /* drive returned ID */
-		rc = 0;			/* drive responded with ID */
-		(void) GET_STAT();	/* clear drive IRQ */
-		restore_flags(flags);
-	} else
-		rc = 2;			/* drive refused ID */
-	if (!HWIF(drive)->irq) {
-		irqs = probe_irq_off(irqs);	/* get our irq number */
-		if (irqs > 0) {
-			HWIF(drive)->irq = irqs; /* save it for later */
-			irqs = probe_irq_on();
-			OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */
-			udelay(5);
-			(void) probe_irq_off(irqs);
-			(void) probe_irq_off(probe_irq_on()); /* clear self-inflicted irq */
-			(void) GET_STAT();	/* clear drive IRQ */
-
-		} else {	/* Mmmm.. multiple IRQs.. don't know which was ours */
-			printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
-#ifdef CONFIG_BLK_DEV_CMD640
-#ifdef CMD640_DUMP_REGS
-			if (HWIF(drive)->chipset == ide_cmd640) {
-				printk("%s: Hmmm.. probably a driver problem.\n", drive->name);
-				CMD640_DUMP_REGS;
-			}
-#endif /* CMD640_DUMP_REGS */
-#endif /* CONFIG_BLK_DEV_CMD640 */
-		}
-	}
-	return rc;
-}
-
-/*
- * do_probe() has the difficult job of finding a drive if it exists,
- * without getting hung up if it doesn't exist, without trampling on
- * ethernet cards, and without leaving any IRQs dangling to haunt us later.
- *
- * If a drive is "known" to exist (from CMOS or kernel parameters),
- * but does not respond right away, the probe will "hang in there"
- * for the maximum wait time (about 30 seconds), otherwise it will
- * exit much more quickly.
- *
- * Returns:	0  device was identified
- *		1  device timed-out (no response to identify request)
- *		2  device aborted the command (refused to identify itself)
- *		3  bad status from device (possible for ATAPI drives)
- *		4  probe was not attempted because failure was obvious
- */
-static int do_probe (ide_drive_t *drive, byte cmd)
-{
-	int rc;
-	ide_hwif_t *hwif = HWIF(drive);
-#ifdef CONFIG_BLK_DEV_IDEATAPI
-	if (drive->present) {	/* avoid waiting for inappropriate probes */
-		if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
-			return 4;
-	}
-#endif	/* CONFIG_BLK_DEV_IDEATAPI */
-#ifdef DEBUG
-	printk("probing for %s: present=%d, media=%d, probetype=%s\n",
-		drive->name, drive->present, drive->media,
-		(cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
-#endif
-	SELECT_DRIVE(hwif,drive);
-	delay_50ms();
-	if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
-		OUT_BYTE(0xa0,IDE_SELECT_REG);	/* exit with drive0 selected */
-		delay_50ms();		/* allow BUSY_STAT to assert & clear */
-		return 3;    /* no i/f present: avoid killing ethernet cards */
-	}
-
-	if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
-	 || drive->present || cmd == WIN_PIDENTIFY)
-	{
-		if ((rc = try_to_identify(drive,cmd)))   /* send cmd and wait */
-			rc = try_to_identify(drive,cmd); /* failed: try again */
-		if (rc == 1)
-			printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
-		(void) GET_STAT();		/* ensure drive irq is clear */
-	} else {
-		rc = 3;				/* not present or maybe ATAPI */
-	}
-	if (drive->select.b.unit != 0) {
-		OUT_BYTE(0xa0,IDE_SELECT_REG);	/* exit with drive0 selected */
-		delay_50ms();
-		(void) GET_STAT();		/* ensure drive irq is clear */
-	}
-	return rc;
-}
-
-/*
- * probe_for_drive() tests for existence of a given drive using do_probe().
- *
- * Returns:	0  no device was found
- *		1  device was found (note: drive->present might still be 0)
- */
-static inline byte probe_for_drive (ide_drive_t *drive)
-{
-	if (drive->noprobe)			/* skip probing? */
-		return drive->present;
-	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
-#ifdef CONFIG_BLK_DEV_IDEATAPI
-		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
-#endif	/* CONFIG_BLK_DEV_IDEATAPI */
-	}
-	if (!drive->present)
-		return 0;			/* drive not found */
-	if (drive->id == NULL) {		/* identification failed? */
-		if (drive->media == ide_disk) {
-			printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
-			 drive->name, drive->cyl, drive->head, drive->sect);
-		}
-#ifdef CONFIG_BLK_DEV_IDECD
-		else if (drive->media == ide_cdrom) {
-			printk("%s: ATAPI cdrom (?)\n", drive->name);
-		}
-#endif	/* CONFIG_BLK_DEV_IDECD */
-		else {
-			drive->present = 0;	/* nuke it */
-		}
-	}
-	return 1;	/* drive was found */
-}
-
-/*
- * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
- * controller that is BIOS compatible with ST-506, and thus showing up in our
- * BIOS table, but not register compatible, and therefore not present in CMOS.
- *
- * Furthermore, we will assume that our ST-506 drives <if any> are the primary
- * drives in the system -- the ones reflected as drive 1 or 2.  The first
- * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
- * nibble.  This will be either a 4 bit drive type or 0xf indicating use byte
- * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.  A non-zero value
- * means we have an AT controller hard disk for that drive.
- *
- * Of course, there is no guarantee that either drive is actually on the
- * "primary" IDE interface, but we don't bother trying to sort that out here.
- * If a drive is not actually on the primary interface, then these parameters
- * will be ignored.  This results in the user having to supply the logical
- * drive geometry as a boot parameter for each drive not on the primary i/f.
- *
- * The only "perfect" way to handle this would be to modify the setup.[cS] code
- * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
- * for us during initialization.  I have the necessary docs -- any takers?  -ml
- */
-static void probe_cmos_for_drives (ide_hwif_t *hwif)
-{
-#ifdef __i386__
-	extern struct drive_info_struct drive_info;
-	byte cmos_disks, *BIOS = (byte *) &drive_info;
-	int unit;
-
-#ifdef CONFIG_BLK_DEV_PROMISE
-	if (hwif->is_promise2)
-		return;
-#endif /* CONFIG_BLK_DEV_PROMISE */
-	outb_p(0x12,0x70);		/* specify CMOS address 0x12 */
-	cmos_disks = inb_p(0x71);	/* read the data from 0x12 */
-	/* Extract drive geometry from CMOS+BIOS if not already setup */
-	for (unit = 0; unit < MAX_DRIVES; ++unit) {
-		ide_drive_t *drive = &hwif->drives[unit];
-		if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present && !drive->nobios) {
-			drive->cyl   = drive->bios_cyl  = *(unsigned short *)BIOS;
-			drive->head  = drive->bios_head = *(BIOS+2);
-			drive->sect  = drive->bios_sect = *(BIOS+14);
-			drive->ctl   = *(BIOS+8);
-			drive->present = 1;
-		}
-		BIOS += 16;
-	}
-#endif
-}
-
-/*
- * This routine only knows how to look for drive units 0 and 1
- * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
- */
-static void probe_hwif (ide_hwif_t *hwif)
-{
-	unsigned int unit;
-
-	if (hwif->noprobe)
-		return;
-	if (hwif->io_base == HD_DATA)
-		probe_cmos_for_drives (hwif);
-#if CONFIG_BLK_DEV_PROMISE
-	if (!hwif->is_promise2 &&
-	   (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1))) {
-#else
-	if (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1)) {
-#endif /* CONFIG_BLK_DEV_PROMISE */
-		int msgout = 0;
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			if (drive->present) {
-				drive->present = 0;
-				printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
-				msgout = 1;
-			}
-		}
-		if (!msgout)
-			printk("%s: ports already in use, skipping probe\n", hwif->name);
-	} else {
-		unsigned long flags;
-		save_flags(flags);
-
-		sti();	/* needed for jiffies and irq probing */
-		/*
-		 * Second drive should only exist if first drive was found,
-		 * but a lot of cdrom drives are configured as single slaves.
-		 */
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			(void) probe_for_drive (drive);
-			if (drive->present && drive->media == ide_disk) {
-				if ((!drive->head || drive->head > 16) && !drive->select.b.lba) {
-					printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
-					 drive->name, drive->head);
-					drive->present = 0;
-				}
-			}
-			if (drive->present && !hwif->present) {
-				hwif->present = 1;
-				request_region(hwif->io_base,  8, hwif->name);
-				request_region(hwif->ctl_port, 1, hwif->name);
-			}
-		}
-		restore_flags(flags);
-		for (unit = 0; unit < MAX_DRIVES; ++unit) {
-			ide_drive_t *drive = &hwif->drives[unit];
-			if (drive->present && drive->media != ide_tape) {
-				ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
-				if (tuneproc != NULL && drive->autotune == 1)
-					tuneproc(drive, 255);	/* auto-tune PIO mode */
-			}
-		}
-	}
-}
-
-/*
- * stridx() returns the offset of c within s,
- * or -1 if c is '\0' or not found within s.
- */
-static int stridx (const char *s, char c)
-{
-	char *i = strchr(s, c);
-	return (i && c) ? i - s : -1;
-}
-
-/*
- * match_parm() does parsing for ide_setup():
+ * match_parm() does parsing for ide_setup():
  *
  * 1. the first char of s must be '='.
  * 2. if the remainder matches one of the supplied keywords,
@@ -2837,7 +2087,7 @@
  *				and quite likely to cause trouble with
  *				older/odd IDE drives.
  *
- * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in Mhz,
+ * "idebus=xx"		: inform IDE driver of VESA/PCI bus speed in MHz,
  *				where "xx" is between 20 and 66 inclusive,
  *				used when tuning chipset PIO modes.
  *				For PCI bus, 25 is correct for a P75 system,
@@ -2892,7 +2142,7 @@
 	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",
-				"slow", NULL};
+				"slow", "swapdata", NULL};
 		unit = s[2] - 'a';
 		hw   = unit / MAX_DRIVES;
 		unit = unit % MAX_DRIVES;
@@ -2925,6 +2175,9 @@
 			case -8: /* "slow" */
 				drive->slow = 1;
 				goto done;
+			case -9: /* swapdata */
+				drive->bswap = 1;
+				goto done;
 			case 3: /* cyl,head,sect */
 				drive->media	= ide_disk;
 				drive->cyl	= drive->bios_cyl  = vals[0];
@@ -2961,7 +2214,7 @@
 		 * Be VERY CAREFUL changing this: note hardcoded indexes below
 		 */
 		const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
-			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
+			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", "reset", NULL};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
 		i = match_parm(&s[4], ide_words, vals, 3);
@@ -2969,7 +2222,7 @@
 		/*
 		 * Cryptic check to ensure chipset not already set for hwif:
 		 */
-		if (i > 0 || i <= -5) {
+		if (i > 0 || (i <= -5 && i != -12)) {
 			if (hwif->chipset != ide_unknown)
 				goto bad_option;
 			if (i <= -5) {
@@ -2980,10 +2233,14 @@
 				 */
 				if (hw != 0)
 					goto bad_hwif;
+				printk("\n");
 			}
 		}
 
 		switch (i) {
+			case -12: /* "reset" */
+				hwif->reset = 1;
+				goto done;
 #ifdef CONFIG_BLK_DEV_PROMISE
 			case -11: /* "dc4030" */
 			{
@@ -3062,8 +2319,8 @@
 			case 2: /* base,ctl */
 				vals[2] = 0;	/* default irq = probe for it */
 			case 3: /* base,ctl,irq */
-				hwif->io_base  = vals[0];
-				hwif->ctl_port = vals[1];
+				ide_init_hwif_ports(hwif->io_ports, (ide_ioreg_t) vals[0], &hwif->irq);
+				hwif->io_ports[IDE_CONTROL_OFFSET] = (ide_ioreg_t) vals[1];
 				hwif->irq      = vals[2];
 				hwif->noprobe  = 0;
 				hwif->chipset  = ide_generic;
@@ -3155,149 +2412,8 @@
 	return 1;
 }
 
-#if MAX_HWIFS > 1
-/*
- * save_match() is used to simplify logic in init_irq() below.
- *
- * A loophole here is that we may not know about a particular
- * hwif's irq until after that hwif is actually probed/initialized..
- * This could be a problem for the case where an hwif is on a
- * dual interface that requires serialization (eg. cmd640) and another
- * hwif using one of the same irqs is initialized beforehand.
- *
- * This routine detects and reports such situations, but does not fix them.
- */
-static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match)
-{
-	ide_hwif_t *m = *match;
-
-	if (m && m->hwgroup && m->hwgroup != new->hwgroup) {
-		if (!new->hwgroup)
-			return;
-		printk("%s: potential irq problem with %s and %s\n", hwif->name, new->name, m->name);
-	}
-	if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */
-		*match = new;
-}
-#endif /* MAX_HWIFS > 1 */
-
-/*
- * This routine sets up the irq for an ide interface, and creates a new
- * hwgroup for the irq/hwif if none was previously assigned.
- *
- * Much of the code is for correctly detecting/handling irq sharing
- * and irq serialization situations.  This is somewhat complex because
- * it handles static as well as dynamic (PCMCIA) IDE interfaces.
- *
- * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
- * interrupts completely disabled.  This can be bad for interrupt latency,
- * but anything else has led to problems on some machines.  We re-enable
- * interrupts as much as we can safely do in most places.
- */
-static int init_irq (ide_hwif_t *hwif)
-{
-	unsigned long flags;
-#if MAX_HWIFS > 1
-	unsigned int index;
-#endif /* MAX_HWIFS > 1 */
-	ide_hwgroup_t *hwgroup;
-	ide_hwif_t *match = NULL;
-
-	save_flags(flags);
-	cli();
-
-	hwif->hwgroup = NULL;
-#if MAX_HWIFS > 1
-	/*
-	 * Group up with any other hwifs that share our irq(s).
-	 */
-	for (index = 0; index < MAX_HWIFS; index++) {
-		ide_hwif_t *h = &ide_hwifs[index];
-		if (h->hwgroup) {  /* scan only initialized hwif's */
-			if (hwif->irq == h->irq) {
-				hwif->sharing_irq = h->sharing_irq = 1;
-				save_match(hwif, h, &match);
-			}
-			if (hwif->serialized) {
-				ide_hwif_t *mate = &ide_hwifs[hwif->index^1];
-				if (index == mate->index || h->irq == mate->irq)
-					save_match(hwif, h, &match);
-			}
-			if (h->serialized) {
-				ide_hwif_t *mate = &ide_hwifs[h->index^1];
-				if (hwif->irq == mate->irq)
-					save_match(hwif, h, &match);
-			}
-		}
-	}
-#endif /* MAX_HWIFS > 1 */
-	/*
-	 * If we are still without a hwgroup, then form a new one
-	 */
-	if (match) {
-		hwgroup = match->hwgroup;
-	} else {
-		hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
-		hwgroup->hwif 	 = hwgroup->next_hwif = hwif->next = hwif;
-		hwgroup->rq      = NULL;
-		hwgroup->handler = NULL;
-		if (hwif->drives[0].present)
-			hwgroup->drive = &hwif->drives[0];
-		else
-			hwgroup->drive = &hwif->drives[1];
-		hwgroup->poll_timeout = 0;
-		init_timer(&hwgroup->timer);
-		hwgroup->timer.function = &timer_expiry;
-		hwgroup->timer.data = (unsigned long) hwgroup;
-	}
-
-	/*
-	 * Allocate the irq, if not already obtained for another hwif
-	 */
-	if (!match || match->irq != hwif->irq) {
-		if (request_irq(hwif->irq, ide_intr, SA_INTERRUPT, hwif->name, hwgroup)) {
-			if (!match)
-				kfree(hwgroup);
-			restore_flags(flags);
-			return 1;
-		}
-	}
-
-	/*
-	 * Everything is okay, so link us into the hwgroup
-	 */
-	hwif->hwgroup = hwgroup;
-	hwif->next = hwgroup->hwif->next;
-	hwgroup->hwif->next = hwif;
-
-	restore_flags(flags);	/* safe now that hwif->hwgroup is set up */
-
-	printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
-		hwif->io_base, hwif->io_base+7, hwif->ctl_port, hwif->irq);
-	if (match)
-		printk(" (%sed with %s)", hwif->sharing_irq ? "shar" : "serializ", match->name);
-	printk("\n");
-	return 0;
-}
-
-static struct file_operations ide_fops = {
-	NULL,			/* lseek - default */
-	block_read,		/* read - general block-dev read */
-	block_write,		/* write - general block-dev write */
-	NULL,			/* readdir - bad */
-	NULL,			/* select */
-	ide_ioctl,		/* ioctl */
-	NULL,			/* mmap */
-	ide_open,		/* open */
-	ide_release,		/* release */
-	block_fsync		/* fsync */
-	,NULL,			/* fasync */
-	ide_check_media_change,	/* check_media_change */
-	revalidate_disk		/* revalidate */
-};
-
 #ifdef CONFIG_PCI
-#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON)
+#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621)
 
 typedef void (ide_pci_init_proc_t)(byte, byte);
 
@@ -3319,7 +2435,7 @@
 	restore_flags(flags);
 }
 
-#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
+#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) || defined(CONFIG_BLK_DEV_OPTI621) */
 #endif /* CONFIG_PCI */
 
 /*
@@ -3350,6 +2466,9 @@
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
 #endif /* CONFIG_BLK_DEV_TRITON */
+#ifdef CONFIG_BLK_DEV_OPTI621
+		ide_probe_pci (PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621, &ide_init_opti621, 0);
+#endif /* CONFIG_BLK_DEV_OPTI621 */
 	}
 #endif /* CONFIG_PCI */
 #ifdef CONFIG_BLK_DEV_CMD640
@@ -3363,196 +2482,287 @@
 #endif
 }
 
-static int hwif_init (int h)
+void ide_init_builtin_drivers (void)
 {
-	ide_hwif_t *hwif = &ide_hwifs[h];
-	void (*rfn)(void);
-	
-	if (!hwif->present)
-		return 0;
-	if (!hwif->irq) {
-		if (!(hwif->irq = default_irqs[h])) {
-			printk("%s: DISABLED, NO IRQ\n", hwif->name);
-			return (hwif->present = 0);
-		}
-	}
-#ifdef CONFIG_BLK_DEV_HD
-	if (hwif->irq == HD_IRQ && hwif->io_base != HD_DATA) {
-		printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
-		return (hwif->present = 0);
-	}
-#endif /* CONFIG_BLK_DEV_HD */
-	
-	hwif->present = 0; /* we set it back to 1 if all is ok below */
-	switch (hwif->major) {
-	case IDE0_MAJOR: rfn = &do_ide0_request; break;
-#if MAX_HWIFS > 1
-	case IDE1_MAJOR: rfn = &do_ide1_request; break;
-#endif
-#if MAX_HWIFS > 2
-	case IDE2_MAJOR: rfn = &do_ide2_request; break;
-#endif
-#if MAX_HWIFS > 3
-	case IDE3_MAJOR: rfn = &do_ide3_request; break;
-#endif
-	default:
-		printk("%s: request_fn NOT DEFINED\n", hwif->name);
-		return (hwif->present = 0);
-	}
-	if (register_blkdev (hwif->major, hwif->name, &ide_fops)) {
-		printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
-	} else if (init_irq (hwif)) {
-		printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
-		(void) unregister_blkdev (hwif->major, hwif->name);
-	} else {
-		init_gendisk(hwif);
-		blk_dev[hwif->major].request_fn = rfn;
-		read_ahead[hwif->major] = 8;	/* (4kB) */
-		hwif->present = 1;	/* success */
-	}
-	return hwif->present;
-}
-
-/*
- * This is gets invoked once during initialization, to set *everything* up
- */
-int ide_init (void)
-{
-	int index;
-
-	init_ide_data ();
 	/*
 	 * Probe for special "known" interface chipsets
 	 */
 	probe_for_hwifs ();
 
-	/*
-	 * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
-	 */
-	for (index = 0; index < MAX_HWIFS; ++index)
-		probe_hwif (&ide_hwifs[index]);
-	for (index = 0; index < MAX_HWIFS; ++index)
-		hwif_init (index);
-
+#ifdef CONFIG_BLK_DEV_IDE
+#ifdef __mc68000__
+	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+		ide_get_lock(&ide_lock, ide_intr, NULL);
+		disable_irq(ide_hwifs[0].irq);
+	}
+#endif /* __mc68000__ */
+
+	(void) ideprobe_init();
+
+#ifdef __mc68000__
+	if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
+		enable_irq(ide_hwifs[0].irq);
+		ide_release_lock(&ide_lock);
+	}
+#endif /* __mc68000__ */
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#ifdef CONFIG_BLK_DEV_IDEDISK
+	(void) idedisk_init();
+#endif /* CONFIG_BLK_DEV_IDEDISK */
+#ifdef CONFIG_BLK_DEV_IDECD
+	(void) ide_cdrom_init();
+#endif /* CONFIG_BLK_DEV_IDECD */
 #ifdef CONFIG_BLK_DEV_IDETAPE
-	idetape_register_chrdev();	/* Register character device interface to the ide tape */
+	(void) idetape_init();
 #endif /* CONFIG_BLK_DEV_IDETAPE */
-	
-	return 0;
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	(void) idefloppy_init();
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 }
 
-#ifdef CONFIG_BLK_DEV_IDE_PCMCIA
-int ide_register(int io_base, int ctl_port, int irq)
+static int default_cleanup (ide_drive_t *drive)
 {
-	int index, i, rc = -1;
-	ide_hwif_t *hwif;
+	return ide_unregister_subdriver(drive);
+}
+
+static void default_do_request(ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	ide_end_request(0, HWGROUP(drive));
+}
+ 
+static void default_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_end_request(uptodate, hwgroup);
+}
+  
+static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			  unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	drive->usage--;
+	return -EIO;
+}
+
+static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+}
+
+static int default_check_media_change (ide_drive_t *drive)
+{
+	return 1;
+}
+
+static void default_pre_reset (ide_drive_t *drive)
+{
+}
+
+static unsigned long default_capacity (ide_drive_t *drive)
+{
+	return 0x7fffffff;	/* cdrom or tape */
+}
+
+static void default_special (ide_drive_t *drive)
+{
+	special_t *s = &drive->special;
+
+	s->all = 0;
+	drive->mult_req = 0;
+}
+
+static void setup_driver_defaults (ide_drive_t *drive)
+{
+	ide_driver_t *d = drive->driver;
+
+	if (d->cleanup == NULL)		d->cleanup = default_cleanup;
+	if (d->do_request == NULL)	d->do_request = default_do_request;
+	if (d->end_request == NULL)	d->end_request = default_end_request;
+	if (d->ioctl == NULL)		d->ioctl = default_ioctl;
+	if (d->open == NULL)		d->open = default_open;
+	if (d->release == NULL)		d->release = default_release;
+	if (d->media_change == NULL)	d->media_change = default_check_media_change;
+	if (d->pre_reset == NULL)	d->pre_reset = default_pre_reset;
+	if (d->capacity == NULL)	d->capacity = default_capacity;
+	if (d->special == NULL)		d->special = default_special;
+}
+
+ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
+{
+	unsigned int unit, index, i;
 	ide_drive_t *drive;
-	unsigned long flags;
 
+	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			drive = &ide_hwifs[index].drives[unit];
+			if (drive->present && drive->media == media &&
+			    drive->driver == driver && ++i > n)
+				return drive;
+		}
+	}
+	return NULL;
+}
+
+int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version)
+{
+	unsigned long flags;
+	
 	save_flags(flags);
 	cli();
-	for (index = 0; index < MAX_HWIFS; ++index) {
-		hwif = &ide_hwifs[index];
-		if (hwif->present) {
-			if (hwif->io_base == io_base || hwif->ctl_port == ctl_port)
-				break; /* this ide port already exists */
-		} else {
-			hwif->io_base = io_base;
-			hwif->ctl_port = ctl_port;
-			hwif->irq = irq;
-			hwif->noprobe = 0;
-			probe_hwif(hwif);
-			if (!hwif_init(index))
-				break;
-			for (i = 0; i < hwif->gd->nr_real; i++) {
-				drive = &hwif->drives[i];
-				revalidate_disk(MKDEV(hwif->major, i<<PARTN_BITS));
-#ifdef CONFIG_BLK_DEV_IDECD
-				if (drive->present && drive->media == ide_cdrom)
-					ide_cdrom_setup(drive);
-#endif /* CONFIG_BLK_DEV_IDECD */
-			}
-			rc = index;
-			break;
-		}
+	if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
+	    drive->busy || drive->usage || drive->media != driver->media) {
+	    	restore_flags(flags);
+		return 1;
 	}
+	drive->driver = driver;
+	setup_driver_defaults(drive);
 	restore_flags(flags);
-	return rc;
+	if (driver->supports_dma && !drive->using_dma && drive->autotune != 2 && HWIF(drive)->dmaproc != NULL)
+		(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
+	drive->revalidate = 1;
+	return 0;
 }
 
-void ide_unregister (unsigned int index)
+int ide_unregister_subdriver (ide_drive_t *drive)
 {
-	struct gendisk *gd, **gdp;
-	ide_hwif_t *hwif, *g;
-	ide_hwgroup_t *hwgroup;
-	int irq_count = 0;
 	unsigned long flags;
-
-	if (index >= MAX_HWIFS)
-		return;
+	
 	save_flags(flags);
 	cli();
-	hwif = &ide_hwifs[index];
-	if (!hwif->present || hwif->drives[0].busy || hwif->drives[1].busy) {
+	if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
 		restore_flags(flags);
-		return;
+		return 1;
 	}
-	hwif->present = 0;
-	hwgroup = hwif->hwgroup;
+	drive->driver = NULL;
+	restore_flags(flags);
+	return 0;
+}
 
-	/*
-	 * 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)
-		free_irq(hwif->irq, hwgroup);
+int ide_register_module (ide_module_t *module)
+{
+	ide_module_t *p = ide_modules;
 
-	/*
-	 * Note that we only release the standard ports,
-	 * and do not even try to handle any extra ports
-	 * allocated for weird IDE interface chipsets.
-	 */
-	release_region(hwif->io_base, 8);
-	release_region(hwif->ctl_port, 1);
+	while (p) {
+		if (p == module)
+			return 1;
+		p = p->next;
+	}
+	module->next = ide_modules;
+	ide_modules = module;
+	revalidate_drives();
+	return 0;
+}
+
+void ide_unregister_module (ide_module_t *module)
+{
+	ide_module_t **p;
+
+	for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next));
+	if (*p)
+		*p = (*p)->next;
+}
+
+struct file_operations ide_fops[] = {{
+	NULL,			/* lseek - default */
+	block_read,		/* read - general block-dev read */
+	block_write,		/* write - general block-dev write */
+	NULL,			/* readdir - bad */
+	NULL,			/* select */
+	ide_ioctl,		/* ioctl */
+	NULL,			/* mmap */
+	ide_open,		/* open */
+	ide_release,		/* release */
+	block_fsync,		/* fsync */
+	NULL,			/* fasync */
+	ide_check_media_change,	/* check_media_change */
+	ide_revalidate_disk	/* revalidate */
+}};
+
+static struct symbol_table ide_syms = {
+#include <linux/symtab_begin.h>
+	X(ide_hwifs),
+	X(ide_register_module),		X(ide_unregister_module),
 
 	/*
-	 * Remove us from the hwgroup, and free
-	 * the hwgroup if we were the only member
+	 * Probe module
 	 */
-	while (hwgroup->hwif->next != hwif)
-		hwgroup->hwif = hwgroup->hwif->next;
-	hwgroup->hwif->next = hwif->next;
-	if (hwgroup->hwif == hwif)
-		hwgroup->hwif = hwif->next;
-	if (hwgroup->next_hwif == hwif)
-		hwgroup->next_hwif = hwif->next;
-	if (hwgroup->hwif == hwif)
-		kfree(hwgroup);
+	X(ide_timer_expiry),		X(ide_intr),
+	X(ide_geninit),			X(ide_fops),
+	X(do_ide0_request),
+#if MAX_HWIFS > 1
+	X(do_ide1_request),
+#endif /* MAX_HWIFS > 1 */
+#if MAX_HWIFS > 2
+	X(do_ide2_request),
+#endif /* MAX_HWIFS > 2 */
+#if MAX_HWIFS > 3
+	X(do_ide3_request),
+#endif /* MAX_HWIFS > 3 */
 
 	/*
-	 * Remove us from the kernel's knowledge
+	 * Driver module
 	 */
-	unregister_blkdev(hwif->major, hwif->name);
-	kfree(blksize_size[hwif->major]);
-	blk_dev[hwif->major].request_fn = NULL;
-	blksize_size[hwif->major] = NULL;
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
-		if (*gdp == hwif->gd)
-			break;
-	if (*gdp == NULL)
-		printk("gd not in disk chain!\n");
-	else {
-		gd = *gdp; *gdp = gd->next;
-		kfree(gd->sizes);
-		kfree(gd->part);
-		kfree(gd);
+	X(ide_scan_devices),		X(ide_register_subdriver),
+	X(ide_unregister_subdriver),	X(ide_input_data),
+	X(ide_output_data),		X(atapi_input_bytes),
+	X(atapi_output_bytes),		X(ide_set_handler),
+	X(ide_dump_status),		X(ide_error),
+	X(ide_fixstring),		X(ide_wait_stat),
+	X(ide_do_reset),		X(ide_init_drive_cmd),
+	X(ide_do_drive_cmd),		X(ide_end_drive_cmd),
+	X(ide_end_request),		X(ide_revalidate_disk),
+	X(ide_cmd),
+
+	X(ide_register),		X(ide_unregister),
+#include <linux/symtab_end.h>
+};
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+int ide_init (void)
+{
+	init_ide_data ();
+
+	initializing = 1;
+	ide_init_builtin_drivers();
+	initializing = 0;
+
+	(void) register_symtab(&ide_syms);
+	return 0;
+}
+
+#ifdef MODULE
+char *options = NULL;
+
+static void parse_options (char *line)
+{
+	char *next = line;
+
+	if (line == NULL || !*line)
+		return;
+	while ((line = next) != NULL) {
+ 		if ((next = strchr(line,' ')) != NULL)
+			*next++ = 0;
+		if (!strncmp(line,"ide",3) || (!strncmp(line,"hd",2) && line[2] != '='))
+			ide_setup(line);
 	}
-	init_hwif_data (index);	/* restore hwif data to pristine status */
-	restore_flags(flags);
 }
-#endif /* CONFIG_BLK_DEV_IDE_PCMCIA */
+
+int init_module (void)
+{
+	parse_options(options);
+	return ide_init();
+}
+
+void cleanup_module (void)
+{
+	int index;
+
+	for (index = 0; index < MAX_HWIFS; ++index)
+		ide_unregister(index);
+}
+#endif /* MODULE */

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov