ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / ide / ide-proc.c
1 /*
2  *  linux/drivers/ide/ide-proc.c        Version 1.05    Mar 05, 2003
3  *
4  *  Copyright (C) 1997-1998     Mark Lord
5  *  Copyright (C) 2003          Red Hat <alan@redhat.com>
6  */
7
8 /*
9  * This is the /proc/ide/ filesystem implementation.
10  *
11  * The major reason this exists is to provide sufficient access
12  * to driver and config data, such that user-mode programs can
13  * be developed to handle chipset tuning for most PCI interfaces.
14  * This should provide better utilities, and less kernel bloat.
15  *
16  * The entire pci config space for a PCI interface chipset can be
17  * retrieved by just reading it.  e.g.    "cat /proc/ide3/config"
18  *
19  * To modify registers *safely*, do something like:
20  *   echo "P40:88" >/proc/ide/ide3/config
21  * That expression writes 0x88 to pci config register 0x40
22  * on the chip which controls ide3.  Multiple tuples can be issued,
23  * and the writes will be completed as an atomic set:
24  *   echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config
25  *
26  * All numbers must be specified using pairs of ascii hex digits.
27  * It is important to note that these writes will be performed
28  * after waiting for the IDE controller (both interfaces)
29  * to be completely idle, to ensure no corruption of I/O in progress.
30  *
31  * Non-PCI registers can also be written, using "R" in place of "P"
32  * in the above examples.  The size of the port transfer is determined
33  * by the number of pairs of hex digits given for the data.  If a two
34  * digit value is given, the write will be a byte operation; if four
35  * digits are used, the write will be performed as a 16-bit operation;
36  * and if eight digits are specified, a 32-bit "dword" write will be
37  * performed.  Odd numbers of digits are not permitted.
38  *
39  * If there is an error *anywhere* in the string of registers/data
40  * then *none* of the writes will be performed.
41  *
42  * Drive/Driver settings can be retrieved by reading the drive's
43  * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
44  * To write a new value "val" into a specific setting "name", use:
45  *   echo "name:val" >/proc/ide/ide0/hda/settings
46  *
47  * Also useful, "cat /proc/ide0/hda/[identify, smart_values,
48  * smart_thresholds, capabilities]" will issue an IDENTIFY /
49  * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS /
50  * SENSE CAPABILITIES command to /dev/hda, and then dump out the
51  * returned data as 256 16-bit words.  The "hdparm" utility will
52  * be updated someday soon to use this mechanism.
53  *
54  * Feel free to develop and distribute fancy GUI configuration
55  * utilities for your favorite PCI chipsets.  I'll be working on
56  * one for the Promise 20246 someday soon.  -ml
57  *
58  */
59
60 #include <linux/config.h>
61 #include <linux/module.h>
62
63 #include <asm/uaccess.h>
64 #include <linux/errno.h>
65 #include <linux/sched.h>
66 #include <linux/proc_fs.h>
67 #include <linux/stat.h>
68 #include <linux/mm.h>
69 #include <linux/pci.h>
70 #include <linux/ctype.h>
71 #include <linux/hdreg.h>
72 #include <linux/ide.h>
73 #include <linux/seq_file.h>
74
75 #include <asm/io.h>
76
77 static int ide_getxdigit(char c)
78 {
79         int digit;
80         if (isdigit(c))
81                 digit = c - '0';
82         else if (isxdigit(c))
83                 digit = tolower(c) - 'a' + 10;
84         else
85                 digit = -1;
86         return digit;
87 }
88
89 static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg)
90 {
91         char errbuf[16];
92         int i;
93         if (len >= sizeof(errbuf))
94                 len = sizeof(errbuf) - 1;
95         for (i = 0; i < len; ++i) {
96                 char c = data[i];
97                 if (!c || c == '\n')
98                         c = '\0';
99                 else if (iscntrl(c))
100                         c = '?';
101                 errbuf[i] = c;
102         }
103         errbuf[i] = '\0';
104         printk("proc_ide: error: %s: '%s'\n", msg, errbuf);
105         return -EINVAL;
106 }
107
108 static int proc_ide_write_config
109         (struct file *file, const char *buffer, unsigned long count, void *data)
110 {
111         ide_hwif_t      *hwif = (ide_hwif_t *)data;
112         int             for_real = 0;
113         unsigned long   startn = 0, n, flags;
114         const char      *start = NULL, *msg = NULL;
115
116         if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO))
117                 return -EACCES;
118         /*
119          * Skip over leading whitespace
120          */
121         while (count && isspace(*buffer)) {
122                 --count;
123                 ++buffer;
124         }
125         /*
126          * Do one full pass to verify all parameters,
127          * then do another to actually write the regs.
128          */
129         spin_lock_irqsave(&ide_lock, flags);
130         do {
131                 const char *p;
132                 if (for_real) {
133                         unsigned long timeout = jiffies + (3 * HZ);
134                         ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup);
135                         ide_hwgroup_t *mategroup = NULL;
136                         if (hwif->mate && hwif->mate->hwgroup)
137                                 mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup);
138                         spin_lock_irqsave(&ide_lock, flags);
139                         while (mygroup->busy ||
140                                (mategroup && mategroup->busy)) {
141                                 spin_unlock_irqrestore(&ide_lock, flags);
142                                 if (time_after(jiffies, timeout)) {
143                                         printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name);
144                                         spin_unlock_irqrestore(&ide_lock, flags);
145                                         return -EBUSY;
146                                 }
147                                 spin_lock_irqsave(&ide_lock, flags);
148                         }
149                 }
150                 p = buffer;
151                 n = count;
152                 while (n > 0) {
153                         int d, digits;
154                         unsigned int reg = 0, val = 0, is_pci;
155                         start = p;
156                         startn = n--;
157                         switch (*p++) {
158                                 case 'R':       is_pci = 0;
159                                                 break;
160                                 case 'P':       is_pci = 1;
161 #ifdef CONFIG_BLK_DEV_IDEPCI
162                                                 if (hwif->pci_dev && !hwif->pci_dev->vendor)
163                                                         break;
164 #endif  /* CONFIG_BLK_DEV_IDEPCI */
165                                                 msg = "not a PCI device";
166                                                 goto parse_error;
167                                 default:        msg = "expected 'R' or 'P'";
168                                                 goto parse_error;
169                         }
170                         digits = 0;
171                         while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
172                                 reg = (reg << 4) | d;
173                                 --n;
174                                 ++p;
175                                 ++digits;
176                         }
177                         if (!digits || (digits > 4) || (is_pci && reg > 0xff)) {
178                                 msg = "bad/missing register number";
179                                 goto parse_error;
180                         }
181                         if (n-- == 0 || *p++ != ':') {
182                                 msg = "missing ':'";
183                                 goto parse_error;
184                         }
185                         digits = 0;
186                         while (n > 0 && (d = ide_getxdigit(*p)) >= 0) {
187                                 val = (val << 4) | d;
188                                 --n;
189                                 ++p;
190                                 ++digits;
191                         }
192                         if (digits != 2 && digits != 4 && digits != 8) {
193                                 msg = "bad data, 2/4/8 digits required";
194                                 goto parse_error;
195                         }
196                         if (n > 0 && !isspace(*p)) {
197                                 msg = "expected whitespace after data";
198                                 goto parse_error;
199                         }
200                         while (n > 0 && isspace(*p)) {
201                                 --n;
202                                 ++p;
203                         }
204 #ifdef CONFIG_BLK_DEV_IDEPCI
205                         if (is_pci && (reg & ((digits >> 1) - 1))) {
206                                 msg = "misaligned access";
207                                 goto parse_error;
208                         }
209 #endif  /* CONFIG_BLK_DEV_IDEPCI */
210                         if (for_real) {
211 #if 0
212                                 printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits);
213 #endif
214                                 if (is_pci) {
215 #ifdef CONFIG_BLK_DEV_IDEPCI
216                                         int rc = 0;
217                                         struct pci_dev *dev = hwif->pci_dev;
218                                         switch (digits) {
219                                                 case 2: msg = "byte";
220                                                         rc = pci_write_config_byte(dev, reg, val);
221                                                         break;
222                                                 case 4: msg = "word";
223                                                         rc = pci_write_config_word(dev, reg, val);
224                                                         break;
225                                                 case 8: msg = "dword";
226                                                         rc = pci_write_config_dword(dev, reg, val);
227                                                         break;
228                                         }
229                                         if (rc) {
230                                                 spin_unlock_irqrestore(&ide_lock, flags);
231                                                 printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n",
232                                                         msg, dev->bus->number, dev->devfn, reg, val);
233                                                 printk("proc_ide_write_config: error %d\n", rc);
234                                                 return -EIO;
235                                         }
236 #endif  /* CONFIG_BLK_DEV_IDEPCI */
237                                 } else {        /* not pci */
238 #if !defined(__mc68000__) && !defined(CONFIG_APUS)
239
240 /*
241  * Geert Uytterhoeven
242  *
243  * unless you can explain me what it really does.
244  * On m68k, we don't have outw() and outl() yet,
245  * and I need a good reason to implement it.
246  * 
247  * BTW, IMHO the main remaining portability problem with the IDE driver 
248  * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms.
249  * 
250  * I think all accesses should be done using
251  * 
252  *     ide_in[bwl](ide_device_instance, offset)
253  *     ide_out[bwl](ide_device_instance, value, offset)
254  * 
255  * so the architecture specific code can #define ide_{in,out}[bwl] to the
256  * appropriate function.
257  * 
258  */
259                                         switch (digits) {
260                                                 case 2: hwif->OUTB(val, reg);
261                                                         break;
262                                                 case 4: hwif->OUTW(val, reg);
263                                                         break;
264                                                 case 8: hwif->OUTL(val, reg);
265                                                         break;
266                                         }
267 #endif /* !__mc68000__ && !CONFIG_APUS */
268                                 }
269                         }
270                 }
271         } while (!for_real++);
272         spin_unlock_irqrestore(&ide_lock, flags);
273         return count;
274 parse_error:
275         spin_unlock_irqrestore(&ide_lock, flags);
276         printk("parse error\n");
277         return xx_xx_parse_error(start, startn, msg);
278 }
279
280 int proc_ide_read_config
281         (char *page, char **start, off_t off, int count, int *eof, void *data)
282 {
283         char            *out = page;
284         int             len;
285
286 #ifdef CONFIG_BLK_DEV_IDEPCI
287         ide_hwif_t      *hwif = (ide_hwif_t *)data;
288         struct pci_dev  *dev = hwif->pci_dev;
289         if ((hwif->pci_dev && hwif->pci_dev->vendor) && dev && dev->bus) {
290                 int reg = 0;
291
292                 out += sprintf(out, "pci bus %02x device %02x vendor %04x "
293                                 "device %04x channel %d\n",
294                         dev->bus->number, dev->devfn,
295                         hwif->pci_dev->vendor, hwif->pci_dev->device,
296                         hwif->channel);
297                 do {
298                         u8 val;
299                         int rc = pci_read_config_byte(dev, reg, &val);
300                         if (rc) {
301                                 printk("proc_ide_read_config: error %d reading"
302                                         " bus %02x dev %02x reg 0x%02x\n",
303                                         rc, dev->bus->number, dev->devfn, reg);
304                                 out += sprintf(out, "??%c",
305                                         (++reg & 0xf) ? ' ' : '\n');
306                         } else
307                                 out += sprintf(out, "%02x%c",
308                                         val, (++reg & 0xf) ? ' ' : '\n');
309                 } while (reg < 0x100);
310         } else
311 #endif  /* CONFIG_BLK_DEV_IDEPCI */
312                 out += sprintf(out, "(none)\n");
313         len = out - page;
314         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
315 }
316
317 EXPORT_SYMBOL(proc_ide_read_config);
318
319 static int ide_getdigit(char c)
320 {
321         int digit;
322         if (isdigit(c))
323                 digit = c - '0';
324         else
325                 digit = -1;
326         return digit;
327 }
328
329 static int proc_ide_read_imodel
330         (char *page, char **start, off_t off, int count, int *eof, void *data)
331 {
332         ide_hwif_t      *hwif = (ide_hwif_t *) data;
333         int             len;
334         const char      *name;
335
336         /*
337          * Neither ide_unknown nor ide_forced should be set at this point.
338          */
339         switch (hwif->chipset) {
340                 case ide_generic:       name = "generic";       break;
341                 case ide_pci:           name = "pci";           break;
342                 case ide_cmd640:        name = "cmd640";        break;
343                 case ide_dtc2278:       name = "dtc2278";       break;
344                 case ide_ali14xx:       name = "ali14xx";       break;
345                 case ide_qd65xx:        name = "qd65xx";        break;
346                 case ide_umc8672:       name = "umc8672";       break;
347                 case ide_ht6560b:       name = "ht6560b";       break;
348                 case ide_pdc4030:       name = "pdc4030";       break;
349                 case ide_rz1000:        name = "rz1000";        break;
350                 case ide_trm290:        name = "trm290";        break;
351                 case ide_cmd646:        name = "cmd646";        break;
352                 case ide_cy82c693:      name = "cy82c693";      break;
353                 case ide_4drives:       name = "4drives";       break;
354                 case ide_pmac:          name = "mac-io";        break;
355                 case ide_pc9800:        name = "pc9800";        break;
356                 default:                name = "(unknown)";     break;
357         }
358         len = sprintf(page, "%s\n", name);
359         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
360 }
361
362 EXPORT_SYMBOL(proc_ide_read_imodel);
363
364 int proc_ide_read_mate
365         (char *page, char **start, off_t off, int count, int *eof, void *data)
366 {
367         ide_hwif_t      *hwif = (ide_hwif_t *) data;
368         int             len;
369
370         if (hwif && hwif->mate && hwif->mate->present)
371                 len = sprintf(page, "%s\n", hwif->mate->name);
372         else
373                 len = sprintf(page, "(none)\n");
374         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
375 }
376
377 EXPORT_SYMBOL(proc_ide_read_mate);
378
379 int proc_ide_read_channel
380         (char *page, char **start, off_t off, int count, int *eof, void *data)
381 {
382         ide_hwif_t      *hwif = (ide_hwif_t *) data;
383         int             len;
384
385         page[0] = hwif->channel ? '1' : '0';
386         page[1] = '\n';
387         len = 2;
388         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
389 }
390
391 EXPORT_SYMBOL(proc_ide_read_channel);
392
393 int proc_ide_read_identify
394         (char *page, char **start, off_t off, int count, int *eof, void *data)
395 {
396         ide_drive_t     *drive = (ide_drive_t *)data;
397         int             len = 0, i = 0;
398         int             err = 0;
399
400         len = sprintf(page, "\n");
401         
402         if (drive)
403         {
404                 unsigned short *val = (unsigned short *) page;
405                 
406                 /*
407                  *      The current code can't handle a driverless
408                  *      identify query taskfile. Now the right fix is
409                  *      to add a 'default' driver but that is a bit
410                  *      more work. 
411                  *
412                  *      FIXME: this has to be fixed for hotswap devices
413                  */
414                  
415                 if(DRIVER(drive))
416                         err = taskfile_lib_get_identify(drive, page);
417                 else    /* This relies on the ID changes */
418                         val = (unsigned short *)drive->id;
419
420                 if(!err)
421                 {                                               
422                         char *out = ((char *)page) + (SECTOR_WORDS * 4);
423                         page = out;
424                         do {
425                                 out += sprintf(out, "%04x%c",
426                                         le16_to_cpu(*val), (++i & 7) ? ' ' : '\n');
427                                 val += 1;
428                         } while (i < (SECTOR_WORDS * 2));
429                         len = out - page;
430                 }
431         }
432         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
433 }
434
435 EXPORT_SYMBOL(proc_ide_read_identify);
436
437 int proc_ide_read_settings
438         (char *page, char **start, off_t off, int count, int *eof, void *data)
439 {
440         ide_drive_t     *drive = (ide_drive_t *) data;
441         ide_settings_t  *setting = (ide_settings_t *) drive->settings;
442         char            *out = page;
443         int             len, rc, mul_factor, div_factor;
444
445         down(&ide_setting_sem);
446         out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
447         out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
448         while(setting) {
449                 mul_factor = setting->mul_factor;
450                 div_factor = setting->div_factor;
451                 out += sprintf(out, "%-24s", setting->name);
452                 if ((rc = ide_read_setting(drive, setting)) >= 0)
453                         out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
454                 else
455                         out += sprintf(out, "%-16s", "write-only");
456                 out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
457                 if (setting->rw & SETTING_READ)
458                         out += sprintf(out, "r");
459                 if (setting->rw & SETTING_WRITE)
460                         out += sprintf(out, "w");
461                 out += sprintf(out, "\n");
462                 setting = setting->next;
463         }
464         len = out - page;
465         up(&ide_setting_sem);
466         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
467 }
468
469 EXPORT_SYMBOL(proc_ide_read_settings);
470
471 #define MAX_LEN 30
472
473 int proc_ide_write_settings
474         (struct file *file, const char *buffer, unsigned long count, void *data)
475 {
476         ide_drive_t     *drive = (ide_drive_t *) data;
477         char            name[MAX_LEN + 1];
478         int             for_real = 0, len;
479         unsigned long   n;
480         const char      *start = NULL;
481         ide_settings_t  *setting;
482
483         if (!capable(CAP_SYS_ADMIN))
484                 return -EACCES;
485         /*
486          * Skip over leading whitespace
487          */
488         while (count && isspace(*buffer)) {
489                 --count;
490                 ++buffer;
491         }
492         /*
493          * Do one full pass to verify all parameters,
494          * then do another to actually write the new settings.
495          */
496         do {
497                 const char *p;
498                 p = buffer;
499                 n = count;
500                 while (n > 0) {
501                         int d, digits;
502                         unsigned int val = 0;
503                         start = p;
504
505                         while (n > 0 && *p != ':') {
506                                 --n;
507                                 p++;
508                         }
509                         if (*p != ':')
510                                 goto parse_error;
511                         len = min(p - start, MAX_LEN);
512                         strncpy(name, start, min(len, MAX_LEN));
513                         name[len] = 0;
514
515                         if (n > 0) {
516                                 --n;
517                                 p++;
518                         } else
519                                 goto parse_error;
520                         
521                         digits = 0;
522                         while (n > 0 && (d = ide_getdigit(*p)) >= 0) {
523                                 val = (val * 10) + d;
524                                 --n;
525                                 ++p;
526                                 ++digits;
527                         }
528                         if (n > 0 && !isspace(*p))
529                                 goto parse_error;
530                         while (n > 0 && isspace(*p)) {
531                                 --n;
532                                 ++p;
533                         }
534                         
535                         down(&ide_setting_sem);
536                         setting = ide_find_setting_by_name(drive, name);
537                         if (!setting)
538                         {
539                                 up(&ide_setting_sem);
540                                 goto parse_error;
541                         }
542                         if (for_real)
543                                 ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor);
544                         up(&ide_setting_sem);
545                 }
546         } while (!for_real++);
547         return count;
548 parse_error:
549         printk("proc_ide_write_settings(): parse error\n");
550         return -EINVAL;
551 }
552
553 EXPORT_SYMBOL(proc_ide_write_settings);
554
555 int proc_ide_read_capacity
556         (char *page, char **start, off_t off, int count, int *eof, void *data)
557 {
558         ide_drive_t     *drive = (ide_drive_t *) data;
559         int             len;
560
561         len = sprintf(page,"%llu\n",
562                       (long long) (DRIVER(drive)->capacity(drive)));
563         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
564 }
565
566 EXPORT_SYMBOL(proc_ide_read_capacity);
567
568 int proc_ide_read_geometry
569         (char *page, char **start, off_t off, int count, int *eof, void *data)
570 {
571         ide_drive_t     *drive = (ide_drive_t *) data;
572         char            *out = page;
573         int             len;
574
575         out += sprintf(out,"physical     %d/%d/%d\n",
576                         drive->cyl, drive->head, drive->sect);
577         out += sprintf(out,"logical      %d/%d/%d\n",
578                         drive->bios_cyl, drive->bios_head, drive->bios_sect);
579
580         len = out - page;
581         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
582 }
583
584 EXPORT_SYMBOL(proc_ide_read_geometry);
585
586 int proc_ide_read_dmodel
587         (char *page, char **start, off_t off, int count, int *eof, void *data)
588 {
589         ide_drive_t     *drive = (ide_drive_t *) data;
590         struct hd_driveid *id = drive->id;
591         int             len;
592
593         len = sprintf(page, "%.40s\n",
594                 (id && id->model[0]) ? (char *)id->model : "(none)");
595         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
596 }
597
598 EXPORT_SYMBOL(proc_ide_read_dmodel);
599
600 int proc_ide_read_driver
601         (char *page, char **start, off_t off, int count, int *eof, void *data)
602 {
603         ide_drive_t     *drive = (ide_drive_t *) data;
604         ide_driver_t    *driver = drive->driver;
605         int             len;
606
607         len = sprintf(page, "%s version %s\n",
608                         driver->name, driver->version);
609         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
610 }
611
612 EXPORT_SYMBOL(proc_ide_read_driver);
613
614 int proc_ide_write_driver
615         (struct file *file, const char *buffer, unsigned long count, void *data)
616 {
617         ide_drive_t     *drive = (ide_drive_t *) data;
618
619         if (!capable(CAP_SYS_ADMIN))
620                 return -EACCES;
621         if (ide_replace_subdriver(drive, buffer))
622                 return -EINVAL;
623         return count;
624 }
625
626 EXPORT_SYMBOL(proc_ide_write_driver);
627
628 int proc_ide_read_media
629         (char *page, char **start, off_t off, int count, int *eof, void *data)
630 {
631         ide_drive_t     *drive = (ide_drive_t *) data;
632         const char      *media;
633         int             len;
634
635         switch (drive->media) {
636                 case ide_disk:  media = "disk\n";
637                                 break;
638                 case ide_cdrom: media = "cdrom\n";
639                                 break;
640                 case ide_tape:  media = "tape\n";
641                                 break;
642                 case ide_floppy:media = "floppy\n";
643                                 break;
644                 default:        media = "UNKNOWN\n";
645                                 break;
646         }
647         strcpy(page,media);
648         len = strlen(media);
649         PROC_IDE_READ_RETURN(page,start,off,count,eof,len);
650 }
651
652 EXPORT_SYMBOL(proc_ide_read_media);
653
654 static ide_proc_entry_t generic_drive_entries[] = {
655         { "driver",     S_IFREG|S_IRUGO,        proc_ide_read_driver,   proc_ide_write_driver },
656         { "identify",   S_IFREG|S_IRUSR,        proc_ide_read_identify, NULL },
657         { "media",      S_IFREG|S_IRUGO,        proc_ide_read_media,    NULL },
658         { "model",      S_IFREG|S_IRUGO,        proc_ide_read_dmodel,   NULL },
659         { "settings",   S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings, proc_ide_write_settings },
660         { NULL, 0, NULL, NULL }
661 };
662
663 void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
664 {
665         struct proc_dir_entry *ent;
666
667         if (!dir || !p)
668                 return;
669         while (p->name != NULL) {
670                 ent = create_proc_entry(p->name, p->mode, dir);
671                 if (!ent) return;
672                 ent->nlink = 1;
673                 ent->data = data;
674                 ent->read_proc = p->read_proc;
675                 ent->write_proc = p->write_proc;
676                 p++;
677         }
678 }
679
680 EXPORT_SYMBOL(ide_add_proc_entries);
681
682 void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
683 {
684         if (!dir || !p)
685                 return;
686         while (p->name != NULL) {
687                 remove_proc_entry(p->name, dir);
688                 p++;
689         }
690 }
691
692 EXPORT_SYMBOL(ide_remove_proc_entries);
693
694 void create_proc_ide_drives(ide_hwif_t *hwif)
695 {
696         int     d;
697         struct proc_dir_entry *ent;
698         struct proc_dir_entry *parent = hwif->proc;
699         char name[64];
700
701         for (d = 0; d < MAX_DRIVES; d++) {
702                 ide_drive_t *drive = &hwif->drives[d];
703
704                 if (!drive->present)
705                         continue;
706                 if (drive->proc)
707                         continue;
708
709                 drive->proc = proc_mkdir(drive->name, parent);
710                 if (drive->proc)
711                         ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
712                 sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name);
713                 ent = proc_symlink(drive->name, proc_ide_root, name);
714                 if (!ent) return;
715         }
716 }
717
718 EXPORT_SYMBOL(create_proc_ide_drives);
719
720 void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive)
721 {
722         ide_driver_t *driver = drive->driver;
723
724         if (drive->proc) {
725                 ide_remove_proc_entries(drive->proc, driver->proc);
726                 ide_remove_proc_entries(drive->proc, generic_drive_entries);
727                 remove_proc_entry(drive->name, proc_ide_root);
728                 remove_proc_entry(drive->name, hwif->proc);
729                 drive->proc = NULL;
730         }
731 }
732
733 EXPORT_SYMBOL(destroy_proc_ide_device);
734
735 void destroy_proc_ide_drives(ide_hwif_t *hwif)
736 {
737         int     d;
738
739         for (d = 0; d < MAX_DRIVES; d++) {
740                 ide_drive_t *drive = &hwif->drives[d];
741                 if (drive->proc)
742                         destroy_proc_ide_device(hwif, drive);
743         }
744 }
745
746 EXPORT_SYMBOL(destroy_proc_ide_drives);
747
748 static ide_proc_entry_t hwif_entries[] = {
749         { "channel",    S_IFREG|S_IRUGO,        proc_ide_read_channel,  NULL },
750         { "config",     S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config,   proc_ide_write_config },
751         { "mate",       S_IFREG|S_IRUGO,        proc_ide_read_mate,     NULL },
752         { "model",      S_IFREG|S_IRUGO,        proc_ide_read_imodel,   NULL },
753         { NULL, 0, NULL, NULL }
754 };
755
756 void create_proc_ide_interfaces(void)
757 {
758         int     h;
759
760         for (h = 0; h < MAX_HWIFS; h++) {
761                 ide_hwif_t *hwif = &ide_hwifs[h];
762
763                 if (!hwif->present)
764                         continue;
765                 if (!hwif->proc) {
766                         hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
767                         if (!hwif->proc)
768                                 return;
769                         ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
770                 }
771                 create_proc_ide_drives(hwif);
772         }
773 }
774
775 EXPORT_SYMBOL(create_proc_ide_interfaces);
776
777 #ifdef CONFIG_BLK_DEV_IDEPCI
778 void ide_pci_create_host_proc(const char *name, get_info_t *get_info)
779 {
780         create_proc_info_entry(name, 0, proc_ide_root, get_info);
781 }
782
783 EXPORT_SYMBOL_GPL(ide_pci_create_host_proc);
784 #endif
785
786 void destroy_proc_ide_interfaces(void)
787 {
788         int     h;
789
790         for (h = 0; h < MAX_HWIFS; h++) {
791                 ide_hwif_t *hwif = &ide_hwifs[h];
792                 int exist = (hwif->proc != NULL);
793 #if 0
794                 if (!hwif->present)
795                         continue;
796 #endif
797                 if (exist) {
798                         destroy_proc_ide_drives(hwif);
799                         ide_remove_proc_entries(hwif->proc, hwif_entries);
800                         remove_proc_entry(hwif->name, proc_ide_root);
801                         hwif->proc = NULL;
802                 } else
803                         continue;
804         }
805 }
806
807 EXPORT_SYMBOL(destroy_proc_ide_interfaces);
808
809 extern struct seq_operations ide_drivers_op;
810 static int ide_drivers_open(struct inode *inode, struct file *file)
811 {
812         return seq_open(file, &ide_drivers_op);
813 }
814 static struct file_operations ide_drivers_operations = {
815         .open           = ide_drivers_open,
816         .read           = seq_read,
817         .llseek         = seq_lseek,
818         .release        = seq_release,
819 };
820
821 void proc_ide_create(void)
822 {
823         struct proc_dir_entry *entry;
824
825         if (!proc_ide_root)
826                 return;
827
828         create_proc_ide_interfaces();
829
830         entry = create_proc_entry("drivers", 0, proc_ide_root);
831         if (entry)
832                 entry->proc_fops = &ide_drivers_operations;
833 }
834
835 EXPORT_SYMBOL(proc_ide_create);
836
837 void proc_ide_destroy(void)
838 {
839         remove_proc_entry("ide/drivers", proc_ide_root);
840         destroy_proc_ide_interfaces();
841         remove_proc_entry("ide", 0);
842 }
843
844 EXPORT_SYMBOL(proc_ide_destroy);