18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/drivers/scsi/scsi_proc.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * The functions in this file provide an interface between
68c2ecf20Sopenharmony_ci * the PROC file system and the SCSI device drivers
78c2ecf20Sopenharmony_ci * It is mainly used for debugging, statistics and to pass
88c2ecf20Sopenharmony_ci * information directly to the lowlevel driver.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
118c2ecf20Sopenharmony_ci * Version: 0.99.8   last change: 95/09/13
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * generic command parser provided by:
148c2ecf20Sopenharmony_ci * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * generic_proc_info() support of xxxx_info() by:
178c2ecf20Sopenharmony_ci * Michael A. Griffith <grif@acm.org>
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/module.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/string.h>
238c2ecf20Sopenharmony_ci#include <linux/mm.h>
248c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
258c2ecf20Sopenharmony_ci#include <linux/errno.h>
268c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
278c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
288c2ecf20Sopenharmony_ci#include <linux/mutex.h>
298c2ecf20Sopenharmony_ci#include <linux/gfp.h>
308c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
338c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
348c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
358c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "scsi_priv.h"
388c2ecf20Sopenharmony_ci#include "scsi_logging.h"
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* 4K page size, but our output routines, use some slack for overruns */
428c2ecf20Sopenharmony_ci#define PROC_BLOCK_SIZE (3*1024)
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_scsi;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Protect sht->present and sht->proc_dir */
478c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(global_host_template_mutex);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
508c2ecf20Sopenharmony_ci                           size_t count, loff_t *ppos)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = PDE_DATA(file_inode(file));
538c2ecf20Sopenharmony_ci	ssize_t ret = -ENOMEM;
548c2ecf20Sopenharmony_ci	char *page;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	if (count > PROC_BLOCK_SIZE)
578c2ecf20Sopenharmony_ci		return -EOVERFLOW;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!shost->hostt->write_info)
608c2ecf20Sopenharmony_ci		return -EINVAL;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	page = (char *)__get_free_page(GFP_KERNEL);
638c2ecf20Sopenharmony_ci	if (page) {
648c2ecf20Sopenharmony_ci		ret = -EFAULT;
658c2ecf20Sopenharmony_ci		if (copy_from_user(page, buf, count))
668c2ecf20Sopenharmony_ci			goto out;
678c2ecf20Sopenharmony_ci		ret = shost->hostt->write_info(shost, page, count);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ciout:
708c2ecf20Sopenharmony_ci	free_page((unsigned long)page);
718c2ecf20Sopenharmony_ci	return ret;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic int proc_scsi_show(struct seq_file *m, void *v)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = m->private;
778c2ecf20Sopenharmony_ci	return shost->hostt->show_info(m, shost);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic int proc_scsi_host_open(struct inode *inode, struct file *file)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	return single_open_size(file, proc_scsi_show, PDE_DATA(inode),
838c2ecf20Sopenharmony_ci				4 * PAGE_SIZE);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic const struct proc_ops proc_scsi_ops = {
878c2ecf20Sopenharmony_ci	.proc_open	= proc_scsi_host_open,
888c2ecf20Sopenharmony_ci	.proc_release	= single_release,
898c2ecf20Sopenharmony_ci	.proc_read	= seq_read,
908c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
918c2ecf20Sopenharmony_ci	.proc_write	= proc_scsi_host_write
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/**
958c2ecf20Sopenharmony_ci * scsi_proc_hostdir_add - Create directory in /proc for a scsi host
968c2ecf20Sopenharmony_ci * @sht: owner of this directory
978c2ecf20Sopenharmony_ci *
988c2ecf20Sopenharmony_ci * Sets sht->proc_dir to the new directory.
998c2ecf20Sopenharmony_ci */
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_civoid scsi_proc_hostdir_add(struct scsi_host_template *sht)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	if (!sht->show_info)
1048c2ecf20Sopenharmony_ci		return;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	mutex_lock(&global_host_template_mutex);
1078c2ecf20Sopenharmony_ci	if (!sht->present++) {
1088c2ecf20Sopenharmony_ci		sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
1098c2ecf20Sopenharmony_ci        	if (!sht->proc_dir)
1108c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
1118c2ecf20Sopenharmony_ci			       __func__, sht->proc_name);
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci	mutex_unlock(&global_host_template_mutex);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci/**
1178c2ecf20Sopenharmony_ci * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host
1188c2ecf20Sopenharmony_ci * @sht: owner of directory
1198c2ecf20Sopenharmony_ci */
1208c2ecf20Sopenharmony_civoid scsi_proc_hostdir_rm(struct scsi_host_template *sht)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	if (!sht->show_info)
1238c2ecf20Sopenharmony_ci		return;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	mutex_lock(&global_host_template_mutex);
1268c2ecf20Sopenharmony_ci	if (!--sht->present && sht->proc_dir) {
1278c2ecf20Sopenharmony_ci		remove_proc_entry(sht->proc_name, proc_scsi);
1288c2ecf20Sopenharmony_ci		sht->proc_dir = NULL;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci	mutex_unlock(&global_host_template_mutex);
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/**
1358c2ecf20Sopenharmony_ci * scsi_proc_host_add - Add entry for this host to appropriate /proc dir
1368c2ecf20Sopenharmony_ci * @shost: host to add
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_civoid scsi_proc_host_add(struct Scsi_Host *shost)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct scsi_host_template *sht = shost->hostt;
1418c2ecf20Sopenharmony_ci	struct proc_dir_entry *p;
1428c2ecf20Sopenharmony_ci	char name[10];
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (!sht->proc_dir)
1458c2ecf20Sopenharmony_ci		return;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	sprintf(name,"%d", shost->host_no);
1488c2ecf20Sopenharmony_ci	p = proc_create_data(name, S_IRUGO | S_IWUSR,
1498c2ecf20Sopenharmony_ci		sht->proc_dir, &proc_scsi_ops, shost);
1508c2ecf20Sopenharmony_ci	if (!p)
1518c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: Failed to register host %d in"
1528c2ecf20Sopenharmony_ci		       "%s\n", __func__, shost->host_no,
1538c2ecf20Sopenharmony_ci		       sht->proc_name);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/**
1578c2ecf20Sopenharmony_ci * scsi_proc_host_rm - remove this host's entry from /proc
1588c2ecf20Sopenharmony_ci * @shost: which host
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_civoid scsi_proc_host_rm(struct Scsi_Host *shost)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	char name[10];
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (!shost->hostt->proc_dir)
1658c2ecf20Sopenharmony_ci		return;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	sprintf(name,"%d", shost->host_no);
1688c2ecf20Sopenharmony_ci	remove_proc_entry(name, shost->hostt->proc_dir);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * proc_print_scsidevice - return data about this host
1728c2ecf20Sopenharmony_ci * @dev: A scsi device
1738c2ecf20Sopenharmony_ci * @data: &struct seq_file to output to.
1748c2ecf20Sopenharmony_ci *
1758c2ecf20Sopenharmony_ci * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type,
1768c2ecf20Sopenharmony_ci * and revision.
1778c2ecf20Sopenharmony_ci */
1788c2ecf20Sopenharmony_cistatic int proc_print_scsidevice(struct device *dev, void *data)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
1818c2ecf20Sopenharmony_ci	struct seq_file *s = data;
1828c2ecf20Sopenharmony_ci	int i;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (!scsi_is_sdev_device(dev))
1858c2ecf20Sopenharmony_ci		goto out;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	sdev = to_scsi_device(dev);
1888c2ecf20Sopenharmony_ci	seq_printf(s,
1898c2ecf20Sopenharmony_ci		"Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n  Vendor: ",
1908c2ecf20Sopenharmony_ci		sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
1918c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
1928c2ecf20Sopenharmony_ci		if (sdev->vendor[i] >= 0x20)
1938c2ecf20Sopenharmony_ci			seq_putc(s, sdev->vendor[i]);
1948c2ecf20Sopenharmony_ci		else
1958c2ecf20Sopenharmony_ci			seq_putc(s, ' ');
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	seq_puts(s, " Model: ");
1998c2ecf20Sopenharmony_ci	for (i = 0; i < 16; i++) {
2008c2ecf20Sopenharmony_ci		if (sdev->model[i] >= 0x20)
2018c2ecf20Sopenharmony_ci			seq_putc(s, sdev->model[i]);
2028c2ecf20Sopenharmony_ci		else
2038c2ecf20Sopenharmony_ci			seq_putc(s, ' ');
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	seq_puts(s, " Rev: ");
2078c2ecf20Sopenharmony_ci	for (i = 0; i < 4; i++) {
2088c2ecf20Sopenharmony_ci		if (sdev->rev[i] >= 0x20)
2098c2ecf20Sopenharmony_ci			seq_putc(s, sdev->rev[i]);
2108c2ecf20Sopenharmony_ci		else
2118c2ecf20Sopenharmony_ci			seq_putc(s, ' ');
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	seq_putc(s, '\n');
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	seq_printf(s, "  Type:   %s ", scsi_device_type(sdev->type));
2178c2ecf20Sopenharmony_ci	seq_printf(s, "               ANSI  SCSI revision: %02x",
2188c2ecf20Sopenharmony_ci			sdev->scsi_level - (sdev->scsi_level > 1));
2198c2ecf20Sopenharmony_ci	if (sdev->scsi_level == 2)
2208c2ecf20Sopenharmony_ci		seq_puts(s, " CCS\n");
2218c2ecf20Sopenharmony_ci	else
2228c2ecf20Sopenharmony_ci		seq_putc(s, '\n');
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ciout:
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci/**
2298c2ecf20Sopenharmony_ci * scsi_add_single_device - Respond to user request to probe for/add device
2308c2ecf20Sopenharmony_ci * @host: user-supplied decimal integer
2318c2ecf20Sopenharmony_ci * @channel: user-supplied decimal integer
2328c2ecf20Sopenharmony_ci * @id: user-supplied decimal integer
2338c2ecf20Sopenharmony_ci * @lun: user-supplied decimal integer
2348c2ecf20Sopenharmony_ci *
2358c2ecf20Sopenharmony_ci * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi.
2368c2ecf20Sopenharmony_ci *
2378c2ecf20Sopenharmony_ci * does scsi_host_lookup() and either user_scan() if that transport
2388c2ecf20Sopenharmony_ci * type supports it, or else scsi_scan_host_selected()
2398c2ecf20Sopenharmony_ci *
2408c2ecf20Sopenharmony_ci * Note: this seems to be aimed exclusively at SCSI parallel busses.
2418c2ecf20Sopenharmony_ci */
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
2468c2ecf20Sopenharmony_ci	int error = -ENXIO;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	shost = scsi_host_lookup(host);
2498c2ecf20Sopenharmony_ci	if (!shost)
2508c2ecf20Sopenharmony_ci		return error;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	if (shost->transportt->user_scan)
2538c2ecf20Sopenharmony_ci		error = shost->transportt->user_scan(shost, channel, id, lun);
2548c2ecf20Sopenharmony_ci	else
2558c2ecf20Sopenharmony_ci		error = scsi_scan_host_selected(shost, channel, id, lun,
2568c2ecf20Sopenharmony_ci						SCSI_SCAN_MANUAL);
2578c2ecf20Sopenharmony_ci	scsi_host_put(shost);
2588c2ecf20Sopenharmony_ci	return error;
2598c2ecf20Sopenharmony_ci}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci/**
2628c2ecf20Sopenharmony_ci * scsi_remove_single_device - Respond to user request to remove a device
2638c2ecf20Sopenharmony_ci * @host: user-supplied decimal integer
2648c2ecf20Sopenharmony_ci * @channel: user-supplied decimal integer
2658c2ecf20Sopenharmony_ci * @id: user-supplied decimal integer
2668c2ecf20Sopenharmony_ci * @lun: user-supplied decimal integer
2678c2ecf20Sopenharmony_ci *
2688c2ecf20Sopenharmony_ci * Description: called by writing "scsi remove-single-device" to
2698c2ecf20Sopenharmony_ci * /proc/scsi/scsi.  Does a scsi_device_lookup() and scsi_remove_device()
2708c2ecf20Sopenharmony_ci */
2718c2ecf20Sopenharmony_cistatic int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct scsi_device *sdev;
2748c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
2758c2ecf20Sopenharmony_ci	int error = -ENXIO;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	shost = scsi_host_lookup(host);
2788c2ecf20Sopenharmony_ci	if (!shost)
2798c2ecf20Sopenharmony_ci		return error;
2808c2ecf20Sopenharmony_ci	sdev = scsi_device_lookup(shost, channel, id, lun);
2818c2ecf20Sopenharmony_ci	if (sdev) {
2828c2ecf20Sopenharmony_ci		scsi_remove_device(sdev);
2838c2ecf20Sopenharmony_ci		scsi_device_put(sdev);
2848c2ecf20Sopenharmony_ci		error = 0;
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	scsi_host_put(shost);
2888c2ecf20Sopenharmony_ci	return error;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/**
2928c2ecf20Sopenharmony_ci * proc_scsi_write - handle writes to /proc/scsi/scsi
2938c2ecf20Sopenharmony_ci * @file: not used
2948c2ecf20Sopenharmony_ci * @buf: buffer to write
2958c2ecf20Sopenharmony_ci * @length: length of buf, at most PAGE_SIZE
2968c2ecf20Sopenharmony_ci * @ppos: not used
2978c2ecf20Sopenharmony_ci *
2988c2ecf20Sopenharmony_ci * Description: this provides a legacy mechanism to add or remove devices by
2998c2ecf20Sopenharmony_ci * Host, Channel, ID, and Lun.  To use,
3008c2ecf20Sopenharmony_ci * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or
3018c2ecf20Sopenharmony_ci * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with
3028c2ecf20Sopenharmony_ci * "0 1 2 3" replaced by the Host, Channel, Id, and Lun.
3038c2ecf20Sopenharmony_ci *
3048c2ecf20Sopenharmony_ci * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB,
3058c2ecf20Sopenharmony_ci * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to
3068c2ecf20Sopenharmony_ci * provide a unique identifier and nothing more.
3078c2ecf20Sopenharmony_ci */
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic ssize_t proc_scsi_write(struct file *file, const char __user *buf,
3118c2ecf20Sopenharmony_ci			       size_t length, loff_t *ppos)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	int host, channel, id, lun;
3148c2ecf20Sopenharmony_ci	char *buffer, *end, *p;
3158c2ecf20Sopenharmony_ci	int err;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (!buf || length > PAGE_SIZE)
3188c2ecf20Sopenharmony_ci		return -EINVAL;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	buffer = (char *)__get_free_page(GFP_KERNEL);
3218c2ecf20Sopenharmony_ci	if (!buffer)
3228c2ecf20Sopenharmony_ci		return -ENOMEM;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	err = -EFAULT;
3258c2ecf20Sopenharmony_ci	if (copy_from_user(buffer, buf, length))
3268c2ecf20Sopenharmony_ci		goto out;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	err = -EINVAL;
3298c2ecf20Sopenharmony_ci	if (length < PAGE_SIZE) {
3308c2ecf20Sopenharmony_ci		end = buffer + length;
3318c2ecf20Sopenharmony_ci		*end = '\0';
3328c2ecf20Sopenharmony_ci	} else {
3338c2ecf20Sopenharmony_ci		end = buffer + PAGE_SIZE - 1;
3348c2ecf20Sopenharmony_ci		if (*end)
3358c2ecf20Sopenharmony_ci			goto out;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/*
3398c2ecf20Sopenharmony_ci	 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
3408c2ecf20Sopenharmony_ci	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3418c2ecf20Sopenharmony_ci	 */
3428c2ecf20Sopenharmony_ci	if (!strncmp("scsi add-single-device", buffer, 22)) {
3438c2ecf20Sopenharmony_ci		p = buffer + 23;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		host    = (p     < end) ? simple_strtoul(p, &p, 0) : 0;
3468c2ecf20Sopenharmony_ci		channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3478c2ecf20Sopenharmony_ci		id      = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3488c2ecf20Sopenharmony_ci		lun     = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		err = scsi_add_single_device(host, channel, id, lun);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
3548c2ecf20Sopenharmony_ci	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
3558c2ecf20Sopenharmony_ci	 */
3568c2ecf20Sopenharmony_ci	} else if (!strncmp("scsi remove-single-device", buffer, 25)) {
3578c2ecf20Sopenharmony_ci		p = buffer + 26;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		host    = (p     < end) ? simple_strtoul(p, &p, 0) : 0;
3608c2ecf20Sopenharmony_ci		channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3618c2ecf20Sopenharmony_ci		id      = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3628c2ecf20Sopenharmony_ci		lun     = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		err = scsi_remove_single_device(host, channel, id, lun);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	/*
3688c2ecf20Sopenharmony_ci	 * convert success returns so that we return the
3698c2ecf20Sopenharmony_ci	 * number of bytes consumed.
3708c2ecf20Sopenharmony_ci	 */
3718c2ecf20Sopenharmony_ci	if (!err)
3728c2ecf20Sopenharmony_ci		err = length;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci out:
3758c2ecf20Sopenharmony_ci	free_page((unsigned long)buffer);
3768c2ecf20Sopenharmony_ci	return err;
3778c2ecf20Sopenharmony_ci}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_cistatic inline struct device *next_scsi_device(struct device *start)
3808c2ecf20Sopenharmony_ci{
3818c2ecf20Sopenharmony_ci	struct device *next = bus_find_next_device(&scsi_bus_type, start);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	put_device(start);
3848c2ecf20Sopenharmony_ci	return next;
3858c2ecf20Sopenharmony_ci}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_cistatic void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	struct device *dev = NULL;
3908c2ecf20Sopenharmony_ci	loff_t n = *pos;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	while ((dev = next_scsi_device(dev))) {
3938c2ecf20Sopenharmony_ci		if (!n--)
3948c2ecf20Sopenharmony_ci			break;
3958c2ecf20Sopenharmony_ci		sfile->private++;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	return dev;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	(*pos)++;
4038c2ecf20Sopenharmony_ci	sfile->private++;
4048c2ecf20Sopenharmony_ci	return next_scsi_device(v);
4058c2ecf20Sopenharmony_ci}
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cistatic void scsi_seq_stop(struct seq_file *sfile, void *v)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	put_device(v);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic int scsi_seq_show(struct seq_file *sfile, void *dev)
4138c2ecf20Sopenharmony_ci{
4148c2ecf20Sopenharmony_ci	if (!sfile->private)
4158c2ecf20Sopenharmony_ci		seq_puts(sfile, "Attached devices:\n");
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return proc_print_scsidevice(dev, sfile);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic const struct seq_operations scsi_seq_ops = {
4218c2ecf20Sopenharmony_ci	.start	= scsi_seq_start,
4228c2ecf20Sopenharmony_ci	.next	= scsi_seq_next,
4238c2ecf20Sopenharmony_ci	.stop	= scsi_seq_stop,
4248c2ecf20Sopenharmony_ci	.show	= scsi_seq_show
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci/**
4288c2ecf20Sopenharmony_ci * proc_scsi_open - glue function
4298c2ecf20Sopenharmony_ci * @inode: not used
4308c2ecf20Sopenharmony_ci * @file: passed to single_open()
4318c2ecf20Sopenharmony_ci *
4328c2ecf20Sopenharmony_ci * Associates proc_scsi_show with this file
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistatic int proc_scsi_open(struct inode *inode, struct file *file)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	/*
4378c2ecf20Sopenharmony_ci	 * We don't really need this for the write case but it doesn't
4388c2ecf20Sopenharmony_ci	 * harm either.
4398c2ecf20Sopenharmony_ci	 */
4408c2ecf20Sopenharmony_ci	return seq_open(file, &scsi_seq_ops);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic const struct proc_ops scsi_scsi_proc_ops = {
4448c2ecf20Sopenharmony_ci	.proc_open	= proc_scsi_open,
4458c2ecf20Sopenharmony_ci	.proc_read	= seq_read,
4468c2ecf20Sopenharmony_ci	.proc_write	= proc_scsi_write,
4478c2ecf20Sopenharmony_ci	.proc_lseek	= seq_lseek,
4488c2ecf20Sopenharmony_ci	.proc_release	= seq_release,
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/**
4528c2ecf20Sopenharmony_ci * scsi_init_procfs - create scsi and scsi/scsi in procfs
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_ciint __init scsi_init_procfs(void)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct proc_dir_entry *pde;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	proc_scsi = proc_mkdir("scsi", NULL);
4598c2ecf20Sopenharmony_ci	if (!proc_scsi)
4608c2ecf20Sopenharmony_ci		goto err1;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	pde = proc_create("scsi/scsi", 0, NULL, &scsi_scsi_proc_ops);
4638c2ecf20Sopenharmony_ci	if (!pde)
4648c2ecf20Sopenharmony_ci		goto err2;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	return 0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cierr2:
4698c2ecf20Sopenharmony_ci	remove_proc_entry("scsi", NULL);
4708c2ecf20Sopenharmony_cierr1:
4718c2ecf20Sopenharmony_ci	return -ENOMEM;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/**
4758c2ecf20Sopenharmony_ci * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_civoid scsi_exit_procfs(void)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	remove_proc_entry("scsi/scsi", NULL);
4808c2ecf20Sopenharmony_ci	remove_proc_entry("scsi", NULL);
4818c2ecf20Sopenharmony_ci}
482