ide-proc.c 17.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/*
 *  Copyright (C) 1997-1998	Mark Lord
A
Alan Cox 已提交
3
 *  Copyright (C) 2003		Red Hat
4 5
 *
 *  Some code was moved here from ide.c, see it for original copyrights.
L
Linus Torvalds 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 */

/*
 * This is the /proc/ide/ filesystem implementation.
 *
 * Drive/Driver settings can be retrieved by reading the drive's
 * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
 * To write a new value "val" into a specific setting "name", use:
 *   echo "name:val" >/proc/ide/ide0/hda/settings
 */

#include <linux/module.h>

#include <asm/uaccess.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/ctype.h>
#include <linux/ide.h>
#include <linux/seq_file.h>

#include <asm/io.h>

31 32
static struct proc_dir_entry *proc_ide_root;

L
Linus Torvalds 已提交
33 34 35 36 37 38 39 40
static int proc_ide_read_imodel
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_hwif_t	*hwif = (ide_hwif_t *) data;
	int		len;
	const char	*name;

	switch (hwif->chipset) {
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	case ide_generic:	name = "generic";	break;
	case ide_pci:		name = "pci";		break;
	case ide_cmd640:	name = "cmd640";	break;
	case ide_dtc2278:	name = "dtc2278";	break;
	case ide_ali14xx:	name = "ali14xx";	break;
	case ide_qd65xx:	name = "qd65xx";	break;
	case ide_umc8672:	name = "umc8672";	break;
	case ide_ht6560b:	name = "ht6560b";	break;
	case ide_cy82c693:	name = "cy82c693";	break;
	case ide_4drives:	name = "4drives";	break;
	case ide_pmac:		name = "mac-io";	break;
	case ide_au1xxx:	name = "au1xxx";	break;
	case ide_palm3710:      name = "palm3710";      break;
	case ide_acorn:		name = "acorn";		break;
	default:		name = "(unknown)";	break;
L
Linus Torvalds 已提交
56 57
	}
	len = sprintf(page, "%s\n", name);
58
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
59 60 61 62 63 64 65 66
}

static int proc_ide_read_mate
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_hwif_t	*hwif = (ide_hwif_t *) data;
	int		len;

67
	if (hwif && hwif->mate)
L
Linus Torvalds 已提交
68 69 70
		len = sprintf(page, "%s\n", hwif->mate->name);
	else
		len = sprintf(page, "(none)\n");
71
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79 80 81 82
}

static int proc_ide_read_channel
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_hwif_t	*hwif = (ide_hwif_t *) data;
	int		len;

	page[0] = hwif->channel ? '1' : '0';
	page[1] = '\n';
	len = 2;
83
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
84 85 86 87 88 89 90 91 92 93 94 95
}

static int proc_ide_read_identify
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *)data;
	int		len = 0, i = 0;
	int		err = 0;

	len = sprintf(page, "\n");

	if (drive) {
96
		__le16 *val = (__le16 *)page;
L
Linus Torvalds 已提交
97 98 99

		err = taskfile_lib_get_identify(drive, page);
		if (!err) {
100 101
			char *out = (char *)page + SECTOR_SIZE;

L
Linus Torvalds 已提交
102 103 104
			page = out;
			do {
				out += sprintf(out, "%04x%c",
105
					le16_to_cpup(val), (++i & 7) ? ' ' : '\n');
L
Linus Torvalds 已提交
106
				val += 1;
107
			} while (i < SECTOR_SIZE / 2);
L
Linus Torvalds 已提交
108 109 110
			len = out - page;
		}
	}
111
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
112 113
}

114
/**
115 116
 *	ide_find_setting	-	find a specific setting
 *	@st: setting table pointer
117 118
 *	@name: setting name
 *
119
 *	Scan's the setting table for a matching entry and returns
120 121 122 123
 *	this or NULL if no entry is found. The caller must hold the
 *	setting semaphore
 */

124 125 126
static
const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
					       char *name)
127
{
128 129
	while (st->name) {
		if (strcmp(st->name, name) == 0)
130
			break;
131
		st++;
132
	}
133
	return st->name ? st : NULL;
134 135 136 137 138 139 140 141
}

/**
 *	ide_read_setting	-	read an IDE setting
 *	@drive: drive to read from
 *	@setting: drive setting
 *
 *	Read a drive setting and return the value. The caller
142
 *	must hold the ide_setting_mtx when making this call.
143 144 145 146 147 148
 *
 *	BUGS: the data return and error are the same return value
 *	so an error -EINVAL and true return of the same value cannot
 *	be told apart
 */

149
static int ide_read_setting(ide_drive_t *drive,
150
			    const struct ide_proc_devset *setting)
151
{
152
	const struct ide_devset *ds = setting->setting;
153 154
	int val = -EINVAL;

155
	if (ds->get)
156
		val = ds->get(drive);
157

158 159 160 161 162 163 164 165 166 167
	return val;
}

/**
 *	ide_write_setting	-	read an IDE setting
 *	@drive: drive to read from
 *	@setting: drive setting
 *	@val: value
 *
 *	Write a drive setting if it is possible. The caller
168
 *	must hold the ide_setting_mtx when making this call.
169 170 171 172 173 174 175 176 177 178
 *
 *	BUGS: the data return and error are the same return value
 *	so an error -EINVAL and true return of the same value cannot
 *	be told apart
 *
 *	FIXME:  This should be changed to enqueue a special request
 *	to the driver to change settings, and then wait on a sema for completion.
 *	The current scheme of polling is kludgy, though safe enough.
 */

179
static int ide_write_setting(ide_drive_t *drive,
180
			     const struct ide_proc_devset *setting, int val)
181
{
182 183
	const struct ide_devset *ds = setting->setting;

184 185
	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
186
	if (!ds->set)
187
		return -EPERM;
188 189
	if ((ds->flags & DS_SYNC)
	    && (val < setting->min || val > setting->max))
190
		return -EINVAL;
191
	return ide_devset_execute(drive, ds, val);
192 193
}

194
ide_devset_get(xfer_rate, current_speed);
195

196 197
static int set_xfer_rate (ide_drive_t *drive, int arg)
{
198
	ide_task_t task;
199 200
	int err;

201
	if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
202 203
		return -EINVAL;

204
	memset(&task, 0, sizeof(task));
205
	task.tf.command = ATA_CMD_SET_FEATURES;
206 207 208 209 210 211
	task.tf.feature = SETFEATURES_XFER;
	task.tf.nsect   = (u8)arg;
	task.tf_flags = IDE_TFLAG_OUT_FEATURE | IDE_TFLAG_OUT_NSECT |
			IDE_TFLAG_IN_NSECT;

	err = ide_no_data_taskfile(drive, &task);
212

213
	if (!err) {
214 215 216 217 218 219
		ide_set_xfer_rate(drive, (u8) arg);
		ide_driveid_update(drive);
	}
	return err;
}

220 221
ide_devset_rw(current_speed, xfer_rate);
ide_devset_rw_field(init_speed, init_speed);
B
Bartlomiej Zolnierkiewicz 已提交
222
ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
223 224 225 226 227 228 229 230 231 232 233 234 235
ide_devset_rw_field(number, dn);

static const struct ide_proc_devset ide_generic_settings[] = {
	IDE_PROC_DEVSET(current_speed, 0, 70),
	IDE_PROC_DEVSET(init_speed, 0, 70),
	IDE_PROC_DEVSET(io_32bit,  0, 1 + (SUPPORT_VLB_SYNC << 1)),
	IDE_PROC_DEVSET(keepsettings, 0, 1),
	IDE_PROC_DEVSET(nice1, 0, 1),
	IDE_PROC_DEVSET(number, 0, 3),
	IDE_PROC_DEVSET(pio_mode, 0, 255),
	IDE_PROC_DEVSET(unmaskirq, 0, 1),
	IDE_PROC_DEVSET(using_dma, 0, 1),
	{ 0 },
236
};
237

L
Linus Torvalds 已提交
238 239
static void proc_ide_settings_warn(void)
{
240
	static int warned;
L
Linus Torvalds 已提交
241 242 243 244 245 246 247 248 249 250 251 252

	if (warned)
		return;

	printk(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
			    "obsolete, and will be removed soon!\n");
	warned = 1;
}

static int proc_ide_read_settings
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
253 254
	const struct ide_proc_devset *setting, *g, *d;
	const struct ide_devset *ds;
L
Linus Torvalds 已提交
255 256 257 258 259 260
	ide_drive_t	*drive = (ide_drive_t *) data;
	char		*out = page;
	int		len, rc, mul_factor, div_factor;

	proc_ide_settings_warn();

261
	mutex_lock(&ide_setting_mtx);
262 263
	g = ide_generic_settings;
	d = drive->settings;
L
Linus Torvalds 已提交
264 265
	out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
	out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
266
	while (g->name || (d && d->name)) {
267
		/* read settings in the alphabetical order */
268 269 270
		if (g->name && d && d->name) {
			if (strcmp(d->name, g->name) < 0)
				setting = d++;
271
			else
272 273 274
				setting = g++;
		} else if (d && d->name) {
			setting = d++;
275
		} else
276
			setting = g++;
277 278
		mul_factor = setting->mulf ? setting->mulf(drive) : 1;
		div_factor = setting->divf ? setting->divf(drive) : 1;
L
Linus Torvalds 已提交
279
		out += sprintf(out, "%-24s", setting->name);
280 281
		rc = ide_read_setting(drive, setting);
		if (rc >= 0)
L
Linus Torvalds 已提交
282 283 284 285
			out += sprintf(out, "%-16d", rc * mul_factor / div_factor);
		else
			out += sprintf(out, "%-16s", "write-only");
		out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
286 287
		ds = setting->setting;
		if (ds->get)
L
Linus Torvalds 已提交
288
			out += sprintf(out, "r");
289
		if (ds->set)
L
Linus Torvalds 已提交
290 291 292 293
			out += sprintf(out, "w");
		out += sprintf(out, "\n");
	}
	len = out - page;
294
	mutex_unlock(&ide_setting_mtx);
295
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
296 297 298 299 300 301 302 303 304
}

#define MAX_LEN	30

static int proc_ide_write_settings(struct file *file, const char __user *buffer,
				   unsigned long count, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
	char		name[MAX_LEN + 1];
305
	int		for_real = 0, mul_factor, div_factor;
L
Linus Torvalds 已提交
306
	unsigned long	n;
307

308
	const struct ide_proc_devset *setting;
L
Linus Torvalds 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
	char *buf, *s;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;

	proc_ide_settings_warn();

	if (count >= PAGE_SIZE)
		return -EINVAL;

	s = buf = (char *)__get_free_page(GFP_USER);
	if (!buf)
		return -ENOMEM;

	if (copy_from_user(buf, buffer, count)) {
		free_page((unsigned long)buf);
		return -EFAULT;
	}

	buf[count] = '\0';

	/*
	 * Skip over leading whitespace
	 */
	while (count && isspace(*s)) {
		--count;
		++s;
	}
	/*
	 * Do one full pass to verify all parameters,
	 * then do another to actually write the new settings.
	 */
	do {
		char *p = s;
		n = count;
		while (n > 0) {
			unsigned val;
			char *q = p;

			while (n > 0 && *p != ':') {
				--n;
				p++;
			}
			if (*p != ':')
				goto parse_error;
			if (p - q > MAX_LEN)
				goto parse_error;
			memcpy(name, q, p - q);
			name[p - q] = 0;

			if (n > 0) {
				--n;
				p++;
			} else
				goto parse_error;

			val = simple_strtoul(p, &q, 10);
			n -= q - p;
			p = q;
			if (n > 0 && !isspace(*p))
				goto parse_error;
			while (n > 0 && isspace(*p)) {
				--n;
				++p;
			}

375
			mutex_lock(&ide_setting_mtx);
376 377
			/* generic settings first, then driver specific ones */
			setting = ide_find_setting(ide_generic_settings, name);
378
			if (!setting) {
379 380 381 382 383 384 385 386 387 388 389
				if (drive->settings)
					setting = ide_find_setting(drive->settings, name);
				if (!setting) {
					mutex_unlock(&ide_setting_mtx);
					goto parse_error;
				}
			}
			if (for_real) {
				mul_factor = setting->mulf ? setting->mulf(drive) : 1;
				div_factor = setting->divf ? setting->divf(drive) : 1;
				ide_write_setting(drive, setting, val * div_factor / mul_factor);
L
Linus Torvalds 已提交
390
			}
391
			mutex_unlock(&ide_setting_mtx);
L
Linus Torvalds 已提交
392 393 394 395 396 397 398 399 400 401 402 403 404
		}
	} while (!for_real++);
	free_page((unsigned long)buf);
	return count;
parse_error:
	free_page((unsigned long)buf);
	printk("proc_ide_write_settings(): parse error\n");
	return -EINVAL;
}

int proc_ide_read_capacity
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
405 406
	int len = sprintf(page, "%llu\n", (long long)0x7fffffff);
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
407 408 409 410 411 412 413 414 415 416 417
}

EXPORT_SYMBOL_GPL(proc_ide_read_capacity);

int proc_ide_read_geometry
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
	char		*out = page;
	int		len;

418
	out += sprintf(out, "physical     %d/%d/%d\n",
L
Linus Torvalds 已提交
419
			drive->cyl, drive->head, drive->sect);
420
	out += sprintf(out, "logical      %d/%d/%d\n",
L
Linus Torvalds 已提交
421 422 423
			drive->bios_cyl, drive->bios_head, drive->bios_sect);

	len = out - page;
424
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
425 426 427 428 429 430 431 432
}

EXPORT_SYMBOL(proc_ide_read_geometry);

static int proc_ide_read_dmodel
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
433
	char		*m = (char *)&drive->id[ATA_ID_PROD];
L
Linus Torvalds 已提交
434 435
	int		len;

436
	len = sprintf(page, "%.40s\n", m[0] ? m : "(none)");
437
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
438 439 440 441 442 443
}

static int proc_ide_read_driver
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
444 445
	struct device	*dev = &drive->gendev;
	ide_driver_t	*ide_drv;
L
Linus Torvalds 已提交
446 447
	int		len;

448 449
	if (dev->driver) {
		ide_drv = container_of(dev->driver, ide_driver_t, gen_driver);
L
Linus Torvalds 已提交
450
		len = sprintf(page, "%s version %s\n",
451
				dev->driver->name, ide_drv->version);
L
Linus Torvalds 已提交
452 453
	} else
		len = sprintf(page, "ide-default version 0.9.newide\n");
454
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
455 456
}

457 458 459 460
static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
{
	struct device *dev = &drive->gendev;
	int ret = 1;
461
	int err;
462 463 464 465

	device_release_driver(dev);
	/* FIXME: device can still be in use by previous driver */
	strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
466 467 468
	err = device_attach(dev);
	if (err < 0)
		printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
469
			__func__, err);
470
	drive->driver_req[0] = 0;
471 472 473 474 475
	if (dev->driver == NULL) {
		err = device_attach(dev);
		if (err < 0)
			printk(KERN_WARNING
				"IDE: %s: device_attach(2) error: %d\n",
476
				__func__, err);
477
	}
478 479 480 481 482 483
	if (dev->driver && !strcmp(dev->driver->name, driver))
		ret = 0;

	return ret;
}

L
Linus Torvalds 已提交
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
static int proc_ide_write_driver
	(struct file *file, const char __user *buffer, unsigned long count, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
	char name[32];

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
	if (count > 31)
		count = 31;
	if (copy_from_user(name, buffer, count))
		return -EFAULT;
	name[count] = '\0';
	if (ide_replace_subdriver(drive, name))
		return -EINVAL;
	return count;
}

static int proc_ide_read_media
	(char *page, char **start, off_t off, int count, int *eof, void *data)
{
	ide_drive_t	*drive = (ide_drive_t *) data;
	const char	*media;
	int		len;

	switch (drive->media) {
510 511 512 513 514 515
	case ide_disk:		media = "disk\n";	break;
	case ide_cdrom:		media = "cdrom\n";	break;
	case ide_tape:		media = "tape\n";	break;
	case ide_floppy:	media = "floppy\n";	break;
	case ide_optical:	media = "optical\n";	break;
	default:		media = "UNKNOWN\n";	break;
L
Linus Torvalds 已提交
516
	}
517
	strcpy(page, media);
L
Linus Torvalds 已提交
518
	len = strlen(media);
519
	PROC_IDE_READ_RETURN(page, start, off, count, eof, len);
L
Linus Torvalds 已提交
520 521 522
}

static ide_proc_entry_t generic_drive_entries[] = {
523 524 525 526 527 528 529
	{ "driver",	S_IFREG|S_IRUGO,	 proc_ide_read_driver,
						 proc_ide_write_driver },
	{ "identify",	S_IFREG|S_IRUSR,	 proc_ide_read_identify, NULL },
	{ "media",	S_IFREG|S_IRUGO,	 proc_ide_read_media,    NULL },
	{ "model",	S_IFREG|S_IRUGO,	 proc_ide_read_dmodel,   NULL },
	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR, proc_ide_read_settings,
						 proc_ide_write_settings },
L
Linus Torvalds 已提交
530 531 532
	{ NULL,	0, NULL, NULL }
};

533
static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
L
Linus Torvalds 已提交
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
{
	struct proc_dir_entry *ent;

	if (!dir || !p)
		return;
	while (p->name != NULL) {
		ent = create_proc_entry(p->name, p->mode, dir);
		if (!ent) return;
		ent->data = data;
		ent->read_proc = p->read_proc;
		ent->write_proc = p->write_proc;
		p++;
	}
}

549
static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
L
Linus Torvalds 已提交
550 551 552 553 554 555 556 557 558
{
	if (!dir || !p)
		return;
	while (p->name != NULL) {
		remove_proc_entry(p->name, dir);
		p++;
	}
}

559 560
void ide_proc_register_driver(ide_drive_t *drive, ide_driver_t *driver)
{
561
	mutex_lock(&ide_setting_mtx);
562
	drive->settings = driver->proc_devsets(drive);
563 564
	mutex_unlock(&ide_setting_mtx);

565
	ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
566 567 568 569 570 571 572 573 574 575 576 577
}

EXPORT_SYMBOL(ide_proc_register_driver);

/**
 *	ide_proc_unregister_driver	-	remove driver specific data
 *	@drive: drive
 *	@driver: driver
 *
 *	Clean up the driver specific /proc files and IDE settings
 *	for a given drive.
 *
578
 *	Takes ide_setting_mtx.
579 580 581 582
 */

void ide_proc_unregister_driver(ide_drive_t *drive, ide_driver_t *driver)
{
583
	ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
584

585
	mutex_lock(&ide_setting_mtx);
586
	/*
587 588
	 * ide_setting_mtx protects both the settings list and the use
	 * of settings (we cannot take a setting out that is being used).
589
	 */
590
	drive->settings = NULL;
591
	mutex_unlock(&ide_setting_mtx);
592 593 594
}
EXPORT_SYMBOL(ide_proc_unregister_driver);

595
void ide_proc_port_register_devices(ide_hwif_t *hwif)
L
Linus Torvalds 已提交
596 597 598 599 600 601 602 603 604
{
	int	d;
	struct proc_dir_entry *ent;
	struct proc_dir_entry *parent = hwif->proc;
	char name[64];

	for (d = 0; d < MAX_DRIVES; d++) {
		ide_drive_t *drive = &hwif->drives[d];

B
Bartlomiej Zolnierkiewicz 已提交
605
		if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0 || drive->proc)
L
Linus Torvalds 已提交
606 607 608 609 610
			continue;

		drive->proc = proc_mkdir(drive->name, parent);
		if (drive->proc)
			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
611
		sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
L
Linus Torvalds 已提交
612 613 614 615 616
		ent = proc_symlink(drive->name, proc_ide_root, name);
		if (!ent) return;
	}
}

617
void ide_proc_unregister_device(ide_drive_t *drive)
L
Linus Torvalds 已提交
618 619 620 621
{
	if (drive->proc) {
		ide_remove_proc_entries(drive->proc, generic_drive_entries);
		remove_proc_entry(drive->name, proc_ide_root);
622
		remove_proc_entry(drive->name, drive->hwif->proc);
L
Linus Torvalds 已提交
623 624 625 626 627 628 629 630 631 632 633
		drive->proc = NULL;
	}
}

static ide_proc_entry_t hwif_entries[] = {
	{ "channel",	S_IFREG|S_IRUGO,	proc_ide_read_channel,	NULL },
	{ "mate",	S_IFREG|S_IRUGO,	proc_ide_read_mate,	NULL },
	{ "model",	S_IFREG|S_IRUGO,	proc_ide_read_imodel,	NULL },
	{ NULL,	0, NULL, NULL }
};

634
void ide_proc_register_port(ide_hwif_t *hwif)
L
Linus Torvalds 已提交
635
{
636 637
	if (!hwif->proc) {
		hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
L
Linus Torvalds 已提交
638

639 640 641 642
		if (!hwif->proc)
			return;

		ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
L
Linus Torvalds 已提交
643 644 645
	}
}

646
void ide_proc_unregister_port(ide_hwif_t *hwif)
L
Linus Torvalds 已提交
647 648 649 650 651 652 653 654
{
	if (hwif->proc) {
		ide_remove_proc_entries(hwif->proc, hwif_entries);
		remove_proc_entry(hwif->name, proc_ide_root);
		hwif->proc = NULL;
	}
}

655 656 657 658 659 660 661 662 663 664 665 666
static int proc_print_driver(struct device_driver *drv, void *data)
{
	ide_driver_t *ide_drv = container_of(drv, ide_driver_t, gen_driver);
	struct seq_file *s = data;

	seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);

	return 0;
}

static int ide_drivers_show(struct seq_file *s, void *p)
{
667 668 669 670 671
	int err;

	err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
	if (err < 0)
		printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
672
			__func__, err);
673 674 675
	return 0;
}

L
Linus Torvalds 已提交
676 677
static int ide_drivers_open(struct inode *inode, struct file *file)
{
678
	return single_open(file, &ide_drivers_show, NULL);
L
Linus Torvalds 已提交
679
}
680

681
static const struct file_operations ide_drivers_operations = {
682
	.owner		= THIS_MODULE,
L
Linus Torvalds 已提交
683 684 685
	.open		= ide_drivers_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
686
	.release	= single_release,
L
Linus Torvalds 已提交
687 688 689 690
};

void proc_ide_create(void)
{
691 692
	proc_ide_root = proc_mkdir("ide", NULL);

L
Linus Torvalds 已提交
693 694 695
	if (!proc_ide_root)
		return;

696
	proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
L
Linus Torvalds 已提交
697 698 699 700
}

void proc_ide_destroy(void)
{
Z
Zhang, Yanmin 已提交
701
	remove_proc_entry("drivers", proc_ide_root);
L
Linus Torvalds 已提交
702 703
	remove_proc_entry("ide", NULL);
}