patch-1.3.5 linux/fs/proc/scsi.c
Next file: linux/include/asm-alpha/io.h
Previous file: linux/fs/proc/root.c
Back to the patch index
Back to the overall index
- Lines: 324
- Date:
Thu Jun 29 10:37:14 1995
- Orig file:
v1.3.4/linux/fs/proc/scsi.c
- Orig date:
Thu Jan 1 02:00:00 1970
diff -u --recursive --new-file v1.3.4/linux/fs/proc/scsi.c linux/fs/proc/scsi.c
@@ -0,0 +1,323 @@
+/*
+ * linux/fs/proc/scsi.c
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ *
+ * The original version was derived from linux/fs/proc/net.c,
+ * which is Copyright (C) 1991, 1992 Linus Torvalds.
+ * Much has been rewritten, but some of the code still remains.
+ *
+ * /proc/scsi directory handling functions
+ *
+ * last change: 95/06/13
+ *
+ * Initial version: March '95
+ * 95/15/05 Added subdirectories for each driver and show every
+ * registered HBA as a single file.
+ * 95/30/05 Added rudimentary write support for parameter passing
+ *
+ * TODO: Improve support to write to the driver files
+ * Optimize directory handling
+ * Add some more comments
+ */
+#include <linux/autoconf.h>
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <linux/mm.h>
+
+/* forward references */
+static int proc_readscsi(struct inode * inode, struct file * file,
+ char * buf, int count);
+static int proc_writescsi(struct inode * inode, struct file * file,
+ char * buf, int count);
+static int proc_readscsidir(struct inode *, struct file *,
+ void *, filldir_t filldir);
+static int proc_lookupscsi(struct inode *,const char *,int,struct inode **);
+static int proc_scsilseek(struct inode *, struct file *, off_t, int);
+
+extern uint count_templates(void);
+extern void build_proc_dir_hba_entries(uint);
+
+/* the *_get_info() functions are in the respective scsi driver code */
+extern int (* dispatch_scsi_info_ptr)(int, char *, char **, off_t, int, int);
+
+
+static struct file_operations proc_scsi_operations = {
+ proc_scsilseek, /* lseek */
+ proc_readscsi, /* read */
+ proc_writescsi, /* write */
+ proc_readscsidir, /* readdir */
+ NULL, /* select */
+ NULL, /* ioctl */
+ NULL, /* mmap */
+ NULL, /* no special open code */
+ NULL, /* no special release code */
+ NULL /* can't fsync */
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+struct inode_operations proc_scsi_inode_operations = {
+ &proc_scsi_operations, /* default scsi directory file-ops */
+ NULL, /* create */
+ proc_lookupscsi,/* lookup */
+ NULL, /* link */
+ NULL, /* unlink */
+ NULL, /* symlink */
+ NULL, /* mkdir */
+ NULL, /* rmdir */
+ NULL, /* mknod */
+ NULL, /* rename */
+ NULL, /* readlink */
+ NULL, /* follow_link */
+ NULL, /* bmap */
+ NULL, /* truncate */
+ NULL /* permission */
+};
+
+struct proc_dir_entry scsi_dir[PROC_SCSI_FILE - PROC_SCSI_SCSI + 3];
+struct proc_dir_entry scsi_hba_dir[(PROC_SCSI_LAST - PROC_SCSI_FILE) * 4];
+
+static struct proc_dir_entry scsi_dir2[] = {
+ { PROC_SCSI, 1, "." },
+ { PROC_ROOT_INO, 2, ".." },
+ { PROC_SCSI_NOT_PRESENT, 11, "not.present" },
+ { 0, 0, NULL }
+};
+
+inline static uint count_dir_entries(uint inode)
+{
+ struct proc_dir_entry *dir;
+ uint i = 0;
+
+
+ if(dispatch_scsi_info_ptr)
+ if (inode <= PROC_SCSI_SCSI_DEBUG)
+ dir = scsi_dir;
+ else
+ dir = scsi_hba_dir;
+ else dir = scsi_dir2;
+
+ while(dir[i].low_ino)
+ i++;
+
+ return(i);
+}
+
+static int proc_lookupscsi(struct inode * dir, const char * name, int len,
+ struct inode ** result)
+{
+ struct proc_dir_entry *de = NULL;
+
+ *result = NULL;
+ if (!dir)
+ return(-ENOENT);
+ if (!S_ISDIR(dir->i_mode)) {
+ iput(dir);
+ return(-ENOENT);
+ }
+ if (dispatch_scsi_info_ptr != NULL)
+ if (dir->i_ino <= PROC_SCSI_SCSI)
+ de = scsi_dir;
+ else {
+ de = &scsi_hba_dir[dispatch_scsi_info_ptr(dir->i_ino, 0, 0, 0, 0, 2)];
+ }
+ else
+ de = scsi_dir2;
+
+ for (; de->name ; de++) {
+ if (!proc_match(len, name, de))
+ continue;
+ *result = iget(dir->i_sb, de->low_ino);
+ iput(dir);
+ if (!*result)
+ return(-ENOENT);
+ return(0);
+ }
+ iput(dir);
+ return(-ENOENT);
+}
+
+static int proc_readscsidir(struct inode * inode, struct file * filp,
+ void * dirent, filldir_t filldir)
+{
+ struct proc_dir_entry * de;
+ unsigned int ino;
+
+ if (!inode || !S_ISDIR(inode->i_mode))
+ return(-EBADF);
+ ino = inode->i_ino;
+ while (((unsigned) filp->f_pos) < count_dir_entries(ino)) {
+ if (dispatch_scsi_info_ptr)
+ if (ino <= PROC_SCSI_SCSI)
+ de = scsi_dir + filp->f_pos;
+ else
+ de = scsi_hba_dir + filp->f_pos;
+ else
+ de = scsi_dir2 + filp->f_pos;
+ if (filldir(dirent, de->name, de->namelen, filp->f_pos, de->low_ino)<0)
+ break;
+ filp->f_pos++;
+ }
+ return(0);
+}
+
+int get_not_present_info(char *buffer, char **start, off_t offset, int length)
+{
+ int len, pos, begin;
+
+ begin = 0;
+ pos = len = sprintf(buffer,
+ "The scsi core module is currently not present\n");
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+
+ return(len);
+}
+
+#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
+ * use some slack for overruns
+ */
+
+static int proc_readscsi(struct inode * inode, struct file * file,
+ char * buf, int count)
+{
+ uint ino;
+ int length;
+ int bytes = count;
+ int copied = 0;
+ int thistime;
+ char * page;
+ char * start;
+
+ if (count < -1) /* Normally I wouldn't do this, */
+ return(-EINVAL); /* but it saves some redundant code.
+ * Now it is possible to seek to the
+ * end of the file */
+ if (!(page = (char *) __get_free_page(GFP_KERNEL)))
+ return(-ENOMEM);
+ ino = inode->i_ino;
+
+
+ while(bytes > 0 || count == -1)
+ {
+
+ thistime = bytes;
+ if(bytes > PROC_BLOCK_SIZE || count == -1)
+ thistime = PROC_BLOCK_SIZE;
+
+ if(dispatch_scsi_info_ptr)
+ length = dispatch_scsi_info_ptr(ino, page, &start,
+ file->f_pos, thistime, 0);
+ else
+ length = get_not_present_info(page, &start, file->f_pos, thistime);
+ if(length < 0) {
+ free_page((ulong) page);
+ return(length);
+ }
+
+ /*
+ * We have been given a non page aligned block of
+ * the data we asked for + a bit. We have been given
+ * the start pointer and we know the length..
+ */
+ if (length <= 0)
+ break;
+ /*
+ * Copy the bytes, if we're not doing a seek to
+ * the end of the file
+ */
+ if (count != -1)
+ memcpy_tofs(buf + copied, start, length);
+ file->f_pos += length; /* Move down the file */
+ bytes -= length;
+ copied += length;
+
+ if(length < thistime)
+ break; /* End of file */
+
+ }
+
+ free_page((ulong) page);
+ return(copied);
+}
+
+
+static int proc_writescsi(struct inode * inode, struct file * file,
+ char * buf, int count)
+{
+ uint ino;
+ int ret = 0;
+ char * page;
+
+ if (!(page = (char *) __get_free_page(GFP_KERNEL)))
+ return(-ENOMEM);
+
+ if(count > PROC_BLOCK_SIZE) {
+ return(-EOVERFLOW);
+ }
+
+ ino = inode->i_ino;
+
+ if(dispatch_scsi_info_ptr != NULL) {
+ memcpy_fromfs(page, buf, count);
+ ret = dispatch_scsi_info_ptr(ino, page, 0, 0, count, 1);
+ } else {
+ free_page((ulong) page);
+ return(-ENOPKG); /* Nothing here */
+ }
+
+ free_page((ulong) page);
+ return(ret);
+}
+
+
+static int proc_scsilseek(struct inode * inode, struct file * file,
+ off_t offset, int orig)
+{
+ switch (orig) {
+ case 0:
+ file->f_pos = offset;
+ return(file->f_pos);
+ case 1:
+ file->f_pos += offset;
+ return(file->f_pos);
+ case 2: /* This ugly hack allows us to */
+ if (offset) /* to determine the length of the */
+ return(-EINVAL); /* file and then later savely to */
+ proc_readscsi(inode, file, 0, -1); /* seek in it */
+ return(file->f_pos);
+ default:
+ return(-EINVAL);
+ }
+}
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only. This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 4
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -4
+ * c-argdecl-indent: 4
+ * c-label-offset: -4
+ * c-continued-statement-offset: 4
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 4
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this