xref: /openbmc/linux/drivers/scsi/scsi_proc.c (revision 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2)
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