📄 ide-proc.c
字号:
/* * linux/drivers/ide/ide-proc.c Version 1.05 Mar 05, 2003 * * Copyright (C) 1997-1998 Mark Lord * Copyright (C) 2003 Red Hat <alan@redhat.com> *//* * This is the /proc/ide/ filesystem implementation. * * The major reason this exists is to provide sufficient access * to driver and config data, such that user-mode programs can * be developed to handle chipset tuning for most PCI interfaces. * 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/config" * * 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 "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. * * 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. * * Feel free to develop and distribute fancy GUI configuration * utilities for your favorite PCI chipsets. I'll be working on * one for the Promise 20246 someday soon. -ml * */#include <linux/config.h>#include <linux/module.h>#include <asm/uaccess.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/mm.h>#include <linux/pci.h>#include <linux/ctype.h>#include <linux/hdreg.h>#include <linux/ide.h>#include <linux/seq_file.h>#include <asm/io.h>static int proc_ide_write_config(struct file *file, const char __user *buffer, unsigned long count, void *data){ ide_hwif_t *hwif = (ide_hwif_t *)data; ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup); ide_hwgroup_t *mategroup = NULL; unsigned long timeout; unsigned long flags; const char *start = NULL, *msg = NULL; struct entry { u32 val; u16 reg; u8 size; u8 pci; } *prog, *q, *r; int want_pci = 0; char *buf, *s; int err; if (hwif->mate && hwif->mate->hwgroup) mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup); if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) return -EACCES; if (count >= PAGE_SIZE) return -EINVAL; s = buf = (char *)__get_free_page(GFP_USER); if (!buf) return -ENOMEM; err = -ENOMEM; q = prog = (struct entry *)__get_free_page(GFP_USER); if (!prog) goto out; err = -EFAULT; if (copy_from_user(buf, buffer, count)) goto out1; buf[count] = '\0'; while (isspace(*s)) s++; while (*s) { char *p; int digits; start = s; if ((char *)(q + 1) > (char *)prog + PAGE_SIZE) { msg = "too many entries"; goto parse_error; } switch (*s++) { case 'R': q->pci = 0; break; case 'P': q->pci = 1; want_pci = 1; break; default: msg = "expected 'R' or 'P'"; goto parse_error; } q->reg = simple_strtoul(s, &p, 16); digits = p - s; if (!digits || digits > 4 || (q->pci && q->reg > 0xff)) { msg = "bad/missing register number"; goto parse_error; } if (*p++ != ':') { msg = "missing ':'"; goto parse_error; } q->val = simple_strtoul(p, &s, 16); digits = s - p; if (digits != 2 && digits != 4 && digits != 8) { msg = "bad data, 2/4/8 digits required"; goto parse_error; } q->size = digits / 2; if (q->pci) {#ifdef CONFIG_BLK_DEV_IDEPCI if (q->reg & (q->size - 1)) { msg = "misaligned access"; goto parse_error; }#else msg = "not a PCI device"; goto parse_error;#endif /* CONFIG_BLK_DEV_IDEPCI */ } q++; if (*s && !isspace(*s++)) { msg = "expected whitespace after data"; goto parse_error; } while (isspace(*s)) s++; } /* * What follows below is fucking insane, even for IDE people. * For now I've dealt with the obvious problems on the parsing * side, but IMNSHO we should simply remove the write access * to /proc/ide/.../config, killing that FPOS completely. */ err = -EBUSY; timeout = jiffies + (3 * HZ); spin_lock_irqsave(&ide_lock, flags); while (mygroup->busy || (mategroup && mategroup->busy)) { spin_unlock_irqrestore(&ide_lock, flags); if (time_after(jiffies, timeout)) { printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name); goto out1; } spin_lock_irqsave(&ide_lock, flags); }#ifdef CONFIG_BLK_DEV_IDEPCI if (want_pci && (!hwif->pci_dev || hwif->pci_dev->vendor)) { spin_unlock_irqrestore(&ide_lock, flags); printk("proc_ide: PCI registers not accessible for %s\n", hwif->name); err = -EINVAL; goto out1; }#endif /* CONFIG_BLK_DEV_IDEPCI */ for (r = prog; r < q; r++) { unsigned int reg = r->reg, val = r->val; if (r->pci) {#ifdef CONFIG_BLK_DEV_IDEPCI int rc = 0; struct pci_dev *dev = hwif->pci_dev; switch (q->size) { case 1: msg = "byte"; rc = pci_write_config_byte(dev, reg, val); break; case 2: msg = "word"; rc = pci_write_config_word(dev, reg, val); break; case 4: msg = "dword"; rc = pci_write_config_dword(dev, reg, val); break; } if (rc) { spin_unlock_irqrestore(&ide_lock, flags); printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n", msg, dev->bus->number, dev->devfn, reg, val); printk("proc_ide_write_config: error %d\n", rc); err = -EIO; goto out1; }#endif /* CONFIG_BLK_DEV_IDEPCI */ } else { /* not pci */ switch (r->size) { case 1: hwif->OUTB(val, reg); break; case 2: hwif->OUTW(val, reg); break; case 4: hwif->OUTL(val, reg); break; } } } spin_unlock_irqrestore(&ide_lock, flags); err = count;out1: free_page((unsigned long)prog);out: free_page((unsigned long)buf); return err;parse_error: printk("parse error\n"); printk("proc_ide: error: %s: '%s'\n", msg, start); err = -EINVAL; goto out1;}static int proc_ide_read_config (char *page, char **start, off_t off, int count, int *eof, void *data){ char *out = page; int len;#ifdef CONFIG_BLK_DEV_IDEPCI ide_hwif_t *hwif = (ide_hwif_t *)data; struct pci_dev *dev = hwif->pci_dev; if ((hwif->pci_dev && hwif->pci_dev->vendor) && dev && dev->bus) { int reg = 0; out += sprintf(out, "pci bus %02x device %02x vendor %04x " "device %04x channel %d\n", dev->bus->number, dev->devfn, hwif->pci_dev->vendor, hwif->pci_dev->device, hwif->channel); do { u8 val; int rc = pci_read_config_byte(dev, reg, &val); if (rc) { printk("proc_ide_read_config: error %d reading" " bus %02x dev %02x reg 0x%02x\n", rc, dev->bus->number, dev->devfn, reg); out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n'); } else out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n'); } while (reg < 0x100); } else#endif /* CONFIG_BLK_DEV_IDEPCI */ out += sprintf(out, "(none)\n"); len = out - page; PROC_IDE_READ_RETURN(page,start,off,count,eof,len);}static int proc_ide_read_imodel (char *page, char **start, off_t off, int count, int *eof, void *data){ ide_hwif_t *hwif = (ide_hwif_t *) data; int len; const char *name; /* * Neither ide_unknown nor ide_forced should be set at this point. */ switch (hwif->chipset) { case ide_generic: name = "generic"; break; case ide_pci: name = "pci"; break; case ide_cmd640: name = "cmd640"; break; case ide_dtc2278: name = "dtc2278"; break; case ide_ali14xx: name = "ali14xx"; break; case ide_qd65xx: name = "qd65xx"; break; case ide_umc8672: name = "umc8672"; break; case ide_ht6560b: name = "ht6560b"; break; case ide_pdc4030: name = "pdc4030"; break; case ide_rz1000: name = "rz1000"; break; case ide_trm290: name = "trm290"; break; case ide_cmd646: name = "cmd646"; break; case ide_cy82c693: name = "cy82c693"; break; case ide_4drives: name = "4drives"; break; case ide_pmac: name = "mac-io"; break; default: name = "(unknown)"; break; } len = sprintf(page, "%s\n", name); PROC_IDE_READ_RETURN(page,start,off,count,eof,len);}static int proc_ide_read_mate (char *page, char **start, off_t off, int count, int *eof, void *data){ ide_hwif_t *hwif = (ide_hwif_t *) data; int len; if (hwif && hwif->mate && hwif->mate->present) len = sprintf(page, "%s\n", hwif->mate->name); else len = sprintf(page, "(none)\n"); PROC_IDE_READ_RETURN(page,start,off,count,eof,len);}static int proc_ide_read_channel (char *page, char **start, off_t off, int count, int *eof, void *data){ ide_hwif_t *hwif = (ide_hwif_t *) data; int len; page[0] = hwif->channel ? '1' : '0'; page[1] = '\n'; len = 2; PROC_IDE_READ_RETURN(page,start,off,count,eof,len);}static int proc_ide_read_identify (char *page, char **start, off_t off, int count, int *eof, void *data){ ide_drive_t *drive = (ide_drive_t *)data; int len = 0, i = 0; int err = 0; len = sprintf(page, "\n"); if (drive) { unsigned short *val = (unsigned short *) page; BUG_ON(!drive->driver); err = taskfile_lib_get_identify(drive, page); if (!err) { char *out = ((char *)page) + (SECTOR_WORDS * 4); page = out; do { out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); val += 1; } while (i < (SECTOR_WORDS * 2)); len = out - page; } } PROC_IDE_READ_RETURN(page,start,off,count,eof,len);}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, rc, mul_factor, div_factor;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -