162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/scsi/scsi_proc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The functions in this file provide an interface between 662306a36Sopenharmony_ci * the PROC file system and the SCSI device drivers 762306a36Sopenharmony_ci * It is mainly used for debugging, statistics and to pass 862306a36Sopenharmony_ci * information directly to the lowlevel driver. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de 1162306a36Sopenharmony_ci * Version: 0.99.8 last change: 95/09/13 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * generic command parser provided by: 1462306a36Sopenharmony_ci * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de> 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * generic_proc_info() support of xxxx_info() by: 1762306a36Sopenharmony_ci * Michael A. Griffith <grif@acm.org> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/string.h> 2362306a36Sopenharmony_ci#include <linux/mm.h> 2462306a36Sopenharmony_ci#include <linux/proc_fs.h> 2562306a36Sopenharmony_ci#include <linux/errno.h> 2662306a36Sopenharmony_ci#include <linux/blkdev.h> 2762306a36Sopenharmony_ci#include <linux/seq_file.h> 2862306a36Sopenharmony_ci#include <linux/mutex.h> 2962306a36Sopenharmony_ci#include <linux/gfp.h> 3062306a36Sopenharmony_ci#include <linux/uaccess.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include <scsi/scsi.h> 3362306a36Sopenharmony_ci#include <scsi/scsi_device.h> 3462306a36Sopenharmony_ci#include <scsi/scsi_host.h> 3562306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "scsi_priv.h" 3862306a36Sopenharmony_ci#include "scsi_logging.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4K page size, but our output routines, use some slack for overruns */ 4262306a36Sopenharmony_ci#define PROC_BLOCK_SIZE (3*1024) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic struct proc_dir_entry *proc_scsi; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Protects scsi_proc_list */ 4762306a36Sopenharmony_cistatic DEFINE_MUTEX(global_host_template_mutex); 4862306a36Sopenharmony_cistatic LIST_HEAD(scsi_proc_list); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/** 5162306a36Sopenharmony_ci * struct scsi_proc_entry - (host template, SCSI proc dir) association 5262306a36Sopenharmony_ci * @entry: entry in scsi_proc_list. 5362306a36Sopenharmony_ci * @sht: SCSI host template associated with the procfs directory. 5462306a36Sopenharmony_ci * @proc_dir: procfs directory associated with the SCSI host template. 5562306a36Sopenharmony_ci * @present: Number of SCSI hosts instantiated for @sht. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistruct scsi_proc_entry { 5862306a36Sopenharmony_ci struct list_head entry; 5962306a36Sopenharmony_ci const struct scsi_host_template *sht; 6062306a36Sopenharmony_ci struct proc_dir_entry *proc_dir; 6162306a36Sopenharmony_ci unsigned int present; 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic ssize_t proc_scsi_host_write(struct file *file, const char __user *buf, 6562306a36Sopenharmony_ci size_t count, loff_t *ppos) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct Scsi_Host *shost = pde_data(file_inode(file)); 6862306a36Sopenharmony_ci ssize_t ret = -ENOMEM; 6962306a36Sopenharmony_ci char *page; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (count > PROC_BLOCK_SIZE) 7262306a36Sopenharmony_ci return -EOVERFLOW; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!shost->hostt->write_info) 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci page = (char *)__get_free_page(GFP_KERNEL); 7862306a36Sopenharmony_ci if (page) { 7962306a36Sopenharmony_ci ret = -EFAULT; 8062306a36Sopenharmony_ci if (copy_from_user(page, buf, count)) 8162306a36Sopenharmony_ci goto out; 8262306a36Sopenharmony_ci ret = shost->hostt->write_info(shost, page, count); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ciout: 8562306a36Sopenharmony_ci free_page((unsigned long)page); 8662306a36Sopenharmony_ci return ret; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int proc_scsi_show(struct seq_file *m, void *v) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct Scsi_Host *shost = m->private; 9262306a36Sopenharmony_ci return shost->hostt->show_info(m, shost); 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistatic int proc_scsi_host_open(struct inode *inode, struct file *file) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return single_open_size(file, proc_scsi_show, pde_data(inode), 9862306a36Sopenharmony_ci 4 * PAGE_SIZE); 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic struct scsi_proc_entry * 10262306a36Sopenharmony_ci__scsi_lookup_proc_entry(const struct scsi_host_template *sht) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct scsi_proc_entry *e; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci lockdep_assert_held(&global_host_template_mutex); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci list_for_each_entry(e, &scsi_proc_list, entry) 10962306a36Sopenharmony_ci if (e->sht == sht) 11062306a36Sopenharmony_ci return e; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return NULL; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic struct scsi_proc_entry * 11662306a36Sopenharmony_ciscsi_lookup_proc_entry(const struct scsi_host_template *sht) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct scsi_proc_entry *e; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci mutex_lock(&global_host_template_mutex); 12162306a36Sopenharmony_ci e = __scsi_lookup_proc_entry(sht); 12262306a36Sopenharmony_ci mutex_unlock(&global_host_template_mutex); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return e; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/** 12862306a36Sopenharmony_ci * scsi_template_proc_dir() - returns the procfs dir for a SCSI host template 12962306a36Sopenharmony_ci * @sht: SCSI host template pointer. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistruct proc_dir_entry * 13262306a36Sopenharmony_ciscsi_template_proc_dir(const struct scsi_host_template *sht) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci struct scsi_proc_entry *e = scsi_lookup_proc_entry(sht); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return e ? e->proc_dir : NULL; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(scsi_template_proc_dir); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic const struct proc_ops proc_scsi_ops = { 14162306a36Sopenharmony_ci .proc_open = proc_scsi_host_open, 14262306a36Sopenharmony_ci .proc_release = single_release, 14362306a36Sopenharmony_ci .proc_read = seq_read, 14462306a36Sopenharmony_ci .proc_lseek = seq_lseek, 14562306a36Sopenharmony_ci .proc_write = proc_scsi_host_write 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * scsi_proc_hostdir_add - Create directory in /proc for a scsi host 15062306a36Sopenharmony_ci * @sht: owner of this directory 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * Sets sht->proc_dir to the new directory. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ciint scsi_proc_hostdir_add(const struct scsi_host_template *sht) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct scsi_proc_entry *e; 15762306a36Sopenharmony_ci int ret; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (!sht->show_info) 16062306a36Sopenharmony_ci return 0; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci mutex_lock(&global_host_template_mutex); 16362306a36Sopenharmony_ci e = __scsi_lookup_proc_entry(sht); 16462306a36Sopenharmony_ci if (!e) { 16562306a36Sopenharmony_ci e = kzalloc(sizeof(*e), GFP_KERNEL); 16662306a36Sopenharmony_ci if (!e) { 16762306a36Sopenharmony_ci ret = -ENOMEM; 16862306a36Sopenharmony_ci goto unlock; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci if (e->present++) 17262306a36Sopenharmony_ci goto success; 17362306a36Sopenharmony_ci e->proc_dir = proc_mkdir(sht->proc_name, proc_scsi); 17462306a36Sopenharmony_ci if (!e->proc_dir) { 17562306a36Sopenharmony_ci printk(KERN_ERR "%s: proc_mkdir failed for %s\n", __func__, 17662306a36Sopenharmony_ci sht->proc_name); 17762306a36Sopenharmony_ci ret = -ENOMEM; 17862306a36Sopenharmony_ci goto unlock; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci e->sht = sht; 18162306a36Sopenharmony_ci list_add_tail(&e->entry, &scsi_proc_list); 18262306a36Sopenharmony_cisuccess: 18362306a36Sopenharmony_ci e = NULL; 18462306a36Sopenharmony_ci ret = 0; 18562306a36Sopenharmony_ciunlock: 18662306a36Sopenharmony_ci mutex_unlock(&global_host_template_mutex); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci kfree(e); 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/** 19362306a36Sopenharmony_ci * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host 19462306a36Sopenharmony_ci * @sht: owner of directory 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_civoid scsi_proc_hostdir_rm(const struct scsi_host_template *sht) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct scsi_proc_entry *e; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!sht->show_info) 20162306a36Sopenharmony_ci return; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mutex_lock(&global_host_template_mutex); 20462306a36Sopenharmony_ci e = __scsi_lookup_proc_entry(sht); 20562306a36Sopenharmony_ci if (e && !--e->present) { 20662306a36Sopenharmony_ci remove_proc_entry(sht->proc_name, proc_scsi); 20762306a36Sopenharmony_ci list_del(&e->entry); 20862306a36Sopenharmony_ci kfree(e); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci mutex_unlock(&global_host_template_mutex); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci/** 21562306a36Sopenharmony_ci * scsi_proc_host_add - Add entry for this host to appropriate /proc dir 21662306a36Sopenharmony_ci * @shost: host to add 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_civoid scsi_proc_host_add(struct Scsi_Host *shost) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci const struct scsi_host_template *sht = shost->hostt; 22162306a36Sopenharmony_ci struct scsi_proc_entry *e; 22262306a36Sopenharmony_ci struct proc_dir_entry *p; 22362306a36Sopenharmony_ci char name[10]; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (!sht->show_info) 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci e = scsi_lookup_proc_entry(sht); 22962306a36Sopenharmony_ci if (!e) 23062306a36Sopenharmony_ci goto err; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci sprintf(name,"%d", shost->host_no); 23362306a36Sopenharmony_ci p = proc_create_data(name, S_IRUGO | S_IWUSR, e->proc_dir, 23462306a36Sopenharmony_ci &proc_scsi_ops, shost); 23562306a36Sopenharmony_ci if (!p) 23662306a36Sopenharmony_ci goto err; 23762306a36Sopenharmony_ci return; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cierr: 24062306a36Sopenharmony_ci shost_printk(KERN_ERR, shost, 24162306a36Sopenharmony_ci "%s: Failed to register host (%s failed)\n", __func__, 24262306a36Sopenharmony_ci e ? "proc_create_data()" : "scsi_proc_hostdir_add()"); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/** 24662306a36Sopenharmony_ci * scsi_proc_host_rm - remove this host's entry from /proc 24762306a36Sopenharmony_ci * @shost: which host 24862306a36Sopenharmony_ci */ 24962306a36Sopenharmony_civoid scsi_proc_host_rm(struct Scsi_Host *shost) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci const struct scsi_host_template *sht = shost->hostt; 25262306a36Sopenharmony_ci struct scsi_proc_entry *e; 25362306a36Sopenharmony_ci char name[10]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (!sht->show_info) 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci e = scsi_lookup_proc_entry(sht); 25962306a36Sopenharmony_ci if (!e) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci sprintf(name,"%d", shost->host_no); 26362306a36Sopenharmony_ci remove_proc_entry(name, e->proc_dir); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci/** 26662306a36Sopenharmony_ci * proc_print_scsidevice - return data about this host 26762306a36Sopenharmony_ci * @dev: A scsi device 26862306a36Sopenharmony_ci * @data: &struct seq_file to output to. 26962306a36Sopenharmony_ci * 27062306a36Sopenharmony_ci * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type, 27162306a36Sopenharmony_ci * and revision. 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_cistatic int proc_print_scsidevice(struct device *dev, void *data) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct scsi_device *sdev; 27662306a36Sopenharmony_ci struct seq_file *s = data; 27762306a36Sopenharmony_ci int i; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!scsi_is_sdev_device(dev)) 28062306a36Sopenharmony_ci goto out; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci sdev = to_scsi_device(dev); 28362306a36Sopenharmony_ci seq_printf(s, 28462306a36Sopenharmony_ci "Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n Vendor: ", 28562306a36Sopenharmony_ci sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); 28662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 28762306a36Sopenharmony_ci if (sdev->vendor[i] >= 0x20) 28862306a36Sopenharmony_ci seq_putc(s, sdev->vendor[i]); 28962306a36Sopenharmony_ci else 29062306a36Sopenharmony_ci seq_putc(s, ' '); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci seq_puts(s, " Model: "); 29462306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 29562306a36Sopenharmony_ci if (sdev->model[i] >= 0x20) 29662306a36Sopenharmony_ci seq_putc(s, sdev->model[i]); 29762306a36Sopenharmony_ci else 29862306a36Sopenharmony_ci seq_putc(s, ' '); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci seq_puts(s, " Rev: "); 30262306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 30362306a36Sopenharmony_ci if (sdev->rev[i] >= 0x20) 30462306a36Sopenharmony_ci seq_putc(s, sdev->rev[i]); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci seq_putc(s, ' '); 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci seq_putc(s, '\n'); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci seq_printf(s, " Type: %s ", scsi_device_type(sdev->type)); 31262306a36Sopenharmony_ci seq_printf(s, " ANSI SCSI revision: %02x", 31362306a36Sopenharmony_ci sdev->scsi_level - (sdev->scsi_level > 1)); 31462306a36Sopenharmony_ci if (sdev->scsi_level == 2) 31562306a36Sopenharmony_ci seq_puts(s, " CCS\n"); 31662306a36Sopenharmony_ci else 31762306a36Sopenharmony_ci seq_putc(s, '\n'); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ciout: 32062306a36Sopenharmony_ci return 0; 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci/** 32462306a36Sopenharmony_ci * scsi_add_single_device - Respond to user request to probe for/add device 32562306a36Sopenharmony_ci * @host: user-supplied decimal integer 32662306a36Sopenharmony_ci * @channel: user-supplied decimal integer 32762306a36Sopenharmony_ci * @id: user-supplied decimal integer 32862306a36Sopenharmony_ci * @lun: user-supplied decimal integer 32962306a36Sopenharmony_ci * 33062306a36Sopenharmony_ci * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi. 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * does scsi_host_lookup() and either user_scan() if that transport 33362306a36Sopenharmony_ci * type supports it, or else scsi_scan_host_selected() 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Note: this seems to be aimed exclusively at SCSI parallel busses. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int scsi_add_single_device(uint host, uint channel, uint id, uint lun) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct Scsi_Host *shost; 34162306a36Sopenharmony_ci int error = -ENXIO; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci shost = scsi_host_lookup(host); 34462306a36Sopenharmony_ci if (!shost) 34562306a36Sopenharmony_ci return error; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (shost->transportt->user_scan) 34862306a36Sopenharmony_ci error = shost->transportt->user_scan(shost, channel, id, lun); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci error = scsi_scan_host_selected(shost, channel, id, lun, 35162306a36Sopenharmony_ci SCSI_SCAN_MANUAL); 35262306a36Sopenharmony_ci scsi_host_put(shost); 35362306a36Sopenharmony_ci return error; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/** 35762306a36Sopenharmony_ci * scsi_remove_single_device - Respond to user request to remove a device 35862306a36Sopenharmony_ci * @host: user-supplied decimal integer 35962306a36Sopenharmony_ci * @channel: user-supplied decimal integer 36062306a36Sopenharmony_ci * @id: user-supplied decimal integer 36162306a36Sopenharmony_ci * @lun: user-supplied decimal integer 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * Description: called by writing "scsi remove-single-device" to 36462306a36Sopenharmony_ci * /proc/scsi/scsi. Does a scsi_device_lookup() and scsi_remove_device() 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_cistatic int scsi_remove_single_device(uint host, uint channel, uint id, uint lun) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct scsi_device *sdev; 36962306a36Sopenharmony_ci struct Scsi_Host *shost; 37062306a36Sopenharmony_ci int error = -ENXIO; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci shost = scsi_host_lookup(host); 37362306a36Sopenharmony_ci if (!shost) 37462306a36Sopenharmony_ci return error; 37562306a36Sopenharmony_ci sdev = scsi_device_lookup(shost, channel, id, lun); 37662306a36Sopenharmony_ci if (sdev) { 37762306a36Sopenharmony_ci scsi_remove_device(sdev); 37862306a36Sopenharmony_ci scsi_device_put(sdev); 37962306a36Sopenharmony_ci error = 0; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci scsi_host_put(shost); 38362306a36Sopenharmony_ci return error; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * proc_scsi_write - handle writes to /proc/scsi/scsi 38862306a36Sopenharmony_ci * @file: not used 38962306a36Sopenharmony_ci * @buf: buffer to write 39062306a36Sopenharmony_ci * @length: length of buf, at most PAGE_SIZE 39162306a36Sopenharmony_ci * @ppos: not used 39262306a36Sopenharmony_ci * 39362306a36Sopenharmony_ci * Description: this provides a legacy mechanism to add or remove devices by 39462306a36Sopenharmony_ci * Host, Channel, ID, and Lun. To use, 39562306a36Sopenharmony_ci * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or 39662306a36Sopenharmony_ci * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with 39762306a36Sopenharmony_ci * "0 1 2 3" replaced by the Host, Channel, Id, and Lun. 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB, 40062306a36Sopenharmony_ci * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to 40162306a36Sopenharmony_ci * provide a unique identifier and nothing more. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic ssize_t proc_scsi_write(struct file *file, const char __user *buf, 40662306a36Sopenharmony_ci size_t length, loff_t *ppos) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci int host, channel, id, lun; 40962306a36Sopenharmony_ci char *buffer, *end, *p; 41062306a36Sopenharmony_ci int err; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!buf || length > PAGE_SIZE) 41362306a36Sopenharmony_ci return -EINVAL; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci buffer = (char *)__get_free_page(GFP_KERNEL); 41662306a36Sopenharmony_ci if (!buffer) 41762306a36Sopenharmony_ci return -ENOMEM; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci err = -EFAULT; 42062306a36Sopenharmony_ci if (copy_from_user(buffer, buf, length)) 42162306a36Sopenharmony_ci goto out; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci err = -EINVAL; 42462306a36Sopenharmony_ci if (length < PAGE_SIZE) { 42562306a36Sopenharmony_ci end = buffer + length; 42662306a36Sopenharmony_ci *end = '\0'; 42762306a36Sopenharmony_ci } else { 42862306a36Sopenharmony_ci end = buffer + PAGE_SIZE - 1; 42962306a36Sopenharmony_ci if (*end) 43062306a36Sopenharmony_ci goto out; 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi 43562306a36Sopenharmony_ci * with "0 1 2 3" replaced by your "Host Channel Id Lun". 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci if (!strncmp("scsi add-single-device", buffer, 22)) { 43862306a36Sopenharmony_ci p = buffer + 23; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci host = (p < end) ? simple_strtoul(p, &p, 0) : 0; 44162306a36Sopenharmony_ci channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 44262306a36Sopenharmony_ci id = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 44362306a36Sopenharmony_ci lun = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci err = scsi_add_single_device(host, channel, id, lun); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi 44962306a36Sopenharmony_ci * with "0 1 2 3" replaced by your "Host Channel Id Lun". 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci } else if (!strncmp("scsi remove-single-device", buffer, 25)) { 45262306a36Sopenharmony_ci p = buffer + 26; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci host = (p < end) ? simple_strtoul(p, &p, 0) : 0; 45562306a36Sopenharmony_ci channel = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 45662306a36Sopenharmony_ci id = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 45762306a36Sopenharmony_ci lun = (p + 1 < end) ? simple_strtoul(p + 1, &p, 0) : 0; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci err = scsi_remove_single_device(host, channel, id, lun); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * convert success returns so that we return the 46462306a36Sopenharmony_ci * number of bytes consumed. 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci if (!err) 46762306a36Sopenharmony_ci err = length; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci out: 47062306a36Sopenharmony_ci free_page((unsigned long)buffer); 47162306a36Sopenharmony_ci return err; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic inline struct device *next_scsi_device(struct device *start) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct device *next = bus_find_next_device(&scsi_bus_type, start); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci put_device(start); 47962306a36Sopenharmony_ci return next; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic void *scsi_seq_start(struct seq_file *sfile, loff_t *pos) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct device *dev = NULL; 48562306a36Sopenharmony_ci loff_t n = *pos; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci while ((dev = next_scsi_device(dev))) { 48862306a36Sopenharmony_ci if (!n--) 48962306a36Sopenharmony_ci break; 49062306a36Sopenharmony_ci sfile->private++; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci return dev; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci (*pos)++; 49862306a36Sopenharmony_ci sfile->private++; 49962306a36Sopenharmony_ci return next_scsi_device(v); 50062306a36Sopenharmony_ci} 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic void scsi_seq_stop(struct seq_file *sfile, void *v) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci put_device(v); 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int scsi_seq_show(struct seq_file *sfile, void *dev) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci if (!sfile->private) 51062306a36Sopenharmony_ci seq_puts(sfile, "Attached devices:\n"); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci return proc_print_scsidevice(dev, sfile); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic const struct seq_operations scsi_seq_ops = { 51662306a36Sopenharmony_ci .start = scsi_seq_start, 51762306a36Sopenharmony_ci .next = scsi_seq_next, 51862306a36Sopenharmony_ci .stop = scsi_seq_stop, 51962306a36Sopenharmony_ci .show = scsi_seq_show 52062306a36Sopenharmony_ci}; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/** 52362306a36Sopenharmony_ci * proc_scsi_open - glue function 52462306a36Sopenharmony_ci * @inode: not used 52562306a36Sopenharmony_ci * @file: passed to single_open() 52662306a36Sopenharmony_ci * 52762306a36Sopenharmony_ci * Associates proc_scsi_show with this file 52862306a36Sopenharmony_ci */ 52962306a36Sopenharmony_cistatic int proc_scsi_open(struct inode *inode, struct file *file) 53062306a36Sopenharmony_ci{ 53162306a36Sopenharmony_ci /* 53262306a36Sopenharmony_ci * We don't really need this for the write case but it doesn't 53362306a36Sopenharmony_ci * harm either. 53462306a36Sopenharmony_ci */ 53562306a36Sopenharmony_ci return seq_open(file, &scsi_seq_ops); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic const struct proc_ops scsi_scsi_proc_ops = { 53962306a36Sopenharmony_ci .proc_open = proc_scsi_open, 54062306a36Sopenharmony_ci .proc_read = seq_read, 54162306a36Sopenharmony_ci .proc_write = proc_scsi_write, 54262306a36Sopenharmony_ci .proc_lseek = seq_lseek, 54362306a36Sopenharmony_ci .proc_release = seq_release, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci/** 54762306a36Sopenharmony_ci * scsi_init_procfs - create scsi and scsi/scsi in procfs 54862306a36Sopenharmony_ci */ 54962306a36Sopenharmony_ciint __init scsi_init_procfs(void) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct proc_dir_entry *pde; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci proc_scsi = proc_mkdir("scsi", NULL); 55462306a36Sopenharmony_ci if (!proc_scsi) 55562306a36Sopenharmony_ci goto err1; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci pde = proc_create("scsi/scsi", 0, NULL, &scsi_scsi_proc_ops); 55862306a36Sopenharmony_ci if (!pde) 55962306a36Sopenharmony_ci goto err2; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return 0; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cierr2: 56462306a36Sopenharmony_ci remove_proc_entry("scsi", NULL); 56562306a36Sopenharmony_cierr1: 56662306a36Sopenharmony_ci return -ENOMEM; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci/** 57062306a36Sopenharmony_ci * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_civoid scsi_exit_procfs(void) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci remove_proc_entry("scsi/scsi", NULL); 57562306a36Sopenharmony_ci remove_proc_entry("scsi", NULL); 57662306a36Sopenharmony_ci} 577