xref: /openbmc/linux/drivers/scsi/scsi_proc.c (revision 9cb78c16)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/drivers/scsi/scsi_proc.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * The functions in this file provide an interface between
51da177e4SLinus Torvalds  * the PROC file system and the SCSI device drivers
61da177e4SLinus Torvalds  * It is mainly used for debugging, statistics and to pass
71da177e4SLinus Torvalds  * information directly to the lowlevel driver.
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
101da177e4SLinus Torvalds  * Version: 0.99.8   last change: 95/09/13
111da177e4SLinus Torvalds  *
121da177e4SLinus Torvalds  * generic command parser provided by:
131da177e4SLinus Torvalds  * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
141da177e4SLinus Torvalds  *
151da177e4SLinus Torvalds  * generic_proc_info() support of xxxx_info() by:
161da177e4SLinus Torvalds  * Michael A. Griffith <grif@acm.org>
171da177e4SLinus Torvalds  */
181da177e4SLinus Torvalds 
191da177e4SLinus Torvalds #include <linux/module.h>
201da177e4SLinus Torvalds #include <linux/init.h>
211da177e4SLinus Torvalds #include <linux/string.h>
221da177e4SLinus Torvalds #include <linux/mm.h>
231da177e4SLinus Torvalds #include <linux/proc_fs.h>
241da177e4SLinus Torvalds #include <linux/errno.h>
251da177e4SLinus Torvalds #include <linux/blkdev.h>
261da177e4SLinus Torvalds #include <linux/seq_file.h>
270b950672SArjan van de Ven #include <linux/mutex.h>
285a0e3ad6STejun Heo #include <linux/gfp.h>
291da177e4SLinus Torvalds #include <asm/uaccess.h>
301da177e4SLinus Torvalds 
311da177e4SLinus Torvalds #include <scsi/scsi.h>
321da177e4SLinus Torvalds #include <scsi/scsi_device.h>
331da177e4SLinus Torvalds #include <scsi/scsi_host.h>
34e02f3f59SChristoph Hellwig #include <scsi/scsi_transport.h>
351da177e4SLinus Torvalds 
361da177e4SLinus Torvalds #include "scsi_priv.h"
371da177e4SLinus Torvalds #include "scsi_logging.h"
381da177e4SLinus Torvalds 
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds /* 4K page size, but our output routines, use some slack for overruns */
411da177e4SLinus Torvalds #define PROC_BLOCK_SIZE (3*1024)
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds static struct proc_dir_entry *proc_scsi;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds /* Protect sht->present and sht->proc_dir */
460b950672SArjan van de Ven static DEFINE_MUTEX(global_host_template_mutex);
471da177e4SLinus Torvalds 
480ffddfbbSAl Viro static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
490ffddfbbSAl Viro                            size_t count, loff_t *ppos)
500ffddfbbSAl Viro {
51d9dda78bSAl Viro 	struct Scsi_Host *shost = PDE_DATA(file_inode(file));
520ffddfbbSAl Viro 	ssize_t ret = -ENOMEM;
530ffddfbbSAl Viro 	char *page;
540ffddfbbSAl Viro 
550ffddfbbSAl Viro 	if (count > PROC_BLOCK_SIZE)
560ffddfbbSAl Viro 		return -EOVERFLOW;
570ffddfbbSAl Viro 
580ffddfbbSAl Viro 	if (!shost->hostt->write_info)
590ffddfbbSAl Viro 		return -EINVAL;
600ffddfbbSAl Viro 
610ffddfbbSAl Viro 	page = (char *)__get_free_page(GFP_KERNEL);
620ffddfbbSAl Viro 	if (page) {
630ffddfbbSAl Viro 		ret = -EFAULT;
640ffddfbbSAl Viro 		if (copy_from_user(page, buf, count))
650ffddfbbSAl Viro 			goto out;
660ffddfbbSAl Viro 		ret = shost->hostt->write_info(shost, page, count);
670ffddfbbSAl Viro 	}
680ffddfbbSAl Viro out:
690ffddfbbSAl Viro 	free_page((unsigned long)page);
700ffddfbbSAl Viro 	return ret;
710ffddfbbSAl Viro }
720ffddfbbSAl Viro 
730ffddfbbSAl Viro static int proc_scsi_show(struct seq_file *m, void *v)
740ffddfbbSAl Viro {
750ffddfbbSAl Viro 	struct Scsi_Host *shost = m->private;
760ffddfbbSAl Viro 	return shost->hostt->show_info(m, shost);
770ffddfbbSAl Viro }
780ffddfbbSAl Viro 
790ffddfbbSAl Viro static int proc_scsi_host_open(struct inode *inode, struct file *file)
800ffddfbbSAl Viro {
81d9dda78bSAl Viro 	return single_open_size(file, proc_scsi_show, PDE_DATA(inode),
82859d22f9SAl Viro 				4 * PAGE_SIZE);
830ffddfbbSAl Viro }
840ffddfbbSAl Viro 
850ffddfbbSAl Viro static const struct file_operations proc_scsi_fops = {
860ffddfbbSAl Viro 	.open = proc_scsi_host_open,
87801d9d26SJan Beulich 	.release = single_release,
880ffddfbbSAl Viro 	.read = seq_read,
890ffddfbbSAl Viro 	.llseek = seq_lseek,
900ffddfbbSAl Viro 	.write = proc_scsi_host_write
910ffddfbbSAl Viro };
920ffddfbbSAl Viro 
93eb44820cSRob Landley /**
94eb44820cSRob Landley  * scsi_proc_hostdir_add - Create directory in /proc for a scsi host
95eb44820cSRob Landley  * @sht: owner of this directory
96eb44820cSRob Landley  *
97eb44820cSRob Landley  * Sets sht->proc_dir to the new directory.
98eb44820cSRob Landley  */
99eb44820cSRob Landley 
1001da177e4SLinus Torvalds void scsi_proc_hostdir_add(struct scsi_host_template *sht)
1011da177e4SLinus Torvalds {
10270ef457dSAl Viro 	if (!sht->show_info)
1031da177e4SLinus Torvalds 		return;
1041da177e4SLinus Torvalds 
1050b950672SArjan van de Ven 	mutex_lock(&global_host_template_mutex);
1061da177e4SLinus Torvalds 	if (!sht->present++) {
1071da177e4SLinus Torvalds 		sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
1081da177e4SLinus Torvalds         	if (!sht->proc_dir)
1091da177e4SLinus Torvalds 			printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
110cadbd4a5SHarvey Harrison 			       __func__, sht->proc_name);
1111da177e4SLinus Torvalds 	}
1120b950672SArjan van de Ven 	mutex_unlock(&global_host_template_mutex);
1131da177e4SLinus Torvalds }
1141da177e4SLinus Torvalds 
115eb44820cSRob Landley /**
116eb44820cSRob Landley  * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host
117eb44820cSRob Landley  * @sht: owner of directory
118eb44820cSRob Landley  */
1191da177e4SLinus Torvalds void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
1201da177e4SLinus Torvalds {
12170ef457dSAl Viro 	if (!sht->show_info)
1221da177e4SLinus Torvalds 		return;
1231da177e4SLinus Torvalds 
1240b950672SArjan van de Ven 	mutex_lock(&global_host_template_mutex);
1251da177e4SLinus Torvalds 	if (!--sht->present && sht->proc_dir) {
1261da177e4SLinus Torvalds 		remove_proc_entry(sht->proc_name, proc_scsi);
1271da177e4SLinus Torvalds 		sht->proc_dir = NULL;
1281da177e4SLinus Torvalds 	}
1290b950672SArjan van de Ven 	mutex_unlock(&global_host_template_mutex);
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
132eb44820cSRob Landley 
133eb44820cSRob Landley /**
134eb44820cSRob Landley  * scsi_proc_host_add - Add entry for this host to appropriate /proc dir
135eb44820cSRob Landley  * @shost: host to add
136eb44820cSRob Landley  */
1371da177e4SLinus Torvalds void scsi_proc_host_add(struct Scsi_Host *shost)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds 	struct scsi_host_template *sht = shost->hostt;
1401da177e4SLinus Torvalds 	struct proc_dir_entry *p;
1411da177e4SLinus Torvalds 	char name[10];
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds 	if (!sht->proc_dir)
1441da177e4SLinus Torvalds 		return;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	sprintf(name,"%d", shost->host_no);
1470ffddfbbSAl Viro 	p = proc_create_data(name, S_IRUGO | S_IWUSR,
1480ffddfbbSAl Viro 		sht->proc_dir, &proc_scsi_fops, shost);
1490ffddfbbSAl Viro 	if (!p)
1501da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Failed to register host %d in"
151cadbd4a5SHarvey Harrison 		       "%s\n", __func__, shost->host_no,
1521da177e4SLinus Torvalds 		       sht->proc_name);
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
155eb44820cSRob Landley /**
156eb44820cSRob Landley  * scsi_proc_host_rm - remove this host's entry from /proc
157eb44820cSRob Landley  * @shost: which host
158eb44820cSRob Landley  */
1591da177e4SLinus Torvalds void scsi_proc_host_rm(struct Scsi_Host *shost)
1601da177e4SLinus Torvalds {
1611da177e4SLinus Torvalds 	char name[10];
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	if (!shost->hostt->proc_dir)
1641da177e4SLinus Torvalds 		return;
1651da177e4SLinus Torvalds 
1661da177e4SLinus Torvalds 	sprintf(name,"%d", shost->host_no);
1671da177e4SLinus Torvalds 	remove_proc_entry(name, shost->hostt->proc_dir);
1681da177e4SLinus Torvalds }
169eb44820cSRob Landley /**
170eb44820cSRob Landley  * proc_print_scsidevice - return data about this host
171eb44820cSRob Landley  * @dev: A scsi device
172eb44820cSRob Landley  * @data: &struct seq_file to output to.
173eb44820cSRob Landley  *
174eb44820cSRob Landley  * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type,
175eb44820cSRob Landley  * and revision.
176eb44820cSRob Landley  */
1771da177e4SLinus Torvalds static int proc_print_scsidevice(struct device *dev, void *data)
1781da177e4SLinus Torvalds {
179b0ed4336SHannes Reinecke 	struct scsi_device *sdev;
1801da177e4SLinus Torvalds 	struct seq_file *s = data;
1811da177e4SLinus Torvalds 	int i;
1821da177e4SLinus Torvalds 
183b0ed4336SHannes Reinecke 	if (!scsi_is_sdev_device(dev))
184b0ed4336SHannes Reinecke 		goto out;
185b0ed4336SHannes Reinecke 
186b0ed4336SHannes Reinecke 	sdev = to_scsi_device(dev);
1871da177e4SLinus Torvalds 	seq_printf(s,
1889cb78c16SHannes Reinecke 		"Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n  Vendor: ",
1891da177e4SLinus Torvalds 		sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
1901da177e4SLinus Torvalds 	for (i = 0; i < 8; i++) {
1911da177e4SLinus Torvalds 		if (sdev->vendor[i] >= 0x20)
1921da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->vendor[i]);
1931da177e4SLinus Torvalds 		else
1941da177e4SLinus Torvalds 			seq_printf(s, " ");
1951da177e4SLinus Torvalds 	}
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds 	seq_printf(s, " Model: ");
1981da177e4SLinus Torvalds 	for (i = 0; i < 16; i++) {
1991da177e4SLinus Torvalds 		if (sdev->model[i] >= 0x20)
2001da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->model[i]);
2011da177e4SLinus Torvalds 		else
2021da177e4SLinus Torvalds 			seq_printf(s, " ");
2031da177e4SLinus Torvalds 	}
2041da177e4SLinus Torvalds 
2051da177e4SLinus Torvalds 	seq_printf(s, " Rev: ");
2061da177e4SLinus Torvalds 	for (i = 0; i < 4; i++) {
2071da177e4SLinus Torvalds 		if (sdev->rev[i] >= 0x20)
2081da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->rev[i]);
2091da177e4SLinus Torvalds 		else
2101da177e4SLinus Torvalds 			seq_printf(s, " ");
2111da177e4SLinus Torvalds 	}
2121da177e4SLinus Torvalds 
2131da177e4SLinus Torvalds 	seq_printf(s, "\n");
2141da177e4SLinus Torvalds 
2154ff36718SMatthew Wilcox 	seq_printf(s, "  Type:   %s ", scsi_device_type(sdev->type));
21674feb53eSAlan Stern 	seq_printf(s, "               ANSI  SCSI revision: %02x",
21774feb53eSAlan Stern 			sdev->scsi_level - (sdev->scsi_level > 1));
2181da177e4SLinus Torvalds 	if (sdev->scsi_level == 2)
2191da177e4SLinus Torvalds 		seq_printf(s, " CCS\n");
2201da177e4SLinus Torvalds 	else
2211da177e4SLinus Torvalds 		seq_printf(s, "\n");
2221da177e4SLinus Torvalds 
223b0ed4336SHannes Reinecke out:
2241da177e4SLinus Torvalds 	return 0;
2251da177e4SLinus Torvalds }
2261da177e4SLinus Torvalds 
227eb44820cSRob Landley /**
228eb44820cSRob Landley  * scsi_add_single_device - Respond to user request to probe for/add device
229eb44820cSRob Landley  * @host: user-supplied decimal integer
230eb44820cSRob Landley  * @channel: user-supplied decimal integer
231eb44820cSRob Landley  * @id: user-supplied decimal integer
232eb44820cSRob Landley  * @lun: user-supplied decimal integer
233eb44820cSRob Landley  *
234eb44820cSRob Landley  * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi.
235eb44820cSRob Landley  *
236eb44820cSRob Landley  * does scsi_host_lookup() and either user_scan() if that transport
237eb44820cSRob Landley  * type supports it, or else scsi_scan_host_selected()
238eb44820cSRob Landley  *
239eb44820cSRob Landley  * Note: this seems to be aimed exclusively at SCSI parallel busses.
240eb44820cSRob Landley  */
241eb44820cSRob Landley 
2421da177e4SLinus Torvalds static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2451da177e4SLinus Torvalds 	int error = -ENXIO;
2461da177e4SLinus Torvalds 
2471da177e4SLinus Torvalds 	shost = scsi_host_lookup(host);
248315cb0adSJames Smart 	if (!shost)
249315cb0adSJames Smart 		return error;
2501da177e4SLinus Torvalds 
251e02f3f59SChristoph Hellwig 	if (shost->transportt->user_scan)
252e02f3f59SChristoph Hellwig 		error = shost->transportt->user_scan(shost, channel, id, lun);
253e02f3f59SChristoph Hellwig 	else
2541da177e4SLinus Torvalds 		error = scsi_scan_host_selected(shost, channel, id, lun, 1);
2551da177e4SLinus Torvalds 	scsi_host_put(shost);
2561da177e4SLinus Torvalds 	return error;
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds 
259eb44820cSRob Landley /**
260eb44820cSRob Landley  * scsi_remove_single_device - Respond to user request to remove a device
261eb44820cSRob Landley  * @host: user-supplied decimal integer
262eb44820cSRob Landley  * @channel: user-supplied decimal integer
263eb44820cSRob Landley  * @id: user-supplied decimal integer
264eb44820cSRob Landley  * @lun: user-supplied decimal integer
265eb44820cSRob Landley  *
266eb44820cSRob Landley  * Description: called by writing "scsi remove-single-device" to
267eb44820cSRob Landley  * /proc/scsi/scsi.  Does a scsi_device_lookup() and scsi_remove_device()
268eb44820cSRob Landley  */
2691da177e4SLinus Torvalds static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
2701da177e4SLinus Torvalds {
2711da177e4SLinus Torvalds 	struct scsi_device *sdev;
2721da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2731da177e4SLinus Torvalds 	int error = -ENXIO;
2741da177e4SLinus Torvalds 
2751da177e4SLinus Torvalds 	shost = scsi_host_lookup(host);
276315cb0adSJames Smart 	if (!shost)
277315cb0adSJames Smart 		return error;
2781da177e4SLinus Torvalds 	sdev = scsi_device_lookup(shost, channel, id, lun);
2791da177e4SLinus Torvalds 	if (sdev) {
2801da177e4SLinus Torvalds 		scsi_remove_device(sdev);
2811da177e4SLinus Torvalds 		scsi_device_put(sdev);
2821da177e4SLinus Torvalds 		error = 0;
2831da177e4SLinus Torvalds 	}
2841da177e4SLinus Torvalds 
2851da177e4SLinus Torvalds 	scsi_host_put(shost);
2861da177e4SLinus Torvalds 	return error;
2871da177e4SLinus Torvalds }
2881da177e4SLinus Torvalds 
289eb44820cSRob Landley /**
290eb44820cSRob Landley  * proc_scsi_write - handle writes to /proc/scsi/scsi
291eb44820cSRob Landley  * @file: not used
292eb44820cSRob Landley  * @buf: buffer to write
293eb44820cSRob Landley  * @length: length of buf, at most PAGE_SIZE
294eb44820cSRob Landley  * @ppos: not used
295eb44820cSRob Landley  *
296eb44820cSRob Landley  * Description: this provides a legacy mechanism to add or remove devices by
297eb44820cSRob Landley  * Host, Channel, ID, and Lun.  To use,
298eb44820cSRob Landley  * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or
299eb44820cSRob Landley  * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with
300eb44820cSRob Landley  * "0 1 2 3" replaced by the Host, Channel, Id, and Lun.
301eb44820cSRob Landley  *
302eb44820cSRob Landley  * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB,
303eb44820cSRob Landley  * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to
304eb44820cSRob Landley  * provide a unique identifier and nothing more.
305eb44820cSRob Landley  */
306eb44820cSRob Landley 
307eb44820cSRob Landley 
3081da177e4SLinus Torvalds static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
3091da177e4SLinus Torvalds 			       size_t length, loff_t *ppos)
3101da177e4SLinus Torvalds {
3111da177e4SLinus Torvalds 	int host, channel, id, lun;
3121da177e4SLinus Torvalds 	char *buffer, *p;
3131da177e4SLinus Torvalds 	int err;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	if (!buf || length > PAGE_SIZE)
3161da177e4SLinus Torvalds 		return -EINVAL;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	buffer = (char *)__get_free_page(GFP_KERNEL);
3191da177e4SLinus Torvalds 	if (!buffer)
3201da177e4SLinus Torvalds 		return -ENOMEM;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	err = -EFAULT;
3231da177e4SLinus Torvalds 	if (copy_from_user(buffer, buf, length))
3241da177e4SLinus Torvalds 		goto out;
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	err = -EINVAL;
3271da177e4SLinus Torvalds 	if (length < PAGE_SIZE)
3281da177e4SLinus Torvalds 		buffer[length] = '\0';
3291da177e4SLinus Torvalds 	else if (buffer[PAGE_SIZE-1])
3301da177e4SLinus Torvalds 		goto out;
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	/*
3331da177e4SLinus Torvalds 	 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
3341da177e4SLinus Torvalds 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3351da177e4SLinus Torvalds 	 */
3361da177e4SLinus Torvalds 	if (!strncmp("scsi add-single-device", buffer, 22)) {
3371da177e4SLinus Torvalds 		p = buffer + 23;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 		host = simple_strtoul(p, &p, 0);
3401da177e4SLinus Torvalds 		channel = simple_strtoul(p + 1, &p, 0);
3411da177e4SLinus Torvalds 		id = simple_strtoul(p + 1, &p, 0);
3421da177e4SLinus Torvalds 		lun = simple_strtoul(p + 1, &p, 0);
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 		err = scsi_add_single_device(host, channel, id, lun);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 	/*
3471da177e4SLinus Torvalds 	 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
3481da177e4SLinus Torvalds 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3491da177e4SLinus Torvalds 	 */
3501da177e4SLinus Torvalds 	} else if (!strncmp("scsi remove-single-device", buffer, 25)) {
3511da177e4SLinus Torvalds 		p = buffer + 26;
3521da177e4SLinus Torvalds 
3531da177e4SLinus Torvalds 		host = simple_strtoul(p, &p, 0);
3541da177e4SLinus Torvalds 		channel = simple_strtoul(p + 1, &p, 0);
3551da177e4SLinus Torvalds 		id = simple_strtoul(p + 1, &p, 0);
3561da177e4SLinus Torvalds 		lun = simple_strtoul(p + 1, &p, 0);
3571da177e4SLinus Torvalds 
3581da177e4SLinus Torvalds 		err = scsi_remove_single_device(host, channel, id, lun);
3591da177e4SLinus Torvalds 	}
3601da177e4SLinus Torvalds 
3612ca48a13SJames Bottomley 	/*
3622ca48a13SJames Bottomley 	 * convert success returns so that we return the
3632ca48a13SJames Bottomley 	 * number of bytes consumed.
3642ca48a13SJames Bottomley 	 */
3652ca48a13SJames Bottomley 	if (!err)
3662ca48a13SJames Bottomley 		err = length;
3672ca48a13SJames Bottomley 
3681da177e4SLinus Torvalds  out:
3691da177e4SLinus Torvalds 	free_page((unsigned long)buffer);
3701da177e4SLinus Torvalds 	return err;
3711da177e4SLinus Torvalds }
3721da177e4SLinus Torvalds 
373e37c4913SJeff Mahoney static int always_match(struct device *dev, void *data)
3741da177e4SLinus Torvalds {
375e37c4913SJeff Mahoney 	return 1;
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds 
378e37c4913SJeff Mahoney static inline struct device *next_scsi_device(struct device *start)
379e37c4913SJeff Mahoney {
380e37c4913SJeff Mahoney 	struct device *next = bus_find_device(&scsi_bus_type, start, NULL,
381e37c4913SJeff Mahoney 					      always_match);
382e37c4913SJeff Mahoney 	put_device(start);
383e37c4913SJeff Mahoney 	return next;
384e37c4913SJeff Mahoney }
385e37c4913SJeff Mahoney 
386e37c4913SJeff Mahoney static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
387e37c4913SJeff Mahoney {
388e37c4913SJeff Mahoney 	struct device *dev = NULL;
389e37c4913SJeff Mahoney 	loff_t n = *pos;
390e37c4913SJeff Mahoney 
391e37c4913SJeff Mahoney 	while ((dev = next_scsi_device(dev))) {
392e37c4913SJeff Mahoney 		if (!n--)
393e37c4913SJeff Mahoney 			break;
394e37c4913SJeff Mahoney 		sfile->private++;
395e37c4913SJeff Mahoney 	}
396e37c4913SJeff Mahoney 	return dev;
397e37c4913SJeff Mahoney }
398e37c4913SJeff Mahoney 
399e37c4913SJeff Mahoney static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
400e37c4913SJeff Mahoney {
401e37c4913SJeff Mahoney 	(*pos)++;
402e37c4913SJeff Mahoney 	sfile->private++;
403e37c4913SJeff Mahoney 	return next_scsi_device(v);
404e37c4913SJeff Mahoney }
405e37c4913SJeff Mahoney 
406e37c4913SJeff Mahoney static void scsi_seq_stop(struct seq_file *sfile, void *v)
407e37c4913SJeff Mahoney {
408e37c4913SJeff Mahoney 	put_device(v);
409e37c4913SJeff Mahoney }
410e37c4913SJeff Mahoney 
411e37c4913SJeff Mahoney static int scsi_seq_show(struct seq_file *sfile, void *dev)
412e37c4913SJeff Mahoney {
413e37c4913SJeff Mahoney 	if (!sfile->private)
414e37c4913SJeff Mahoney 		seq_puts(sfile, "Attached devices:\n");
415e37c4913SJeff Mahoney 
416e37c4913SJeff Mahoney 	return proc_print_scsidevice(dev, sfile);
417e37c4913SJeff Mahoney }
418e37c4913SJeff Mahoney 
419e37c4913SJeff Mahoney static const struct seq_operations scsi_seq_ops = {
420e37c4913SJeff Mahoney 	.start	= scsi_seq_start,
421e37c4913SJeff Mahoney 	.next	= scsi_seq_next,
422e37c4913SJeff Mahoney 	.stop	= scsi_seq_stop,
423e37c4913SJeff Mahoney 	.show	= scsi_seq_show
424e37c4913SJeff Mahoney };
425e37c4913SJeff Mahoney 
426eb44820cSRob Landley /**
427eb44820cSRob Landley  * proc_scsi_open - glue function
428eb44820cSRob Landley  * @inode: not used
429eb44820cSRob Landley  * @file: passed to single_open()
430eb44820cSRob Landley  *
431eb44820cSRob Landley  * Associates proc_scsi_show with this file
432eb44820cSRob Landley  */
4331da177e4SLinus Torvalds static int proc_scsi_open(struct inode *inode, struct file *file)
4341da177e4SLinus Torvalds {
4351da177e4SLinus Torvalds 	/*
436eb44820cSRob Landley 	 * We don't really need this for the write case but it doesn't
4371da177e4SLinus Torvalds 	 * harm either.
4381da177e4SLinus Torvalds 	 */
439e37c4913SJeff Mahoney 	return seq_open(file, &scsi_seq_ops);
4401da177e4SLinus Torvalds }
4411da177e4SLinus Torvalds 
44200977a59SArjan van de Ven static const struct file_operations proc_scsi_operations = {
443a973909fSDenis V. Lunev 	.owner		= THIS_MODULE,
4441da177e4SLinus Torvalds 	.open		= proc_scsi_open,
4451da177e4SLinus Torvalds 	.read		= seq_read,
4461da177e4SLinus Torvalds 	.write		= proc_scsi_write,
4471da177e4SLinus Torvalds 	.llseek		= seq_lseek,
448e37c4913SJeff Mahoney 	.release	= seq_release,
4491da177e4SLinus Torvalds };
4501da177e4SLinus Torvalds 
451eb44820cSRob Landley /**
452eb44820cSRob Landley  * scsi_init_procfs - create scsi and scsi/scsi in procfs
453eb44820cSRob Landley  */
4541da177e4SLinus Torvalds int __init scsi_init_procfs(void)
4551da177e4SLinus Torvalds {
4561da177e4SLinus Torvalds 	struct proc_dir_entry *pde;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 	proc_scsi = proc_mkdir("scsi", NULL);
4591da177e4SLinus Torvalds 	if (!proc_scsi)
4601da177e4SLinus Torvalds 		goto err1;
4611da177e4SLinus Torvalds 
462a973909fSDenis V. Lunev 	pde = proc_create("scsi/scsi", 0, NULL, &proc_scsi_operations);
4631da177e4SLinus Torvalds 	if (!pde)
4641da177e4SLinus Torvalds 		goto err2;
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	return 0;
4671da177e4SLinus Torvalds 
4681da177e4SLinus Torvalds err2:
4691da177e4SLinus Torvalds 	remove_proc_entry("scsi", NULL);
4701da177e4SLinus Torvalds err1:
4711da177e4SLinus Torvalds 	return -ENOMEM;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds 
474eb44820cSRob Landley /**
475eb44820cSRob Landley  * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
476eb44820cSRob Landley  */
4771da177e4SLinus Torvalds void scsi_exit_procfs(void)
4781da177e4SLinus Torvalds {
4791da177e4SLinus Torvalds 	remove_proc_entry("scsi/scsi", NULL);
4801da177e4SLinus Torvalds 	remove_proc_entry("scsi", NULL);
4811da177e4SLinus Torvalds }
482