root/fs/proc/scsi.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. count_dir_entries
  2. proc_lookupscsi
  3. get_not_present_info
  4. proc_readscsi
  5. proc_writescsi
  6. proc_scsilseek

   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  *
  19  *  TODO: Improve support to write to the driver files
  20  *        Optimize directory handling 
  21  *        Add some more comments
  22  */
  23 #include <linux/autoconf.h>
  24 #include <asm/segment.h>
  25 #include <linux/errno.h>
  26 #include <linux/sched.h>
  27 #include <linux/proc_fs.h>
  28 #include <linux/stat.h>
  29 #include <linux/config.h>
  30 #include <linux/mm.h>
  31 
  32 /* forward references */
  33 static int proc_readscsi(struct inode * inode, struct file * file,
  34                          char * buf, int count);
  35 static int proc_writescsi(struct inode * inode, struct file * file,
  36                          const char * buf, int count);
  37 static int proc_lookupscsi(struct inode *,const char *,int,struct inode **);
  38 static int proc_scsilseek(struct inode *, struct file *, off_t, int);
  39 
  40 extern uint count_templates(void);
  41 extern void build_proc_dir_hba_entries(uint);
  42 
  43 /* the *_get_info() functions are in the respective scsi driver code */
  44 extern int (* dispatch_scsi_info_ptr)(int, char *, char **, off_t, int, int);
  45     
  46     
  47 static struct file_operations proc_scsi_operations = {
  48     proc_scsilseek,     /* lseek   */
  49     proc_readscsi,      /* read    */
  50     proc_writescsi,     /* write   */
  51     NULL,               /* readdir */
  52     NULL,               /* select  */
  53     NULL,               /* ioctl   */
  54     NULL,               /* mmap    */
  55     NULL,               /* no special open code    */
  56     NULL,               /* no special release code */
  57     NULL                /* can't fsync */
  58 };
  59 
  60 /*
  61  * proc directories can do almost nothing..
  62  */
  63 struct inode_operations proc_scsi_inode_operations = {
  64     &proc_scsi_operations,  /* default scsi directory file-ops */
  65     NULL,           /* create      */
  66     proc_lookupscsi,/* lookup      */
  67     NULL,           /* link        */
  68     NULL,           /* unlink      */
  69     NULL,           /* symlink     */
  70     NULL,           /* mkdir       */
  71     NULL,           /* rmdir       */
  72     NULL,           /* mknod       */
  73     NULL,           /* rename      */
  74     NULL,           /* readlink    */
  75     NULL,           /* follow_link */
  76     NULL,           /* bmap        */
  77     NULL,           /* truncate    */
  78     NULL            /* permission  */
  79 };
  80 
  81 struct proc_dir_entry scsi_dir[PROC_SCSI_FILE - PROC_SCSI_SCSI + 3]; 
  82 struct proc_dir_entry scsi_hba_dir[(PROC_SCSI_LAST - PROC_SCSI_FILE) * 4]; 
  83 
  84 static struct proc_dir_entry scsi_dir2[] = {
  85     { PROC_SCSI,                 1, "." },
  86     { PROC_ROOT_INO,             2, ".." },
  87     { PROC_SCSI_NOT_PRESENT,    11, "not.present" },
  88     { 0,                         0, NULL }
  89 };
  90 
  91 inline static uint count_dir_entries(uint inode, uint *num)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93     struct proc_dir_entry *dir;
  94     uint index, flag;
  95 
  96     (uint) *num = flag = index = 0;    
  97     
  98     if(dispatch_scsi_info_ptr) {
  99         if (inode == PROC_SCSI) { 
 100             dir = scsi_dir;
 101             while(dir[(uint)*num].low_ino)
 102                 (*num)++;
 103         } else {
 104             /* Here we do not simply count the entries. Since the array
 105              * contains the directories of all drivers, we need to return
 106              * a pointer to the beginning of the directory information
 107              * and its length.
 108              */
 109             dir = scsi_hba_dir;
 110             while(dir[index].low_ino || dir[index].low_ino <= PROC_SCSI_LAST) {
 111                 if(dir[index].low_ino == inode)
 112                     flag = 1;
 113                 if(dir[index].low_ino == 0) {
 114                     if(flag == 1)
 115                         break;
 116                     else
 117                         *num = 0;
 118                 } else {
 119                     (*num)++;
 120                 }
 121                 index++;
 122             }
 123             return(index - (*num));
 124         }
 125     }
 126     else {
 127         dir = scsi_dir2;
 128         while(dir[(uint)*num].low_ino)
 129             (*num)++;
 130     }   
 131     return(0);
 132 }
 133 
 134 static int proc_lookupscsi(struct inode * dir, const char * name, int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 135                            struct inode ** result)
 136 {
 137     struct proc_dir_entry *de = NULL;
 138 
 139     *result = NULL;
 140     if (!dir)
 141         return(-ENOENT);
 142     if (!S_ISDIR(dir->i_mode)) {
 143         iput(dir);
 144         return(-ENOENT);
 145     }
 146     if (dispatch_scsi_info_ptr != NULL) {
 147         if (dir->i_ino <= PROC_SCSI_SCSI)
 148             de = scsi_dir;
 149         else {
 150             de = &scsi_hba_dir[dispatch_scsi_info_ptr(dir->i_ino, 0, 0, 0, 0, 2)];
 151         }
 152     }
 153     else
 154         de = scsi_dir2;
 155     
 156     for (; de->name ; de++) {
 157         if (!proc_match(len, name, de))
 158             continue;
 159         *result = proc_get_inode(dir->i_sb, de->low_ino, de);
 160         iput(dir);
 161         if (!*result)
 162             return(-ENOENT);
 163         return(0);
 164     }
 165     iput(dir);
 166     return(-ENOENT);
 167 }
 168 
 169 int get_not_present_info(char *buffer, char **start, off_t offset, int length)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     int len, pos, begin;
 172     
 173     begin = 0;
 174     pos = len = sprintf(buffer, 
 175                         "No low-level scsi modules are currently present\n");
 176     if(pos < offset) {
 177         len = 0;
 178         begin = pos;
 179     }
 180     
 181     *start = buffer + (offset - begin);   /* Start of wanted data */
 182     len -= (offset - begin);
 183     if(len > length)
 184         len = length;
 185     
 186     return(len);
 187 }
 188 
 189 #define PROC_BLOCK_SIZE (3*1024)     /* 4K page size, but our output routines 
 190                                       * use some slack for overruns 
 191                                       */
 192 
 193 static int proc_readscsi(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 194                          char * buf, int count)
 195 {
 196     int length;
 197     int bytes = count;
 198     int copied = 0;
 199     int thistime;
 200     char * page;
 201     char * start;
 202     
 203     if (count < -1)               /* Normally I wouldn't do this, */ 
 204         return(-EINVAL);          /* but it saves some redundant code.
 205                                    * Now it is possible to seek to the 
 206                                    * end of the file */
 207     if (!(page = (char *) __get_free_page(GFP_KERNEL)))
 208         return(-ENOMEM);
 209     
 210     while(bytes > 0 || count == -1) {   
 211         thistime = bytes;
 212         if(bytes > PROC_BLOCK_SIZE || count == -1)
 213             thistime = PROC_BLOCK_SIZE;
 214         
 215         if(dispatch_scsi_info_ptr)
 216             length = dispatch_scsi_info_ptr(inode->i_ino, page, &start, 
 217                                             file->f_pos, thistime, 0);
 218         else
 219             length = get_not_present_info(page, &start, file->f_pos, thistime);
 220         if(length < 0) {
 221             free_page((ulong) page);
 222             return(length);
 223         }
 224         
 225         /*
 226          *  We have been given a non page aligned block of
 227          *  the data we asked for + a bit. We have been given
 228          *  the start pointer and we know the length.. 
 229          */
 230         if (length <= 0)
 231             break;
 232         /*
 233          *  Copy the bytes, if we're not doing a seek to 
 234          *      the end of the file 
 235          */
 236         if (count != -1)
 237             memcpy_tofs(buf + copied, start, length);
 238         file->f_pos += length;  /* Move down the file */
 239         bytes -= length;
 240         copied += length;
 241         
 242         if(length < thistime)
 243             break;  /* End of file */
 244         
 245     }
 246     
 247     free_page((ulong) page);
 248     return(copied);
 249 }
 250 
 251 
 252 static int proc_writescsi(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 253                          const char * buf, int count)
 254 {
 255     int ret = 0;
 256     char * page;
 257     
 258     if (!(page = (char *) __get_free_page(GFP_KERNEL)))
 259         return(-ENOMEM);
 260 
 261     if(count > PROC_BLOCK_SIZE) {
 262         return(-EOVERFLOW);
 263     }
 264 
 265     if(dispatch_scsi_info_ptr != NULL) {
 266         memcpy_fromfs(page, buf, count);
 267         ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
 268     } else {
 269         free_page((ulong) page);   
 270         return(-ENOPKG);          /* Nothing here */
 271     }
 272     
 273     free_page((ulong) page);
 274     return(ret);
 275 }
 276 
 277 
 278 static int proc_scsilseek(struct inode * inode, struct file * file, 
     /* [previous][next][first][last][top][bottom][index][help] */
 279                           off_t offset, int orig)
 280 {
 281     switch (orig) {
 282     case 0:
 283         file->f_pos = offset;
 284         return(file->f_pos);
 285     case 1:
 286         file->f_pos += offset;
 287         return(file->f_pos);
 288     case 2:                  /* This ugly hack allows us to    */
 289         if (offset)          /* to determine the length of the */
 290             return(-EINVAL); /* file and then later savely to  */ 
 291         proc_readscsi(inode, file, 0, -1); /* seek in it       */ 
 292         return(file->f_pos);
 293     default:
 294         return(-EINVAL);
 295     }
 296 }
 297 
 298 /*
 299  * Overrides for Emacs so that we almost follow Linus's tabbing style.
 300  * Emacs will notice this stuff at the end of the file and automatically
 301  * adjust the settings for this buffer only.  This must remain at the end
 302  * of the file.
 303  * ---------------------------------------------------------------------------
 304  * Local variables:
 305  * c-indent-level: 4
 306  * c-brace-imaginary-offset: 0
 307  * c-brace-offset: -4
 308  * c-argdecl-indent: 4
 309  * c-label-offset: -4
 310  * c-continued-statement-offset: 4
 311  * c-continued-brace-offset: 0
 312  * indent-tabs-mode: nil
 313  * tab-width: 8
 314  * End:
 315  */

/* [previous][next][first][last][top][bottom][index][help] */