patch-2.1.79 linux/drivers/block/ide-proc.c

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

diff -u --recursive --new-file v2.1.78/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide-proc.c	Version 1.02	December 31, 1997
+ *  linux/drivers/block/ide-proc.c	Version 1.03	January   2, 1998
  *
  *  Copyright (C) 1997-1998	Mark Lord
  */
@@ -38,8 +38,15 @@
  * If there is an error *anywhere* in the string of registers/data
  * then *none* of the writes will be performed.
  *
- * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY
- * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the
+ * Drive/Driver settings can be retrieved by reading the drive's
+ * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
+ * To write a new value "val" into a specific setting "name", use:
+ *   echo "name:val" >/proc/ide/ide0/hda/settings
+ *
+ * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
+ * smart_thresholds, capabilities]" will issue an IDENTIFY /
+ * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
+ * SENSE CAPABILITIES command to /dev/hda, and then dump out the
  * returned data as 256 16-bit words.  The "hdparm" utility will
  * be updated someday soon to use this mechanism.
  *
@@ -80,6 +87,16 @@
 	return digit;
 }
 
+static int ide_getdigit(char c)
+{
+	int digit;
+	if (isdigit(c))
+		digit = c - '0';
+	else
+		digit = -1;
+	return digit;
+}
+
 static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
 {
 	char errbuf[16];
@@ -237,6 +254,24 @@
 	return xx_xx_parse_error(start, startn, msg);
 }
 
+static int proc_ide_read_drivers
+	(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	char		*out = page;
+	int		len;
+	ide_module_t	*p = ide_modules;
+	ide_driver_t	*driver;
+
+	while (p) {
+		driver = (ide_driver_t *) p->info;
+		if (p->type == IDE_DRIVER_MODULE && driver)
+			out += sprintf(out, "%s version %s\n", driver->name, driver->version);
+		p = p->next;
+	}
+	len = out - page;
+	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
+}
+
 static int proc_ide_read_config
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -325,22 +360,9 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-static int proc_ide_get_identify (ide_drive_t *drive, byte *buf)
+static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
 {
-	struct request rq;
-	byte *end;
-
-	ide_init_drive_cmd(&rq);
-	rq.buffer = buf;
-	*buf++ = (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY;
-	*buf++ = 0;
-	*buf++ = 0;
-	*buf++ = 1;
-	end = buf + (SECTOR_WORDS * 4);
-	while (buf != end)
-		*buf++ = 0;	/* pre-zero it, in case identify fails */
-	(void) ide_do_drive_cmd(drive, &rq, ide_wait);
-	return 0;
+	return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
 }
 
 static int proc_ide_read_identify
@@ -366,25 +388,130 @@
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
 	ide_drive_t	*drive = (ide_drive_t *) data;
-	int		major = HWIF(drive)->major;
-	int		minor = drive->select.b.unit << PARTN_BITS;
+	ide_settings_t	*setting = (ide_settings_t *) drive->settings;
 	char		*out = page;
-	int		len;
+	int		len, rc, mul_factor, div_factor;
 
-	out += sprintf(out,"multcount    %i\n", drive->mult_count);
-	out += sprintf(out,"io_32bit     %i\n", drive->io_32bit);
-	out += sprintf(out,"unmaskirq    %i\n", drive->unmask);
-	out += sprintf(out,"using_dma    %i\n", drive->using_dma);
-	out += sprintf(out,"nowerr       %i\n", drive->bad_wstat == BAD_R_STAT);
-	out += sprintf(out,"keepsettings %i\n", drive->keep_settings);
-	out += sprintf(out,"nice         %i/%i/%i\n", drive->nice0, drive->nice1, drive->nice2);
-	out += sprintf(out,"dsc_overlap  %i\n", drive->dsc_overlap);
-	out += sprintf(out,"max_sectors  %i\n", max_sectors[major][minor]);
-	out += sprintf(out,"readahead    %i\n", max_readahead[major][minor] / 1024);
+	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
+	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
+	while(setting) {
+		mul_factor = setting->mul_factor;
+		div_factor = setting->div_factor;
+		out += sprintf(out, "%-24s", setting->name);
+		if ((rc = ide_read_setting(drive, setting)) >= 0)
+			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
+		else
+			out += sprintf(out, "%-16s", "write-only");
+		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
+		if (setting->rw & SETTING_READ)
+			out += sprintf(out, "r");
+		if (setting->rw & SETTING_WRITE)
+			out += sprintf(out, "w");
+		out += sprintf(out, "\n");
+		setting = setting->next;
+	}
 	len = out - page;
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+#define MAX_LEN	30
+
+static int proc_ide_write_settings
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+	ide_hwif_t	*hwif = HWIF(drive);
+	char		name[MAX_LEN + 1];
+	int		for_real = 0, len;
+	unsigned long	n, flags;
+	const char	*start = NULL;
+	ide_settings_t	*setting;
+
+	if (!suser())
+		return -EACCES;
+
+	/*
+	 * Skip over leading whitespace
+	 */
+	while (count && isspace(*buffer)) {
+		--count;
+		++buffer;
+	}
+	/*
+	 * Do one full pass to verify all parameters,
+	 * then do another to actually write the pci regs.
+	 */
+	save_flags(flags);
+	do {
+		const char *p;
+		if (for_real) {
+			unsigned long timeout = jiffies + (3 * HZ);
+			ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
+			ide_hwgroup_t *mategroup = NULL;
+			if (hwif->mate && hwif->mate->hwgroup)
+				mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
+			cli();	/* ensure all PCI writes are done together */
+			while (mygroup->active || (mategroup && mategroup->active)) {
+				restore_flags(flags);
+				if (0 < (signed long)(jiffies - timeout)) {
+					printk("/proc/ide/%s/pci: channel(s) busy, cannot write\n", hwif->name);
+					return -EBUSY;
+				}
+				cli();
+			}
+		}
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int val = 0;
+			start = p;
+
+			while (n > 0 && *p != ':') {
+				--n;
+				p++;
+			}
+			if (*p != ':')
+				goto parse_error;
+			len = IDE_MIN(p - start, MAX_LEN);
+			strncpy(name, start, IDE_MIN(len, MAX_LEN));
+			name[len] = 0;
+
+			if (n > 0) {
+				--n;
+				p++;
+			} else
+				goto parse_error;
+			
+			digits = 0;
+			while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
+				val = (val * 10) + d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (n > 0 && !isspace(*p))
+				goto parse_error;
+			while (n > 0 && isspace(*p)) {
+				--n;
+				++p;
+			}
+			setting = ide_find_setting_by_name(drive, name);
+			if (!setting)
+				goto parse_error;
+
+			if (for_real)
+				ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
+		}
+	} while (!for_real++);
+	restore_flags(flags);
+	return count;
+parse_error:
+	restore_flags(flags);
+	printk("proc_ide_write_settings(): parse error\n");
+	return -EINVAL;
+}
+
 int proc_ide_read_capacity
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -437,6 +564,18 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
+static int proc_ide_write_driver
+	(struct file *file, const char *buffer, unsigned long count, void *data)
+{
+	ide_drive_t	*drive = (ide_drive_t *) data;
+
+	if (!suser())
+		return -EACCES;
+	if (ide_replace_subdriver(drive, buffer))
+		return -EINVAL;
+	return count;
+}
+
 static int proc_ide_read_media
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
@@ -461,13 +600,12 @@
 	PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
 }
 
-
 static ide_proc_entry_t generic_drive_entries[] = {
-	{ "driver", proc_ide_read_driver, NULL },
+	{ "driver", proc_ide_read_driver, proc_ide_write_driver },
 	{ "identify", proc_ide_read_identify, NULL },
 	{ "media", proc_ide_read_media, NULL },
 	{ "model", proc_ide_read_dmodel, NULL },
-	{ "settings", proc_ide_read_settings, NULL },
+	{ "settings", proc_ide_read_settings, proc_ide_write_settings },
 	{ NULL, NULL, NULL }
 };
 
@@ -497,9 +635,16 @@
 	}
 }
 
-static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent)
+static int proc_ide_readlink(struct proc_dir_entry *de, char *page)
+{
+	int n = (de->name[2] - 'a') / 2;
+	return sprintf(page, "ide%d/%s", n, de->name);
+}
+
+static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent, struct proc_dir_entry *root)
 {
 	int	d;
+	struct proc_dir_entry *ent;
 
 	for (d = 0; d < MAX_DRIVES; d++) {
 		ide_drive_t *drive = &hwif->drives[d];
@@ -509,6 +654,12 @@
 		drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
 		if (drive->proc)
 			ide_add_proc_entries(drive, generic_drive_entries);
+
+		ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root);
+		if (!ent) return;
+		ent->data = drive;
+		ent->readlink_proc = proc_ide_readlink;
+		ent->nlink = 1;
 	}
 }
 
@@ -555,14 +706,18 @@
 		ent->data = hwif;
 		ent->read_proc  = proc_ide_read_type;
 
-		create_proc_ide_drives(hwif, hwif_ent);
+		create_proc_ide_drives(hwif, hwif_ent, parent);
 	}
 }
 
 void proc_ide_init(void)
 {
-	struct proc_dir_entry *ent;
-	ent = create_proc_entry("ide", S_IFDIR, 0);
+	struct proc_dir_entry *root, *ent;
+	root = create_proc_entry("ide", S_IFDIR, 0);
+	if (!root) return;
+	create_proc_ide_interfaces(root);
+
+	ent = create_proc_entry("drivers", 0, root);
 	if (!ent) return;
-	create_proc_ide_interfaces(ent);
+	ent->read_proc  = proc_ide_read_drivers;
 }

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