Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

ide-proc.c

Go to the documentation of this file.
00001 /*
00002  *  linux/drivers/block/ide-proc.c      Version 1.03    January   2, 1998
00003  *
00004  *  Copyright (C) 1997-1998     Mark Lord
00005  */
00006 
00007 /*
00008  * This is the /proc/ide/ filesystem implementation.
00009  *
00010  * The major reason this exists is to provide sufficient access
00011  * to driver and config data, such that user-mode programs can
00012  * be developed to handle chipset tuning for most PCI interfaces.
00013  * This should provide better utilities, and less kernel bloat.
00014  *
00015  * The entire pci config space for a PCI interface chipset can be
00016  * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
00017  *
00018  * To modify registers *safely*, do something like:
00019  *   echo "P40:88" >/proc/ide/ide3/config
00020  * That expression writes 0x88 to pci config register 0x40
00021  * on the chip which controls ide3.  Multiple tuples can be issued,
00022  * and the writes will be completed as an atomic set:
00023  *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
00024  *
00025  * All numbers must be specified using pairs of ascii hex digits.
00026  * It is important to note that these writes will be performed
00027  * after waiting for the IDE controller (both interfaces)
00028  * to be completely idle, to ensure no corruption of I/O in progress.
00029  *
00030  * Non-PCI registers can also be written, using "R" in place of "P"
00031  * in the above examples.  The size of the port transfer is determined
00032  * by the number of pairs of hex digits given for the data.  If a two
00033  * digit value is given, the write will be a byte operation; if four
00034  * digits are used, the write will be performed as a 16-bit operation;
00035  * and if eight digits are specified, a 32-bit "dword" write will be
00036  * performed.  Odd numbers of digits are not permitted.
00037  *
00038  * If there is an error *anywhere* in the string of registers/data
00039  * then *none* of the writes will be performed.
00040  *
00041  * Drive/Driver settings can be retrieved by reading the drive's
00042  * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
00043  * To write a new value "val" into a specific setting "name", use:
00044  *   echo "name:val" >/proc/ide/ide0/hda/settings
00045  *
00046  * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
00047  * smart_thresholds, capabilities]" will issue an IDENTIFY /
00048  * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
00049  * SENSE CAPABILITIES command to /dev/hda, and then dump out the
00050  * returned data as 256 16-bit words.  The "hdparm" utility will
00051  * be updated someday soon to use this mechanism.
00052  *
00053  * Feel free to develop and distribute fancy GUI configuration
00054  * utilities for you favorite PCI chipsets.  I'll be working on
00055  * one for the Promise 20246 someday soon.  -ml
00056  *
00057  */
00058 
00059 #include <linux/config.h>
00060 #include <asm/uaccess.h>
00061 #include <linux/errno.h>
00062 #include <linux/sched.h>
00063 #include <linux/proc_fs.h>
00064 #include <linux/stat.h>
00065 #include <linux/mm.h>
00066 #include <linux/pci.h>
00067 #include <linux/ctype.h>
00068 #include <asm/io.h>
00069 #include "ide.h"
00070 
00071 #ifndef MIN
00072 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
00073 #endif
00074 
00075 static int ide_getxdigit(char c)
00076 {
00077         int digit;
00078         if (isdigit(c))
00079                 digit = c - '0';
00080         else if (isxdigit(c))
00081                 digit = tolower(c) - 'a' + 10;
00082         else
00083                 digit = -1;
00084         return digit;
00085 }
00086 
00087 static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
00088 {
00089         char errbuf[16];
00090         int i;
00091         if (len >= sizeof(errbuf))
00092                 len = sizeof(errbuf) - 1;
00093         for (i = 0; i < len; ++i) {
00094                 char c = data[i];
00095                 if (!c || c == '\n')
00096                         c = '\0';
00097                 else if (iscntrl(c))
00098                         c = '?';
00099                 errbuf[i] = c;
00100         }
00101         errbuf[i] = '\0';
00102         printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
00103         return -EINVAL;
00104 }
00105 
00106 static int proc_ide_write_config
00107         (struct file *file, const char *buffer, unsigned long count, void *data)
00108 {
00109         ide_hwif_t      *hwif = (ide_hwif_t *)data;
00110         int             for_real = 0;
00111         unsigned long   startn = 0, n, flags;
00112         const char      *start = NULL, *msg = NULL;
00113 
00114         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
00115                 return -EACCES;
00116         /*
00117          * Skip over leading whitespace
00118          */
00119         while (count && isspace(*buffer)) {
00120                 --count;
00121                 ++buffer;
00122         }
00123         /*
00124          * Do one full pass to verify all parameters,
00125          * then do another to actually write the regs.
00126          */
00127         save_flags(flags);      /* all CPUs */
00128         do {
00129                 const char *p;
00130                 if (for_real) {
00131                         unsigned long timeout = jiffies + (3 * HZ);
00132                         ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
00133                         ide_hwgroup_t *mategroup = NULL;
00134                         if (hwif->mate && hwif->mate->hwgroup)
00135                                 mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
00136                         cli();  /* all CPUs; ensure all writes are done together */
00137                         while (mygroup->busy || (mategroup && mategroup->busy)) {
00138                                 sti();  /* all CPUs */
00139                                 if (0 < (signed long)(jiffies - timeout)) {
00140                                         printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
00141                                         restore_flags(flags);   /* all CPUs */
00142                                         return -EBUSY;
00143                                 }
00144                                 cli();  /* all CPUs */
00145                         }
00146                 }
00147                 p = buffer;
00148                 n = count;
00149                 while (n > 0) {
00150                         int d, digits;
00151                         unsigned int reg = 0, val = 0, is_pci;
00152                         start = p;
00153                         startn = n--;
00154                         switch (*p++) {
00155                                 case 'R':       is_pci = 0;
00156                                                 break;
00157                                 case 'P':       is_pci = 1;
00158 #ifdef CONFIG_BLK_DEV_IDEPCI
00159                                                 if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
00160                                                         break;
00161 #endif  /* CONFIG_BLK_DEV_IDEPCI */
00162                                                 msg = "not a PCI device";
00163                                                 goto parse_error;
00164                                 default:        msg = "expected 'R' or 'P'";
00165                                                 goto parse_error;
00166                         }
00167                         digits = 0;
00168                         while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
00169                                 reg = (reg << 4) | d;
00170                                 --n;
00171                                 ++p;
00172                                 ++digits;
00173                         }
00174                         if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
00175                                 msg = "bad/missing register number";
00176                                 goto parse_error;
00177                         }
00178                         if (n-- == 0 || *p++ != ':') {
00179                                 msg = "missing ':'";
00180                                 goto parse_error;
00181                         }
00182                         digits = 0;
00183                         while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
00184                                 val = (val << 4) | d;
00185                                 --n;
00186                                 ++p;
00187                                 ++digits;
00188                         }
00189                         if (digits != 2 && digits != 4 && digits != 8) {
00190                                 msg = "bad data, 2/4/8 digits required";
00191                                 goto parse_error;
00192                         }
00193                         if (n > 0 && !isspace(*p)) {
00194                                 msg = "expected whitespace after data";
00195                                 goto parse_error;
00196                         }
00197                         while (n > 0 && isspace(*p)) {
00198                                 --n;
00199                                 ++p;
00200                         }
00201 #ifdef CONFIG_BLK_DEV_IDEPCI
00202                         if (is_pci && (reg & ((digits >> 1) - 1))) {
00203                                 msg = "misaligned access";
00204                                 goto parse_error;
00205                         }
00206 #endif  /* CONFIG_BLK_DEV_IDEPCI */
00207                         if (for_real) {
00208 #if 0
00209                                 printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? 'PCI' : 'non-PCI', reg, val, digits);
00210 #endif
00211                                 if (is_pci) {
00212 #ifdef CONFIG_BLK_DEV_IDEPCI
00213                                         int rc = 0;
00214                                         struct pci_dev *dev = hwif->pci_dev;
00215                                         switch (digits) {
00216                                                 case 2: msg = "byte";
00217                                                         rc = pci_write_config_byte(dev, reg, val);
00218                                                         break;
00219                                                 case 4: msg = "word";
00220                                                         rc = pci_write_config_word(dev, reg, val);
00221                                                         break;
00222                                                 case 8: msg = "dword";
00223                                                         rc = pci_write_config_dword(dev, reg, val);
00224                                                         break;
00225                                         }
00226                                         if (rc) {
00227                                                 restore_flags(flags);   /* all CPUs */
00228                                                 printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
00229                                                         msg, dev->bus->number, dev->devfn, reg, val);
00230                                                 printk("proc_ide_write_config: error %d\n", rc);
00231                                                 return -EIO;
00232                                         }
00233 #endif  /* CONFIG_BLK_DEV_IDEPCI */
00234                                 } else {        /* not pci */
00235                                         switch (digits) {
00236                                                 case 2: outb(val, reg);
00237                                                         break;
00238                                                 case 4: outw(val, reg);
00239                                                         break;
00240                                                 case 8: outl(val, reg);
00241                                                         break;
00242                                         }
00243                                 }
00244                         }
00245                 }
00246         } while (!for_real++);
00247         restore_flags(flags);   /* all CPUs */
00248         return count;
00249 parse_error:
00250         restore_flags(flags);   /* all CPUs */
00251         printk("parse error\n");
00252         return xx_xx_parse_error(start, startn, msg);
00253 }
00254 
00255 static int proc_ide_read_config
00256         (char *page, char **start, off_t off, int count, int *eof, void *data)
00257 {
00258         char            *out = page;
00259         int             len;
00260 
00261 #ifdef CONFIG_BLK_DEV_IDEPCI
00262         ide_hwif_t      *hwif = (ide_hwif_t *)data;
00263         struct pci_dev  *dev = hwif->pci_dev;
00264         if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) {
00265                 int reg = 0;
00266 
00267                 out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n",
00268                         dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel);
00269                 do {
00270                         byte val;
00271                         int rc = pci_read_config_byte(dev, reg, &val);
00272                         if (rc) {
00273                                 printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n",
00274                                         rc, dev->bus->number, dev->devfn, reg);
00275                                 out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n');
00276                         } else
00277                                 out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n');
00278                 } while (reg < 0x100);
00279         } else
00280 #endif  /* CONFIG_BLK_DEV_IDEPCI */
00281                 out += sprintf(out, "(none)\n");
00282         len = out - page;
00283         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00284 }
00285 
00286 
00287 static int ide_getdigit(char c)
00288 {
00289         int digit;
00290         if (isdigit(c))
00291                 digit = c - '0';
00292         else
00293                 digit = -1;
00294         return digit;
00295 }
00296 
00297 static int proc_ide_read_drivers
00298         (char *page, char **start, off_t off, int count, int *eof, void *data)
00299 {
00300         char            *out = page;
00301         int             len;
00302         ide_module_t    *p = ide_modules;
00303         ide_driver_t    *driver;
00304 
00305         while (p) {
00306                 driver = (ide_driver_t *) p->info;
00307                 if (p->type == IDE_DRIVER_MODULE && driver)
00308                         out += sprintf(out, "%s version %s\n", driver->name, driver->version);
00309                 p = p->next;
00310         }
00311         len = out - page;
00312         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00313 }
00314 
00315 static int proc_ide_read_imodel
00316         (char *page, char **start, off_t off, int count, int *eof, void *data)
00317 {
00318         ide_hwif_t      *hwif = (ide_hwif_t *) data;
00319         int             len;
00320         const char      *name;
00321 
00322         switch (hwif->chipset) {
00323                 case ide_unknown:       name = "(none)";        break;
00324                 case ide_generic:       name = "generic";       break;
00325                 case ide_pci:           name = "pci";           break;
00326                 case ide_cmd640:        name = "cmd640";        break;
00327                 case ide_dtc2278:       name = "dtc2278";       break;
00328                 case ide_ali14xx:       name = "ali14xx";       break;
00329                 case ide_qd6580:        name = "qd6580";        break;
00330                 case ide_umc8672:       name = "umc8672";       break;
00331                 case ide_ht6560b:       name = "ht6560b";       break;
00332                 case ide_pdc4030:       name = "pdc4030";       break;
00333                 case ide_rz1000:        name = "rz1000";        break;
00334                 case ide_trm290:        name = "trm290";        break;
00335                 case ide_4drives:       name = "4drives";       break;
00336                 case ide_pmac:          name = "mac-io";        break;
00337                 default:                name = "(unknown)";     break;
00338         }
00339         len = sprintf(page, "%s\n", name);
00340         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00341 }
00342 
00343 static int proc_ide_read_mate
00344         (char *page, char **start, off_t off, int count, int *eof, void *data)
00345 {
00346         ide_hwif_t      *hwif = (ide_hwif_t *) data;
00347         int             len;
00348 
00349         if (hwif && hwif->mate && hwif->mate->present)
00350                 len = sprintf(page, "%s\n", hwif->mate->name);
00351         else
00352                 len = sprintf(page, "(none)\n");
00353         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00354 }
00355 
00356 static int proc_ide_read_channel
00357         (char *page, char **start, off_t off, int count, int *eof, void *data)
00358 {
00359         ide_hwif_t      *hwif = (ide_hwif_t *) data;
00360         int             len;
00361 
00362         page[0] = hwif->channel ? '1' : '0';
00363         page[1] = '\n';
00364         len = 2;
00365         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00366 }
00367 
00368 static int proc_ide_get_identify(ide_drive_t *drive, byte *buf)
00369 {
00370         return ide_wait_cmd(drive, (drive->media == ide_disk) ? WIN_IDENTIFY : WIN_PIDENTIFY, 0, 0, 1, buf);
00371 }
00372 
00373 static int proc_ide_read_identify
00374         (char *page, char **start, off_t off, int count, int *eof, void *data)
00375 {
00376         ide_drive_t     *drive = (ide_drive_t *)data;
00377         int             len = 0, i = 0;
00378 
00379         if (!proc_ide_get_identify(drive, page)) {
00380                 unsigned short *val = ((unsigned short *)page) + 2;
00381                 char *out = ((char *)val) + (SECTOR_WORDS * 4);
00382                 page = out;
00383                 do {
00384                         out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
00385                         val += 1;
00386                 } while (i < (SECTOR_WORDS * 2));
00387                 len = out - page;
00388         }
00389         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00390 }
00391 
00392 static int proc_ide_read_settings
00393         (char *page, char **start, off_t off, int count, int *eof, void *data)
00394 {
00395         ide_drive_t     *drive = (ide_drive_t *) data;
00396         ide_settings_t  *setting = (ide_settings_t *) drive->settings;
00397         char            *out = page;
00398         int             len, rc, mul_factor, div_factor;
00399 
00400         out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
00401         out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
00402         while(setting) {
00403                 mul_factor = setting->mul_factor;
00404                 div_factor = setting->div_factor;
00405                 out += sprintf(out, "%-24s", setting->name);
00406                 if ((rc = ide_read_setting(drive, setting)) >= 0)
00407                         out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
00408                 else
00409                         out += sprintf(out, "%-16s", "write-only");
00410                 out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
00411                 if (setting->rw & SETTING_READ)
00412                         out += sprintf(out, "r");
00413                 if (setting->rw & SETTING_WRITE)
00414                         out += sprintf(out, "w");
00415                 out += sprintf(out, "\n");
00416                 setting = setting->next;
00417         }
00418         len = out - page;
00419         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00420 }
00421 
00422 #define MAX_LEN 30
00423 
00424 static int proc_ide_write_settings
00425         (struct file *file, const char *buffer, unsigned long count, void *data)
00426 {
00427         ide_drive_t     *drive = (ide_drive_t *) data;
00428         char            name[MAX_LEN + 1];
00429         int             for_real = 0, len;
00430         unsigned long   n;
00431         const char      *start = NULL;
00432         ide_settings_t  *setting;
00433 
00434         if (!capable(CAP_SYS_ADMIN))
00435                 return -EACCES;
00436         /*
00437          * Skip over leading whitespace
00438          */
00439         while (count && isspace(*buffer)) {
00440                 --count;
00441                 ++buffer;
00442         }
00443         /*
00444          * Do one full pass to verify all parameters,
00445          * then do another to actually write the new settings.
00446          */
00447         do {
00448                 const char *p;
00449                 p = buffer;
00450                 n = count;
00451                 while (n > 0) {
00452                         int d, digits;
00453                         unsigned int val = 0;
00454                         start = p;
00455 
00456                         while (n > 0 && *p != ':') {
00457                                 --n;
00458                                 p++;
00459                         }
00460                         if (*p != ':')
00461                                 goto parse_error;
00462                         len = IDE_MIN(p - start, MAX_LEN);
00463                         strncpy(name, start, IDE_MIN(len, MAX_LEN));
00464                         name[len] = 0;
00465 
00466                         if (n > 0) {
00467                                 --n;
00468                                 p++;
00469                         } else
00470                                 goto parse_error;
00471                         
00472                         digits = 0;
00473                         while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
00474                                 val = (val * 10) + d;
00475                                 --n;
00476                                 ++p;
00477                                 ++digits;
00478                         }
00479                         if (n > 0 && !isspace(*p))
00480                                 goto parse_error;
00481                         while (n > 0 && isspace(*p)) {
00482                                 --n;
00483                                 ++p;
00484                         }
00485                         setting = ide_find_setting_by_name(drive, name);
00486                         if (!setting)
00487                                 goto parse_error;
00488 
00489                         if (for_real)
00490                                 ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
00491                 }
00492         } while (!for_real++);
00493         return count;
00494 parse_error:
00495         printk("proc_ide_write_settings(): parse error\n");
00496         return -EINVAL;
00497 }
00498 
00499 int proc_ide_read_capacity
00500         (char *page, char **start, off_t off, int count, int *eof, void *data)
00501 {
00502         ide_drive_t     *drive = (ide_drive_t *) data;
00503         ide_driver_t    *driver = (ide_driver_t *) drive->driver;
00504         int             len;
00505 
00506         if (!driver)
00507                 len = sprintf(page, "(none)\n");
00508         else
00509                 len = sprintf(page,"%li\n", ((ide_driver_t *)drive->driver)->capacity(drive));
00510         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00511 }
00512 
00513 int proc_ide_read_geometry
00514         (char *page, char **start, off_t off, int count, int *eof, void *data)
00515 {
00516         ide_drive_t     *drive = (ide_drive_t *) data;
00517         char            *out = page;
00518         int             len;
00519 
00520         out += sprintf(out,"physical     %d/%d/%d\n",
00521                        drive->cyl, drive->head, drive->sect);
00522         out += sprintf(out,"logical      %d/%d/%d\n",
00523                        drive->bios_cyl, drive->bios_head, drive->bios_sect);
00524         len = out - page;
00525         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00526 }
00527 
00528 static int proc_ide_read_dmodel
00529         (char *page, char **start, off_t off, int count, int *eof, void *data)
00530 {
00531         ide_drive_t     *drive = (ide_drive_t *) data;
00532         struct hd_driveid *id = drive->id;
00533         int             len;
00534 
00535         len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)");
00536         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00537 }
00538 
00539 static int proc_ide_read_driver
00540         (char *page, char **start, off_t off, int count, int *eof, void *data)
00541 {
00542         ide_drive_t     *drive = (ide_drive_t *) data;
00543         ide_driver_t    *driver = (ide_driver_t *) drive->driver;
00544         int             len;
00545 
00546         if (!driver)
00547                 len = sprintf(page, "(none)\n");
00548         else
00549                 len = sprintf(page, "%s version %s\n", driver->name, driver->version);
00550         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00551 }
00552 
00553 static int proc_ide_write_driver
00554         (struct file *file, const char *buffer, unsigned long count, void *data)
00555 {
00556         ide_drive_t     *drive = (ide_drive_t *) data;
00557 
00558         if (!capable(CAP_SYS_ADMIN))
00559                 return -EACCES;
00560         if (ide_replace_subdriver(drive, buffer))
00561                 return -EINVAL;
00562         return count;
00563 }
00564 
00565 static int proc_ide_read_media
00566         (char *page, char **start, off_t off, int count, int *eof, void *data)
00567 {
00568         ide_drive_t     *drive = (ide_drive_t *) data;
00569         const char      *media;
00570         int             len;
00571 
00572         switch (drive->media) {
00573                 case ide_disk:  media = "disk\n";
00574                                 break;
00575                 case ide_cdrom: media = "cdrom\n";
00576                                 break;
00577                 case ide_tape:  media = "tape\n";
00578                                 break;
00579                 case ide_floppy:media = "floppy\n";
00580                                 break;
00581                 default:        media = "UNKNOWN\n";
00582                                 break;
00583         }
00584         strcpy(page,media);
00585         len = strlen(media);
00586         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
00587 }
00588 
00589 static ide_proc_entry_t generic_drive_entries[] = {
00590         { "driver",     S_IFREG|S_IRUGO,        proc_ide_read_driver,   proc_ide_write_driver },
00591         { "identify",   S_IFREG|S_IRUSR,        proc_ide_read_identify, NULL },
00592         { "media",      S_IFREG|S_IRUGO,        proc_ide_read_media,    NULL },
00593         { "model",      S_IFREG|S_IRUGO,        proc_ide_read_dmodel,   NULL },
00594         { "settings",   S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings, proc_ide_write_settings },
00595         { NULL, 0, NULL, NULL }
00596 };
00597 
00598 void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
00599 {
00600         struct proc_dir_entry *ent;
00601 
00602         if (!dir || !p)
00603                 return;
00604         while (p->name != NULL) {
00605                 ent = create_proc_entry(p->name, p->mode, dir);
00606                 if (!ent) return;
00607                 ent->nlink = 1;
00608                 ent->data = data;
00609                 ent->read_proc = p->read_proc;
00610                 ent->write_proc = p->write_proc;
00611                 p++;
00612         }
00613 }
00614 
00615 void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
00616 {
00617         if (!dir || !p)
00618                 return;
00619         while (p->name != NULL) {
00620                 remove_proc_entry(p->name, dir);
00621                 p++;
00622         }
00623 }
00624 
00625 static int proc_ide_readlink(struct proc_dir_entry *de, char *page)
00626 {
00627         int n = (de->name[2] - 'a') / 2;
00628         return sprintf(page, "ide%d/%s", n, de->name);
00629 }
00630 
00631 static void create_proc_ide_drives (ide_hwif_t *hwif, struct proc_dir_entry *parent, struct proc_dir_entry *root)
00632 {
00633         int     d;
00634         struct proc_dir_entry *ent;
00635 
00636         for (d = 0; d < MAX_DRIVES; d++) {
00637                 ide_drive_t *drive = &hwif->drives[d];
00638 
00639                 if (!drive->present)
00640                         continue;
00641                 drive->proc = create_proc_entry(drive->name, S_IFDIR, parent);
00642                 if (drive->proc)
00643                         ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
00644 
00645                 ent = create_proc_entry(drive->name, S_IFLNK | S_IRUGO | S_IWUGO | S_IXUGO, root);
00646                 if (!ent) return;
00647                 ent->data = drive;
00648                 ent->readlink_proc = proc_ide_readlink;
00649                 ent->nlink = 1;
00650         }
00651 }
00652 
00653 static ide_proc_entry_t hwif_entries[] = {
00654         { "channel",    S_IFREG|S_IRUGO,        proc_ide_read_channel,  NULL },
00655         { "config",     S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config,   proc_ide_write_config },
00656         { "mate",       S_IFREG|S_IRUGO,        proc_ide_read_mate,     NULL },
00657         { "model",      S_IFREG|S_IRUGO,        proc_ide_read_imodel,   NULL },
00658         { NULL, 0, NULL, NULL }
00659 };
00660 
00661 static void create_proc_ide_interfaces (struct proc_dir_entry *parent)
00662 {
00663         int     h;
00664         struct proc_dir_entry *hwif_ent;
00665 
00666         for (h = 0; h < MAX_HWIFS; h++) {
00667                 ide_hwif_t *hwif = &ide_hwifs[h];
00668 
00669                 if (!hwif->present)
00670                         continue;
00671                 hwif_ent = create_proc_entry(hwif->name, S_IFDIR, parent);
00672                 if (!hwif_ent) return;
00673                 ide_add_proc_entries(hwif_ent, hwif_entries, hwif);
00674                 create_proc_ide_drives(hwif, hwif_ent, parent);
00675         }
00676 }
00677 
00678 void proc_ide_create(void)
00679 {
00680         struct proc_dir_entry *root, *ent;
00681         root = create_proc_entry("ide", S_IFDIR, 0);
00682         if (!root) return;
00683         create_proc_ide_interfaces(root);
00684 
00685         ent = create_proc_entry("drivers", 0, root);
00686         if (!ent) return;
00687         ent->read_proc  = proc_ide_read_drivers;
00688 }
00689 
00690 void proc_ide_destroy(void)
00691 {
00692         /*
00693          * Mmmm.. does this free up all resources,
00694          * or do we need to do a more proper cleanup here ??
00695          */
00696         remove_proc_entry("ide/drivers", 0);
00697         remove_proc_entry("ide", 0);
00698 }