xref: /kernel/linux/linux-5.10/drivers/ide/ide-proc.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 1997-1998	Mark Lord
48c2ecf20Sopenharmony_ci *  Copyright (C) 2003		Red Hat
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Some code was moved here from ide.c, see it for original copyrights.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * This is the /proc/ide/ filesystem implementation.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Drive/Driver settings can be retrieved by reading the drive's
138c2ecf20Sopenharmony_ci * "settings" files.  e.g.    "cat /proc/ide0/hda/settings"
148c2ecf20Sopenharmony_ci * To write a new value "val" into a specific setting "name", use:
158c2ecf20Sopenharmony_ci *   echo "name:val" >/proc/ide/ide0/hda/settings
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
218c2ecf20Sopenharmony_ci#include <linux/errno.h>
228c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
238c2ecf20Sopenharmony_ci#include <linux/stat.h>
248c2ecf20Sopenharmony_ci#include <linux/mm.h>
258c2ecf20Sopenharmony_ci#include <linux/pci.h>
268c2ecf20Sopenharmony_ci#include <linux/ctype.h>
278c2ecf20Sopenharmony_ci#include <linux/ide.h>
288c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
298c2ecf20Sopenharmony_ci#include <linux/slab.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include <asm/io.h>
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_ide_root;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int ide_imodel_proc_show(struct seq_file *m, void *v)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;
388c2ecf20Sopenharmony_ci	const char	*name;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	switch (hwif->chipset) {
418c2ecf20Sopenharmony_ci	case ide_generic:	name = "generic";	break;
428c2ecf20Sopenharmony_ci	case ide_pci:		name = "pci";		break;
438c2ecf20Sopenharmony_ci	case ide_cmd640:	name = "cmd640";	break;
448c2ecf20Sopenharmony_ci	case ide_dtc2278:	name = "dtc2278";	break;
458c2ecf20Sopenharmony_ci	case ide_ali14xx:	name = "ali14xx";	break;
468c2ecf20Sopenharmony_ci	case ide_qd65xx:	name = "qd65xx";	break;
478c2ecf20Sopenharmony_ci	case ide_umc8672:	name = "umc8672";	break;
488c2ecf20Sopenharmony_ci	case ide_ht6560b:	name = "ht6560b";	break;
498c2ecf20Sopenharmony_ci	case ide_4drives:	name = "4drives";	break;
508c2ecf20Sopenharmony_ci	case ide_pmac:		name = "mac-io";	break;
518c2ecf20Sopenharmony_ci	case ide_au1xxx:	name = "au1xxx";	break;
528c2ecf20Sopenharmony_ci	case ide_palm3710:      name = "palm3710";      break;
538c2ecf20Sopenharmony_ci	case ide_acorn:		name = "acorn";		break;
548c2ecf20Sopenharmony_ci	default:		name = "(unknown)";	break;
558c2ecf20Sopenharmony_ci	}
568c2ecf20Sopenharmony_ci	seq_printf(m, "%s\n", name);
578c2ecf20Sopenharmony_ci	return 0;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int ide_mate_proc_show(struct seq_file *m, void *v)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (hwif && hwif->mate)
658c2ecf20Sopenharmony_ci		seq_printf(m, "%s\n", hwif->mate->name);
668c2ecf20Sopenharmony_ci	else
678c2ecf20Sopenharmony_ci		seq_printf(m, "(none)\n");
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int ide_channel_proc_show(struct seq_file *m, void *v)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	ide_hwif_t	*hwif = (ide_hwif_t *) m->private;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	seq_printf(m, "%c\n", hwif->channel ? '1' : '0');
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int ide_identify_proc_show(struct seq_file *m, void *v)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	ide_drive_t *drive = (ide_drive_t *)m->private;
828c2ecf20Sopenharmony_ci	u8 *buf;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (!drive) {
858c2ecf20Sopenharmony_ci		seq_putc(m, '\n');
868c2ecf20Sopenharmony_ci		return 0;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	buf = kmalloc(SECTOR_SIZE, GFP_KERNEL);
908c2ecf20Sopenharmony_ci	if (!buf)
918c2ecf20Sopenharmony_ci		return -ENOMEM;
928c2ecf20Sopenharmony_ci	if (taskfile_lib_get_identify(drive, buf) == 0) {
938c2ecf20Sopenharmony_ci		__le16 *val = (__le16 *)buf;
948c2ecf20Sopenharmony_ci		int i;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci		for (i = 0; i < SECTOR_SIZE / 2; i++) {
978c2ecf20Sopenharmony_ci			seq_printf(m, "%04x%c", le16_to_cpu(val[i]),
988c2ecf20Sopenharmony_ci					(i % 8) == 7 ? '\n' : ' ');
998c2ecf20Sopenharmony_ci		}
1008c2ecf20Sopenharmony_ci	} else
1018c2ecf20Sopenharmony_ci		seq_putc(m, buf[0]);
1028c2ecf20Sopenharmony_ci	kfree(buf);
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/**
1078c2ecf20Sopenharmony_ci *	ide_find_setting	-	find a specific setting
1088c2ecf20Sopenharmony_ci *	@st: setting table pointer
1098c2ecf20Sopenharmony_ci *	@name: setting name
1108c2ecf20Sopenharmony_ci *
1118c2ecf20Sopenharmony_ci *	Scan's the setting table for a matching entry and returns
1128c2ecf20Sopenharmony_ci *	this or NULL if no entry is found. The caller must hold the
1138c2ecf20Sopenharmony_ci *	setting semaphore
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic
1178c2ecf20Sopenharmony_ciconst struct ide_proc_devset *ide_find_setting(const struct ide_proc_devset *st,
1188c2ecf20Sopenharmony_ci					       char *name)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	while (st->name) {
1218c2ecf20Sopenharmony_ci		if (strcmp(st->name, name) == 0)
1228c2ecf20Sopenharmony_ci			break;
1238c2ecf20Sopenharmony_ci		st++;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci	return st->name ? st : NULL;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci/**
1298c2ecf20Sopenharmony_ci *	ide_read_setting	-	read an IDE setting
1308c2ecf20Sopenharmony_ci *	@drive: drive to read from
1318c2ecf20Sopenharmony_ci *	@setting: drive setting
1328c2ecf20Sopenharmony_ci *
1338c2ecf20Sopenharmony_ci *	Read a drive setting and return the value. The caller
1348c2ecf20Sopenharmony_ci *	must hold the ide_setting_mtx when making this call.
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci *	BUGS: the data return and error are the same return value
1378c2ecf20Sopenharmony_ci *	so an error -EINVAL and true return of the same value cannot
1388c2ecf20Sopenharmony_ci *	be told apart
1398c2ecf20Sopenharmony_ci */
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic int ide_read_setting(ide_drive_t *drive,
1428c2ecf20Sopenharmony_ci			    const struct ide_proc_devset *setting)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	const struct ide_devset *ds = setting->setting;
1458c2ecf20Sopenharmony_ci	int val = -EINVAL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (ds->get)
1488c2ecf20Sopenharmony_ci		val = ds->get(drive);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return val;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/**
1548c2ecf20Sopenharmony_ci *	ide_write_setting	-	read an IDE setting
1558c2ecf20Sopenharmony_ci *	@drive: drive to read from
1568c2ecf20Sopenharmony_ci *	@setting: drive setting
1578c2ecf20Sopenharmony_ci *	@val: value
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci *	Write a drive setting if it is possible. The caller
1608c2ecf20Sopenharmony_ci *	must hold the ide_setting_mtx when making this call.
1618c2ecf20Sopenharmony_ci *
1628c2ecf20Sopenharmony_ci *	BUGS: the data return and error are the same return value
1638c2ecf20Sopenharmony_ci *	so an error -EINVAL and true return of the same value cannot
1648c2ecf20Sopenharmony_ci *	be told apart
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci *	FIXME:  This should be changed to enqueue a special request
1678c2ecf20Sopenharmony_ci *	to the driver to change settings, and then wait on a sema for completion.
1688c2ecf20Sopenharmony_ci *	The current scheme of polling is kludgy, though safe enough.
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int ide_write_setting(ide_drive_t *drive,
1728c2ecf20Sopenharmony_ci			     const struct ide_proc_devset *setting, int val)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	const struct ide_devset *ds = setting->setting;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
1778c2ecf20Sopenharmony_ci		return -EACCES;
1788c2ecf20Sopenharmony_ci	if (!ds->set)
1798c2ecf20Sopenharmony_ci		return -EPERM;
1808c2ecf20Sopenharmony_ci	if ((ds->flags & DS_SYNC)
1818c2ecf20Sopenharmony_ci	    && (val < setting->min || val > setting->max))
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci	return ide_devset_execute(drive, ds, val);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ciide_devset_get(xfer_rate, current_speed);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int set_xfer_rate (ide_drive_t *drive, int arg)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct ide_cmd cmd;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	if (arg < XFER_PIO_0 || arg > XFER_UDMA_6)
1938c2ecf20Sopenharmony_ci		return -EINVAL;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	memset(&cmd, 0, sizeof(cmd));
1968c2ecf20Sopenharmony_ci	cmd.tf.command = ATA_CMD_SET_FEATURES;
1978c2ecf20Sopenharmony_ci	cmd.tf.feature = SETFEATURES_XFER;
1988c2ecf20Sopenharmony_ci	cmd.tf.nsect   = (u8)arg;
1998c2ecf20Sopenharmony_ci	cmd.valid.out.tf = IDE_VALID_FEATURE | IDE_VALID_NSECT;
2008c2ecf20Sopenharmony_ci	cmd.valid.in.tf  = IDE_VALID_NSECT;
2018c2ecf20Sopenharmony_ci	cmd.tf_flags   = IDE_TFLAG_SET_XFER;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return ide_no_data_taskfile(drive, &cmd);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ciide_devset_rw(current_speed, xfer_rate);
2078c2ecf20Sopenharmony_ciide_devset_rw_field(init_speed, init_speed);
2088c2ecf20Sopenharmony_ciide_devset_rw_flag(nice1, IDE_DFLAG_NICE1);
2098c2ecf20Sopenharmony_ciide_devset_ro_field(number, dn);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic const struct ide_proc_devset ide_generic_settings[] = {
2128c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(current_speed, 0, 70),
2138c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(init_speed, 0, 70),
2148c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(io_32bit,  0, 1 + (SUPPORT_VLB_SYNC << 1)),
2158c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(keepsettings, 0, 1),
2168c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(nice1, 0, 1),
2178c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(number, 0, 3),
2188c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(pio_mode, 0, 255),
2198c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(unmaskirq, 0, 1),
2208c2ecf20Sopenharmony_ci	IDE_PROC_DEVSET(using_dma, 0, 1),
2218c2ecf20Sopenharmony_ci	{ NULL },
2228c2ecf20Sopenharmony_ci};
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic void proc_ide_settings_warn(void)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	printk_once(KERN_WARNING "Warning: /proc/ide/hd?/settings interface is "
2278c2ecf20Sopenharmony_ci			    "obsolete, and will be removed soon!\n");
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int ide_settings_proc_show(struct seq_file *m, void *v)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	const struct ide_proc_devset *setting, *g, *d;
2338c2ecf20Sopenharmony_ci	const struct ide_devset *ds;
2348c2ecf20Sopenharmony_ci	ide_drive_t	*drive = (ide_drive_t *) m->private;
2358c2ecf20Sopenharmony_ci	int		rc, mul_factor, div_factor;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	proc_ide_settings_warn();
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	mutex_lock(&ide_setting_mtx);
2408c2ecf20Sopenharmony_ci	g = ide_generic_settings;
2418c2ecf20Sopenharmony_ci	d = drive->settings;
2428c2ecf20Sopenharmony_ci	seq_printf(m, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n");
2438c2ecf20Sopenharmony_ci	seq_printf(m, "----\t\t\t-----\t\t---\t\t---\t\t----\n");
2448c2ecf20Sopenharmony_ci	while (g->name || (d && d->name)) {
2458c2ecf20Sopenharmony_ci		/* read settings in the alphabetical order */
2468c2ecf20Sopenharmony_ci		if (g->name && d && d->name) {
2478c2ecf20Sopenharmony_ci			if (strcmp(d->name, g->name) < 0)
2488c2ecf20Sopenharmony_ci				setting = d++;
2498c2ecf20Sopenharmony_ci			else
2508c2ecf20Sopenharmony_ci				setting = g++;
2518c2ecf20Sopenharmony_ci		} else if (d && d->name) {
2528c2ecf20Sopenharmony_ci			setting = d++;
2538c2ecf20Sopenharmony_ci		} else
2548c2ecf20Sopenharmony_ci			setting = g++;
2558c2ecf20Sopenharmony_ci		mul_factor = setting->mulf ? setting->mulf(drive) : 1;
2568c2ecf20Sopenharmony_ci		div_factor = setting->divf ? setting->divf(drive) : 1;
2578c2ecf20Sopenharmony_ci		seq_printf(m, "%-24s", setting->name);
2588c2ecf20Sopenharmony_ci		rc = ide_read_setting(drive, setting);
2598c2ecf20Sopenharmony_ci		if (rc >= 0)
2608c2ecf20Sopenharmony_ci			seq_printf(m, "%-16d", rc * mul_factor / div_factor);
2618c2ecf20Sopenharmony_ci		else
2628c2ecf20Sopenharmony_ci			seq_printf(m, "%-16s", "write-only");
2638c2ecf20Sopenharmony_ci		seq_printf(m, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor);
2648c2ecf20Sopenharmony_ci		ds = setting->setting;
2658c2ecf20Sopenharmony_ci		if (ds->get)
2668c2ecf20Sopenharmony_ci			seq_printf(m, "r");
2678c2ecf20Sopenharmony_ci		if (ds->set)
2688c2ecf20Sopenharmony_ci			seq_printf(m, "w");
2698c2ecf20Sopenharmony_ci		seq_printf(m, "\n");
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	mutex_unlock(&ide_setting_mtx);
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic int ide_settings_proc_open(struct inode *inode, struct file *file)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	return single_open(file, ide_settings_proc_show, PDE_DATA(inode));
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci#define MAX_LEN	30
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic ssize_t ide_settings_proc_write(struct file *file, const char __user *buffer,
2838c2ecf20Sopenharmony_ci				       size_t count, loff_t *pos)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	ide_drive_t	*drive = PDE_DATA(file_inode(file));
2868c2ecf20Sopenharmony_ci	char		name[MAX_LEN + 1];
2878c2ecf20Sopenharmony_ci	int		for_real = 0, mul_factor, div_factor;
2888c2ecf20Sopenharmony_ci	unsigned long	n;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	const struct ide_proc_devset *setting;
2918c2ecf20Sopenharmony_ci	char *buf, *s;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
2948c2ecf20Sopenharmony_ci		return -EACCES;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	proc_ide_settings_warn();
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	if (count >= PAGE_SIZE)
2998c2ecf20Sopenharmony_ci		return -EINVAL;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	s = buf = (char *)__get_free_page(GFP_USER);
3028c2ecf20Sopenharmony_ci	if (!buf)
3038c2ecf20Sopenharmony_ci		return -ENOMEM;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (copy_from_user(buf, buffer, count)) {
3068c2ecf20Sopenharmony_ci		free_page((unsigned long)buf);
3078c2ecf20Sopenharmony_ci		return -EFAULT;
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	buf[count] = '\0';
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/*
3138c2ecf20Sopenharmony_ci	 * Skip over leading whitespace
3148c2ecf20Sopenharmony_ci	 */
3158c2ecf20Sopenharmony_ci	while (count && isspace(*s)) {
3168c2ecf20Sopenharmony_ci		--count;
3178c2ecf20Sopenharmony_ci		++s;
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	/*
3208c2ecf20Sopenharmony_ci	 * Do one full pass to verify all parameters,
3218c2ecf20Sopenharmony_ci	 * then do another to actually write the new settings.
3228c2ecf20Sopenharmony_ci	 */
3238c2ecf20Sopenharmony_ci	do {
3248c2ecf20Sopenharmony_ci		char *p = s;
3258c2ecf20Sopenharmony_ci		n = count;
3268c2ecf20Sopenharmony_ci		while (n > 0) {
3278c2ecf20Sopenharmony_ci			unsigned val;
3288c2ecf20Sopenharmony_ci			char *q = p;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci			while (n > 0 && *p != ':') {
3318c2ecf20Sopenharmony_ci				--n;
3328c2ecf20Sopenharmony_ci				p++;
3338c2ecf20Sopenharmony_ci			}
3348c2ecf20Sopenharmony_ci			if (*p != ':')
3358c2ecf20Sopenharmony_ci				goto parse_error;
3368c2ecf20Sopenharmony_ci			if (p - q > MAX_LEN)
3378c2ecf20Sopenharmony_ci				goto parse_error;
3388c2ecf20Sopenharmony_ci			memcpy(name, q, p - q);
3398c2ecf20Sopenharmony_ci			name[p - q] = 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci			if (n > 0) {
3428c2ecf20Sopenharmony_ci				--n;
3438c2ecf20Sopenharmony_ci				p++;
3448c2ecf20Sopenharmony_ci			} else
3458c2ecf20Sopenharmony_ci				goto parse_error;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci			val = simple_strtoul(p, &q, 10);
3488c2ecf20Sopenharmony_ci			n -= q - p;
3498c2ecf20Sopenharmony_ci			p = q;
3508c2ecf20Sopenharmony_ci			if (n > 0 && !isspace(*p))
3518c2ecf20Sopenharmony_ci				goto parse_error;
3528c2ecf20Sopenharmony_ci			while (n > 0 && isspace(*p)) {
3538c2ecf20Sopenharmony_ci				--n;
3548c2ecf20Sopenharmony_ci				++p;
3558c2ecf20Sopenharmony_ci			}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci			mutex_lock(&ide_setting_mtx);
3588c2ecf20Sopenharmony_ci			/* generic settings first, then driver specific ones */
3598c2ecf20Sopenharmony_ci			setting = ide_find_setting(ide_generic_settings, name);
3608c2ecf20Sopenharmony_ci			if (!setting) {
3618c2ecf20Sopenharmony_ci				if (drive->settings)
3628c2ecf20Sopenharmony_ci					setting = ide_find_setting(drive->settings, name);
3638c2ecf20Sopenharmony_ci				if (!setting) {
3648c2ecf20Sopenharmony_ci					mutex_unlock(&ide_setting_mtx);
3658c2ecf20Sopenharmony_ci					goto parse_error;
3668c2ecf20Sopenharmony_ci				}
3678c2ecf20Sopenharmony_ci			}
3688c2ecf20Sopenharmony_ci			if (for_real) {
3698c2ecf20Sopenharmony_ci				mul_factor = setting->mulf ? setting->mulf(drive) : 1;
3708c2ecf20Sopenharmony_ci				div_factor = setting->divf ? setting->divf(drive) : 1;
3718c2ecf20Sopenharmony_ci				ide_write_setting(drive, setting, val * div_factor / mul_factor);
3728c2ecf20Sopenharmony_ci			}
3738c2ecf20Sopenharmony_ci			mutex_unlock(&ide_setting_mtx);
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci	} while (!for_real++);
3768c2ecf20Sopenharmony_ci	free_page((unsigned long)buf);
3778c2ecf20Sopenharmony_ci	return count;
3788c2ecf20Sopenharmony_ciparse_error:
3798c2ecf20Sopenharmony_ci	free_page((unsigned long)buf);
3808c2ecf20Sopenharmony_ci	printk("%s(): parse error\n", __func__);
3818c2ecf20Sopenharmony_ci	return -EINVAL;
3828c2ecf20Sopenharmony_ci}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_cistatic const struct proc_ops ide_settings_proc_ops = {
3858c2ecf20Sopenharmony_ci	.proc_open	= ide_settings_proc_open,
3868c2ecf20Sopenharmony_ci	.proc_read	= seq_read,
3878c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
3888c2ecf20Sopenharmony_ci	.proc_release	= single_release,
3898c2ecf20Sopenharmony_ci	.proc_write	= ide_settings_proc_write,
3908c2ecf20Sopenharmony_ci};
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ciint ide_capacity_proc_show(struct seq_file *m, void *v)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	seq_printf(m, "%llu\n", (long long)0x7fffffff);
3958c2ecf20Sopenharmony_ci	return 0;
3968c2ecf20Sopenharmony_ci}
3978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ide_capacity_proc_show);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ciint ide_geometry_proc_show(struct seq_file *m, void *v)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	ide_drive_t	*drive = (ide_drive_t *) m->private;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	seq_printf(m, "physical     %d/%d/%d\n",
4048c2ecf20Sopenharmony_ci			drive->cyl, drive->head, drive->sect);
4058c2ecf20Sopenharmony_ci	seq_printf(m, "logical      %d/%d/%d\n",
4068c2ecf20Sopenharmony_ci			drive->bios_cyl, drive->bios_head, drive->bios_sect);
4078c2ecf20Sopenharmony_ci	return 0;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_geometry_proc_show);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int ide_dmodel_proc_show(struct seq_file *seq, void *v)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	ide_drive_t	*drive = (ide_drive_t *) seq->private;
4148c2ecf20Sopenharmony_ci	char		*m = (char *)&drive->id[ATA_ID_PROD];
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	seq_printf(seq, "%.40s\n", m[0] ? m : "(none)");
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic int ide_driver_proc_show(struct seq_file *m, void *v)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	ide_drive_t		*drive = (ide_drive_t *)m->private;
4238c2ecf20Sopenharmony_ci	struct device		*dev = &drive->gendev;
4248c2ecf20Sopenharmony_ci	struct ide_driver	*ide_drv;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (dev->driver) {
4278c2ecf20Sopenharmony_ci		ide_drv = to_ide_driver(dev->driver);
4288c2ecf20Sopenharmony_ci		seq_printf(m, "%s version %s\n",
4298c2ecf20Sopenharmony_ci				dev->driver->name, ide_drv->version);
4308c2ecf20Sopenharmony_ci	} else
4318c2ecf20Sopenharmony_ci		seq_printf(m, "ide-default version 0.9.newide\n");
4328c2ecf20Sopenharmony_ci	return 0;
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic int ide_media_proc_show(struct seq_file *m, void *v)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	ide_drive_t	*drive = (ide_drive_t *) m->private;
4388c2ecf20Sopenharmony_ci	const char	*media;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	switch (drive->media) {
4418c2ecf20Sopenharmony_ci	case ide_disk:		media = "disk\n";	break;
4428c2ecf20Sopenharmony_ci	case ide_cdrom:		media = "cdrom\n";	break;
4438c2ecf20Sopenharmony_ci	case ide_tape:		media = "tape\n";	break;
4448c2ecf20Sopenharmony_ci	case ide_floppy:	media = "floppy\n";	break;
4458c2ecf20Sopenharmony_ci	case ide_optical:	media = "optical\n";	break;
4468c2ecf20Sopenharmony_ci	default:		media = "UNKNOWN\n";	break;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	seq_puts(m, media);
4498c2ecf20Sopenharmony_ci	return 0;
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_cistatic int ide_media_proc_open(struct inode *inode, struct file *file)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	return single_open(file, ide_media_proc_show, PDE_DATA(inode));
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic const struct file_operations ide_media_proc_fops = {
4588c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
4598c2ecf20Sopenharmony_ci	.open		= ide_media_proc_open,
4608c2ecf20Sopenharmony_ci	.read		= seq_read,
4618c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
4628c2ecf20Sopenharmony_ci	.release	= single_release,
4638c2ecf20Sopenharmony_ci};
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic ide_proc_entry_t generic_drive_entries[] = {
4668c2ecf20Sopenharmony_ci	{ "driver",	S_IFREG|S_IRUGO,	 ide_driver_proc_show	},
4678c2ecf20Sopenharmony_ci	{ "identify",	S_IFREG|S_IRUSR,	 ide_identify_proc_show	},
4688c2ecf20Sopenharmony_ci	{ "media",	S_IFREG|S_IRUGO,	 ide_media_proc_show	},
4698c2ecf20Sopenharmony_ci	{ "model",	S_IFREG|S_IRUGO,	 ide_dmodel_proc_show	},
4708c2ecf20Sopenharmony_ci	{}
4718c2ecf20Sopenharmony_ci};
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_cistatic void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (!dir || !p)
4788c2ecf20Sopenharmony_ci		return;
4798c2ecf20Sopenharmony_ci	while (p->name != NULL) {
4808c2ecf20Sopenharmony_ci		ent = proc_create_single_data(p->name, p->mode, dir, p->show, data);
4818c2ecf20Sopenharmony_ci		if (!ent) return;
4828c2ecf20Sopenharmony_ci		p++;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_cistatic void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	if (!dir || !p)
4898c2ecf20Sopenharmony_ci		return;
4908c2ecf20Sopenharmony_ci	while (p->name != NULL) {
4918c2ecf20Sopenharmony_ci		remove_proc_entry(p->name, dir);
4928c2ecf20Sopenharmony_ci		p++;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_civoid ide_proc_register_driver(ide_drive_t *drive, struct ide_driver *driver)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	mutex_lock(&ide_setting_mtx);
4998c2ecf20Sopenharmony_ci	drive->settings = driver->proc_devsets(drive);
5008c2ecf20Sopenharmony_ci	mutex_unlock(&ide_setting_mtx);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	ide_add_proc_entries(drive->proc, driver->proc_entries(drive), drive);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_proc_register_driver);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/**
5088c2ecf20Sopenharmony_ci *	ide_proc_unregister_driver	-	remove driver specific data
5098c2ecf20Sopenharmony_ci *	@drive: drive
5108c2ecf20Sopenharmony_ci *	@driver: driver
5118c2ecf20Sopenharmony_ci *
5128c2ecf20Sopenharmony_ci *	Clean up the driver specific /proc files and IDE settings
5138c2ecf20Sopenharmony_ci *	for a given drive.
5148c2ecf20Sopenharmony_ci *
5158c2ecf20Sopenharmony_ci *	Takes ide_setting_mtx.
5168c2ecf20Sopenharmony_ci */
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_civoid ide_proc_unregister_driver(ide_drive_t *drive, struct ide_driver *driver)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	ide_remove_proc_entries(drive->proc, driver->proc_entries(drive));
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	mutex_lock(&ide_setting_mtx);
5238c2ecf20Sopenharmony_ci	/*
5248c2ecf20Sopenharmony_ci	 * ide_setting_mtx protects both the settings list and the use
5258c2ecf20Sopenharmony_ci	 * of settings (we cannot take a setting out that is being used).
5268c2ecf20Sopenharmony_ci	 */
5278c2ecf20Sopenharmony_ci	drive->settings = NULL;
5288c2ecf20Sopenharmony_ci	mutex_unlock(&ide_setting_mtx);
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ide_proc_unregister_driver);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_civoid ide_proc_port_register_devices(ide_hwif_t *hwif)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct proc_dir_entry *ent;
5358c2ecf20Sopenharmony_ci	struct proc_dir_entry *parent = hwif->proc;
5368c2ecf20Sopenharmony_ci	ide_drive_t *drive;
5378c2ecf20Sopenharmony_ci	char name[64];
5388c2ecf20Sopenharmony_ci	int i;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	ide_port_for_each_dev(i, drive, hwif) {
5418c2ecf20Sopenharmony_ci		if ((drive->dev_flags & IDE_DFLAG_PRESENT) == 0)
5428c2ecf20Sopenharmony_ci			continue;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci		drive->proc = proc_mkdir(drive->name, parent);
5458c2ecf20Sopenharmony_ci		if (drive->proc) {
5468c2ecf20Sopenharmony_ci			ide_add_proc_entries(drive->proc, generic_drive_entries, drive);
5478c2ecf20Sopenharmony_ci			proc_create_data("settings", S_IFREG|S_IRUSR|S_IWUSR,
5488c2ecf20Sopenharmony_ci					drive->proc, &ide_settings_proc_ops,
5498c2ecf20Sopenharmony_ci					drive);
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci		sprintf(name, "ide%d/%s", (drive->name[2]-'a')/2, drive->name);
5528c2ecf20Sopenharmony_ci		ent = proc_symlink(drive->name, proc_ide_root, name);
5538c2ecf20Sopenharmony_ci		if (!ent) return;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci}
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_civoid ide_proc_unregister_device(ide_drive_t *drive)
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	if (drive->proc) {
5608c2ecf20Sopenharmony_ci		remove_proc_entry("settings", drive->proc);
5618c2ecf20Sopenharmony_ci		ide_remove_proc_entries(drive->proc, generic_drive_entries);
5628c2ecf20Sopenharmony_ci		remove_proc_entry(drive->name, proc_ide_root);
5638c2ecf20Sopenharmony_ci		remove_proc_entry(drive->name, drive->hwif->proc);
5648c2ecf20Sopenharmony_ci		drive->proc = NULL;
5658c2ecf20Sopenharmony_ci	}
5668c2ecf20Sopenharmony_ci}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_cistatic ide_proc_entry_t hwif_entries[] = {
5698c2ecf20Sopenharmony_ci	{ "channel",	S_IFREG|S_IRUGO,	ide_channel_proc_show	},
5708c2ecf20Sopenharmony_ci	{ "mate",	S_IFREG|S_IRUGO,	ide_mate_proc_show	},
5718c2ecf20Sopenharmony_ci	{ "model",	S_IFREG|S_IRUGO,	ide_imodel_proc_show	},
5728c2ecf20Sopenharmony_ci	{}
5738c2ecf20Sopenharmony_ci};
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_civoid ide_proc_register_port(ide_hwif_t *hwif)
5768c2ecf20Sopenharmony_ci{
5778c2ecf20Sopenharmony_ci	if (!hwif->proc) {
5788c2ecf20Sopenharmony_ci		hwif->proc = proc_mkdir(hwif->name, proc_ide_root);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		if (!hwif->proc)
5818c2ecf20Sopenharmony_ci			return;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		ide_add_proc_entries(hwif->proc, hwif_entries, hwif);
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_civoid ide_proc_unregister_port(ide_hwif_t *hwif)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	if (hwif->proc) {
5908c2ecf20Sopenharmony_ci		ide_remove_proc_entries(hwif->proc, hwif_entries);
5918c2ecf20Sopenharmony_ci		remove_proc_entry(hwif->name, proc_ide_root);
5928c2ecf20Sopenharmony_ci		hwif->proc = NULL;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic int proc_print_driver(struct device_driver *drv, void *data)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct ide_driver *ide_drv = to_ide_driver(drv);
5998c2ecf20Sopenharmony_ci	struct seq_file *s = data;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	seq_printf(s, "%s version %s\n", drv->name, ide_drv->version);
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	return 0;
6048c2ecf20Sopenharmony_ci}
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_cistatic int ide_drivers_show(struct seq_file *s, void *p)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	int err;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
6118c2ecf20Sopenharmony_ci	if (err < 0)
6128c2ecf20Sopenharmony_ci		printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
6138c2ecf20Sopenharmony_ci			__func__, err);
6148c2ecf20Sopenharmony_ci	return 0;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ciDEFINE_PROC_SHOW_ATTRIBUTE(ide_drivers);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_civoid proc_ide_create(void)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	proc_ide_root = proc_mkdir("ide", NULL);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (!proc_ide_root)
6248c2ecf20Sopenharmony_ci		return;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	proc_create("drivers", 0, proc_ide_root, &ide_drivers_proc_ops);
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_civoid proc_ide_destroy(void)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	remove_proc_entry("drivers", proc_ide_root);
6328c2ecf20Sopenharmony_ci	remove_proc_entry("ide", NULL);
6338c2ecf20Sopenharmony_ci}
634