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/malloc.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 (count > PROC_BLOCK_SIZE)
103 return -EOVERFLOW;
104
105 if (!(page = (char *) __get_free_page(GFP_KERNEL)))
106 return -ENOMEM;
107 copy_from_user(page, buf, count);
108
109 if (hpnt->hostt->proc_info == NULL)
110 ret = -ENOSYS;
111 else
112 ret = hpnt->hostt->proc_info(page, &start, 0, count,
113 hpnt->host_no, 1);
114 free_page((ulong) page);
115 return(ret);
116 }
117
118 void build_proc_dir_entries(Scsi_Host_Template * tpnt)
119 {
120 struct Scsi_Host *hpnt;
121 char name[10]; /* see scsi_unregister_host() */
122
123 tpnt->proc_dir = proc_mkdir(tpnt->proc_name, proc_scsi);
124 tpnt->proc_dir->owner = tpnt->module;
125
126 hpnt = scsi_hostlist;
127 while (hpnt) {
128 if (tpnt == hpnt->hostt) {
129 struct proc_dir_entry *p;
130 sprintf(name,"%d",hpnt->host_no);
131 p = create_proc_read_entry(name,
132 S_IFREG | S_IRUGO | S_IWUSR,
133 tpnt->proc_dir,
134 proc_scsi_read,
135 (void *)hpnt);
136 if (!p)
137 panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
138 p->write_proc=proc_scsi_write;
139 p->owner = tpnt->module;
140 }
141 hpnt = hpnt->next;
142 }
143 }
144
145 /*
146 * parseHandle *parseInit(char *buf, char *cmdList, int cmdNum);
147 * gets a pointer to a null terminated data buffer
148 * and a list of commands with blanks as delimiter
149 * in between.
150 * The commands have to be alphanumerically sorted.
151 * cmdNum has to contain the number of commands.
152 * On success, a pointer to a handle structure
153 * is returned, NULL on failure
154 *
155 * int parseOpt(parseHandle *handle, char **param);
156 * processes the next parameter. On success, the
157 * index of the appropriate command in the cmdList
158 * is returned, starting with zero.
159 * param points to the null terminated parameter string.
160 * On failure, -1 is returned.
161 *
162 * The databuffer buf may only contain pairs of commands
163 * options, separated by blanks:
164 * <Command> <Parameter> [<Command> <Parameter>]*
165 */
166
167 typedef struct {
168 char *buf, /* command buffer */
169 *cmdList, /* command list */
170 *bufPos, /* actual position */
171 **cmdPos, /* cmdList index */
172 cmdNum; /* cmd number */
173 } parseHandle;
174
175 inline int parseFree(parseHandle * handle)
176 { /* free memory */
177 kfree(handle->cmdPos);
178 kfree(handle);
179
180 return -1;
181 }
182
183 parseHandle *parseInit(char *buf, char *cmdList, int cmdNum)
184 {
185 char *ptr; /* temp pointer */
186 parseHandle *handle; /* new handle */
187
188 if (!buf || !cmdList) /* bad input ? */
189 return NULL;
190 handle = (parseHandle *) kmalloc(sizeof(parseHandle), GFP_KERNEL);
191 if (!handle)
192 return NULL; /* out of memory */
193 handle->cmdPos = (char **) kmalloc(sizeof(int) * cmdNum, GFP_KERNEL);
194 if (!handle->cmdPos) {
195 kfree(handle);
196 return NULL; /* out of memory */
197 }
198 handle->buf = handle->bufPos = buf; /* init handle */
199 handle->cmdList = cmdList;
200 handle->cmdNum = cmdNum;
201
202 handle->cmdPos[cmdNum = 0] = cmdList;
203 for (ptr = cmdList; *ptr; ptr++) { /* scan command string */
204 if (*ptr == ' ') { /* and insert zeroes */
205 *ptr++ = 0;
206 handle->cmdPos[++cmdNum] = ptr++;
207 }
208 }
209 return handle;
210 }
211
212 int parseOpt(parseHandle * handle, char **param)
213 {
214 int cmdIndex = 0, cmdLen = 0;
215 char *startPos;
216
217 if (!handle) /* invalid handle */
218 return (parseFree(handle));
219 /* skip spaces */
220 for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
221 if (!*(handle->bufPos))
222 return (parseFree(handle)); /* end of data */
223
224 startPos = handle->bufPos; /* store cmd start */
225 for (; handle->cmdPos[cmdIndex][cmdLen] && *(handle->bufPos); handle->bufPos++) { /* no string end? */
226 for (;;) {
227 if (*(handle->bufPos) == handle->cmdPos[cmdIndex][cmdLen])
228 break; /* char matches ? */
229 else if (memcmp(startPos, (char *) (handle->cmdPos[++cmdIndex]), cmdLen))
230 return (parseFree(handle)); /* unknown command */
231
232 if (cmdIndex >= handle->cmdNum)
233 return (parseFree(handle)); /* unknown command */
234 }
235
236 cmdLen++; /* next char */
237 }
238
239 /* Get param. First skip all blanks, then insert zero after param */
240
241 for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
242 *param = handle->bufPos;
243
244 for (; *(handle->bufPos) && *(handle->bufPos) != ' '; handle->bufPos++);
245 *(handle->bufPos++) = 0;
246
247 return (cmdIndex);
248 }
249
250 void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
251 {
252
253 int x, y = *size;
254 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
255
256 y = sprintf(buffer + len,
257 "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
258 scd->host->host_no, scd->channel, scd->id, scd->lun);
259 for (x = 0; x < 8; x++) {
260 if (scd->vendor[x] >= 0x20)
261 y += sprintf(buffer + len + y, "%c", scd->vendor[x]);
262 else
263 y += sprintf(buffer + len + y, " ");
264 }
265 y += sprintf(buffer + len + y, " Model: ");
266 for (x = 0; x < 16; x++) {
267 if (scd->model[x] >= 0x20)
268 y += sprintf(buffer + len + y, "%c", scd->model[x]);
269 else
270 y += sprintf(buffer + len + y, " ");
271 }
272 y += sprintf(buffer + len + y, " Rev: ");
273 for (x = 0; x < 4; x++) {
274 if (scd->rev[x] >= 0x20)
275 y += sprintf(buffer + len + y, "%c", scd->rev[x]);
276 else
277 y += sprintf(buffer + len + y, " ");
278 }
279 y += sprintf(buffer + len + y, "\n");
280
281 y += sprintf(buffer + len + y, " Type: %s ",
282 scd->type < MAX_SCSI_DEVICE_CODE ?
283 scsi_device_types[(int) scd->type] : "Unknown ");
284 y += sprintf(buffer + len + y, " ANSI"
285 " SCSI revision: %02x", (scd->scsi_level - 1) ? scd->scsi_level - 1 : 1);
286 if (scd->scsi_level == 2)
287 y += sprintf(buffer + len + y, " CCS\n");
288 else
289 y += sprintf(buffer + len + y, "\n");
290
291 *size = y;
292 return;
293 }
294
295 #else /* if !CONFIG_PROC_FS */
296
297 void proc_print_scsidevice(Scsi_Device * scd, char *buffer, int *size, int len)
298 {
299 }
300
301 #endif /* CONFIG_PROC_FS */
302
303 /*
304 * Overrides for Emacs so that we get a uniform tabbing style.
305 * Emacs will notice this stuff at the end of the file and automatically
306 * adjust the settings for this buffer only. This must remain at the end
307 * of the file.
308 * ---------------------------------------------------------------------------
309 * Local variables:
310 * c-indent-level: 4
311 * c-brace-imaginary-offset: 0
312 * c-brace-offset: -4
313 * c-argdecl-indent: 4
314 * c-label-offset: -4
315 * c-continued-statement-offset: 4
316 * c-continued-brace-offset: 0
317 * indent-tabs-mode: nil
318 * tab-width: 8
319 * End:
320 */
321
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.