/*
|
/*
|
* linux/fs/proc/scsi.c
|
* linux/fs/proc/scsi.c
|
* (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
|
* (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
|
*
|
*
|
* The original version was derived from linux/fs/proc/net.c,
|
* The original version was derived from linux/fs/proc/net.c,
|
* which is Copyright (C) 1991, 1992 Linus Torvalds.
|
* which is Copyright (C) 1991, 1992 Linus Torvalds.
|
* Much has been rewritten, but some of the code still remains.
|
* Much has been rewritten, but some of the code still remains.
|
*
|
*
|
* /proc/scsi directory handling functions
|
* /proc/scsi directory handling functions
|
*
|
*
|
* last change: 95/07/04
|
* last change: 95/07/04
|
*
|
*
|
* Initial version: March '95
|
* Initial version: March '95
|
* 95/05/15 Added subdirectories for each driver and show every
|
* 95/05/15 Added subdirectories for each driver and show every
|
* registered HBA as a single file.
|
* registered HBA as a single file.
|
* 95/05/30 Added rudimentary write support for parameter passing
|
* 95/05/30 Added rudimentary write support for parameter passing
|
* 95/07/04 Fixed bugs in directory handling
|
* 95/07/04 Fixed bugs in directory handling
|
* 95/09/13 Update to support the new proc-dir tree
|
* 95/09/13 Update to support the new proc-dir tree
|
*
|
*
|
* TODO: Improve support to write to the driver files
|
* TODO: Improve support to write to the driver files
|
* Add some more comments
|
* Add some more comments
|
*/
|
*/
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/proc_fs.h>
|
#include <linux/proc_fs.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
/* forward references */
|
/* forward references */
|
static int proc_readscsi(struct inode * inode, struct file * file,
|
static int proc_readscsi(struct inode * inode, struct file * file,
|
char * buf, int count);
|
char * buf, int count);
|
static int proc_writescsi(struct inode * inode, struct file * file,
|
static int proc_writescsi(struct inode * inode, struct file * file,
|
const char * buf, int count);
|
const char * buf, int count);
|
static int proc_scsilseek(struct inode *, struct file *, off_t, int);
|
static int proc_scsilseek(struct inode *, struct file *, off_t, int);
|
|
|
extern void build_proc_dir_hba_entries(uint);
|
extern void build_proc_dir_hba_entries(uint);
|
|
|
/* the *_get_info() functions are in the respective scsi driver code */
|
/* the *_get_info() functions are in the respective scsi driver code */
|
int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
|
int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
|
off_t offset, int length, int inout) = 0;
|
off_t offset, int length, int inout) = 0;
|
|
|
static struct file_operations proc_scsi_operations = {
|
static struct file_operations proc_scsi_operations = {
|
proc_scsilseek, /* lseek */
|
proc_scsilseek, /* lseek */
|
proc_readscsi, /* read */
|
proc_readscsi, /* read */
|
proc_writescsi, /* write */
|
proc_writescsi, /* write */
|
proc_readdir, /* readdir */
|
proc_readdir, /* readdir */
|
NULL, /* select */
|
NULL, /* select */
|
NULL, /* ioctl */
|
NULL, /* ioctl */
|
NULL, /* mmap */
|
NULL, /* mmap */
|
NULL, /* no special open code */
|
NULL, /* no special open code */
|
NULL, /* no special release code */
|
NULL, /* no special release code */
|
NULL /* can't fsync */
|
NULL /* can't fsync */
|
};
|
};
|
|
|
/*
|
/*
|
* proc directories can do almost nothing..
|
* proc directories can do almost nothing..
|
*/
|
*/
|
struct inode_operations proc_scsi_inode_operations = {
|
struct inode_operations proc_scsi_inode_operations = {
|
&proc_scsi_operations, /* default scsi directory file-ops */
|
&proc_scsi_operations, /* default scsi directory file-ops */
|
NULL, /* create */
|
NULL, /* create */
|
proc_lookup, /* lookup */
|
proc_lookup, /* lookup */
|
NULL, /* link */
|
NULL, /* link */
|
NULL, /* unlink */
|
NULL, /* unlink */
|
NULL, /* symlink */
|
NULL, /* symlink */
|
NULL, /* mkdir */
|
NULL, /* mkdir */
|
NULL, /* rmdir */
|
NULL, /* rmdir */
|
NULL, /* mknod */
|
NULL, /* mknod */
|
NULL, /* rename */
|
NULL, /* rename */
|
NULL, /* readlink */
|
NULL, /* readlink */
|
NULL, /* follow_link */
|
NULL, /* follow_link */
|
NULL, /* readpage */
|
NULL, /* readpage */
|
NULL, /* writepage */
|
NULL, /* writepage */
|
NULL, /* bmap */
|
NULL, /* bmap */
|
NULL, /* truncate */
|
NULL, /* truncate */
|
NULL /* permission */
|
NULL /* permission */
|
};
|
};
|
|
|
int get_not_present_info(char *buffer, char **start, off_t offset, int length)
|
int get_not_present_info(char *buffer, char **start, off_t offset, int length)
|
{
|
{
|
int len, pos, begin;
|
int len, pos, begin;
|
|
|
begin = 0;
|
begin = 0;
|
pos = len = sprintf(buffer,
|
pos = len = sprintf(buffer,
|
"No low-level scsi modules are currently present\n");
|
"No low-level scsi modules are currently present\n");
|
if(pos < offset) {
|
if(pos < offset) {
|
len = 0;
|
len = 0;
|
begin = pos;
|
begin = pos;
|
}
|
}
|
|
|
*start = buffer + (offset - begin); /* Start of wanted data */
|
*start = buffer + (offset - begin); /* Start of wanted data */
|
len -= (offset - begin);
|
len -= (offset - begin);
|
if(len > length)
|
if(len > length)
|
len = length;
|
len = length;
|
|
|
return(len);
|
return(len);
|
}
|
}
|
|
|
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
|
#define PROC_BLOCK_SIZE (3*1024) /* 4K page size, but our output routines
|
* use some slack for overruns
|
* use some slack for overruns
|
*/
|
*/
|
|
|
static int proc_readscsi(struct inode * inode, struct file * file,
|
static int proc_readscsi(struct inode * inode, struct file * file,
|
char * buf, int count)
|
char * buf, int count)
|
{
|
{
|
int length;
|
int length;
|
int bytes = count;
|
int bytes = count;
|
int copied = 0;
|
int copied = 0;
|
int thistime;
|
int thistime;
|
char * page;
|
char * page;
|
char * start;
|
char * start;
|
|
|
if (count < -1) /* Normally I wouldn't do this, */
|
if (count < -1) /* Normally I wouldn't do this, */
|
return(-EINVAL); /* but it saves some redundant code.
|
return(-EINVAL); /* but it saves some redundant code.
|
* Now it is possible to seek to the
|
* Now it is possible to seek to the
|
* end of the file */
|
* end of the file */
|
if (!(page = (char *) __get_free_page(GFP_KERNEL)))
|
if (!(page = (char *) __get_free_page(GFP_KERNEL)))
|
return(-ENOMEM);
|
return(-ENOMEM);
|
|
|
while(bytes > 0 || count == -1) {
|
while(bytes > 0 || count == -1) {
|
thistime = bytes;
|
thistime = bytes;
|
if(bytes > PROC_BLOCK_SIZE || count == -1)
|
if(bytes > PROC_BLOCK_SIZE || count == -1)
|
thistime = PROC_BLOCK_SIZE;
|
thistime = PROC_BLOCK_SIZE;
|
|
|
if(dispatch_scsi_info_ptr)
|
if(dispatch_scsi_info_ptr)
|
length = dispatch_scsi_info_ptr(inode->i_ino, page, &start,
|
length = dispatch_scsi_info_ptr(inode->i_ino, page, &start,
|
file->f_pos, thistime, 0);
|
file->f_pos, thistime, 0);
|
else
|
else
|
length = get_not_present_info(page, &start, file->f_pos, thistime);
|
length = get_not_present_info(page, &start, file->f_pos, thistime);
|
if(length < 0) {
|
if(length < 0) {
|
free_page((ulong) page);
|
free_page((ulong) page);
|
return(length);
|
return(length);
|
}
|
}
|
|
|
/*
|
/*
|
* We have been given a non page aligned block of
|
* We have been given a non page aligned block of
|
* the data we asked for + a bit. We have been given
|
* the data we asked for + a bit. We have been given
|
* the start pointer and we know the length..
|
* the start pointer and we know the length..
|
*/
|
*/
|
if (length <= 0)
|
if (length <= 0)
|
break;
|
break;
|
/*
|
/*
|
* Copy the bytes, if we're not doing a seek to
|
* Copy the bytes, if we're not doing a seek to
|
* the end of the file
|
* the end of the file
|
*/
|
*/
|
if (count != -1)
|
if (count != -1)
|
memcpy_tofs(buf + copied, start, length);
|
memcpy_tofs(buf + copied, start, length);
|
file->f_pos += length; /* Move down the file */
|
file->f_pos += length; /* Move down the file */
|
bytes -= length;
|
bytes -= length;
|
copied += length;
|
copied += length;
|
|
|
if(length < thistime)
|
if(length < thistime)
|
break; /* End of file */
|
break; /* End of file */
|
|
|
}
|
}
|
|
|
free_page((ulong) page);
|
free_page((ulong) page);
|
return(copied);
|
return(copied);
|
}
|
}
|
|
|
|
|
static int proc_writescsi(struct inode * inode, struct file * file,
|
static int proc_writescsi(struct inode * inode, struct file * file,
|
const char * buf, int count)
|
const char * buf, int count)
|
{
|
{
|
int ret = 0;
|
int ret = 0;
|
char * page;
|
char * page;
|
|
|
if(count > PROC_BLOCK_SIZE) {
|
if(count > PROC_BLOCK_SIZE) {
|
return(-EOVERFLOW);
|
return(-EOVERFLOW);
|
}
|
}
|
|
|
if(dispatch_scsi_info_ptr != NULL) {
|
if(dispatch_scsi_info_ptr != NULL) {
|
if (!(page = (char *) __get_free_page(GFP_KERNEL)))
|
if (!(page = (char *) __get_free_page(GFP_KERNEL)))
|
return(-ENOMEM);
|
return(-ENOMEM);
|
memcpy_fromfs(page, buf, count);
|
memcpy_fromfs(page, buf, count);
|
ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
|
ret = dispatch_scsi_info_ptr(inode->i_ino, page, 0, 0, count, 1);
|
} else
|
} else
|
return(-ENOPKG); /* Nothing here */
|
return(-ENOPKG); /* Nothing here */
|
|
|
free_page((ulong) page);
|
free_page((ulong) page);
|
return(ret);
|
return(ret);
|
}
|
}
|
|
|
|
|
static int proc_scsilseek(struct inode * inode, struct file * file,
|
static int proc_scsilseek(struct inode * inode, struct file * file,
|
off_t offset, int orig)
|
off_t offset, int orig)
|
{
|
{
|
switch (orig) {
|
switch (orig) {
|
case 0:
|
case 0:
|
file->f_pos = offset;
|
file->f_pos = offset;
|
return(file->f_pos);
|
return(file->f_pos);
|
case 1:
|
case 1:
|
file->f_pos += offset;
|
file->f_pos += offset;
|
return(file->f_pos);
|
return(file->f_pos);
|
case 2: /* This ugly hack allows us to */
|
case 2: /* This ugly hack allows us to */
|
if (offset) /* to determine the length of the */
|
if (offset) /* to determine the length of the */
|
return(-EINVAL); /* file and then later safely to */
|
return(-EINVAL); /* file and then later safely to */
|
proc_readscsi(inode, file, 0, -1); /* seek in it */
|
proc_readscsi(inode, file, 0, -1); /* seek in it */
|
return(file->f_pos);
|
return(file->f_pos);
|
default:
|
default:
|
return(-EINVAL);
|
return(-EINVAL);
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Overrides for Emacs so that we almost follow Linus's tabbing style.
|
* 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
|
* 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
|
* adjust the settings for this buffer only. This must remain at the end
|
* of the file.
|
* of the file.
|
* ---------------------------------------------------------------------------
|
* ---------------------------------------------------------------------------
|
* Local variables:
|
* Local variables:
|
* c-indent-level: 4
|
* c-indent-level: 4
|
* c-brace-imaginary-offset: 0
|
* c-brace-imaginary-offset: 0
|
* c-brace-offset: -4
|
* c-brace-offset: -4
|
* c-argdecl-indent: 4
|
* c-argdecl-indent: 4
|
* c-label-offset: -4
|
* c-label-offset: -4
|
* c-continued-statement-offset: 4
|
* c-continued-statement-offset: 4
|
* c-continued-brace-offset: 0
|
* c-continued-brace-offset: 0
|
* indent-tabs-mode: nil
|
* indent-tabs-mode: nil
|
* tab-width: 8
|
* tab-width: 8
|
* End:
|
* End:
|
*/
|
*/
|
|
|