File: /usr/src/linux/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/config.h>	/* for CONFIG_PROC_FS */
20     #define __NO_VERSION__
21     #include <linux/module.h>
22     
23     #include <linux/string.h>
24     #include <linux/mm.h>
25     #include <linux/slab.h>
26     #include <linux/proc_fs.h>
27     #include <linux/errno.h>
28     #include <linux/stat.h>
29     #include <linux/blk.h>
30     
31     #include <asm/uaccess.h>
32     
33     #include "scsi.h"
34     #include "hosts.h"
35     
36     #ifndef TRUE
37     #define TRUE  1
38     #define FALSE 0
39     #endif
40     
41     #ifdef CONFIG_PROC_FS
42     
43     /* generic_proc_info
44      * Used if the driver currently has no own support for /proc/scsi
45      */
46     int generic_proc_info(char *buffer, char **start, off_t offset, int length, 
47     		      const char *(*info) (struct Scsi_Host *),
48     		      struct Scsi_Host *sh)
49     {
50     	int len, pos, begin;
51     
52     	begin = 0;
53     	if (info && sh) {
54     		pos = len = sprintf(buffer, "%s\n", info(sh));
55     	} else {
56     		pos = len = sprintf(buffer,
57     			"The driver does not yet support the proc-fs\n");
58     	}
59     	if (pos < offset) {
60     		len = 0;
61     		begin = pos;
62     	}
63     	*start = buffer + (offset - begin);	/* Start of wanted data */
64     	len -= (offset - begin);
65     	if (len > length)
66     		len = length;
67     
68     	return (len);
69     }
70     
71     /* dispatch_scsi_info is the central dispatcher 
72      * It is the interface between the proc-fs and the SCSI subsystem code
73      */
74     static int proc_scsi_read(char *buffer, char **start, off_t offset,
75     	int length, int *eof, void *data)
76     {
77     	struct Scsi_Host *hpnt = data;
78     	int n;
79     
80     	if (hpnt->hostt->proc_info == NULL)
81     		n = generic_proc_info(buffer, start, offset, length,
82     				      hpnt->hostt->info, hpnt);
83     	else
84     		n = (hpnt->hostt->proc_info(buffer, start, offset,
85     					   length, hpnt->host_no, 0));
86     	*eof = (n<length);
87     	return n;
88     }
89     
90     #define PROC_BLOCK_SIZE (3*1024)     /* 4K page size, but our output routines 
91     				      * use some slack for overruns 
92     				      */
93     
94     static int proc_scsi_write(struct file * file, const char * buf,
95                                unsigned long count, void *data)
96     {
97     	struct Scsi_Host *hpnt = data;
98     	ssize_t ret = 0;
99     	char * page;
100     	char *start;
101         
102     	if (hpnt->hostt->proc_info == NULL)
103     		ret = -ENOSYS;
104     
105     	if (count > PROC_BLOCK_SIZE)
106     		return -EOVERFLOW;
107     
108     	if (!(page = (char *) __get_free_page(GFP_KERNEL)))
109     		return -ENOMEM;
110     	if(copy_from_user(page, buf, count))
111     	{
112     		free_page((ulong) page);
113     		return -EFAULT;
114     	}
115     
116     	ret = hpnt->hostt->proc_info(page, &start, 0, count,
117     				     hpnt->host_no, 1);
118     
119     	free_page((ulong) page);
120     	return(ret);
121     }
122     
123     void build_proc_dir_entries(Scsi_Host_Template * tpnt)
124     {
125     	struct Scsi_Host *hpnt;
126     	char name[10];	/* see scsi_unregister_host() */
127     
128     	tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
129             if (!tpnt->proc_dir) {
130                     printk(KERN_ERR "Unable to proc_mkdir in scsi.c/build_proc_dir_entries");
131                     return;
132             }
133     	tpnt->proc_dir->owner = tpnt->module;
134     
135     	hpnt = scsi_hostlist;
136     	while (hpnt) {
137     		if (tpnt == hpnt->hostt) {
138     			struct proc_dir_entry *p;
139     			sprintf(name,"%d",hpnt->host_no);
140     			p = create_proc_read_entry(name,
141     					S_IFREG | S_IRUGO | S_IWUSR,
142     					tpnt->proc_dir,
143     					proc_scsi_read,
144     					(void *)hpnt);
145     			if (!p)
146     				panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
147     			p->write_proc=proc_scsi_write;
148     			p->owner = tpnt->module;
149     		}
150     		hpnt = hpnt->next;
151     	}
152     }
153     
154     /*
155      *  parseHandle *parseInit(char *buf, char *cmdList, int cmdNum); 
156      *              gets a pointer to a null terminated data buffer
157      *              and a list of commands with blanks as delimiter 
158      *      in between. 
159      *      The commands have to be alphanumerically sorted. 
160      *      cmdNum has to contain the number of commands.
161      *              On success, a pointer to a handle structure
162      *              is returned, NULL on failure
163      *
164      *      int parseOpt(parseHandle *handle, char **param);
165      *              processes the next parameter. On success, the
166      *              index of the appropriate command in the cmdList
167      *              is returned, starting with zero.
168      *              param points to the null terminated parameter string.
169      *              On failure, -1 is returned.
170      *
171      *      The databuffer buf may only contain pairs of commands
172      *          options, separated by blanks:
173      *              <Command> <Parameter> [<Command> <Parameter>]*
174      */
175     
176     typedef struct {
177     	char *buf,		/* command buffer  */
178     	*cmdList,		/* command list    */
179     	*bufPos,		/* actual position */
180     	**cmdPos,		/* cmdList index   */
181     	 cmdNum;		/* cmd number      */
182     } parseHandle;
183     
184     inline int parseFree(parseHandle * handle)
185     {				/* free memory     */
186     	kfree(handle->cmdPos);
187     	kfree(handle);
188     
189     	return -1;
190     }
191     
192     parseHandle *parseInit(char *buf, char *cmdList, int cmdNum)
193     {
194     	char *ptr;		/* temp pointer    */
195     	parseHandle *handle;	/* new handle      */
196     
197     	if (!buf || !cmdList)	/* bad input ?     */
198     		return NULL;
199     	handle = (parseHandle *) kmalloc(sizeof(parseHandle), GFP_KERNEL);
200     	if (!handle)
201     		return NULL;	/* out of memory   */
202     	handle->cmdPos = (char **) kmalloc(sizeof(int) * cmdNum, GFP_KERNEL);
203     	if (!handle->cmdPos) {
204     		kfree(handle);
205     		return NULL;	/* out of memory   */
206     	}
207     	handle->buf = handle->bufPos = buf;	/* init handle     */
208     	handle->cmdList = cmdList;
209     	handle->cmdNum = cmdNum;
210     
211     	handle->cmdPos[cmdNum = 0] = cmdList;
212     	for (ptr = cmdList; *ptr; ptr++) {	/* scan command string */
213     		if (*ptr == ' ') {	/* and insert zeroes   */
214     			*ptr++ = 0;
215     			handle->cmdPos[++cmdNum] = ptr++;
216     		}
217     	}
218     	return handle;
219     }
220     
221     int parseOpt(parseHandle * handle, char **param)
222     {
223     	int cmdIndex = 0, cmdLen = 0;
224     	char *startPos;
225     
226     	if (!handle)		/* invalid handle  */
227     		return (parseFree(handle));
228     	/* skip spaces     */
229     	for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
230     	if (!*(handle->bufPos))
231     		return (parseFree(handle));	/* end of data     */
232     
233     	startPos = handle->bufPos;	/* store cmd start */
234     	for (; handle->cmdPos[cmdIndex][cmdLen] && *(handle->bufPos); handle->bufPos++) {	/* no string end?  */
235     		for (;;) {
236     			if (*(handle->bufPos) == handle->cmdPos[cmdIndex][cmdLen])
237     				break;	/* char matches ?  */
238     			else if (memcmp(startPos, (char *) (handle->cmdPos[++cmdIndex]), cmdLen))
239     				return (parseFree(handle));	/* unknown command */
240     
241     			if (cmdIndex >= handle->cmdNum)
242     				return (parseFree(handle));	/* unknown command */
243     		}
244     
245     		cmdLen++;	/* next char       */
246     	}
247     
248     	/* Get param. First skip all blanks, then insert zero after param  */
249     
250     	for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
251     	*param = handle->bufPos;
252     
253     	for (; *(handle->bufPos) && *(handle->bufPos) != ' '; handle->bufPos++);
254     	*(handle->bufPos++) = 0;
255     
256     	return (cmdIndex);
257     }
258     
259     void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
260     {
261     
262     	int x, y = *size;
263     	extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
264     
265     	y = sprintf(buffer + len,
266     	     "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n  Vendor: ",
267     		    scd->host->host_no, scd->channel, scd->id, scd->lun);
268     	for (x = 0; x < 8; x++) {
269     		if (scd->vendor[x] >= 0x20)
270     			y += sprintf(buffer + len + y, "%c", scd->vendor[x]);
271     		else
272     			y += sprintf(buffer + len + y, " ");
273     	}
274     	y += sprintf(buffer + len + y, " Model: ");
275     	for (x = 0; x < 16; x++) {
276     		if (scd->model[x] >= 0x20)
277     			y += sprintf(buffer + len + y, "%c", scd->model[x]);
278     		else
279     			y += sprintf(buffer + len + y, " ");
280     	}
281     	y += sprintf(buffer + len + y, " Rev: ");
282     	for (x = 0; x < 4; x++) {
283     		if (scd->rev[x] >= 0x20)
284     			y += sprintf(buffer + len + y, "%c", scd->rev[x]);
285     		else
286     			y += sprintf(buffer + len + y, " ");
287     	}
288     	y += sprintf(buffer + len + y, "\n");
289     
290     	y += sprintf(buffer + len + y, "  Type:   %s ",
291     		     scd->type < MAX_SCSI_DEVICE_CODE ?
292     	       scsi_device_types[(int) scd->type] : "Unknown          ");
293     	y += sprintf(buffer + len + y, "               ANSI"
294     		     " SCSI revision: %02x", (scd->scsi_level - 1) ? scd->scsi_level - 1 : 1);
295     	if (scd->scsi_level == 2)
296     		y += sprintf(buffer + len + y, " CCS\n");
297     	else
298     		y += sprintf(buffer + len + y, "\n");
299     
300     	*size = y;
301     	return;
302     }
303     
304     #else				/* if !CONFIG_PROC_FS */
305     
306     void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
307     {
308     }
309     
310     #endif				/* CONFIG_PROC_FS */
311     
312     /*
313      * Overrides for Emacs so that we get a uniform tabbing style.
314      * Emacs will notice this stuff at the end of the file and automatically
315      * adjust the settings for this buffer only.  This must remain at the end
316      * of the file.
317      * ---------------------------------------------------------------------------
318      * Local variables:
319      * c-indent-level: 4
320      * c-brace-imaginary-offset: 0
321      * c-brace-offset: -4
322      * c-argdecl-indent: 4
323      * c-label-offset: -4
324      * c-continued-statement-offset: 4
325      * c-continued-brace-offset: 0
326      * indent-tabs-mode: nil
327      * tab-width: 8
328      * End:
329      */
330