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

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

diff -u --recursive --new-file v2.1.76/linux/drivers/block/ide-proc.c linux/drivers/block/ide-proc.c
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/proc_ide.c	Version 1.01	December 12, 1997
+ *  linux/drivers/block/ide-proc.c	Version 1.02	December 31, 1997
  *
  *  Copyright (C) 1997-1998	Mark Lord
  */
@@ -13,15 +13,30 @@
  * This should provide better utilities, and less kernel bloat.
  *
  * The entire pci config space for a PCI interface chipset can be
- * retrieved by just reading it.  e.g.    "cat /proc/ide3/pci"
+ * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
  *
- * To modify registers, do something like:
- *   echo "40:88" >/proc/ide/ide3/pci
+ * To modify registers *safely*, do something like:
+ *   echo "P40:88" >/proc/ide/ide3/config
  * That expression writes 0x88 to pci config register 0x40
  * on the chip which controls ide3.  Multiple tuples can be issued,
  * and the writes will be completed as an atomic set:
- *   echo "40:88 41:35 42:00 43:00" >/proc/ide/ide3/pci
- * All numbers must be pairs of ascii hex digits.
+ *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
+ *
+ * All numbers must be specified using pairs of ascii hex digits.
+ * It is important to note that these writes will be performed
+ * after waiting for the IDE controller (both interfaces)
+ * to be completely idle, to ensure no corruption of I/O in progress.
+ *
+ * Non-PCI registers can also be written, using "R" in place of "P"
+ * in the above examples.  The size of the port transfer is determined
+ * by the number of pairs of hex digits given for the data.  If a two
+ * digit value is given, the write will be a byte operation; if four
+ * digits are used, the write will be performed as a 16-bit operation;
+ * and if eight digits are specified, a 32-bit "dword" write will be
+ * performed.  Odd numbers of digits are not permitted.
+ *
+ * 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
@@ -44,6 +59,7 @@
 #include <linux/pci.h>
 #include <linux/bios32.h>
 #include <linux/ctype.h>
+#include <asm/io.h>
 #include "ide.h"
 
 #ifndef MIN
@@ -64,13 +80,14 @@
 	return digit;
 }
 
-
-static int xx_xx_parse_error (const char *start, unsigned long maxlen)
+static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
 {
-	char errbuf[7];
-	int i, len = MIN(6, maxlen);
+	char errbuf[16];
+	int i;
+	if (len >= sizeof(errbuf))
+		len = sizeof(errbuf) - 1;
 	for (i = 0; i < len; ++i) {
-		char c = start[i];
+		char c = data[i];
 		if (!c || c == '\n')
 			c = '\0';
 		else if (iscntrl(c))
@@ -78,17 +95,17 @@
 		errbuf[i] = c;
 	}
 	errbuf[i] = '\0';
-	printk("proc_ide: error: expected 'xx:xx', but got '%s'\n", errbuf);
+	printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
 	return -EINVAL;
 }
 
-static int proc_ide_write_pci
+static int proc_ide_write_config
 	(struct file *file, const char *buffer, unsigned long count, void *data)
 {
 	ide_hwif_t	*hwif = (ide_hwif_t *)data;
 	int		for_real = 0;
-	unsigned long	n, flags;
-	const char	*start;
+	unsigned long	startn = 0, n, flags;
+	const char	*start = NULL, *msg = NULL;
 
 	if (!suser())
 		return -EACCES;
@@ -105,56 +122,109 @@
 	 */
 	save_flags(flags);
 	do {
-		const char *p = buffer;
-		n = count;
+		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 (((ide_hwgroup_t *)(hwif->hwgroup))->active || (hwif->mate && ((ide_hwgroup_t *)(hwif->mate->hwgroup))->active)) {
-				sti();
+			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);
-					restore_flags(flags);
 					return -EBUSY;
 				}
 				cli();
 			}
 		}
-		while (n) {
-			int d1, d2, rc;
-			byte reg, val;
+		p = buffer;
+		n = count;
+		while (n > 0) {
+			int d, digits;
+			unsigned int reg = 0, val = 0, is_pci;
 			start = p;
-#if 0
-			printk("loop(%d): n=%ld, input=%.5s\n", for_real, n, p);
-#endif
-			if (n < 5)
-				goto parse_error;
-			if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+			startn = n--;
+			switch (*p++) {
+				case 'R':	is_pci = 0;
+						break;
+				case 'P':	is_pci = 1;
+						break;
+				default:	msg = "expected 'R' or 'P'";
+						goto parse_error;
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				reg = (reg << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
+				msg = "bad/missing register number";
 				goto parse_error;
-			reg = (d1 << 4) | d2;
-			if (*p++ != ':')
+			}
+			if (--n < 0 || *p++ != ':') {
+				msg = "missing ':'";
 				goto parse_error;
-			if (0 > (d1 = ide_getxdigit(*p++)) || 0 > (d2 = ide_getxdigit(*p++)))
+			}
+			digits = 0;
+			while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
+				val = (val << 4) | d;
+				--n;
+				++p;
+				++digits;
+			}
+			if (digits != 2 && digits != 4 && digits != 8) {
+				msg = "bad data, 2/4/8 digits required";
 				goto parse_error;
-			val = (d1 << 4) | d2;
-			if (n > 5 && !isspace(*p))
+			}
+			if (n > 0 && !isspace(*p)) {
+				msg = "expected whitespace after data";
 				goto parse_error;
-			n -= 5;
-			while (n && isspace(*p)) {
+			}
+			while (n > 0 && isspace(*p)) {
 				--n;
 				++p;
 			}
+			if (is_pci && (reg & ((digits >> 1) - 1))) {
+				msg = "misaligned access";
+				goto parse_error;
+			}
 			if (for_real) {
 #if 0
-				printk("proc_ide_write_pci: reg=0x%02x, val=0x%02x\n", reg, val);
+				printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits);
 #endif
-				rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
-				if (rc) {
-					restore_flags(flags);
-					printk("proc_ide_write_pci: error writing bus %d fn %d reg 0x%02x value 0x%02x\n",
-						hwif->pci_bus, hwif->pci_fn, reg, val);
-					printk("proc_ide_write_pci: %s\n", pcibios_strerror(rc));
-					return -EIO;
+				if (is_pci) {
+					int rc = 0;
+					switch (digits) {
+						case 2:	msg = "byte";
+							rc = pcibios_write_config_byte(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+						case 4:	msg = "word";
+							rc = pcibios_write_config_word(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+						case 8:	msg = "dword";
+							rc = pcibios_write_config_dword(hwif->pci_bus, hwif->pci_fn, reg, val);
+							break;
+					}
+					if (rc) {
+						restore_flags(flags);
+						printk("proc_ide_write_config: error writing %s at bus %d fn %d reg 0x%x value 0x%x\n",
+							msg, hwif->pci_bus, hwif->pci_fn, reg, val);
+						printk("proc_ide_write_config: %s\n", pcibios_strerror(rc));
+						return -EIO;
+					}
+				} else {	/* not pci */
+					switch (digits) {
+						case 2:	outb(val, reg);
+							break;
+						case 4:	outw(val, reg);
+							break;
+						case 8:	outl(val, reg);
+							break;
+					}
 				}
 			}
 		}
@@ -163,25 +233,26 @@
 	return count;
 parse_error:
 	restore_flags(flags);
-	return xx_xx_parse_error(start, n);
+	printk("parse error\n");
+	return xx_xx_parse_error(start, startn, msg);
 }
 
-static int proc_ide_read_pci
+static int proc_ide_read_config
 	(char *page, char **start, off_t off, int count, int *eof, void *data)
 {
 	ide_hwif_t	*hwif = (ide_hwif_t *)data;
 	char		*out = page;
 	int		len, reg = 0;
 
-	out += sprintf(out, "Bus %d Function %d Vendor %04x Device %04x Channel %d\n",
+	out += sprintf(out, "pci bus %d function %d vendor %04x device %04x channel %d\n",
 		hwif->pci_bus, hwif->pci_fn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
 	do {
 		byte val;
 		int rc = pcibios_read_config_byte(hwif->pci_bus, hwif->pci_fn, reg, &val);
 		if (rc) {
-			printk("proc_ide_read_pci: error reading bus %d fn %d reg 0x%02x\n",
+			printk("proc_ide_read_config: error reading bus %d fn %d reg 0x%02x\n",
 				hwif->pci_bus, hwif->pci_fn, reg);
-			printk("proc_ide_read_pci: %s\n", pcibios_strerror(rc));
+			printk("proc_ide_read_config: %s\n", pcibios_strerror(rc));
 			return -EIO;
 			out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
 		} else
@@ -455,11 +526,11 @@
 		if (!hwif_ent) return;
 #ifdef CONFIG_PCI
 		if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) {
-			ent = create_proc_entry("pci", 0, hwif_ent);
+			ent = create_proc_entry("config", 0, hwif_ent);
 			if (!ent) return;
 			ent->data = hwif;
-			ent->read_proc  = proc_ide_read_pci;
-			ent->write_proc = proc_ide_write_pci;;
+			ent->read_proc  = proc_ide_read_config;
+			ent->write_proc = proc_ide_write_config;;
 
 			ent = create_proc_entry("model", 0, hwif_ent);
 			if (!ent) return;

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