1 /*
2 * linux/fs/proc/scsi.c
3 * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
4 *
5 * The original version was derived from linux/fs/proc/net.c,
6 * which is Copyright (C) 1991, 1992 Linus Torvalds.
7 * Much has been rewritten, but some of the code still remains.
8 *
9 * /proc/scsi directory handling functions
10 *
11 * last change: 95/07/04
12 *
13 * Initial version: March '95
14 * 95/05/15 Added subdirectories for each driver and show every
15 * registered HBA as a single file.
16 * 95/05/30 Added rudimentary write support for parameter passing
17 * 95/07/04 Fixed bugs in directory handling
18 * 95/09/13 Update to support the new proc-dir tree
19 *
20 * TODO: Improve support to write to the driver files
21 * Add some more comments
22 */
23 #include <linux/errno.h>
24 #include <linux/sched.h>
25 #include <linux/proc_fs.h>
26 #include <linux/stat.h>
27 #include <linux/mm.h>
28
29 #include <asm/segment.h>
30
31 /* forward references */
32 static int proc_readscsi(struct inode * inode, struct file * file,
33 char * buf, int count);
34 static int proc_writescsi(struct inode * inode, struct file * file,
35 const char * buf, int count);
36 static int proc_scsilseek(struct inode *, struct file *, off_t, int);
37
38 extern void build_proc_dir_hba_entries(uint);
39
40 /* the *_get_info() functions are in the respective scsi driver code */
41 int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
42 off_t offset, int length, int inout) = 0;
43
44 static struct file_operations proc_scsi_operations = {
45 proc_scsilseek, /* lseek */
46 proc_readscsi, /* read */
47 proc_writescsi, /* write */
48 proc_readdir, /* readdir */
49 NULL, /* select */
50 NULL, /* ioctl */
51 NULL, /* mmap */
52 NULL, /* no special open code */
53 NULL, /* no special release code */
54 NULL /* can't fsync */
55 };
56
57 /*
58 * proc directories can do almost nothing..
59 */
60 struct inode_operations proc_scsi_inode_operations = {
61 &proc_scsi_operations, /* default scsi directory file-ops */
62 NULL, /* create */
63 proc_lookup, /* lookup */
64 NULL, /* link */
65 NULL, /* unlink */
66 NULL, /* symlink */
67 NULL, /* mkdir */
68 NULL, /* rmdir */
69 NULL, /* mknod */
70 NULL, /* rename */
71 NULL, /* readlink */
72 NULL, /* follow_link */
73 NULL, /* readpage */
74 NULL, /* writepage */
75 NULL, /* bmap */
76 NULL, /* truncate */
77 NULL /* permission */
78 };
79
80 int get_not_present_info(char *buffer, char **start, off_t offset, int length)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
81 {
82 int len, pos, begin;
83
84 begin = 0;
85 pos = len = sprintf(buffer,
86 "No low-level scsi modules are currently present\n");
87 if(pos < offset) {
88 len = 0;
89 begin = pos;
90 }
91
92 *start = buffer + (offset - begin); /* Start of wanted data */
93 len -= (offset - begin);
94 if(len > length)
95 len = length;
96
97 return(len);
98 }
99
100 #define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
101 * use some slack for overruns
102 */
103
104 static int proc_readscsi(struct inode * inode, struct file * file,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
105 char * buf, int count)
106 {
107 int length;
108 int bytes = count;
109 int copied = 0;
110 int thistime;
111 char * page;
112 char * start;
113
114 if (count < -1) /* Normally I wouldn't do this, */
115 return(-EINVAL); /* but it saves some redundant code.
116 * Now it is possible to seek to the
117 * end of the file */
118 if (!(page = (char *) __get_free_page(GFP_KERNEL)))
119 return(-ENOMEM);
120
121 while(bytes > 0 || count == -1) {
122 thistime = bytes;
123 if(bytes > PROC_BLOCK_SIZE || count == -1)
124 thistime = PROC_BLOCK_SIZE;
125
126 if(dispatch_scsi_info_ptr)
127 length = dispatch_scsi_info_ptr(inode->i_ino, page, &start,
128 file->f_pos, thistime, 0);
129 else
130 length = get_not_present_info(page, &start, file->f_pos, thistime);
131 if(length < 0) {
132 free_page((ulong) page);
133 return(length);
134 }
135
136 /*
137 * We have been given a non page aligned block of
138 * the data we asked for + a bit. We have been given
139 * the start pointer and we know the length..
140 */
141 if (length <= 0)
142 break;
143 /*
144 * Copy the bytes, if we're not doing a seek to
145 * the end of the file
146 */
147 if (count != -1)
148 memcpy_tofs(buf + copied, start, length);
149 file->f_pos += length; /* Move down the file */
150 bytes -= length;
151 copied += length;
152
153 if(length < thistime)
154 break; /* End of file */
155
156 }
157
158 free_page((ulong) page);
159 return(copied);
160 }
161
162
163 static int proc_writescsi(struct inode * inode, struct file * file,
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
164 const char * buf, int count)
165 {
166 int ret = 0;
167 char * page;
168
169 if(count > PROC_BLOCK_SIZE) {
170 return(-EOVERFLOW);
171 }
172
173 if(dispatch_scsi_info_ptr != NULL) {
174 if (!(page = (char *) __get_free_page(GFP_KERNEL)))
175 return(-ENOMEM);
176 memcpy_fromfs(page, buf, count);
177 ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
178 } else
179 return(-ENOPKG); /* Nothing here */
180
181 free_page((ulong) page);
182 return(ret);
183 }
184
185
186 static int proc_scsilseek(struct inode * inode, struct file * file,
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
187 off_t offset, int orig)
188 {
189 switch (orig) {
190 case 0:
191 file->f_pos = offset;
192 return(file->f_pos);
193 case 1:
194 file->f_pos += offset;
195 return(file->f_pos);
196 case 2: /* This ugly hack allows us to */
197 if (offset) /* to determine the length of the */
198 return(-EINVAL); /* file and then later savely to */
199 proc_readscsi(inode, file, 0, -1); /* seek in it */
200 return(file->f_pos);
201 default:
202 return(-EINVAL);
203 }
204 }
205
206 /*
207 * Overrides for Emacs so that we almost follow Linus's tabbing style.
208 * Emacs will notice this stuff at the end of the file and automatically
209 * adjust the settings for this buffer only. This must remain at the end
210 * of the file.
211 * ---------------------------------------------------------------------------
212 * Local variables:
213 * c-indent-level: 4
214 * c-brace-imaginary-offset: 0
215 * c-brace-offset: -4
216 * c-argdecl-indent: 4
217 * c-label-offset: -4
218 * c-continued-statement-offset: 4
219 * c-continued-brace-offset: 0
220 * indent-tabs-mode: nil
221 * tab-width: 8
222 * End:
223 */