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