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  */
generic_proc_info(char * buffer,char ** start,off_t offset,int length,const char * (* info)(struct Scsi_Host *),struct Scsi_Host * sh)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  */
proc_scsi_read(char * buffer,char ** start,off_t offset,int length,int * eof,void * data)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 
proc_scsi_write(struct file * file,const char * buf,unsigned long count,void * data)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 
build_proc_dir_entries(Scsi_Host_Template * tpnt)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 
parseFree(parseHandle * handle)184 inline int parseFree(parseHandle * handle)
185 {				/* free memory     */
186 	kfree(handle->cmdPos);
187 	kfree(handle);
188 
189 	return -1;
190 }
191 
parseInit(char * buf,char * cmdList,int cmdNum)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 
parseOpt(parseHandle * handle,char ** param)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 
proc_print_scsidevice(Scsi_Device * scd,char * buffer,int * size,int len)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 
proc_print_scsidevice(Scsi_Device * scd,char * buffer,int * size,int len)306 void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
307 {
308 }
309 
310 #endif				/* CONFIG_PROC_FS */
311