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

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

diff -u --recursive --new-file v2.1.72/linux/drivers/block/ide.c linux/drivers/block/ide.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 6.05  November 30, 1997
+ *  linux/drivers/block/ide.c	Version 6.11  December 5, 1997
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -95,6 +95,9 @@
  *			added support for BIOS-enabled UltraDMA
  *			rename all "promise" things to "pdc4030"
  *			fix EZ-DRIVE handling on small disks
+ * Version 6.11		fix probe error in ide_scan_devices()
+ *			fix ancient "jiffies" polling bugs
+ *			mask all hwgroup interrupts on each irq entry
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -111,18 +114,14 @@
 #include <linux/kernel.h>
 #include <linux/timer.h>
 #include <linux/mm.h>
-#include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/major.h>
-#include <linux/blkdev.h>
 #include <linux/errno.h>
-#include <linux/hdreg.h>
 #include <linux/genhd.h>
 #include <linux/malloc.h>
 #include <linux/bios32.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
-#include <linux/init.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
@@ -312,11 +311,12 @@
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
+			unsigned long flags;
+			save_flags(flags);
 			cli();
 			do_vlb_sync(IDE_NSECTOR_REG);
 			insl(IDE_DATA_REG, buffer, wcount);
-			if (drive->unmask)
-				sti();
+			restore_flags(flags);
 		} else
 #endif /* SUPPORT_VLB_SYNC */
 			insl(IDE_DATA_REG, buffer, wcount);
@@ -344,11 +344,12 @@
 	if (io_32bit) {
 #if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
+			unsigned long flags;
+			save_flags(flags);
 			cli();
 			do_vlb_sync(IDE_NSECTOR_REG);
 			outsl(IDE_DATA_REG, buffer, wcount);
-			if (drive->unmask)
-				sti();
+			restore_flags(flags);
 		} else
 #endif /* SUPPORT_VLB_SYNC */
 			outsl(IDE_DATA_REG, buffer, wcount);
@@ -469,13 +470,13 @@
 	ide_hwgroup_t *hwgroup = HWGROUP(drive);
 	byte stat;
 
-	OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+	SELECT_DRIVE(HWIF(drive),drive);
 	udelay (10);
 
 	if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
 		printk("%s: ATAPI reset complete\n", drive->name);
 	} else {
-		if (jiffies < hwgroup->poll_timeout) {
+		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
 			return;	/* continue polling */
 		}
@@ -500,7 +501,7 @@
 	byte tmp;
 
 	if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
-		if (jiffies < hwgroup->poll_timeout) {
+		if (0 < (signed long)(hwgroup->poll_timeout - jiffies)) {
 			ide_set_handler (drive, &reset_pollfunc, HZ/20);
 			return;	/* continue polling */
 		}
@@ -542,7 +543,7 @@
 		drive->unmask = 0;
 		drive->io_32bit = 0;
 		if (drive->using_dma)
-			HWIF(drive)->dmaproc(ide_dma_off, drive);
+			(void) HWIF(drive)->dmaproc(ide_dma_off, drive);
 	}
 	if (drive->driver != NULL)
 		DRIVER(drive)->pre_reset(drive);
@@ -576,7 +577,7 @@
 	/* For an ATAPI device, first try an ATAPI SRST. */
 	if (drive->media != ide_disk && !do_not_try_atapi) {
 		pre_reset(drive);
-		OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+		SELECT_DRIVE(hwif,drive);
 		udelay (20);
 		OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
 		hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
@@ -808,7 +809,7 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	byte *args = (byte *) rq->buffer;
-	byte stat = GET_STAT();
+	byte test, stat = GET_STAT();
 
 	ide_sti();
 	if ((stat & DRQ_STAT) && args && args[3]) {
@@ -818,7 +819,10 @@
 		drive->io_32bit = io_32bit;
 		stat = GET_STAT();
 	}
-	if (OK_STAT(stat,READY_STAT,BAD_STAT))
+	test = stat;
+	if (drive->media == ide_cdrom)
+		test = stat &~BUSY_STAT;
+	if (OK_STAT(test,READY_STAT,BAD_STAT))
 		ide_end_drive_cmd (drive, stat, GET_ERR());
 	else
 		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
@@ -870,7 +874,7 @@
 		ide_sti();
 		timeout += jiffies;
 		while ((stat = GET_STAT()) & BUSY_STAT) {
-			if (jiffies > timeout) {
+			if (0 < (signed long)(jiffies - timeout)) {
 				restore_flags(flags);
 				ide_error(drive, "status timeout", stat);
 				return 1;
@@ -1007,7 +1011,7 @@
 	do {
 		if (!drive->queue)
 			continue;
-		if (drive->sleep && drive->sleep > jiffies)
+		if (drive->sleep && 0 < (signed long)(drive->sleep - jiffies))
 			continue;
 		if (!best) {
 			best = drive;
@@ -1019,7 +1023,7 @@
 			best = drive;
 	} while ((drive = drive->next) != hwgroup->drive);
 	if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) {
-		long t = (signed) (WAKEUP(best) - jiffies);
+		long t = (signed) (WAKEUP(best) - jiffies);	/* BUGGY? */
 		if (t >= WAIT_MIN_SLEEP) {
 			/*
 			 * We *may* have some time to spare, but first let's see if
@@ -1029,7 +1033,7 @@
 			do {
 				if (drive->sleep)	/* this drive tried to be nice to us */
 					continue;
-				if (WAKEUP(drive) > jiffies - best->service_time && WAKEUP(drive) < jiffies + t) {
+				if (WAKEUP(drive) > (jiffies - best->service_time) && WAKEUP(drive) < (jiffies + t)) {  /* BUGGY? */
 					ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
 					goto repeat;
 				}
@@ -1057,7 +1061,7 @@
 			sleep = drive->sleep;
 	} while ((drive = drive->next) != hwgroup->drive);
 	if (sleep) {
-		if (sleep < jiffies + WAIT_MIN_SLEEP)
+		if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep)) 
 			sleep = jiffies + WAIT_MIN_SLEEP;
 		hwgroup->timer.expires = sleep;
 		add_timer(&hwgroup->timer);
@@ -1189,7 +1193,7 @@
 			handler(drive);
 		else {				/* abort the operation */
 			if (hwgroup->hwif->dmaproc)
-				(void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+				(void) hwgroup->hwif->dmaproc (ide_dma_end, drive);
 			ide_error(drive, "irq timeout", GET_STAT());
 		}
 		cli();
@@ -1229,7 +1233,6 @@
 static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
 {
 	byte stat;
-	unsigned int unit;
 	ide_hwif_t *hwif = hwgroup->hwif;
 
 	/*
@@ -1237,27 +1240,18 @@
 	 */
 	do {
 		if (hwif->irq == irq) {
-			for (unit = 0; unit < MAX_DRIVES; ++unit) {
-				ide_drive_t *drive = &hwif->drives[unit];
-				if (!drive->present)
-					continue;
-				SELECT_DRIVE(hwif,drive);
-				udelay(100);  /* Ugly, but wait_stat() may not be safe here */
-				if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT)) {
-					/* Try to not flood the console with msgs */
-					static unsigned long last_msgtime = 0;
-					if ((last_msgtime + (HZ/2)) < jiffies) {
-						last_msgtime = jiffies;
-						(void) ide_dump_status(drive, "unexpected_intr", stat);
-					}
+			stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
+			if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
+				/* Try to not flood the console with msgs */
+				static unsigned long last_msgtime = 0;
+				if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
+					last_msgtime = jiffies;
+					printk("%s%s: unexpected interrupt, status=0x%02x\n",
+					 hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat);
 				}
-				if ((stat & DRQ_STAT))
-					try_to_flush_leftover_data(drive);
 			}
 		}
 	} while ((hwif = hwif->next) != hwgroup->hwif);
-	SELECT_DRIVE(hwif,hwgroup->drive); /* Ugh.. probably interrupts current I/O */
-	udelay(100);  /* Ugly, but wait_stat() may not be safe here */
 }
 
 /*
@@ -1266,14 +1260,28 @@
 void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
 {
 	ide_hwgroup_t *hwgroup = dev_id;
+	ide_hwif_t *hwif = hwgroup->hwif;
 	ide_handler_t *handler;
 
-	if (!ide_ack_intr (hwgroup->hwif->io_ports[IDE_STATUS_OFFSET],
-			   hwgroup->hwif->io_ports[IDE_IRQ_OFFSET]))
+	if (!ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]))
 		return;
-
-	if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
+	do {
+		if (hwif->irq != irq) disable_irq(hwif->irq);
+	} while ((hwif = hwif->next) != hwgroup->hwif);
+	if (irq == hwif->irq && (handler = hwgroup->handler) != NULL) {
 		ide_drive_t *drive = hwgroup->drive;
+#if 1	/* temporary, remove later -- FIXME */
+		{
+			struct request *rq = hwgroup->rq;
+			if (rq != NULL
+			 &&( MAJOR(rq->rq_dev) != HWIF(drive)->major
+			 || (MINOR(rq->rq_dev) >> PARTN_BITS) != drive->select.b.unit))
+			{
+				printk("ide_intr: got IRQ from wrong device: email mlord@pobox.com!!\n");
+				return;
+			}
+		}
+#endif	/* temporary */
 		hwgroup->handler = NULL;
 		del_timer(&(hwgroup->timer));
 		if (drive->unmask)
@@ -1289,6 +1297,10 @@
 		unexpected_intr(irq, hwgroup);
 	}
 	cli();
+	hwif = hwgroup->hwif;
+	do {
+		if (hwif->irq != irq) enable_irq(hwif->irq);
+	} while ((hwif = hwif->next) != hwgroup->hwif);
 }
 
 /*
@@ -1627,6 +1639,7 @@
 	 */
 	unregister_blkdev(hwif->major, hwif->name);
 	kfree(blksize_size[hwif->major]);
+	kfree(max_sectors[hwif->major]);
 	blk_dev[hwif->major].request_fn = NULL;
 	blk_dev[hwif->major].data = NULL;
 	blk_dev[hwif->major].queue = NULL;
@@ -1686,13 +1699,15 @@
 static int ide_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
-	int err;
+	int err, major, minor;
 	ide_drive_t *drive;
 	unsigned long flags;
 	struct request rq;
+	kdev_t dev;
 
-	if (!inode || !(inode->i_rdev))
+	if (!inode || !(dev = inode->i_rdev))
 		return -EINVAL;
+	major = MAJOR(dev); minor = MINOR(dev);
 	if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
 		return -ENODEV;
 	ide_init_drive_cmd (&rq);
@@ -1726,6 +1741,23 @@
 	 	case BLKGETSIZE:   /* Return device size */
 			return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
 
+		case BLKFRASET:
+			if (!suser()) return -EACCES;
+			max_readahead[major][minor] = arg;
+			return 0;
+
+		case BLKFRAGET:
+			return put_user(max_readahead[major][minor], (long *) arg);
+
+		case BLKSECTSET:
+			if (!suser()) return -EACCES;
+			if (!arg || arg > 0xff) return -EINVAL;
+			max_sectors[major][minor] = arg;
+			return 0;
+
+		case BLKSECTGET:
+			return put_user(max_sectors[major][minor], (long *) arg);
+
 		case BLKRRPART: /* Re-read partition tables */
 			if (!suser()) return -EACCES;
 			return ide_revalidate_disk(inode->i_rdev);
@@ -2074,6 +2106,9 @@
  *				This is the default for most chipsets,
  *				except the cmd640.
  * "idex=serialize"	: do not overlap operations on idex and ide(x^1)
+ * "idex=four"		: four drives on idex and ide(x^1) share same ports
+ * "idex=reset"		: reset interface before first use
+ * "idex=nodma"		: do not enable DMA by default on either drive
  *
  * The following are valid ONLY on ide0,
  * and the defaults for the base,ctl ports must not be altered.
@@ -2175,8 +2210,8 @@
 		/*
 		 * 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", "four", "reset", NULL};
+		const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune", "reset", "nodma", "four",
+			"qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL};
 		hw = s[3] - '0';
 		hwif = &ide_hwifs[hw];
 		i = match_parm(&s[4], ide_words, vals, 3);
@@ -2184,42 +2219,19 @@
 		/*
 		 * Cryptic check to ensure chipset not already set for hwif:
 		 */
-		if (i > 0 || (i <= -5 && i != -13)) {
+		if (i > 0 || i <= -7) {			/* is parameter a chipset name? */
 			if (hwif->chipset != ide_unknown)
-				goto bad_option;
-			if (i <= -5) {
-				if (ide_hwifs[1].chipset != ide_unknown)
-					goto bad_option;
-				/*
-				 * Interface keywords work only for ide0:
-				 */
-				if (hw != 0)
-					goto bad_hwif;
-				printk("\n");
-			}
+				goto bad_option;	/* chipset already specified */
+			if (i != -7 && hw != 0)
+				goto bad_hwif;		/* chipset drivers are for "ide0=" only */
+			if (ide_hwifs[hw^1].chipset != ide_unknown)
+				goto bad_option;	/* chipset for 2nd port already specified */
+			printk("\n");
 		}
 
 		switch (i) {
-			case -13: /* "reset" */
-				hwif->reset = 1;
-				goto done;
-#ifdef CONFIG_BLK_DEV_4DRIVES
-			case -12: /* "four" drives on one set of ports */
-			{
-				ide_hwif_t *mate = &ide_hwifs[hw^1];
-				mate->mate = hwif;
-				hwif->mate = mate;
-				hwif->chipset = mate->chipset = ide_4drives;
-				hwif->serialized = mate->serialized = 1;
-				mate->drives[0].select.all ^= 0x20;
-				mate->drives[1].select.all ^= 0x20;
-				memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
-				mate->irq = hwif->irq;
-				goto done;
-			}
-#endif /* CONFIG_BLK_DEV_4DRIVES */
 #ifdef CONFIG_BLK_DEV_PDC4030
-			case -11: /* "dc4030" */
+			case -14: /* "dc4030" */
 			{
 				extern void setup_pdc4030(ide_hwif_t *);
 				setup_pdc4030(hwif);
@@ -2227,7 +2239,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_PDC4030 */
 #ifdef CONFIG_BLK_DEV_ALI14XX
-			case -10: /* "ali14xx" */
+			case -13: /* "ali14xx" */
 			{
 				extern void init_ali14xx (void);
 				init_ali14xx();
@@ -2235,7 +2247,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_ALI14XX */
 #ifdef CONFIG_BLK_DEV_UMC8672
-			case -9: /* "umc8672" */
+			case -12: /* "umc8672" */
 			{
 				extern void init_umc8672 (void);
 				init_umc8672();
@@ -2243,7 +2255,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_UMC8672 */
 #ifdef CONFIG_BLK_DEV_DTC2278
-			case -8: /* "dtc2278" */
+			case -11: /* "dtc2278" */
 			{
 				extern void init_dtc2278 (void);
 				init_dtc2278();
@@ -2251,7 +2263,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_DTC2278 */
 #ifdef CONFIG_BLK_DEV_CMD640
-			case -7: /* "cmd640_vlb" */
+			case -10: /* "cmd640_vlb" */
 			{
 				extern int cmd640_vlb; /* flag for cmd640.c */
 				cmd640_vlb = 1;
@@ -2259,7 +2271,7 @@
 			}
 #endif /* CONFIG_BLK_DEV_CMD640 */
 #ifdef CONFIG_BLK_DEV_HT6560B
-			case -6: /* "ht6560b" */
+			case -9: /* "ht6560b" */
 			{
 				extern void init_ht6560b (void);
 				init_ht6560b();
@@ -2267,13 +2279,31 @@
 			}
 #endif /* CONFIG_BLK_DEV_HT6560B */
 #if CONFIG_BLK_DEV_QD6580
-			case -5: /* "qd6580" (has secondary i/f) */
+			case -8: /* "qd6580" */
 			{
 				extern void init_qd6580 (void);
 				init_qd6580();
 				goto done;
 			}
 #endif /* CONFIG_BLK_DEV_QD6580 */
+#ifdef CONFIG_BLK_DEV_4DRIVES
+			case -7: /* "four" drives on one set of ports */
+			{
+				ide_hwif_t *mate = &ide_hwifs[hw^1];
+				mate->drives[0].select.all ^= 0x20;
+				mate->drives[1].select.all ^= 0x20;
+				hwif->chipset = mate->chipset = ide_4drives;
+				mate->irq = hwif->irq;
+				memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports));
+				goto do_serialize;
+			}
+#endif /* CONFIG_BLK_DEV_4DRIVES */
+			case -6: /* nodma */
+				hwif->no_autodma = 1;
+				goto done;
+			case -5: /* "reset" */
+				hwif->reset = 1;
+				goto done;
 			case -4: /* "noautotune" */
 				hwif->drives[0].autotune = 2;
 				hwif->drives[1].autotune = 2;
@@ -2284,8 +2314,9 @@
 				goto done;
 			case -2: /* "serialize" */
 			do_serialize:
-				ide_hwifs[hw].serialized = 1;   /* serialize */
-				ide_hwifs[hw^1].serialized = 1; /* with mate */
+				hwif->mate = &ide_hwifs[hw^1];
+				hwif->mate->mate = hwif;
+				hwif->serialized = hwif->mate->serialized = 1;
 				goto done;
 
 			case -1: /* "noprobe" */
@@ -2395,44 +2426,37 @@
 
 /*
  * probe_for_hwifs() finds/initializes "known" IDE interfaces
- *
- * This routine should ideally be using pcibios_find_class() to find all
- * PCI IDE interfaces, but that function causes some systems to "go weird".
  */
 __initfunc(static void probe_for_hwifs (void))
 {
 #ifdef CONFIG_PCI
-	/*
-	 * Find/initialize PCI IDE interfaces
-	 */
 	if (pcibios_present())
 	{
-#ifdef CONFIG_BLK_DEV_IDEDMA
-		{
-			extern void ide_scan_pcibus(void);
-			ide_scan_pcibus();
-		}
-#endif
+#ifdef CONFIG_BLK_DEV_IDEPCI
+		ide_scan_pcibus();
+#else
 #ifdef CONFIG_BLK_DEV_RZ1000
 		{
 			extern void ide_probe_for_rz100x(void);
 			ide_probe_for_rz100x();
 		}
-#endif
+#endif	/* CONFIG_BLK_DEV_RZ1000 */
+#endif	/* CONFIG_BLK_DEV_IDEPCI */
 	}
-#endif /* CONFIG_PCI */
+#endif	/* CONFIG_PCI */
+
 #ifdef CONFIG_BLK_DEV_CMD640
 	{
 		extern void ide_probe_for_cmd640x(void);
 		ide_probe_for_cmd640x();
 	}
-#endif
+#endif	/* CONFIG_BLK_DEV_CMD640 */
 #ifdef CONFIG_BLK_DEV_PDC4030
 	{
 		extern int init_pdc4030(void);
 		(void) init_pdc4030();
 	}
-#endif
+#endif	/* CONFIG_BLK_DEV_PDC4030 */
 }
 
 __initfunc(void ide_init_builtin_drivers (void))
@@ -2460,6 +2484,10 @@
 #endif /* __mc68000__ */
 #endif /* CONFIG_BLK_DEV_IDE */
 
+#ifdef CONFIG_PROC_FS
+	proc_ide_init();
+#endif
+
 	/*
 	 * Attempt to match drivers for the available drives
 	 */
@@ -2552,18 +2580,19 @@
 ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
 {
 	unsigned int unit, index, i;
-	ide_drive_t *drive;
 
 	for (index = 0; index < MAX_HWIFS; ++index)
 		if (ide_hwifs[index].present) goto search;
 	ide_init_module(IDE_PROBE_MODULE);
 search:
 	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;
+		ide_hwif_t *hwif = &ide_hwifs[index];
+		if (hwif->present) {
+			for (unit = 0; unit < MAX_DRIVES; ++unit) {
+				ide_drive_t *drive = &hwif->drives[unit];
+				if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+					return drive;
+			}
 		}
 	}
 	return NULL;
@@ -2590,6 +2619,7 @@
 		drive->nice1 = 1;
 	}
 	drive->revalidate = 1;
+	ide_add_proc_entries(drive, driver->proc);
 	return 0;
 }
 
@@ -2603,6 +2633,7 @@
 		restore_flags(flags);
 		return 1;
 	}
+	ide_remove_proc_entries(drive, DRIVER(drive)->proc);
 	drive->driver = NULL;
 	restore_flags(flags);
 	return 0;
@@ -2694,6 +2725,9 @@
 EXPORT_SYMBOL(ide_revalidate_disk);
 EXPORT_SYMBOL(ide_cmd);
 EXPORT_SYMBOL(ide_stall_queue);
+EXPORT_SYMBOL(ide_add_proc_entries);
+EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(proc_ide_read_geometry);
 
 EXPORT_SYMBOL(ide_register);
 EXPORT_SYMBOL(ide_unregister);

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