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

/*
 * 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>
28
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
29
30
31

#include <asm/io.h>

32
33
static struct proc_dir_entry *proc_ide_root;

Alexey Dobriyan's avatar
Alexey Dobriyan committed
34
static int ide_imodel_proc_show(struct seq_file *m, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
35
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
36
	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;
Linus Torvalds's avatar
Linus Torvalds committed
37
38
39
	const char	*name;

	switch (hwif->chipset) {
40
41
42
43
44
45
46
47
48
49
50
51
52
53
	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_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;
Linus Torvalds's avatar
Linus Torvalds committed
54
	}
Alexey Dobriyan's avatar
Alexey Dobriyan committed
55
56
	seq_printf(m, "%s\n", name);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
57
58
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
59
static int ide_imodel_proc_open(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
60
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
	return single_open(file, ide_imodel_proc_show, PDE(inode)->data);
}

static const struct file_operations ide_imodel_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_imodel_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static int ide_mate_proc_show(struct seq_file *m, void *v)
{
	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;
Linus Torvalds's avatar
Linus Torvalds committed
75

76
	if (hwif && hwif->mate)
Alexey Dobriyan's avatar
Alexey Dobriyan committed
77
		seq_printf(m, "%s\n", hwif->mate->name);
Linus Torvalds's avatar
Linus Torvalds committed
78
	else
Alexey Dobriyan's avatar
Alexey Dobriyan committed
79
80
		seq_printf(m, "(none)\n");
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
81
82
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
83
static int ide_mate_proc_open(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
84
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
85
86
	return single_open(file, ide_mate_proc_show, PDE(inode)->data);
}
Linus Torvalds's avatar
Linus Torvalds committed
87

Alexey Dobriyan's avatar
Alexey Dobriyan committed
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static const struct file_operations ide_mate_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_mate_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static int ide_channel_proc_show(struct seq_file *m, void *v)
{
	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;

	seq_printf(m, "%c\n", hwif->channel ? '1' : '0');
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
102
103
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
104
static int ide_channel_proc_open(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
105
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
106
107
108
109
110
111
112
113
114
115
	return single_open(file, ide_channel_proc_show, PDE(inode)->data);
}

static const struct file_operations ide_channel_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_channel_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};
Linus Torvalds's avatar
Linus Torvalds committed
116

Alexey Dobriyan's avatar
Alexey Dobriyan committed
117
118
119
120
static int ide_identify_proc_show(struct seq_file *m, void *v)
{
	ide_drive_t *drive = (ide_drive_t *)m->private;
	u8 *buf;
Linus Torvalds's avatar
Linus Torvalds committed
121

Alexey Dobriyan's avatar
Alexey Dobriyan committed
122
123
124
125
	if (!drive) {
		seq_putc(m, '\n');
		return 0;
	}
Linus Torvalds's avatar
Linus Torvalds committed
126

Alexey Dobriyan's avatar
Alexey Dobriyan committed
127
128
129
130
131
132
	buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;
	if (taskfile_lib_get_identify(drive, buf) == 0) {
		__le16 *val = (__le16 *)buf;
		int i;
133

Alexey Dobriyan's avatar
Alexey Dobriyan committed
134
135
136
		for (i = 0; i < SECTOR_SIZE / 2; i++) {
			seq_printf(m, "%04x%c", le16_to_cpu(val[i]),
					(i % 8) == 7 ? '\n' : ' ');
Linus Torvalds's avatar
Linus Torvalds committed
137
		}
Alexey Dobriyan's avatar
Alexey Dobriyan committed
138
139
140
141
	} else
		seq_putc(m, buf[0]);
	kfree(buf);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
142
143
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
144
145
146
147
148
149
150
151
152
153
154
155
156
static int ide_identify_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_identify_proc_show, PDE(inode)->data);
}

static const struct file_operations ide_identify_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_identify_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

157
/**
158
159
 *	ide_find_setting	-	find a specific setting
 *	@st: setting table pointer
160
161
 *	@name: setting name
 *
162
 *	Scan's the setting table for a matching entry and returns
163
164
165
166
 *	this or NULL if no entry is found. The caller must hold the
 *	setting semaphore
 */

167
168
169
static
const struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
					       char *name)
170
{
171
172
	while (st->name) {
		if (strcmp(st->name, name) == 0)
173
			break;
174
		st++;
175
	}
176
	return st->name ? st : NULL;
177
178
179
180
181
182
183
184
}

/**
 *	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
185
 *	must hold the ide_setting_mtx when making this call.
186
187
188
189
190
191
 *
 *	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
 */

192
static int ide_read_setting(ide_drive_t *drive,
193
			    const struct ide_proc_devset *setting)
194
{
195
	const struct ide_devset *ds = setting->setting;
196
197
	int val = -EINVAL;

198
	if (ds->get)
199
		val = ds->get(drive);
200

201
202
203
204
205
206
207
208
209
210
	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
211
 *	must hold the ide_setting_mtx when making this call.
212
213
214
215
216
217
218
219
220
221
 *
 *	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.
 */

222
static int ide_write_setting(ide_drive_t *drive,
223
			     const struct ide_proc_devset *setting, int val)
224
{
225
226
	const struct ide_devset *ds = setting->setting;

227
228
	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
229
	if (!ds->set)
230
		return -EPERM;
231
232
	if ((ds->flags & DS_SYNC)
	    && (val < setting->min || val > setting->max))
233
		return -EINVAL;
234
	return ide_devset_execute(drive, ds, val);
235
236
}

237
ide_devset_get(xfer_rate, current_speed);
238

239
240
static int set_xfer_rate (ide_drive_t *drive, int arg)
{
241
	struct ide_cmd cmd;
242

243
	if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
244
245
		return -EINVAL;

246
247
248
249
	memset(&cmd, 0, sizeof(cmd));
	cmd.tf.command = ATA_CMD_SET_FEATURES;
	cmd.tf.feature = SETFEATURES_XFER;
	cmd.tf.nsect   = (u8)arg;
250
251
	cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
	cmd.valid.in.tf  = IDE_VALID_NSECT;
252
	cmd.tf_flags   = IDE_TFLAG_SET_XFER;
253

254
	return ide_no_data_taskfile(drive, &cmd);
255
256
}

257
258
ide_devset_rw(current_speed, xfer_rate);
ide_devset_rw_field(init_speed, init_speed);
259
ide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
260
261
262
263
264
265
266
267
268
269
270
271
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),
272
	{ NULL },
273
};
274

Linus Torvalds's avatar
Linus Torvalds committed
275
276
static void proc_ide_settings_warn(void)
{
Marcin Slusarz's avatar
Marcin Slusarz committed
277
	printk_once(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
Linus Torvalds's avatar
Linus Torvalds committed
278
279
280
			    "obsolete, and will be removed soon!\n");
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
281
static int ide_settings_proc_show(struct seq_file *m, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
282
{
283
284
	const struct ide_proc_devset *setting, *g, *d;
	const struct ide_devset *ds;
Alexey Dobriyan's avatar
Alexey Dobriyan committed
285
286
	ide_drive_t	*drive = (ide_drive_t *) m->private;
	int		rc, mul_factor, div_factor;
Linus Torvalds's avatar
Linus Torvalds committed
287
288
289

	proc_ide_settings_warn();

290
	mutex_lock(&ide_setting_mtx);
291
292
	g = ide_generic_settings;
	d = drive->settings;
Alexey Dobriyan's avatar
Alexey Dobriyan committed
293
294
	seq_printf(m, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
	seq_printf(m, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
295
	while (g->name || (d && d->name)) {
296
		/* read settings in the alphabetical order */
297
298
299
		if (g->name && d && d->name) {
			if (strcmp(d->name, g->name) < 0)
				setting = d++;
300
			else
301
302
303
				setting = g++;
		} else if (d && d->name) {
			setting = d++;
304
		} else
305
			setting = g++;
306
307
		mul_factor = setting->mulf ? setting->mulf(drive) : 1;
		div_factor = setting->divf ? setting->divf(drive) : 1;
Alexey Dobriyan's avatar
Alexey Dobriyan committed
308
		seq_printf(m, "%-24s", setting->name);
309
310
		rc = ide_read_setting(drive, setting);
		if (rc >= 0)
Alexey Dobriyan's avatar
Alexey Dobriyan committed
311
			seq_printf(m, "%-16d", rc * mul_factor / div_factor);
Linus Torvalds's avatar
Linus Torvalds committed
312
		else
Alexey Dobriyan's avatar
Alexey Dobriyan committed
313
314
			seq_printf(m, "%-16s", "write-only");
		seq_printf(m, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
315
316
		ds = setting->setting;
		if (ds->get)
Alexey Dobriyan's avatar
Alexey Dobriyan committed
317
			seq_printf(m, "r");
318
		if (ds->set)
Alexey Dobriyan's avatar
Alexey Dobriyan committed
319
320
			seq_printf(m, "w");
		seq_printf(m, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
321
	}
322
	mutex_unlock(&ide_setting_mtx);
Alexey Dobriyan's avatar
Alexey Dobriyan committed
323
324
325
326
327
328
	return 0;
}

static int ide_settings_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_settings_proc_show, PDE(inode)->data);
Linus Torvalds's avatar
Linus Torvalds committed
329
330
331
332
}

#define MAX_LEN	30

Alexey Dobriyan's avatar
Alexey Dobriyan committed
333
334
static ssize_t ide_settings_proc_write(struct file *file, const char __user *buffer,
				       size_t count, loff_t *pos)
Linus Torvalds's avatar
Linus Torvalds committed
335
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
336
	ide_drive_t	*drive = (ide_drive_t *) PDE(file->f_path.dentry->d_inode)->data;
Linus Torvalds's avatar
Linus Torvalds committed
337
	char		name[MAX_LEN + 1];
338
	int		for_real = 0, mul_factor, div_factor;
Linus Torvalds's avatar
Linus Torvalds committed
339
	unsigned long	n;
340

341
	const struct ide_proc_devset *setting;
Linus Torvalds's avatar
Linus Torvalds committed
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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
	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;
			}

408
			mutex_lock(&ide_setting_mtx);
409
410
			/* generic settings first, then driver specific ones */
			setting = ide_find_setting(ide_generic_settings, name);
411
			if (!setting) {
412
413
414
415
416
417
418
419
420
421
422
				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);
Linus Torvalds's avatar
Linus Torvalds committed
423
			}
424
			mutex_unlock(&ide_setting_mtx);
Linus Torvalds's avatar
Linus Torvalds committed
425
426
427
428
429
430
		}
	} while (!for_real++);
	free_page((unsigned long)buf);
	return count;
parse_error:
	free_page((unsigned long)buf);
Alexey Dobriyan's avatar
Alexey Dobriyan committed
431
	printk("%s(): parse error\n", __func__);
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
	return -EINVAL;
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
435
436
437
438
439
440
441
442
443
444
static const struct file_operations ide_settings_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_settings_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.write		= ide_settings_proc_write,
};

static int ide_capacity_proc_show(struct seq_file *m, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
445
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
446
447
	seq_printf(m, "%llu\n", (long long)0x7fffffff);
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
448
449
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
450
451
452
453
454
455
456
457
458
459
460
461
462
static int ide_capacity_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_capacity_proc_show, NULL);
}

const struct file_operations ide_capacity_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_capacity_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};
EXPORT_SYMBOL_GPL(ide_capacity_proc_fops);
Linus Torvalds's avatar
Linus Torvalds committed
463

Alexey Dobriyan's avatar
Alexey Dobriyan committed
464
static int ide_geometry_proc_show(struct seq_file *m, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
465
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
466
	ide_drive_t	*drive = (ide_drive_t *) m->private;
Linus Torvalds's avatar
Linus Torvalds committed
467

Alexey Dobriyan's avatar
Alexey Dobriyan committed
468
	seq_printf(m, "physical     %d/%d/%d\n",
Linus Torvalds's avatar
Linus Torvalds committed
469
			drive->cyl, drive->head, drive->sect);
Alexey Dobriyan's avatar
Alexey Dobriyan committed
470
	seq_printf(m, "logical      %d/%d/%d\n",
Linus Torvalds's avatar
Linus Torvalds committed
471
			drive->bios_cyl, drive->bios_head, drive->bios_sect);
Alexey Dobriyan's avatar
Alexey Dobriyan committed
472
473
	return 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
474

Alexey Dobriyan's avatar
Alexey Dobriyan committed
475
476
477
static int ide_geometry_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_geometry_proc_show, PDE(inode)->data);
Linus Torvalds's avatar
Linus Torvalds committed
478
479
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
480
481
482
483
484
485
486
487
const struct file_operations ide_geometry_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_geometry_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};
EXPORT_SYMBOL(ide_geometry_proc_fops);
Linus Torvalds's avatar
Linus Torvalds committed
488

Alexey Dobriyan's avatar
Alexey Dobriyan committed
489
static int ide_dmodel_proc_show(struct seq_file *seq, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
490
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
491
	ide_drive_t	*drive = (ide_drive_t *) seq->private;
492
	char		*m = (char *)&drive->id[ATA_ID_PROD];
Linus Torvalds's avatar
Linus Torvalds committed
493

Alexey Dobriyan's avatar
Alexey Dobriyan committed
494
495
	seq_printf(seq, "%.40s\n", m[0] ? m : "(none)");
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
496
497
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
498
static int ide_dmodel_proc_open(struct inode *inode, struct file *file)
Linus Torvalds's avatar
Linus Torvalds committed
499
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
500
501
502
503
504
505
506
507
508
509
510
511
512
513
	return single_open(file, ide_dmodel_proc_show, PDE(inode)->data);
}

static const struct file_operations ide_dmodel_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_dmodel_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

static int ide_driver_proc_show(struct seq_file *m, void *v)
{
	ide_drive_t		*drive = (ide_drive_t *)m->private;
514
515
	struct device		*dev = &drive->gendev;
	struct ide_driver	*ide_drv;
Linus Torvalds's avatar
Linus Torvalds committed
516

517
	if (dev->driver) {
518
		ide_drv = to_ide_driver(dev->driver);
Alexey Dobriyan's avatar
Alexey Dobriyan committed
519
		seq_printf(m, "%s version %s\n",
520
				dev->driver->name, ide_drv->version);
Linus Torvalds's avatar
Linus Torvalds committed
521
	} else
Alexey Dobriyan's avatar
Alexey Dobriyan committed
522
523
524
525
526
527
528
		seq_printf(m, "ide-default version 0.9.newide\n");
	return 0;
}

static int ide_driver_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_driver_proc_show, PDE(inode)->data);
Linus Torvalds's avatar
Linus Torvalds committed
529
530
}

531
532
533
534
static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
{
	struct device *dev = &drive->gendev;
	int ret = 1;
535
	int err;
536
537
538
539

	device_release_driver(dev);
	/* FIXME: device can still be in use by previous driver */
	strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
540
541
542
	err = device_attach(dev);
	if (err < 0)
		printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
543
			__func__, err);
544
	drive->driver_req[0] = 0;
545
546
547
548
549
	if (dev->driver == NULL) {
		err = device_attach(dev);
		if (err < 0)
			printk(KERN_WARNING
				"IDE: %s: device_attach(2) error: %d\n",
550
				__func__, err);
551
	}
552
553
554
555
556
557
	if (dev->driver && !strcmp(dev->driver->name, driver))
		ret = 0;

	return ret;
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
558
559
static ssize_t ide_driver_proc_write(struct file *file, const char __user *buffer,
				     size_t count, loff_t *pos)
Linus Torvalds's avatar
Linus Torvalds committed
560
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
561
	ide_drive_t	*drive = (ide_drive_t *) PDE(file->f_path.dentry->d_inode)->data;
Linus Torvalds's avatar
Linus Torvalds committed
562
563
564
565
566
567
568
569
570
571
572
573
574
575
	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;
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
576
577
578
579
580
581
582
583
584
585
static const struct file_operations ide_driver_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_driver_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.write		= ide_driver_proc_write,
};

static int ide_media_proc_show(struct seq_file *m, void *v)
Linus Torvalds's avatar
Linus Torvalds committed
586
{
Alexey Dobriyan's avatar
Alexey Dobriyan committed
587
	ide_drive_t	*drive = (ide_drive_t *) m->private;
Linus Torvalds's avatar
Linus Torvalds committed
588
589
590
	const char	*media;

	switch (drive->media) {
591
592
593
594
595
596
	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;
Linus Torvalds's avatar
Linus Torvalds committed
597
	}
Alexey Dobriyan's avatar
Alexey Dobriyan committed
598
599
600
601
602
603
604
	seq_puts(m, media);
	return 0;
}

static int ide_media_proc_open(struct inode *inode, struct file *file)
{
	return single_open(file, ide_media_proc_show, PDE(inode)->data);
Linus Torvalds's avatar
Linus Torvalds committed
605
606
}

Alexey Dobriyan's avatar
Alexey Dobriyan committed
607
608
609
610
611
612
613
614
static const struct file_operations ide_media_proc_fops = {
	.owner		= THIS_MODULE,
	.open		= ide_media_proc_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

Linus Torvalds's avatar
Linus Torvalds committed
615
static ide_proc_entry_t generic_drive_entries[] = {
Alexey Dobriyan's avatar
Alexey Dobriyan committed
616
617
618
619
620
621
	{ "driver",	S_IFREG|S_IRUGO,	 &ide_driver_proc_fops	},
	{ "identify",	S_IFREG|S_IRUSR,	 &ide_identify_proc_fops},
	{ "media",	S_IFREG|S_IRUGO,	 &ide_media_proc_fops	},
	{ "model",	S_IFREG|S_IRUGO,	 &ide_dmodel_proc_fops	},
	{ "settings",	S_IFREG|S_IRUSR|S_IWUSR, &ide_settings_proc_fops},
	{}
Linus Torvalds's avatar
Linus Torvalds committed
622
623
};

624
static void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
Linus Torvalds's avatar
Linus Torvalds committed
625
626
627
628
629
630
{
	struct proc_dir_entry *ent;

	if (!dir || !p)
		return;
	while (p->name != NULL) {
Alexey Dobriyan's avatar
Alexey Dobriyan committed
631
		ent = proc_create_data(p->name, p->mode, dir, p->proc_fops, data);
Linus Torvalds's avatar
Linus Torvalds committed
632
633
634
635
636
		if (!ent) return;
		p++;
	}
}

637
static void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
Linus Torvalds's avatar
Linus Torvalds committed
638
639
640
641
642
643
644
645
646
{
	if (!dir || !p)
		return;
	while (p->name != NULL) {
		remove_proc_entry(p->name, dir);
		p++;
	}
}

647
void ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver)
648
{
649
	mutex_lock(&ide_setting_mtx);
650
	drive->settings = driver->proc_devsets(drive);
651
652
	mutex_unlock(&ide_setting_mtx);

653
	ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
654
655
656
657
658
659
660
661
662
663
664
665
}

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.
 *
666
 *	Takes ide_setting_mtx.
667
668
 */

669
void ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver)
670
{
671
	ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
672

673
	mutex_lock(&ide_setting_mtx);
674
	/*
675
676
	 * ide_setting_mtx protects both the settings list and the use
	 * of settings (we cannot take a setting out that is being used).
677
	 */
678
	drive->settings = NULL;
679
	mutex_unlock(&ide_setting_mtx);
680
681
682
}
EXPORT_SYMBOL(ide_proc_unregister_driver);

683
void ide_proc_port_register_devices(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
684
685
686
{
	struct proc_dir_entry *ent;
	struct proc_dir_entry *parent = hwif->proc;
687
	ide_drive_t *drive;
Linus Torvalds's avatar
Linus Torvalds committed
688
	char name[64];
689
	int i;
Linus Torvalds's avatar
Linus Torvalds committed
690

691
	ide_port_for_each_dev(i, drive, hwif) {
692
		if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
693
694
695
696
697
			continue;

		drive->proc = proc_mkdir(drive->name, parent);
		if (drive->proc)
			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
698
		sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
Linus Torvalds's avatar
Linus Torvalds committed
699
700
701
702
703
		ent = proc_symlink(drive->name, proc_ide_root, name);
		if (!ent) return;
	}
}

704
void ide_proc_unregister_device(ide_drive_t *drive)
Linus Torvalds's avatar
Linus Torvalds committed
705
706
707
708
{
	if (drive->proc) {
		ide_remove_proc_entries(drive->proc, generic_drive_entries);
		remove_proc_entry(drive->name, proc_ide_root);
709
		remove_proc_entry(drive->name, drive->hwif->proc);
Linus Torvalds's avatar
Linus Torvalds committed
710
711
712
713
714
		drive->proc = NULL;
	}
}

static ide_proc_entry_t hwif_entries[] = {
Alexey Dobriyan's avatar
Alexey Dobriyan committed
715
716
717
718
	{ "channel",	S_IFREG|S_IRUGO,	&ide_channel_proc_fops	},
	{ "mate",	S_IFREG|S_IRUGO,	&ide_mate_proc_fops	},
	{ "model",	S_IFREG|S_IRUGO,	&ide_imodel_proc_fops	},
	{}
Linus Torvalds's avatar
Linus Torvalds committed
719
720
};

721
void ide_proc_register_port(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
722
{
723
724
	if (!hwif->proc) {
		hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
Linus Torvalds's avatar
Linus Torvalds committed
725

726
727
728
729
		if (!hwif->proc)
			return;

		ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
Linus Torvalds's avatar
Linus Torvalds committed
730
731
732
	}
}

733
void ide_proc_unregister_port(ide_hwif_t *hwif)
Linus Torvalds's avatar
Linus Torvalds committed
734
735
736
737
738
739
740
741
{
	if (hwif->proc) {
		ide_remove_proc_entries(hwif->proc, hwif_entries);
		remove_proc_entry(hwif->name, proc_ide_root);
		hwif->proc = NULL;
	}
}

742
743
static int proc_print_driver(struct device_driver *drv, void *data)
{
744
	struct ide_driver *ide_drv = to_ide_driver(drv);
745
746
747
748
749
750
751
752
753
	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)
{
754
755
756
757
758
	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",
759
			__func__, err);
760
761
762
	return 0;
}

Linus Torvalds's avatar
Linus Torvalds committed
763
764
static int ide_drivers_open(struct inode *inode, struct file *file)
{
765
	return single_open(file, &ide_drivers_show, NULL);
Linus Torvalds's avatar
Linus Torvalds committed
766
}
767

768
static const struct file_operations ide_drivers_operations = {
769
	.owner		= THIS_MODULE,
Linus Torvalds's avatar
Linus Torvalds committed
770
771
772
	.open		= ide_drivers_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
773
	.release	= single_release,
Linus Torvalds's avatar
Linus Torvalds committed
774
775
776
777
};

void proc_ide_create(void)
{
778
779
	proc_ide_root = proc_mkdir("ide", NULL);

Linus Torvalds's avatar
Linus Torvalds committed
780
781
782
	if (!proc_ide_root)
		return;

783
	proc_create("drivers", 0, proc_ide_root, &ide_drivers_operations);
Linus Torvalds's avatar
Linus Torvalds committed
784
785
786
787
}

void proc_ide_destroy(void)
{
Zhang, Yanmin's avatar
Zhang, Yanmin committed
788
	remove_proc_entry("drivers", proc_ide_root);
Linus Torvalds's avatar
Linus Torvalds committed
789
790
	remove_proc_entry("ide", NULL);
}