ide-proc.c revision 79cb380397c834a35952d8497651d93b543ef968
1/*
2 *  Copyright (C) 1997-1998	Mark Lord
3 *  Copyright (C) 2003		Red Hat <alan@redhat.com>
4 *
5 *  Some code was moved here from ide.c, see it for original copyrights.
6 */
7
8/*
9 * This is the /proc/ide/ filesystem implementation.
10 *
11 * Drive/Driver settings can be retrieved by reading the drive's
12 * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
13 * To write a new value "val" into a specific setting "name", use:
14 *   echo "name:val" >/proc/ide/ide0/hda/settings
15 */
16
17#include <linux/module.h>
18
19#include <asm/uaccess.h>
20#include <linux/errno.h>
21#include <linux/proc_fs.h>
22#include <linux/stat.h>
23#include <linux/mm.h>
24#include <linux/pci.h>
25#include <linux/ctype.h>
26#include <linux/ide.h>
27#include <linux/seq_file.h>
28
29#include <asm/io.h>
30
31static struct proc_dir_entry *proc_ide_root;
32
33static int proc_ide_read_imodel
34	(char *page, char **start, off_t off, int count, int *eof, void *data)
35{
36	ide_hwif_t	*hwif = (ide_hwif_t *) data;
37	int		len;
38	const char	*name;
39
40	switch (hwif->chipset) {
41	case ide_generic:	name = "generic";	break;
42	case ide_pci:		name = "pci";		break;
43	case ide_cmd640:	name = "cmd640";	break;
44	case ide_dtc2278:	name = "dtc2278";	break;
45	case ide_ali14xx:	name = "ali14xx";	break;
46	case ide_qd65xx:	name = "qd65xx";	break;
47	case ide_umc8672:	name = "umc8672";	break;
48	case ide_ht6560b:	name = "ht6560b";	break;
49	case ide_rz1000:	name = "rz1000";	break;
50	case ide_trm290:	name = "trm290";	break;
51	case ide_cmd646:	name = "cmd646";	break;
52	case ide_cy82c693:	name = "cy82c693";	break;
53	case ide_4drives:	name = "4drives";	break;
54	case ide_pmac:		name = "mac-io";	break;
55	case ide_au1xxx:	name = "au1xxx";	break;
56	case ide_palm3710:      name = "palm3710";      break;
57	case ide_acorn:		name = "acorn";		break;
58	default:		name = "(unknown)";	break;
59	}
60	len = sprintf(page, "%s\n", name);
61	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
62}
63
64static int proc_ide_read_mate
65	(char *page, char **start, off_t off, int count, int *eof, void *data)
66{
67	ide_hwif_t	*hwif = (ide_hwif_t *) data;
68	int		len;
69
70	if (hwif && hwif->mate)
71		len = sprintf(page, "%s\n", hwif->mate->name);
72	else
73		len = sprintf(page, "(none)\n");
74	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
75}
76
77static int proc_ide_read_channel
78	(char *page, char **start, off_t off, int count, int *eof, void *data)
79{
80	ide_hwif_t	*hwif = (ide_hwif_t *) data;
81	int		len;
82
83	page[0] = hwif->channel ? '1' : '0';
84	page[1] = '\n';
85	len = 2;
86	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
87}
88
89static int proc_ide_read_identify
90	(char *page, char **start, off_t off, int count, int *eof, void *data)
91{
92	ide_drive_t	*drive = (ide_drive_t *)data;
93	int		len = 0, i = 0;
94	int		err = 0;
95
96	len = sprintf(page, "\n");
97
98	if (drive) {
99		__le16 *val = (__le16 *)page;
100
101		err = taskfile_lib_get_identify(drive, page);
102		if (!err) {
103			char *out = (char *)page + SECTOR_SIZE;
104
105			page = out;
106			do {
107				out += sprintf(out, "%04x%c",
108					le16_to_cpup(val), (++i & 7) ? ' ' : '\n');
109				val += 1;
110			} while (i < SECTOR_SIZE / 2);
111			len = out - page;
112		}
113	}
114	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
115}
116
117/**
118 *	ide_find_setting	-	find a specific setting
119 *	@st: setting table pointer
120 *	@name: setting name
121 *
122 *	Scan's the setting table for a matching entry and returns
123 *	this or NULL if no entry is found. The caller must hold the
124 *	setting semaphore
125 */
126
127static
128const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
129					       char *name)
130{
131	while (st->name) {
132		if (strcmp(st->name, name) == 0)
133			break;
134		st++;
135	}
136	return st->name ? st : NULL;
137}
138
139/**
140 *	ide_read_setting	-	read an IDE setting
141 *	@drive: drive to read from
142 *	@setting: drive setting
143 *
144 *	Read a drive setting and return the value. The caller
145 *	must hold the ide_setting_mtx when making this call.
146 *
147 *	BUGS: the data return and error are the same return value
148 *	so an error -EINVAL and true return of the same value cannot
149 *	be told apart
150 */
151
152static int ide_read_setting(ide_drive_t *drive,
153			    const struct ide_proc_devset *setting)
154{
155	const struct ide_devset *ds = setting->setting;
156	int val = -EINVAL;
157
158	if (ds->get) {
159		unsigned long flags;
160
161		spin_lock_irqsave(&ide_lock, flags);
162		val = ds->get(drive);
163		spin_unlock_irqrestore(&ide_lock, flags);
164	}
165
166	return val;
167}
168
169/**
170 *	ide_write_setting	-	read an IDE setting
171 *	@drive: drive to read from
172 *	@setting: drive setting
173 *	@val: value
174 *
175 *	Write a drive setting if it is possible. The caller
176 *	must hold the ide_setting_mtx when making this call.
177 *
178 *	BUGS: the data return and error are the same return value
179 *	so an error -EINVAL and true return of the same value cannot
180 *	be told apart
181 *
182 *	FIXME:  This should be changed to enqueue a special request
183 *	to the driver to change settings, and then wait on a sema for completion.
184 *	The current scheme of polling is kludgy, though safe enough.
185 */
186
187static int ide_write_setting(ide_drive_t *drive,
188			     const struct ide_proc_devset *setting, int val)
189{
190	const struct ide_devset *ds = setting->setting;
191
192	if (!capable(CAP_SYS_ADMIN))
193		return -EACCES;
194	if (!ds->set)
195		return -EPERM;
196	if ((ds->flags & DS_SYNC)
197	    && (val < setting->min || val > setting->max))
198		return -EINVAL;
199	return ide_devset_execute(drive, ds, val);
200}
201
202ide_devset_get(xfer_rate, current_speed);
203
204static int set_xfer_rate (ide_drive_t *drive, int arg)
205{
206	ide_task_t task;
207	int err;
208
209	if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
210		return -EINVAL;
211
212	memset(&task, 0, sizeof(task));
213	task.tf.command = ATA_CMD_SET_FEATURES;
214	task.tf.feature = SETFEATURES_XFER;
215	task.tf.nsect   = (u8)arg;
216	task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT |
217			IDE_TFLAG_IN_NSECT;
218
219	err = ide_no_data_taskfile(drive, &task);
220
221	if (!err) {
222		ide_set_xfer_rate(drive, (u8) arg);
223		ide_driveid_update(drive);
224	}
225	return err;
226}
227
228ide_devset_rw(current_speed, xfer_rate);
229ide_devset_rw_field(init_speed, init_speed);
230ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
231ide_devset_rw_field(number, dn);
232
233static const struct ide_proc_devset ide_generic_settings[] = {
234	IDE_PROC_DEVSET(current_speed, 0, 70),
235	IDE_PROC_DEVSET(init_speed, 0, 70),
236	IDE_PROC_DEVSET(io_32bit,  0, 1 + (SUPPORT_VLB_SYNC << 1)),
237	IDE_PROC_DEVSET(keepsettings, 0, 1),
238	IDE_PROC_DEVSET(nice1, 0, 1),
239	IDE_PROC_DEVSET(number, 0, 3),
240	IDE_PROC_DEVSET(pio_mode, 0, 255),
241	IDE_PROC_DEVSET(unmaskirq, 0, 1),
242	IDE_PROC_DEVSET(using_dma, 0, 1),
243	{ 0 },
244};
245
246static void proc_ide_settings_warn(void)
247{
248	static int warned;
249
250	if (warned)
251		return;
252
253	printk(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
254			    "obsolete, and will be removed soon!\n");
255	warned = 1;
256}
257
258static int proc_ide_read_settings
259	(char *page, char **start, off_t off, int count, int *eof, void *data)
260{
261	const struct ide_proc_devset *setting, *g, *d;
262	const struct ide_devset *ds;
263	ide_drive_t	*drive = (ide_drive_t *) data;
264	char		*out = page;
265	int		len, rc, mul_factor, div_factor;
266
267	proc_ide_settings_warn();
268
269	mutex_lock(&ide_setting_mtx);
270	g = ide_generic_settings;
271	d = drive->settings;
272	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
273	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
274	while (g->name || (d && d->name)) {
275		/* read settings in the alphabetical order */
276		if (g->name && d && d->name) {
277			if (strcmp(d->name, g->name) < 0)
278				setting = d++;
279			else
280				setting = g++;
281		} else if (d && d->name) {
282			setting = d++;
283		} else
284			setting = g++;
285		mul_factor = setting->mulf ? setting->mulf(drive) : 1;
286		div_factor = setting->divf ? setting->divf(drive) : 1;
287		out += sprintf(out, "%-24s", setting->name);
288		rc = ide_read_setting(drive, setting);
289		if (rc >= 0)
290			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
291		else
292			out += sprintf(out, "%-16s", "write-only");
293		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
294		ds = setting->setting;
295		if (ds->get)
296			out += sprintf(out, "r");
297		if (ds->set)
298			out += sprintf(out, "w");
299		out += sprintf(out, "\n");
300	}
301	len = out - page;
302	mutex_unlock(&ide_setting_mtx);
303	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
304}
305
306#define MAX_LEN	30
307
308static int proc_ide_write_settings(struct file *file, const char __user *buffer,
309				   unsigned long count, void *data)
310{
311	ide_drive_t	*drive = (ide_drive_t *) data;
312	char		name[MAX_LEN + 1];
313	int		for_real = 0, mul_factor, div_factor;
314	unsigned long	n;
315
316	const struct ide_proc_devset *setting;
317	char *buf, *s;
318
319	if (!capable(CAP_SYS_ADMIN))
320		return -EACCES;
321
322	proc_ide_settings_warn();
323
324	if (count >= PAGE_SIZE)
325		return -EINVAL;
326
327	s = buf = (char *)__get_free_page(GFP_USER);
328	if (!buf)
329		return -ENOMEM;
330
331	if (copy_from_user(buf, buffer, count)) {
332		free_page((unsigned long)buf);
333		return -EFAULT;
334	}
335
336	buf[count] = '\0';
337
338	/*
339	 * Skip over leading whitespace
340	 */
341	while (count && isspace(*s)) {
342		--count;
343		++s;
344	}
345	/*
346	 * Do one full pass to verify all parameters,
347	 * then do another to actually write the new settings.
348	 */
349	do {
350		char *p = s;
351		n = count;
352		while (n > 0) {
353			unsigned val;
354			char *q = p;
355
356			while (n > 0 && *p != ':') {
357				--n;
358				p++;
359			}
360			if (*p != ':')
361				goto parse_error;
362			if (p - q > MAX_LEN)
363				goto parse_error;
364			memcpy(name, q, p - q);
365			name[p - q] = 0;
366
367			if (n > 0) {
368				--n;
369				p++;
370			} else
371				goto parse_error;
372
373			val = simple_strtoul(p, &q, 10);
374			n -= q - p;
375			p = q;
376			if (n > 0 && !isspace(*p))
377				goto parse_error;
378			while (n > 0 && isspace(*p)) {
379				--n;
380				++p;
381			}
382
383			mutex_lock(&ide_setting_mtx);
384			/* generic settings first, then driver specific ones */
385			setting = ide_find_setting(ide_generic_settings, name);
386			if (!setting) {
387				if (drive->settings)
388					setting = ide_find_setting(drive->settings, name);
389				if (!setting) {
390					mutex_unlock(&ide_setting_mtx);
391					goto parse_error;
392				}
393			}
394			if (for_real) {
395				mul_factor = setting->mulf ? setting->mulf(drive) : 1;
396				div_factor = setting->divf ? setting->divf(drive) : 1;
397				ide_write_setting(drive, setting, val * div_factor / mul_factor);
398			}
399			mutex_unlock(&ide_setting_mtx);
400		}
401	} while (!for_real++);
402	free_page((unsigned long)buf);
403	return count;
404parse_error:
405	free_page((unsigned long)buf);
406	printk("proc_ide_write_settings(): parse error\n");
407	return -EINVAL;
408}
409
410int proc_ide_read_capacity
411	(char *page, char **start, off_t off, int count, int *eof, void *data)
412{
413	int len = sprintf(page, "%llu\n", (long long)0x7fffffff);
414	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
415}
416
417EXPORT_SYMBOL_GPL(proc_ide_read_capacity);
418
419int proc_ide_read_geometry
420	(char *page, char **start, off_t off, int count, int *eof, void *data)
421{
422	ide_drive_t	*drive = (ide_drive_t *) data;
423	char		*out = page;
424	int		len;
425
426	out += sprintf(out, "physical     %d/%d/%d\n",
427			drive->cyl, drive->head, drive->sect);
428	out += sprintf(out, "logical      %d/%d/%d\n",
429			drive->bios_cyl, drive->bios_head, drive->bios_sect);
430
431	len = out - page;
432	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
433}
434
435EXPORT_SYMBOL(proc_ide_read_geometry);
436
437static int proc_ide_read_dmodel
438	(char *page, char **start, off_t off, int count, int *eof, void *data)
439{
440	ide_drive_t	*drive = (ide_drive_t *) data;
441	char		*m = (char *)&drive->id[ATA_ID_PROD];
442	int		len;
443
444	len = sprintf(page, "%.40s\n", m[0] ? m : "(none)");
445	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
446}
447
448static int proc_ide_read_driver
449	(char *page, char **start, off_t off, int count, int *eof, void *data)
450{
451	ide_drive_t	*drive = (ide_drive_t *) data;
452	struct device	*dev = &drive->gendev;
453	ide_driver_t	*ide_drv;
454	int		len;
455
456	if (dev->driver) {
457		ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
458		len = sprintf(page, "%s version %s\n",
459				dev->driver->name, ide_drv->version);
460	} else
461		len = sprintf(page, "ide-default version 0.9.newide\n");
462	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
463}
464
465static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
466{
467	struct device *dev = &drive->gendev;
468	int ret = 1;
469	int err;
470
471	device_release_driver(dev);
472	/* FIXME: device can still be in use by previous driver */
473	strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
474	err = device_attach(dev);
475	if (err < 0)
476		printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
477			__func__, err);
478	drive->driver_req[0] = 0;
479	if (dev->driver == NULL) {
480		err = device_attach(dev);
481		if (err < 0)
482			printk(KERN_WARNING
483				"IDE: %s: device_attach(2) error: %d\n",
484				__func__, err);
485	}
486	if (dev->driver && !strcmp(dev->driver->name, driver))
487		ret = 0;
488
489	return ret;
490}
491
492static int proc_ide_write_driver
493	(struct file *file, const char __user *buffer, unsigned long count, void *data)
494{
495	ide_drive_t	*drive = (ide_drive_t *) data;
496	char name[32];
497
498	if (!capable(CAP_SYS_ADMIN))
499		return -EACCES;
500	if (count > 31)
501		count = 31;
502	if (copy_from_user(name, buffer, count))
503		return -EFAULT;
504	name[count] = '\0';
505	if (ide_replace_subdriver(drive, name))
506		return -EINVAL;
507	return count;
508}
509
510static int proc_ide_read_media
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	const char	*media;
515	int		len;
516
517	switch (drive->media) {
518	case ide_disk:		media = "disk\n";	break;
519	case ide_cdrom:		media = "cdrom\n";	break;
520	case ide_tape:		media = "tape\n";	break;
521	case ide_floppy:	media = "floppy\n";	break;
522	case ide_optical:	media = "optical\n";	break;
523	default:		media = "UNKNOWN\n";	break;
524	}
525	strcpy(page, media);
526	len = strlen(media);
527	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
528}
529
530static ide_proc_entry_t generic_drive_entries[] = {
531	{ "driver",	S_IFREG|S_IRUGO,	 proc_ide_read_driver,
532						 proc_ide_write_driver },
533	{ "identify",	S_IFREG|S_IRUSR,	 proc_ide_read_identify, NULL },
534	{ "media",	S_IFREG|S_IRUGO,	 proc_ide_read_media,    NULL },
535	{ "model",	S_IFREG|S_IRUGO,	 proc_ide_read_dmodel,   NULL },
536	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR, proc_ide_read_settings,
537						 proc_ide_write_settings },
538	{ NULL,	0, NULL, NULL }
539};
540
541static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
542{
543	struct proc_dir_entry *ent;
544
545	if (!dir || !p)
546		return;
547	while (p->name != NULL) {
548		ent = create_proc_entry(p->name, p->mode, dir);
549		if (!ent) return;
550		ent->data = data;
551		ent->read_proc = p->read_proc;
552		ent->write_proc = p->write_proc;
553		p++;
554	}
555}
556
557static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
558{
559	if (!dir || !p)
560		return;
561	while (p->name != NULL) {
562		remove_proc_entry(p->name, dir);
563		p++;
564	}
565}
566
567void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver)
568{
569	mutex_lock(&ide_setting_mtx);
570	drive->settings = driver->proc_devsets(drive);
571	mutex_unlock(&ide_setting_mtx);
572
573	ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
574}
575
576EXPORT_SYMBOL(ide_proc_register_driver);
577
578/**
579 *	ide_proc_unregister_driver	-	remove driver specific data
580 *	@drive: drive
581 *	@driver: driver
582 *
583 *	Clean up the driver specific /proc files and IDE settings
584 *	for a given drive.
585 *
586 *	Takes ide_setting_mtx and ide_lock.
587 *	Caller must hold none of the locks.
588 */
589
590void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver)
591{
592	unsigned long flags;
593
594	ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
595
596	mutex_lock(&ide_setting_mtx);
597	spin_lock_irqsave(&ide_lock, flags);
598	/*
599	 * ide_setting_mtx protects the settings list
600	 * ide_lock protects the use of settings
601	 *
602	 * so we need to hold both, ide_settings_sem because we want to
603	 * modify the settings list, and ide_lock because we cannot take
604	 * a setting out that is being used.
605	 *
606	 * OTOH both ide_{read,write}_setting are only ever used under
607	 * ide_setting_mtx.
608	 */
609	drive->settings = NULL;
610	spin_unlock_irqrestore(&ide_lock, flags);
611	mutex_unlock(&ide_setting_mtx);
612}
613EXPORT_SYMBOL(ide_proc_unregister_driver);
614
615void ide_proc_port_register_devices(ide_hwif_t *hwif)
616{
617	int	d;
618	struct proc_dir_entry *ent;
619	struct proc_dir_entry *parent = hwif->proc;
620	char name[64];
621
622	for (d = 0; d < MAX_DRIVES; d++) {
623		ide_drive_t *drive = &hwif->drives[d];
624
625		if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc)
626			continue;
627
628		drive->proc = proc_mkdir(drive->name, parent);
629		if (drive->proc)
630			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
631		sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
632		ent = proc_symlink(drive->name, proc_ide_root, name);
633		if (!ent) return;
634	}
635}
636
637void ide_proc_unregister_device(ide_drive_t *drive)
638{
639	if (drive->proc) {
640		ide_remove_proc_entries(drive->proc, generic_drive_entries);
641		remove_proc_entry(drive->name, proc_ide_root);
642		remove_proc_entry(drive->name, drive->hwif->proc);
643		drive->proc = NULL;
644	}
645}
646
647static ide_proc_entry_t hwif_entries[] = {
648	{ "channel",	S_IFREG|S_IRUGO,	proc_ide_read_channel,	NULL },
649	{ "mate",	S_IFREG|S_IRUGO,	proc_ide_read_mate,	NULL },
650	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_imodel,	NULL },
651	{ NULL,	0, NULL, NULL }
652};
653
654void ide_proc_register_port(ide_hwif_t *hwif)
655{
656	if (!hwif->proc) {
657		hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
658
659		if (!hwif->proc)
660			return;
661
662		ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
663	}
664}
665
666void ide_proc_unregister_port(ide_hwif_t *hwif)
667{
668	if (hwif->proc) {
669		ide_remove_proc_entries(hwif->proc, hwif_entries);
670		remove_proc_entry(hwif->name, proc_ide_root);
671		hwif->proc = NULL;
672	}
673}
674
675static int proc_print_driver(struct device_driver *drv, void *data)
676{
677	ide_driver_t *ide_drv = container_of(drv, ide_driver_t, gen_driver);
678	struct seq_file *s = data;
679
680	seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
681
682	return 0;
683}
684
685static int ide_drivers_show(struct seq_file *s, void *p)
686{
687	int err;
688
689	err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
690	if (err < 0)
691		printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
692			__func__, err);
693	return 0;
694}
695
696static int ide_drivers_open(struct inode *inode, struct file *file)
697{
698	return single_open(file, &ide_drivers_show, NULL);
699}
700
701static const struct file_operations ide_drivers_operations = {
702	.owner		= THIS_MODULE,
703	.open		= ide_drivers_open,
704	.read		= seq_read,
705	.llseek		= seq_lseek,
706	.release	= single_release,
707};
708
709void proc_ide_create(void)
710{
711	proc_ide_root = proc_mkdir("ide", NULL);
712
713	if (!proc_ide_root)
714		return;
715
716	proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
717}
718
719void proc_ide_destroy(void)
720{
721	remove_proc_entry("drivers", proc_ide_root);
722	remove_proc_entry("ide", NULL);
723}
724