xref: /openbmc/linux/drivers/scsi/scsi_proc.c (revision d9dda78b)
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,
870ffddfbbSAl Viro 	.read = seq_read,
880ffddfbbSAl Viro 	.llseek = seq_lseek,
890ffddfbbSAl Viro 	.write = proc_scsi_host_write
900ffddfbbSAl Viro };
910ffddfbbSAl Viro 
92eb44820cSRob Landley /**
93eb44820cSRob Landley  * scsi_proc_hostdir_add - Create directory in /proc for a scsi host
94eb44820cSRob Landley  * @sht: owner of this directory
95eb44820cSRob Landley  *
96eb44820cSRob Landley  * Sets sht->proc_dir to the new directory.
97eb44820cSRob Landley  */
98eb44820cSRob Landley 
991da177e4SLinus Torvalds void scsi_proc_hostdir_add(struct scsi_host_template *sht)
1001da177e4SLinus Torvalds {
10170ef457dSAl Viro 	if (!sht->show_info)
1021da177e4SLinus Torvalds 		return;
1031da177e4SLinus Torvalds 
1040b950672SArjan van de Ven 	mutex_lock(&global_host_template_mutex);
1051da177e4SLinus Torvalds 	if (!sht->present++) {
1061da177e4SLinus Torvalds 		sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
1071da177e4SLinus Torvalds         	if (!sht->proc_dir)
1081da177e4SLinus Torvalds 			printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
109cadbd4a5SHarvey Harrison 			       __func__, sht->proc_name);
1101da177e4SLinus Torvalds 	}
1110b950672SArjan van de Ven 	mutex_unlock(&global_host_template_mutex);
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds 
114eb44820cSRob Landley /**
115eb44820cSRob Landley  * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host
116eb44820cSRob Landley  * @sht: owner of directory
117eb44820cSRob Landley  */
1181da177e4SLinus Torvalds void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
1191da177e4SLinus Torvalds {
12070ef457dSAl Viro 	if (!sht->show_info)
1211da177e4SLinus Torvalds 		return;
1221da177e4SLinus Torvalds 
1230b950672SArjan van de Ven 	mutex_lock(&global_host_template_mutex);
1241da177e4SLinus Torvalds 	if (!--sht->present && sht->proc_dir) {
1251da177e4SLinus Torvalds 		remove_proc_entry(sht->proc_name, proc_scsi);
1261da177e4SLinus Torvalds 		sht->proc_dir = NULL;
1271da177e4SLinus Torvalds 	}
1280b950672SArjan van de Ven 	mutex_unlock(&global_host_template_mutex);
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
131eb44820cSRob Landley 
132eb44820cSRob Landley /**
133eb44820cSRob Landley  * scsi_proc_host_add - Add entry for this host to appropriate /proc dir
134eb44820cSRob Landley  * @shost: host to add
135eb44820cSRob Landley  */
1361da177e4SLinus Torvalds void scsi_proc_host_add(struct Scsi_Host *shost)
1371da177e4SLinus Torvalds {
1381da177e4SLinus Torvalds 	struct scsi_host_template *sht = shost->hostt;
1391da177e4SLinus Torvalds 	struct proc_dir_entry *p;
1401da177e4SLinus Torvalds 	char name[10];
1411da177e4SLinus Torvalds 
1421da177e4SLinus Torvalds 	if (!sht->proc_dir)
1431da177e4SLinus Torvalds 		return;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	sprintf(name,"%d", shost->host_no);
1460ffddfbbSAl Viro 	p = proc_create_data(name, S_IRUGO | S_IWUSR,
1470ffddfbbSAl Viro 		sht->proc_dir, &proc_scsi_fops, shost);
1480ffddfbbSAl Viro 	if (!p)
1491da177e4SLinus Torvalds 		printk(KERN_ERR "%s: Failed to register host %d in"
150cadbd4a5SHarvey Harrison 		       "%s\n", __func__, shost->host_no,
1511da177e4SLinus Torvalds 		       sht->proc_name);
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds 
154eb44820cSRob Landley /**
155eb44820cSRob Landley  * scsi_proc_host_rm - remove this host's entry from /proc
156eb44820cSRob Landley  * @shost: which host
157eb44820cSRob Landley  */
1581da177e4SLinus Torvalds void scsi_proc_host_rm(struct Scsi_Host *shost)
1591da177e4SLinus Torvalds {
1601da177e4SLinus Torvalds 	char name[10];
1611da177e4SLinus Torvalds 
1621da177e4SLinus Torvalds 	if (!shost->hostt->proc_dir)
1631da177e4SLinus Torvalds 		return;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	sprintf(name,"%d", shost->host_no);
1661da177e4SLinus Torvalds 	remove_proc_entry(name, shost->hostt->proc_dir);
1671da177e4SLinus Torvalds }
168eb44820cSRob Landley /**
169eb44820cSRob Landley  * proc_print_scsidevice - return data about this host
170eb44820cSRob Landley  * @dev: A scsi device
171eb44820cSRob Landley  * @data: &struct seq_file to output to.
172eb44820cSRob Landley  *
173eb44820cSRob Landley  * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type,
174eb44820cSRob Landley  * and revision.
175eb44820cSRob Landley  */
1761da177e4SLinus Torvalds static int proc_print_scsidevice(struct device *dev, void *data)
1771da177e4SLinus Torvalds {
178b0ed4336SHannes Reinecke 	struct scsi_device *sdev;
1791da177e4SLinus Torvalds 	struct seq_file *s = data;
1801da177e4SLinus Torvalds 	int i;
1811da177e4SLinus Torvalds 
182b0ed4336SHannes Reinecke 	if (!scsi_is_sdev_device(dev))
183b0ed4336SHannes Reinecke 		goto out;
184b0ed4336SHannes Reinecke 
185b0ed4336SHannes Reinecke 	sdev = to_scsi_device(dev);
1861da177e4SLinus Torvalds 	seq_printf(s,
1871da177e4SLinus Torvalds 		"Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n  Vendor: ",
1881da177e4SLinus Torvalds 		sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
1891da177e4SLinus Torvalds 	for (i = 0; i < 8; i++) {
1901da177e4SLinus Torvalds 		if (sdev->vendor[i] >= 0x20)
1911da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->vendor[i]);
1921da177e4SLinus Torvalds 		else
1931da177e4SLinus Torvalds 			seq_printf(s, " ");
1941da177e4SLinus Torvalds 	}
1951da177e4SLinus Torvalds 
1961da177e4SLinus Torvalds 	seq_printf(s, " Model: ");
1971da177e4SLinus Torvalds 	for (i = 0; i < 16; i++) {
1981da177e4SLinus Torvalds 		if (sdev->model[i] >= 0x20)
1991da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->model[i]);
2001da177e4SLinus Torvalds 		else
2011da177e4SLinus Torvalds 			seq_printf(s, " ");
2021da177e4SLinus Torvalds 	}
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds 	seq_printf(s, " Rev: ");
2051da177e4SLinus Torvalds 	for (i = 0; i < 4; i++) {
2061da177e4SLinus Torvalds 		if (sdev->rev[i] >= 0x20)
2071da177e4SLinus Torvalds 			seq_printf(s, "%c", sdev->rev[i]);
2081da177e4SLinus Torvalds 		else
2091da177e4SLinus Torvalds 			seq_printf(s, " ");
2101da177e4SLinus Torvalds 	}
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds 	seq_printf(s, "\n");
2131da177e4SLinus Torvalds 
2144ff36718SMatthew Wilcox 	seq_printf(s, "  Type:   %s ", scsi_device_type(sdev->type));
21574feb53eSAlan Stern 	seq_printf(s, "               ANSI  SCSI revision: %02x",
21674feb53eSAlan Stern 			sdev->scsi_level - (sdev->scsi_level > 1));
2171da177e4SLinus Torvalds 	if (sdev->scsi_level == 2)
2181da177e4SLinus Torvalds 		seq_printf(s, " CCS\n");
2191da177e4SLinus Torvalds 	else
2201da177e4SLinus Torvalds 		seq_printf(s, "\n");
2211da177e4SLinus Torvalds 
222b0ed4336SHannes Reinecke out:
2231da177e4SLinus Torvalds 	return 0;
2241da177e4SLinus Torvalds }
2251da177e4SLinus Torvalds 
226eb44820cSRob Landley /**
227eb44820cSRob Landley  * scsi_add_single_device - Respond to user request to probe for/add device
228eb44820cSRob Landley  * @host: user-supplied decimal integer
229eb44820cSRob Landley  * @channel: user-supplied decimal integer
230eb44820cSRob Landley  * @id: user-supplied decimal integer
231eb44820cSRob Landley  * @lun: user-supplied decimal integer
232eb44820cSRob Landley  *
233eb44820cSRob Landley  * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi.
234eb44820cSRob Landley  *
235eb44820cSRob Landley  * does scsi_host_lookup() and either user_scan() if that transport
236eb44820cSRob Landley  * type supports it, or else scsi_scan_host_selected()
237eb44820cSRob Landley  *
238eb44820cSRob Landley  * Note: this seems to be aimed exclusively at SCSI parallel busses.
239eb44820cSRob Landley  */
240eb44820cSRob Landley 
2411da177e4SLinus Torvalds static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
2421da177e4SLinus Torvalds {
2431da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2441da177e4SLinus Torvalds 	int error = -ENXIO;
2451da177e4SLinus Torvalds 
2461da177e4SLinus Torvalds 	shost = scsi_host_lookup(host);
247315cb0adSJames Smart 	if (!shost)
248315cb0adSJames Smart 		return error;
2491da177e4SLinus Torvalds 
250e02f3f59SChristoph Hellwig 	if (shost->transportt->user_scan)
251e02f3f59SChristoph Hellwig 		error = shost->transportt->user_scan(shost, channel, id, lun);
252e02f3f59SChristoph Hellwig 	else
2531da177e4SLinus Torvalds 		error = scsi_scan_host_selected(shost, channel, id, lun, 1);
2541da177e4SLinus Torvalds 	scsi_host_put(shost);
2551da177e4SLinus Torvalds 	return error;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds 
258eb44820cSRob Landley /**
259eb44820cSRob Landley  * scsi_remove_single_device - Respond to user request to remove a device
260eb44820cSRob Landley  * @host: user-supplied decimal integer
261eb44820cSRob Landley  * @channel: user-supplied decimal integer
262eb44820cSRob Landley  * @id: user-supplied decimal integer
263eb44820cSRob Landley  * @lun: user-supplied decimal integer
264eb44820cSRob Landley  *
265eb44820cSRob Landley  * Description: called by writing "scsi remove-single-device" to
266eb44820cSRob Landley  * /proc/scsi/scsi.  Does a scsi_device_lookup() and scsi_remove_device()
267eb44820cSRob Landley  */
2681da177e4SLinus Torvalds static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
2691da177e4SLinus Torvalds {
2701da177e4SLinus Torvalds 	struct scsi_device *sdev;
2711da177e4SLinus Torvalds 	struct Scsi_Host *shost;
2721da177e4SLinus Torvalds 	int error = -ENXIO;
2731da177e4SLinus Torvalds 
2741da177e4SLinus Torvalds 	shost = scsi_host_lookup(host);
275315cb0adSJames Smart 	if (!shost)
276315cb0adSJames Smart 		return error;
2771da177e4SLinus Torvalds 	sdev = scsi_device_lookup(shost, channel, id, lun);
2781da177e4SLinus Torvalds 	if (sdev) {
2791da177e4SLinus Torvalds 		scsi_remove_device(sdev);
2801da177e4SLinus Torvalds 		scsi_device_put(sdev);
2811da177e4SLinus Torvalds 		error = 0;
2821da177e4SLinus Torvalds 	}
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds 	scsi_host_put(shost);
2851da177e4SLinus Torvalds 	return error;
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds 
288eb44820cSRob Landley /**
289eb44820cSRob Landley  * proc_scsi_write - handle writes to /proc/scsi/scsi
290eb44820cSRob Landley  * @file: not used
291eb44820cSRob Landley  * @buf: buffer to write
292eb44820cSRob Landley  * @length: length of buf, at most PAGE_SIZE
293eb44820cSRob Landley  * @ppos: not used
294eb44820cSRob Landley  *
295eb44820cSRob Landley  * Description: this provides a legacy mechanism to add or remove devices by
296eb44820cSRob Landley  * Host, Channel, ID, and Lun.  To use,
297eb44820cSRob Landley  * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or
298eb44820cSRob Landley  * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with
299eb44820cSRob Landley  * "0 1 2 3" replaced by the Host, Channel, Id, and Lun.
300eb44820cSRob Landley  *
301eb44820cSRob Landley  * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB,
302eb44820cSRob Landley  * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to
303eb44820cSRob Landley  * provide a unique identifier and nothing more.
304eb44820cSRob Landley  */
305eb44820cSRob Landley 
306eb44820cSRob Landley 
3071da177e4SLinus Torvalds static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
3081da177e4SLinus Torvalds 			       size_t length, loff_t *ppos)
3091da177e4SLinus Torvalds {
3101da177e4SLinus Torvalds 	int host, channel, id, lun;
3111da177e4SLinus Torvalds 	char *buffer, *p;
3121da177e4SLinus Torvalds 	int err;
3131da177e4SLinus Torvalds 
3141da177e4SLinus Torvalds 	if (!buf || length > PAGE_SIZE)
3151da177e4SLinus Torvalds 		return -EINVAL;
3161da177e4SLinus Torvalds 
3171da177e4SLinus Torvalds 	buffer = (char *)__get_free_page(GFP_KERNEL);
3181da177e4SLinus Torvalds 	if (!buffer)
3191da177e4SLinus Torvalds 		return -ENOMEM;
3201da177e4SLinus Torvalds 
3211da177e4SLinus Torvalds 	err = -EFAULT;
3221da177e4SLinus Torvalds 	if (copy_from_user(buffer, buf, length))
3231da177e4SLinus Torvalds 		goto out;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	err = -EINVAL;
3261da177e4SLinus Torvalds 	if (length < PAGE_SIZE)
3271da177e4SLinus Torvalds 		buffer[length] = '\0';
3281da177e4SLinus Torvalds 	else if (buffer[PAGE_SIZE-1])
3291da177e4SLinus Torvalds 		goto out;
3301da177e4SLinus Torvalds 
3311da177e4SLinus Torvalds 	/*
3321da177e4SLinus Torvalds 	 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
3331da177e4SLinus Torvalds 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3341da177e4SLinus Torvalds 	 */
3351da177e4SLinus Torvalds 	if (!strncmp("scsi add-single-device", buffer, 22)) {
3361da177e4SLinus Torvalds 		p = buffer + 23;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 		host = simple_strtoul(p, &p, 0);
3391da177e4SLinus Torvalds 		channel = simple_strtoul(p + 1, &p, 0);
3401da177e4SLinus Torvalds 		id = simple_strtoul(p + 1, &p, 0);
3411da177e4SLinus Torvalds 		lun = simple_strtoul(p + 1, &p, 0);
3421da177e4SLinus Torvalds 
3431da177e4SLinus Torvalds 		err = scsi_add_single_device(host, channel, id, lun);
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 	/*
3461da177e4SLinus Torvalds 	 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
3471da177e4SLinus Torvalds 	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3481da177e4SLinus Torvalds 	 */
3491da177e4SLinus Torvalds 	} else if (!strncmp("scsi remove-single-device", buffer, 25)) {
3501da177e4SLinus Torvalds 		p = buffer + 26;
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 		host = simple_strtoul(p, &p, 0);
3531da177e4SLinus Torvalds 		channel = simple_strtoul(p + 1, &p, 0);
3541da177e4SLinus Torvalds 		id = simple_strtoul(p + 1, &p, 0);
3551da177e4SLinus Torvalds 		lun = simple_strtoul(p + 1, &p, 0);
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 		err = scsi_remove_single_device(host, channel, id, lun);
3581da177e4SLinus Torvalds 	}
3591da177e4SLinus Torvalds 
3602ca48a13SJames Bottomley 	/*
3612ca48a13SJames Bottomley 	 * convert success returns so that we return the
3622ca48a13SJames Bottomley 	 * number of bytes consumed.
3632ca48a13SJames Bottomley 	 */
3642ca48a13SJames Bottomley 	if (!err)
3652ca48a13SJames Bottomley 		err = length;
3662ca48a13SJames Bottomley 
3671da177e4SLinus Torvalds  out:
3681da177e4SLinus Torvalds 	free_page((unsigned long)buffer);
3691da177e4SLinus Torvalds 	return err;
3701da177e4SLinus Torvalds }
3711da177e4SLinus Torvalds 
372e37c4913SJeff Mahoney static int always_match(struct device *dev, void *data)
3731da177e4SLinus Torvalds {
374e37c4913SJeff Mahoney 	return 1;
3751da177e4SLinus Torvalds }
3761da177e4SLinus Torvalds 
377e37c4913SJeff Mahoney static inline struct device *next_scsi_device(struct device *start)
378e37c4913SJeff Mahoney {
379e37c4913SJeff Mahoney 	struct device *next = bus_find_device(&scsi_bus_type, start, NULL,
380e37c4913SJeff Mahoney 					      always_match);
381e37c4913SJeff Mahoney 	put_device(start);
382e37c4913SJeff Mahoney 	return next;
383e37c4913SJeff Mahoney }
384e37c4913SJeff Mahoney 
385e37c4913SJeff Mahoney static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
386e37c4913SJeff Mahoney {
387e37c4913SJeff Mahoney 	struct device *dev = NULL;
388e37c4913SJeff Mahoney 	loff_t n = *pos;
389e37c4913SJeff Mahoney 
390e37c4913SJeff Mahoney 	while ((dev = next_scsi_device(dev))) {
391e37c4913SJeff Mahoney 		if (!n--)
392e37c4913SJeff Mahoney 			break;
393e37c4913SJeff Mahoney 		sfile->private++;
394e37c4913SJeff Mahoney 	}
395e37c4913SJeff Mahoney 	return dev;
396e37c4913SJeff Mahoney }
397e37c4913SJeff Mahoney 
398e37c4913SJeff Mahoney static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
399e37c4913SJeff Mahoney {
400e37c4913SJeff Mahoney 	(*pos)++;
401e37c4913SJeff Mahoney 	sfile->private++;
402e37c4913SJeff Mahoney 	return next_scsi_device(v);
403e37c4913SJeff Mahoney }
404e37c4913SJeff Mahoney 
405e37c4913SJeff Mahoney static void scsi_seq_stop(struct seq_file *sfile, void *v)
406e37c4913SJeff Mahoney {
407e37c4913SJeff Mahoney 	put_device(v);
408e37c4913SJeff Mahoney }
409e37c4913SJeff Mahoney 
410e37c4913SJeff Mahoney static int scsi_seq_show(struct seq_file *sfile, void *dev)
411e37c4913SJeff Mahoney {
412e37c4913SJeff Mahoney 	if (!sfile->private)
413e37c4913SJeff Mahoney 		seq_puts(sfile, "Attached devices:\n");
414e37c4913SJeff Mahoney 
415e37c4913SJeff Mahoney 	return proc_print_scsidevice(dev, sfile);
416e37c4913SJeff Mahoney }
417e37c4913SJeff Mahoney 
418e37c4913SJeff Mahoney static const struct seq_operations scsi_seq_ops = {
419e37c4913SJeff Mahoney 	.start	= scsi_seq_start,
420e37c4913SJeff Mahoney 	.next	= scsi_seq_next,
421e37c4913SJeff Mahoney 	.stop	= scsi_seq_stop,
422e37c4913SJeff Mahoney 	.show	= scsi_seq_show
423e37c4913SJeff Mahoney };
424e37c4913SJeff Mahoney 
425eb44820cSRob Landley /**
426eb44820cSRob Landley  * proc_scsi_open - glue function
427eb44820cSRob Landley  * @inode: not used
428eb44820cSRob Landley  * @file: passed to single_open()
429eb44820cSRob Landley  *
430eb44820cSRob Landley  * Associates proc_scsi_show with this file
431eb44820cSRob Landley  */
4321da177e4SLinus Torvalds static int proc_scsi_open(struct inode *inode, struct file *file)
4331da177e4SLinus Torvalds {
4341da177e4SLinus Torvalds 	/*
435eb44820cSRob Landley 	 * We don't really need this for the write case but it doesn't
4361da177e4SLinus Torvalds 	 * harm either.
4371da177e4SLinus Torvalds 	 */
438e37c4913SJeff Mahoney 	return seq_open(file, &scsi_seq_ops);
4391da177e4SLinus Torvalds }
4401da177e4SLinus Torvalds 
44100977a59SArjan van de Ven static const struct file_operations proc_scsi_operations = {
442a973909fSDenis V. Lunev 	.owner		= THIS_MODULE,
4431da177e4SLinus Torvalds 	.open		= proc_scsi_open,
4441da177e4SLinus Torvalds 	.read		= seq_read,
4451da177e4SLinus Torvalds 	.write		= proc_scsi_write,
4461da177e4SLinus Torvalds 	.llseek		= seq_lseek,
447e37c4913SJeff Mahoney 	.release	= seq_release,
4481da177e4SLinus Torvalds };
4491da177e4SLinus Torvalds 
450eb44820cSRob Landley /**
451eb44820cSRob Landley  * scsi_init_procfs - create scsi and scsi/scsi in procfs
452eb44820cSRob Landley  */
4531da177e4SLinus Torvalds int __init scsi_init_procfs(void)
4541da177e4SLinus Torvalds {
4551da177e4SLinus Torvalds 	struct proc_dir_entry *pde;
4561da177e4SLinus Torvalds 
4571da177e4SLinus Torvalds 	proc_scsi = proc_mkdir("scsi", NULL);
4581da177e4SLinus Torvalds 	if (!proc_scsi)
4591da177e4SLinus Torvalds 		goto err1;
4601da177e4SLinus Torvalds 
461a973909fSDenis V. Lunev 	pde = proc_create("scsi/scsi", 0, NULL, &proc_scsi_operations);
4621da177e4SLinus Torvalds 	if (!pde)
4631da177e4SLinus Torvalds 		goto err2;
4641da177e4SLinus Torvalds 
4651da177e4SLinus Torvalds 	return 0;
4661da177e4SLinus Torvalds 
4671da177e4SLinus Torvalds err2:
4681da177e4SLinus Torvalds 	remove_proc_entry("scsi", NULL);
4691da177e4SLinus Torvalds err1:
4701da177e4SLinus Torvalds 	return -ENOMEM;
4711da177e4SLinus Torvalds }
4721da177e4SLinus Torvalds 
473eb44820cSRob Landley /**
474eb44820cSRob Landley  * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
475eb44820cSRob Landley  */
4761da177e4SLinus Torvalds void scsi_exit_procfs(void)
4771da177e4SLinus Torvalds {
4781da177e4SLinus Torvalds 	remove_proc_entry("scsi/scsi", NULL);
4791da177e4SLinus Torvalds 	remove_proc_entry("scsi", NULL);
4801da177e4SLinus Torvalds }
481