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