patch-2.1.79 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.78/linux/drivers/block/ide.c linux/drivers/block/ide.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 6.11  December 5, 1997
+ *  linux/drivers/block/ide.c	Version 6.12  January  2, 1998
  *
  *  Copyright (C) 1994-1998  Linus Torvalds & authors (see below)
  */
@@ -98,6 +98,8 @@
  * Version 6.11		fix probe error in ide_scan_devices()
  *			fix ancient "jiffies" polling bugs
  *			mask all hwgroup interrupts on each irq entry
+ * Version 6.12		integrate ioctl and proc interfaces
+ *			fix parsing of "idex=" command line parameter
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -151,7 +153,7 @@
 /*
  * ide_modules keeps track of the available IDE chipset/probe/driver modules.
  */
-static ide_module_t *ide_modules = NULL;
+ide_module_t *ide_modules = NULL;
 
 /*
  * This is declared extern in ide.h, for access by other IDE modules:
@@ -809,7 +811,8 @@
 {
 	struct request *rq = HWGROUP(drive)->rq;
 	byte *args = (byte *) rq->buffer;
-	byte test, stat = GET_STAT();
+	byte stat = GET_STAT();
+	int retries = 10;
 
 	ide_sti();
 	if ((stat & DRQ_STAT) && args && args[3]) {
@@ -817,12 +820,11 @@
 		drive->io_32bit = 0;
 		ide_input_data(drive, &args[4], args[3] * SECTOR_WORDS);
 		drive->io_32bit = io_32bit;
-		stat = GET_STAT();
+		while (((stat = GET_STAT()) & BUSY_STAT) && retries--)
+			udelay(100);
 	}
-	test = stat;
-	if (drive->media == ide_cdrom)
-		test = stat &~BUSY_STAT;
-	if (OK_STAT(test,READY_STAT,BAD_STAT))
+
+	if (OK_STAT(stat, READY_STAT, BAD_STAT))
 		ide_end_drive_cmd (drive, stat, GET_ERR());
 	else
 		ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
@@ -901,6 +903,10 @@
 		printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x xx=0x%02x\n",
 		 drive->name, args[0], args[1], args[2], args[3]);
 #endif
+		if (args[0] == WIN_SMART) {
+			OUT_BYTE(0x4f, IDE_LCYL_REG);
+			OUT_BYTE(0xc2, IDE_HCYL_REG);
+		}
 		OUT_BYTE(args[2],IDE_FEATURE_REG);
 		ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
 		return;
@@ -1556,6 +1562,22 @@
 	return 0;
 }
 
+int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
+{
+	if (!drive->present || drive->busy || drive->usage)
+		goto abort;
+	if (drive->driver != NULL && DRIVER(drive)->cleanup(drive))
+		goto abort;
+	strncpy(drive->driver_req, driver, 9);
+	ide_init_module(IDE_DRIVER_MODULE);
+	drive->driver_req[0] = 0;
+	ide_init_module(IDE_DRIVER_MODULE);
+	if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver))
+		return 0;
+abort:
+	return 1;
+}
+
 void ide_unregister (unsigned int index)
 {
 	struct gendisk *gd, **gdp;
@@ -1697,20 +1719,229 @@
 	return hwif->present ? index : -1;
 }
 
+void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
+
+	while ((*p) && strcmp((*p)->name, name) < 0)
+		p = &((*p)->next);
+	if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL)
+		goto abort;
+	memset(setting, 0, sizeof(*setting));
+	if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL)
+		goto abort;
+	strcpy(setting->name, name);		setting->rw = rw;
+	setting->read_ioctl = read_ioctl;	setting->write_ioctl = write_ioctl;
+	setting->data_type = data_type;		setting->min = min;
+	setting->max = max;			setting->mul_factor = mul_factor;
+	setting->div_factor = div_factor;	setting->data = data;
+	setting->set = set;			setting->next = *p;
+	if (drive->driver)
+		setting->auto_remove = 1;
+	*p = setting;
+	return;
+abort:
+	if (setting)
+		kfree(setting);
+}
+
+void ide_remove_setting(ide_drive_t *drive, char *name)
+{
+	ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting;
+
+	while ((*p) && strcmp((*p)->name, name))
+		p = &((*p)->next);
+	if ((setting = (*p)) == NULL)
+		return;
+	(*p) = setting->next;
+	kfree(setting->name);
+	kfree(setting);
+}
+
+static ide_settings_t *ide_find_setting_by_ioctl(ide_drive_t *drive, int cmd)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (setting->read_ioctl == cmd || setting->write_ioctl == cmd)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+ide_settings_t *ide_find_setting_by_name(ide_drive_t *drive, char *name)
+{
+	ide_settings_t *setting = drive->settings;
+
+	while (setting) {
+		if (strcmp(setting->name, name) == 0)
+			break;
+		setting = setting->next;
+	}
+	return setting;
+}
+
+static void auto_remove_settings(ide_drive_t *drive)
+{
+	ide_settings_t *setting;
+repeat:
+	setting = drive->settings;
+	while (setting) {
+		if (setting->auto_remove) {
+			ide_remove_setting(drive, setting->name);
+			goto repeat;
+		}
+		setting = setting->next;
+	}
+}
+
+int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
+{
+	if (!(setting->rw & SETTING_READ))
+		return -EINVAL;
+	switch(setting->data_type) {
+		case TYPE_BYTE:
+			return *((u8 *) setting->data);
+		case TYPE_SHORT:
+			return *((u16 *) setting->data);
+		case TYPE_INT:
+		case TYPE_INTA:
+			return *((u32 *) setting->data);
+		default:
+			return -EINVAL;
+	}
+}
+
+int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
+{
+	unsigned long flags;
+	int i, rc = 0;
+	u32 *p;
+
+	if (!suser())
+		return -EACCES;
+	if (!(setting->rw & SETTING_WRITE))
+		return -EPERM;
+	if (val < setting->min || val > setting->max)
+		return -EINVAL;
+	save_flags(flags);
+	cli();
+	if (setting->set)
+		rc = setting->set(drive, val);
+	else switch (setting->data_type) {
+		case TYPE_BYTE:
+			*((u8 *) setting->data) = val;
+			break;
+		case TYPE_SHORT:
+			*((u16 *) setting->data) = val;
+			break;
+		case TYPE_INT:
+			*((u32 *) setting->data) = val;
+			break;
+		case TYPE_INTA:
+			p = (u32 *) setting->data;
+			for (i = 0; i < 1 << PARTN_BITS; i++, p++)
+				*p = val;
+			break;
+	}
+	restore_flags(flags);
+	return rc;
+}
+
+static int set_io_32bit(ide_drive_t *drive, int arg)
+{
+	drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+	if (HWIF(drive)->chipset == ide_dtc2278)
+		HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+	return 0;
+}
+
+static int set_using_dma(ide_drive_t *drive, int arg)
+{
+	if (!drive->driver || !DRIVER(drive)->supports_dma)
+		return -EPERM;
+	if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+		return -EPERM;
+	if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive))
+		return -EIO;
+	return 0;
+}
+
+static int set_pio_mode(ide_drive_t *drive, int arg)
+{
+	struct request rq;
+
+	if (!HWIF(drive)->tuneproc)
+		return -ENOSYS;
+	if (drive->special.b.set_tune)
+		return -EBUSY;
+	ide_init_drive_cmd(&rq);
+	drive->tune_req = (byte) arg;
+	drive->special.b.set_tune = 1;
+	(void) ide_do_drive_cmd (drive, &rq, ide_wait);
+	return 0;
+}
+
+void ide_add_generic_settings(ide_drive_t *drive)
+{
+/*
+ *			drive	setting name		read/write access				read ioctl		write ioctl		data type	min	max				mul_factor	div_factor	data pointer			set function
+ */
+	ide_add_setting(drive,	"io_32bit",		drive->no_io_32bit ? SETTING_READ : SETTING_RW,	HDIO_GET_32BIT,		HDIO_SET_32BIT,		TYPE_BYTE,	0,	1 + (SUPPORT_VLB_SYNC << 1),	1,		1,		&drive->io_32bit,		set_io_32bit);
+	ide_add_setting(drive,	"keepsettings",		SETTING_RW,					HDIO_GET_KEEPSETTINGS,	HDIO_SET_KEEPSETTINGS,	TYPE_BYTE,	0,	1,				1,		1,		&drive->keep_settings,		NULL);
+	ide_add_setting(drive,	"nice1",		SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->nice1,			NULL);
+	ide_add_setting(drive,	"pio_mode",		SETTING_WRITE,					-1,			HDIO_SET_PIO_MODE,	TYPE_BYTE,	0,	255,				1,		1,		NULL,				set_pio_mode);
+	ide_add_setting(drive,	"slow",			SETTING_RW,					-1,			-1,			TYPE_BYTE,	0,	1,				1,		1,		&drive->slow,			NULL);
+	ide_add_setting(drive,	"unmaskirq",		drive->no_unmask ? SETTING_READ : SETTING_RW,	HDIO_GET_UNMASKINTR,	HDIO_SET_UNMASKINTR,	TYPE_BYTE,	0,	1,				1,		1,		&drive->unmask,			NULL);
+	ide_add_setting(drive,	"using_dma",		SETTING_RW,					HDIO_GET_DMA,		HDIO_SET_DMA,		TYPE_BYTE,	0,	1,				1,		1,		&drive->using_dma,		set_using_dma);
+}
+
+int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf)
+{
+	struct request rq;
+	byte buffer[4];
+
+	if (!buf)
+		buf = buffer;
+	memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors);
+	ide_init_drive_cmd(&rq);
+	rq.buffer = buf;
+	*buf++ = cmd;
+	*buf++ = nsect;
+	*buf++ = feature;
+	*buf++ = sectors;
+	return ide_do_drive_cmd(drive, &rq, ide_wait);
+}
+
 static int ide_ioctl (struct inode *inode, struct file *file,
 			unsigned int cmd, unsigned long arg)
 {
 	int err, major, minor;
 	ide_drive_t *drive;
-	unsigned long flags;
 	struct request rq;
 	kdev_t dev;
+	ide_settings_t *setting;
 
 	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;
+
+	if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) {
+		if (cmd == setting->read_ioctl) {
+			err = ide_read_setting(drive, setting);
+			return err >= 0 ? put_user(err, (long *) arg) : err;
+		} else {
+			if ((MINOR(inode->i_rdev) & PARTN_MASK))
+				return -EINVAL;
+			return ide_write_setting(drive, setting, arg);
+		}
+	}
+
 	ide_init_drive_cmd (&rq);
 	switch (cmd) {
 		case HDIO_GETGEO:
@@ -1730,54 +1961,13 @@
 			invalidate_buffers(inode->i_rdev);
 			return 0;
 
-		case BLKRASET:
-			if (!suser()) return -EACCES;
-			if(arg > 0xff) return -EINVAL;
-			read_ahead[MAJOR(inode->i_rdev)] = arg;
-			return 0;
-
-		case BLKRAGET:
-			return put_user(read_ahead[MAJOR(inode->i_rdev)], (long *) arg);
-
 	 	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);
 
-		case HDIO_GET_KEEPSETTINGS:
-			return put_user(drive->keep_settings, (long *) arg);
-
-		case HDIO_GET_UNMASKINTR:
-			return put_user(drive->unmask, (long *) arg);
-
-		case HDIO_GET_DMA:
-			return put_user(drive->using_dma, (long *) arg);
-
-		case HDIO_GET_32BIT:
-			return put_user(drive->io_32bit, (long *) arg);
-
-		case HDIO_GET_MULTCOUNT:
-			return put_user(drive->mult_count, (long *) arg);
-
 		case HDIO_GET_IDENTITY:
 			if (MINOR(inode->i_rdev) & PARTN_MASK)
 				return -EINVAL;
@@ -1792,99 +1982,13 @@
 #endif
 			return 0;
 
-		case HDIO_GET_NOWERR:
-			return put_user(drive->bad_wstat == BAD_R_STAT, (long *) arg);
-
 		case HDIO_GET_NICE:
-		{
-			long nice = 0;
-
-			nice |= drive->dsc_overlap << IDE_NICE_DSC_OVERLAP;
-			nice |= drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP;
-			nice |= drive->nice0 << IDE_NICE_0;
-			nice |= drive->nice1 << IDE_NICE_1;
-			nice |= drive->nice2 << IDE_NICE_2;
-			return put_user(nice, (long *) arg);
-		}
-
-		case HDIO_SET_DMA:
-			if (!suser()) return -EACCES;
-			if (drive->driver != NULL && !DRIVER(drive)->supports_dma)
-				return -EPERM;
-			if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
-				return -EPERM;
-		case HDIO_SET_KEEPSETTINGS:
-		case HDIO_SET_UNMASKINTR:
-		case HDIO_SET_NOWERR:
-			if (arg > 1)
-				return -EINVAL;
-		case HDIO_SET_32BIT:
-			if (!suser()) return -EACCES;
-			if ((MINOR(inode->i_rdev) & PARTN_MASK))
-				return -EINVAL;
-			save_flags(flags);
-			cli();
-			switch (cmd) {
-				case HDIO_SET_DMA:
-					if (!(HWIF(drive)->dmaproc)) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) {
-						restore_flags(flags);
-						return -EIO;
-					}
-					break;
-				case HDIO_SET_KEEPSETTINGS:
-					drive->keep_settings = arg;
-					break;
-				case HDIO_SET_UNMASKINTR:
-					if (arg && drive->no_unmask) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					drive->unmask = arg;
-					break;
-				case HDIO_SET_NOWERR:
-					drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
-					break;
-				case HDIO_SET_32BIT:
-					if (arg > (1 + (SUPPORT_VLB_SYNC<<1))) {
-						restore_flags(flags);
-						return -EINVAL;
-					}
-					if (arg && drive->no_io_32bit) {
-						restore_flags(flags);
-						return -EPERM;
-					}
-					drive->io_32bit = arg;
-#ifdef CONFIG_BLK_DEV_DTC2278
-					if (HWIF(drive)->chipset == ide_dtc2278)
-						HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
-#endif /* CONFIG_BLK_DEV_DTC2278 */
-					break;
-			}
-			restore_flags(flags);
-			return 0;
-
-		case HDIO_SET_MULTCOUNT:
-			if (!suser()) return -EACCES;
-			if (MINOR(inode->i_rdev) & PARTN_MASK)
-				return -EINVAL;
-			if (drive->id && arg > drive->id->max_multsect)
-				return -EINVAL;
-			save_flags(flags);
-			cli();
-			if (drive->special.b.set_multmode) {
-				restore_flags(flags);
-				return -EBUSY;
-			}
-			drive->mult_req = arg;
-			drive->special.b.set_multmode = 1;
-			restore_flags(flags);
-			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-			return (drive->mult_count == arg) ? 0 : -EIO;
-
+			return put_user(drive->dsc_overlap	<<	IDE_NICE_DSC_OVERLAP	|
+					drive->atapi_overlap	<<	IDE_NICE_ATAPI_OVERLAP	|
+					drive->nice0		<< 	IDE_NICE_0		|
+					drive->nice1		<<	IDE_NICE_1		|
+					drive->nice2		<<	IDE_NICE_2,
+					(long *) arg);
 		case HDIO_DRIVE_CMD:
 		{
 			byte args[4], *argbuf = args;
@@ -1901,31 +2005,13 @@
 					return -ENOMEM;
 				memcpy(argbuf, args, 4);
 			}
-			rq.buffer = argbuf;
-			err = ide_do_drive_cmd(drive, &rq, ide_wait);
+			err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf);
 			if (copy_to_user((void *)arg, argbuf, argsize))
 				err = -EFAULT;
 			if (argsize > 4)
 				kfree(argbuf);
 			return err;
 		}
-		case HDIO_SET_PIO_MODE:
-			if (!suser()) return -EACCES;
-			if (MINOR(inode->i_rdev) & PARTN_MASK)
-				return -EINVAL;
-			if (!HWIF(drive)->tuneproc)
-				return -ENOSYS;
-			save_flags(flags);
-			cli();
-			if (drive->special.b.set_tune) {
-				restore_flags(flags);
-				return -EBUSY;
-			}
-			drive->tune_req = (byte) arg;
-			drive->special.b.set_tune = 1;
-			restore_flags(flags);
-			(void) ide_do_drive_cmd (drive, &rq, ide_wait);
-			return 0;
 
 		case HDIO_SCAN_HWIF:
 		{
@@ -2223,7 +2309,7 @@
 		if (i > 0 || i <= -7) {			/* is parameter a chipset name? */
 			if (hwif->chipset != ide_unknown)
 				goto bad_option;	/* chipset already specified */
-			if (i != -7 && hw != 0)
+			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 */
@@ -2578,7 +2664,7 @@
 	if (d->special == NULL)		d->special = default_special;
 }
 
-ide_drive_t *ide_scan_devices (byte media, ide_driver_t *driver, int n)
+ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n)
 {
 	unsigned int unit, index, i;
 
@@ -2588,12 +2674,15 @@
 search:
 	for (index = 0, i = 0; index < MAX_HWIFS; ++index) {
 		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;
-			}
+		if (!hwif->present)
+			continue;
+		for (unit = 0; unit < MAX_DRIVES; ++unit) {
+			ide_drive_t *drive = &hwif->drives[unit];
+			char *req = drive->driver_req;
+			if (*req && !strstr(name, req))
+				continue;
+			if (drive->present && drive->media == media && drive->driver == driver && ++i > n)
+				return drive;
 		}
 	}
 	return NULL;
@@ -2642,6 +2731,7 @@
 	}
 	ide_remove_proc_entries(drive, DRIVER(drive)->proc);
 	ide_remove_proc_entries(drive, generic_subdriver_entries);
+	auto_remove_settings(drive);
 	drive->driver = NULL;
 	restore_flags(flags);
 	return 0;
@@ -2735,6 +2825,8 @@
 EXPORT_SYMBOL(ide_stall_queue);
 EXPORT_SYMBOL(ide_add_proc_entries);
 EXPORT_SYMBOL(ide_remove_proc_entries);
+EXPORT_SYMBOL(ide_add_setting);
+EXPORT_SYMBOL(ide_remove_setting);
 EXPORT_SYMBOL(proc_ide_read_geometry);
 
 EXPORT_SYMBOL(ide_register);

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