1 /* 2 * linux/drivers/scsi/scsi_proc.c 3 * 4 * The functions in this file provide an interface between 5 * the PROC file system and the SCSI device drivers 6 * It is mainly used for debugging, statistics and to pass 7 * information directly to the lowlevel driver. 8 * 9 * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de 10 * Version: 0.99.8 last change: 95/09/13 11 * 12 * generic command parser provided by: 13 * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de> 14 * 15 * generic_proc_info() support of xxxx_info() by: 16 * Michael A. Griffith <grif@acm.org> 17 */ 18 19 #include <linux/module.h> 20 #include <linux/init.h> 21 #include <linux/string.h> 22 #include <linux/mm.h> 23 #include <linux/slab.h> 24 #include <linux/proc_fs.h> 25 #include <linux/errno.h> 26 #include <linux/blkdev.h> 27 #include <linux/seq_file.h> 28 #include <asm/uaccess.h> 29 30 #include <scsi/scsi.h> 31 #include <scsi/scsi_device.h> 32 #include <scsi/scsi_host.h> 33 34 #include "scsi_priv.h" 35 #include "scsi_logging.h" 36 37 38 /* 4K page size, but our output routines, use some slack for overruns */ 39 #define PROC_BLOCK_SIZE (3*1024) 40 41 static struct proc_dir_entry *proc_scsi; 42 43 /* Protect sht->present and sht->proc_dir */ 44 static DECLARE_MUTEX(global_host_template_sem); 45 46 static int proc_scsi_read(char *buffer, char **start, off_t offset, 47 int length, int *eof, void *data) 48 { 49 struct Scsi_Host *shost = data; 50 int n; 51 52 n = shost->hostt->proc_info(shost, buffer, start, offset, length, 0); 53 *eof = (n < length); 54 55 return n; 56 } 57 58 static int proc_scsi_write_proc(struct file *file, const char __user *buf, 59 unsigned long count, void *data) 60 { 61 struct Scsi_Host *shost = data; 62 ssize_t ret = -ENOMEM; 63 char *page; 64 char *start; 65 66 if (count > PROC_BLOCK_SIZE) 67 return -EOVERFLOW; 68 69 page = (char *)__get_free_page(GFP_KERNEL); 70 if (page) { 71 ret = -EFAULT; 72 if (copy_from_user(page, buf, count)) 73 goto out; 74 ret = shost->hostt->proc_info(shost, page, &start, 0, count, 1); 75 } 76 out: 77 free_page((unsigned long)page); 78 return ret; 79 } 80 81 void scsi_proc_hostdir_add(struct scsi_host_template *sht) 82 { 83 if (!sht->proc_info) 84 return; 85 86 down(&global_host_template_sem); 87 if (!sht->present++) { 88 sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi); 89 if (!sht->proc_dir) 90 printk(KERN_ERR "%s: proc_mkdir failed for %s\n", 91 __FUNCTION__, sht->proc_name); 92 else 93 sht->proc_dir->owner = sht->module; 94 } 95 up(&global_host_template_sem); 96 } 97 98 void scsi_proc_hostdir_rm(struct scsi_host_template *sht) 99 { 100 if (!sht->proc_info) 101 return; 102 103 down(&global_host_template_sem); 104 if (!--sht->present && sht->proc_dir) { 105 remove_proc_entry(sht->proc_name, proc_scsi); 106 sht->proc_dir = NULL; 107 } 108 up(&global_host_template_sem); 109 } 110 111 void scsi_proc_host_add(struct Scsi_Host *shost) 112 { 113 struct scsi_host_template *sht = shost->hostt; 114 struct proc_dir_entry *p; 115 char name[10]; 116 117 if (!sht->proc_dir) 118 return; 119 120 sprintf(name,"%d", shost->host_no); 121 p = create_proc_read_entry(name, S_IFREG | S_IRUGO | S_IWUSR, 122 sht->proc_dir, proc_scsi_read, shost); 123 if (!p) { 124 printk(KERN_ERR "%s: Failed to register host %d in" 125 "%s\n", __FUNCTION__, shost->host_no, 126 sht->proc_name); 127 return; 128 } 129 130 p->write_proc = proc_scsi_write_proc; 131 p->owner = sht->module; 132 } 133 134 void scsi_proc_host_rm(struct Scsi_Host *shost) 135 { 136 char name[10]; 137 138 if (!shost->hostt->proc_dir) 139 return; 140 141 sprintf(name,"%d", shost->host_no); 142 remove_proc_entry(name, shost->hostt->proc_dir); 143 } 144 145 static int proc_print_scsidevice(struct device *dev, void *data) 146 { 147 struct scsi_device *sdev = to_scsi_device(dev); 148 struct seq_file *s = data; 149 int i; 150 151 seq_printf(s, 152 "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ", 153 sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); 154 for (i = 0; i < 8; i++) { 155 if (sdev->vendor[i] >= 0x20) 156 seq_printf(s, "%c", sdev->vendor[i]); 157 else 158 seq_printf(s, " "); 159 } 160 161 seq_printf(s, " Model: "); 162 for (i = 0; i < 16; i++) { 163 if (sdev->model[i] >= 0x20) 164 seq_printf(s, "%c", sdev->model[i]); 165 else 166 seq_printf(s, " "); 167 } 168 169 seq_printf(s, " Rev: "); 170 for (i = 0; i < 4; i++) { 171 if (sdev->rev[i] >= 0x20) 172 seq_printf(s, "%c", sdev->rev[i]); 173 else 174 seq_printf(s, " "); 175 } 176 177 seq_printf(s, "\n"); 178 179 seq_printf(s, " Type: %s ", 180 sdev->type < MAX_SCSI_DEVICE_CODE ? 181 scsi_device_types[(int) sdev->type] : "Unknown "); 182 seq_printf(s, " ANSI" 183 " SCSI revision: %02x", (sdev->scsi_level - 1) ? 184 sdev->scsi_level - 1 : 1); 185 if (sdev->scsi_level == 2) 186 seq_printf(s, " CCS\n"); 187 else 188 seq_printf(s, "\n"); 189 190 return 0; 191 } 192 193 static int scsi_add_single_device(uint host, uint channel, uint id, uint lun) 194 { 195 struct Scsi_Host *shost; 196 int error = -ENXIO; 197 198 shost = scsi_host_lookup(host); 199 if (IS_ERR(shost)) 200 return PTR_ERR(shost); 201 202 error = scsi_scan_host_selected(shost, channel, id, lun, 1); 203 scsi_host_put(shost); 204 return error; 205 } 206 207 static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun) 208 { 209 struct scsi_device *sdev; 210 struct Scsi_Host *shost; 211 int error = -ENXIO; 212 213 shost = scsi_host_lookup(host); 214 if (IS_ERR(shost)) 215 return PTR_ERR(shost); 216 sdev = scsi_device_lookup(shost, channel, id, lun); 217 if (sdev) { 218 scsi_remove_device(sdev); 219 scsi_device_put(sdev); 220 error = 0; 221 } 222 223 scsi_host_put(shost); 224 return error; 225 } 226 227 static ssize_t proc_scsi_write(struct file *file, const char __user *buf, 228 size_t length, loff_t *ppos) 229 { 230 int host, channel, id, lun; 231 char *buffer, *p; 232 int err; 233 234 if (!buf || length > PAGE_SIZE) 235 return -EINVAL; 236 237 buffer = (char *)__get_free_page(GFP_KERNEL); 238 if (!buffer) 239 return -ENOMEM; 240 241 err = -EFAULT; 242 if (copy_from_user(buffer, buf, length)) 243 goto out; 244 245 err = -EINVAL; 246 if (length < PAGE_SIZE) 247 buffer[length] = '\0'; 248 else if (buffer[PAGE_SIZE-1]) 249 goto out; 250 251 /* 252 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi 253 * with "0 1 2 3" replaced by your "Host Channel Id Lun". 254 */ 255 if (!strncmp("scsi add-single-device", buffer, 22)) { 256 p = buffer + 23; 257 258 host = simple_strtoul(p, &p, 0); 259 channel = simple_strtoul(p + 1, &p, 0); 260 id = simple_strtoul(p + 1, &p, 0); 261 lun = simple_strtoul(p + 1, &p, 0); 262 263 err = scsi_add_single_device(host, channel, id, lun); 264 if (err >= 0) 265 err = length; 266 267 /* 268 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi 269 * with "0 1 2 3" replaced by your "Host Channel Id Lun". 270 */ 271 } else if (!strncmp("scsi remove-single-device", buffer, 25)) { 272 p = buffer + 26; 273 274 host = simple_strtoul(p, &p, 0); 275 channel = simple_strtoul(p + 1, &p, 0); 276 id = simple_strtoul(p + 1, &p, 0); 277 lun = simple_strtoul(p + 1, &p, 0); 278 279 err = scsi_remove_single_device(host, channel, id, lun); 280 } 281 282 out: 283 free_page((unsigned long)buffer); 284 return err; 285 } 286 287 static int proc_scsi_show(struct seq_file *s, void *p) 288 { 289 seq_printf(s, "Attached devices:\n"); 290 bus_for_each_dev(&scsi_bus_type, NULL, s, proc_print_scsidevice); 291 return 0; 292 } 293 294 static int proc_scsi_open(struct inode *inode, struct file *file) 295 { 296 /* 297 * We don't really needs this for the write case but it doesn't 298 * harm either. 299 */ 300 return single_open(file, proc_scsi_show, NULL); 301 } 302 303 static struct file_operations proc_scsi_operations = { 304 .open = proc_scsi_open, 305 .read = seq_read, 306 .write = proc_scsi_write, 307 .llseek = seq_lseek, 308 .release = single_release, 309 }; 310 311 int __init scsi_init_procfs(void) 312 { 313 struct proc_dir_entry *pde; 314 315 proc_scsi = proc_mkdir("scsi", NULL); 316 if (!proc_scsi) 317 goto err1; 318 319 pde = create_proc_entry("scsi/scsi", 0, NULL); 320 if (!pde) 321 goto err2; 322 pde->proc_fops = &proc_scsi_operations; 323 324 return 0; 325 326 err2: 327 remove_proc_entry("scsi", NULL); 328 err1: 329 return -ENOMEM; 330 } 331 332 void scsi_exit_procfs(void) 333 { 334 remove_proc_entry("scsi/scsi", NULL); 335 remove_proc_entry("scsi", NULL); 336 } 337