diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-03-17 22:05:47 +0000 |
commit | 27cfca1ec98e91261b1a5355d10a8996464b63af (patch) | |
tree | 8e895a53e372fa682b4c0a585b9377d67ed70d0e /drivers/block/ide-proc.c | |
parent | 6a76fb7214c477ccf6582bd79c5b4ccc4f9c41b1 (diff) |
Look Ma' what I found on my harddisk ...
o New faster syscalls for 2.1.x, too
o Upgrade to 2.1.89.
Don't try to run this. It's flaky as hell. But feel free to debug ...
Diffstat (limited to 'drivers/block/ide-proc.c')
-rw-r--r-- | drivers/block/ide-proc.c | 440 |
1 files changed, 330 insertions, 110 deletions
diff --git a/drivers/block/ide-proc.c b/drivers/block/ide-proc.c index 55ad22521..e9dad1c26 100644 --- a/drivers/block/ide-proc.c +++ b/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.03 January 2, 1998 * * Copyright (C) 1997-1998 Mark Lord */ @@ -13,18 +13,40 @@ * 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 * - * Also useful, "cat /proc/ide0/hda/identify" will issue an IDENTIFY - * (or PACKET_IDENTIFY) command to /dev/hda, and then dump out the + * 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. + * + * 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. * @@ -44,29 +66,13 @@ #include <linux/pci.h> #include <linux/bios32.h> #include <linux/ctype.h> +#include <asm/io.h> #include "ide.h" #ifndef MIN #define MIN(a,b) (((a) < (b)) ? (a) : (b)) #endif -/* - * Standard exit stuff: - */ -#define PROC_IDE_READ_RETURN(page,start,off,count,eof,len) \ -{ \ - len -= off; \ - if (len < count) { \ - *eof = 1; \ - if (len <= 0) \ - return 0; \ - } else \ - len = count; \ - *start = page + off; \ - return len; \ -} - - #ifdef CONFIG_PCI static int ide_getxdigit(char c) @@ -81,13 +87,14 @@ static int ide_getxdigit(char c) 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)) @@ -95,17 +102,17 @@ static int xx_xx_parse_error (const char *start, unsigned long maxlen) 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; @@ -122,55 +129,109 @@ static int proc_ide_write_pci */ 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(); - if (0 < (signed long)(timeout - jiffies)) { + 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(); } } - 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; + } } } } @@ -179,25 +240,26 @@ static int proc_ide_write_pci 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 @@ -212,15 +274,40 @@ static int proc_ide_read_imodel { ide_hwif_t *hwif = (ide_hwif_t *) data; int len; - const char *vids, *dids; - vids = pci_strvendor(hwif->pci_devid.vid); - dids = pci_strdev(hwif->pci_devid.vid, hwif->pci_devid.did); - len = sprintf(page,"%s: %s\n", vids ? vids : "(none)", dids ? dids : "(none)"); + len = sprintf(page,"%04x: %04x\n", hwif->pci_devid.vid, hwif->pci_devid.did); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } #endif /* CONFIG_PCI */ +static int ide_getdigit(char c) +{ + int digit; + if (isdigit(c)) + digit = c - '0'; + else + digit = -1; + return digit; +} + +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_type (char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -270,22 +357,9 @@ static int proc_ide_read_channel 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 @@ -311,27 +385,141 @@ static int proc_ide_read_settings (char *page, char **start, off_t off, int count, int *eof, void *data) { ide_drive_t *drive = (ide_drive_t *) data; + ide_settings_t *setting = (ide_settings_t *) drive->settings; char *out = page; - int len; - - 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); + int len, rc, mul_factor, div_factor; + + 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) { ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; int len; - len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive)); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -373,6 +561,18 @@ static int proc_ide_read_driver 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) { @@ -397,14 +597,12 @@ static int proc_ide_read_media PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } - static ide_proc_entry_t generic_drive_entries[] = { - { "capacity", proc_ide_read_capacity, NULL }, - { "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 } }; @@ -415,8 +613,12 @@ void ide_add_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p) if (!drive->proc || !p) return; while (p->name != NULL) { - ent = create_proc_entry(p->name, 0, drive->proc); + mode_t mode = S_IFREG|S_IRUSR; + if (!strcmp(p->name,"settings")) + mode |= S_IWUSR; + ent = create_proc_entry(p->name, mode, drive->proc); if (!ent) return; + ent->nlink = 1; ent->data = drive; ent->read_proc = p->read_proc; ent->write_proc = p->write_proc; @@ -434,9 +636,16 @@ void ide_remove_proc_entries(ide_drive_t *drive, ide_proc_entry_t *p) } } -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]; @@ -446,6 +655,12 @@ static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *par 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; } } @@ -463,11 +678,12 @@ static void create_proc_ide_interfaces (struct proc_dir_entry *parent) 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", S_IFREG|S_IRUSR|S_IWUSR, hwif_ent); if (!ent) return; + ent->nlink = 1; 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; @@ -492,14 +708,18 @@ static void create_proc_ide_interfaces (struct proc_dir_entry *parent) 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; } |