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