Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / drivers / scsi / scsi_proc.c
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 }