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