summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/sg.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
committer <ralf@linux-mips.org>1997-01-07 02:33:00 +0000
commitbeb116954b9b7f3bb56412b2494b562f02b864b1 (patch)
tree120e997879884e1b9d93b265221b939d2ef1ade1 /drivers/scsi/sg.c
parent908d4681a1dc3792ecafbe64265783a86c4cccb6 (diff)
Import of Linux/MIPS 2.1.14
Diffstat (limited to 'drivers/scsi/sg.c')
-rw-r--r--drivers/scsi/sg.c915
1 files changed, 574 insertions, 341 deletions
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 9224533c1..3e1382fc1 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1,11 +1,12 @@
/*
- History:
- Started: Aug 9 by Lawrence Foard (entropy@world.std.com), to allow user
- process control of SCSI devices.
- Development Sponsored by Killy Corp. NY NY
-
- Borrows code from st driver.
-*/
+ * History:
+ * Started: Aug 9 by Lawrence Foard (entropy@world.std.com),
+ * to allow user process control of SCSI devices.
+ * Development Sponsored by Killy Corp. NY NY
+ *
+ * Borrows code from st driver.
+ */
+#include <linux/module.h>
#include <linux/fs.h>
#include <linux/kernel.h>
@@ -17,425 +18,657 @@
#include <linux/ioctl.h>
#include <linux/fcntl.h>
#include <asm/io.h>
-#include <asm/segment.h>
+#include <asm/uaccess.h>
#include <asm/system.h>
-#include "../block/blk.h"
+#include <linux/blk.h>
#include "scsi.h"
#include "hosts.h"
-#include "scsi_ioctl.h"
-#include "sg.h"
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
-static void sg_init(void);
+static int sg_init(void);
static int sg_attach(Scsi_Device *);
static int sg_detect(Scsi_Device *);
static void sg_detach(Scsi_Device *);
-struct Scsi_Device_Template sg_template = {NULL, NULL, "sg", 0xff,
- SCSI_GENERIC_MAJOR, 0, 0, 0, 0,
- sg_detect, sg_init,
- NULL, sg_attach, sg_detach};
+struct Scsi_Device_Template sg_template = {NULL, NULL, "sg", NULL, 0xff,
+ SCSI_GENERIC_MAJOR, 0, 0, 0, 0,
+ sg_detect, sg_init,
+ NULL, sg_attach, sg_detach};
#ifdef SG_BIG_BUFF
-static char *big_buff;
+static char *big_buff = NULL;
static struct wait_queue *big_wait; /* wait for buffer available */
static int big_inuse=0;
#endif
struct scsi_generic
- {
- Scsi_Device *device;
- int users; /* how many people have it open? */
- struct wait_queue *generic_wait; /* wait for device to be available */
- struct wait_queue *read_wait; /* wait for response */
- struct wait_queue *write_wait; /* wait for free buffer */
- int timeout; /* current default value for device */
- int buff_len; /* length of current buffer */
- char *buff; /* the buffer */
- struct sg_header header; /* header of pending command */
- char exclude; /* opened for exclusive access */
- char pending; /* don't accept writes now */
- char complete; /* command complete allow a read */
- };
+{
+ Scsi_Device *device;
+ int users; /* how many people have it open? */
+ struct wait_queue *generic_wait; /* wait for device to be available */
+ struct wait_queue *read_wait; /* wait for response */
+ struct wait_queue *write_wait; /* wait for free buffer */
+ int timeout; /* current default value for device */
+ int buff_len; /* length of current buffer */
+ char *buff; /* the buffer */
+ struct sg_header header; /* header of pending command */
+ char exclude; /* opened for exclusive access */
+ char pending; /* don't accept writes now */
+ char complete; /* command complete allow a read */
+};
static struct scsi_generic *scsi_generics=NULL;
static void sg_free(char *buff,int size);
static int sg_ioctl(struct inode * inode,struct file * file,
- unsigned int cmd_in, unsigned long arg)
- {
- int dev = MINOR(inode->i_rdev);
- if ((dev<0) || (dev>=sg_template.dev_max))
- return -ENXIO;
- switch(cmd_in)
- {
+ unsigned int cmd_in, unsigned long arg)
+{
+ int result;
+ int dev = MINOR(inode->i_rdev);
+ if ((dev<0) || (dev>=sg_template.dev_max))
+ return -ENXIO;
+ switch(cmd_in)
+ {
case SG_SET_TIMEOUT:
- scsi_generics[dev].timeout=get_fs_long((int *) arg);
- return 0;
+ result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
+ if (result) return result;
+
+ get_user(scsi_generics[dev].timeout, (int *) arg);
+ return 0;
case SG_GET_TIMEOUT:
- return scsi_generics[dev].timeout;
+ return scsi_generics[dev].timeout;
default:
- return scsi_ioctl(scsi_generics[dev].device, cmd_in, (void *) arg);
- }
- }
+ return scsi_ioctl(scsi_generics[dev].device, cmd_in, (void *) arg);
+ }
+}
static int sg_open(struct inode * inode, struct file * filp)
- {
- int dev=MINOR(inode->i_rdev);
- int flags=filp->f_flags;
- if (dev>=sg_template.dev_max || !scsi_generics[dev].device)
- return -ENXIO;
- if (O_RDWR!=(flags & O_ACCMODE))
- return -EACCES;
- if (flags & O_EXCL)
- {
- while(scsi_generics[dev].users)
- {
- if (flags & O_NONBLOCK)
- return -EBUSY;
- interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- }
- scsi_generics[dev].exclude=1;
- }
- else
- while(scsi_generics[dev].exclude)
+{
+ int dev=MINOR(inode->i_rdev);
+ int flags=filp->f_flags;
+ if (dev>=sg_template.dev_max || !scsi_generics[dev].device)
+ return -ENXIO;
+ if (O_RDWR!=(flags & O_ACCMODE))
+ return -EACCES;
+
+ /*
+ * If we want exclusive access, then wait until the device is not
+ * busy, and then set the flag to prevent anyone else from using it.
+ */
+ if (flags & O_EXCL)
{
- if (flags & O_NONBLOCK)
- return -EBUSY;
- interruptible_sleep_on(&scsi_generics[dev].generic_wait);
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
+ while(scsi_generics[dev].users)
+ {
+ if (flags & O_NONBLOCK)
+ return -EBUSY;
+ interruptible_sleep_on(&scsi_generics[dev].generic_wait);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ }
+ scsi_generics[dev].exclude=1;
}
- if (!scsi_generics[dev].users && scsi_generics[dev].pending && scsi_generics[dev].complete)
- {
- if (scsi_generics[dev].buff != NULL)
- sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
- scsi_generics[dev].buff=NULL;
- scsi_generics[dev].pending=0;
- }
- if (!scsi_generics[dev].users)
- scsi_generics[dev].timeout=SG_DEFAULT_TIMEOUT;
- if (scsi_generics[dev].device->host->hostt->usage_count)
- (*scsi_generics[dev].device->host->hostt->usage_count)++;
- scsi_generics[dev].users++;
- return 0;
- }
+ else
+ /*
+ * Wait until nobody has an exclusive open on
+ * this device.
+ */
+ while(scsi_generics[dev].exclude)
+ {
+ if (flags & O_NONBLOCK)
+ return -EBUSY;
+ interruptible_sleep_on(&scsi_generics[dev].generic_wait);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * OK, we should have grabbed the device. Mark the thing so
+ * that other processes know that we have it, and initialize the
+ * state variables to known values.
+ */
+ if (!scsi_generics[dev].users
+ && scsi_generics[dev].pending
+ && scsi_generics[dev].complete)
+ {
+ if (scsi_generics[dev].buff != NULL)
+ sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+ scsi_generics[dev].buff=NULL;
+ scsi_generics[dev].pending=0;
+ }
+ if (!scsi_generics[dev].users)
+ scsi_generics[dev].timeout=SG_DEFAULT_TIMEOUT;
+ if (scsi_generics[dev].device->host->hostt->usage_count)
+ (*scsi_generics[dev].device->host->hostt->usage_count)++;
+ if(sg_template.usage_count) (*sg_template.usage_count)++;
+ scsi_generics[dev].users++;
+ return 0;
+}
static void sg_close(struct inode * inode, struct file * filp)
- {
- int dev=MINOR(inode->i_rdev);
- scsi_generics[dev].users--;
- if (scsi_generics[dev].device->host->hostt->usage_count)
- (*scsi_generics[dev].device->host->hostt->usage_count)--;
- scsi_generics[dev].exclude=0;
- wake_up(&scsi_generics[dev].generic_wait);
- }
+{
+ int dev=MINOR(inode->i_rdev);
+ scsi_generics[dev].users--;
+ if (scsi_generics[dev].device->host->hostt->usage_count)
+ (*scsi_generics[dev].device->host->hostt->usage_count)--;
+ if(sg_template.usage_count) (*sg_template.usage_count)--;
+ scsi_generics[dev].exclude=0;
+ wake_up(&scsi_generics[dev].generic_wait);
+}
static char *sg_malloc(int size)
- {
- if (size<=4096)
- return (char *) scsi_malloc(size);
+{
+ if (size<=4096)
+ return (char *) scsi_malloc(size);
#ifdef SG_BIG_BUFF
- if (size<SG_BIG_BUFF)
- {
- while(big_inuse)
- {
- interruptible_sleep_on(&big_wait);
- if (current->signal & ~current->blocked)
- return NULL;
- }
- big_inuse=1;
- return big_buff;
- }
+ if (size<=SG_BIG_BUFF)
+ {
+ while(big_inuse)
+ {
+ interruptible_sleep_on(&big_wait);
+ if (current->signal & ~current->blocked)
+ return NULL;
+ }
+ big_inuse=1;
+ return big_buff;
+ }
#endif
- return NULL;
- }
+ return NULL;
+}
static void sg_free(char *buff,int size)
- {
+{
#ifdef SG_BIG_BUFF
- if (buff==big_buff)
- {
- big_inuse=0;
- wake_up(&big_wait);
- return;
- }
+ if (buff==big_buff)
+ {
+ big_inuse=0;
+ wake_up(&big_wait);
+ return;
+ }
#endif
- scsi_free(buff,size);
- }
-
-static int sg_read(struct inode *inode,struct file *filp,char *buf,int count)
- {
- int dev=MINOR(inode->i_rdev);
- int i;
- struct scsi_generic *device=&scsi_generics[dev];
- if ((i=verify_area(VERIFY_WRITE,buf,count)))
- return i;
- while(!device->pending || !device->complete)
- {
- if (filp->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- interruptible_sleep_on(&device->read_wait);
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- }
- device->header.pack_len=device->header.reply_len;
- device->header.result=0;
- if (count>=sizeof(struct sg_header))
- {
- memcpy_tofs(buf,&device->header,sizeof(struct sg_header));
- buf+=sizeof(struct sg_header);
- if (count>device->header.pack_len)
- count=device->header.pack_len;
- if (count > sizeof(struct sg_header)) {
- memcpy_tofs(buf,device->buff,count-sizeof(struct sg_header));
+ scsi_free(buff,size);
+}
+
+/*
+ * Read back the results of a previous command. We use the pending and
+ * complete semaphores to tell us whether the buffer is available for us
+ * and whether the command is actually done.
+ */
+static long sg_read(struct inode *inode,struct file *filp,char *buf,unsigned long count)
+{
+ int dev=MINOR(inode->i_rdev);
+ int i;
+ unsigned long flags;
+ struct scsi_generic *device=&scsi_generics[dev];
+ if ((i=verify_area(VERIFY_WRITE,buf,count)))
+ return i;
+
+ /*
+ * Wait until the command is actually done.
+ */
+ save_flags(flags);
+ cli();
+ while(!device->pending || !device->complete)
+ {
+ if (filp->f_flags & O_NONBLOCK)
+ {
+ restore_flags(flags);
+ return -EAGAIN;
+ }
+ interruptible_sleep_on(&device->read_wait);
+ if (current->signal & ~current->blocked)
+ {
+ restore_flags(flags);
+ return -ERESTARTSYS;
+ }
}
- }
- else
- count=0;
- sg_free(device->buff,device->buff_len);
- device->buff = NULL;
- device->pending=0;
- wake_up(&device->write_wait);
- return count;
- }
+ restore_flags(flags);
+ /*
+ * Now copy the result back to the user buffer.
+ */
+ device->header.pack_len=device->header.reply_len;
+
+ if (count>=sizeof(struct sg_header))
+ {
+ copy_to_user(buf,&device->header,sizeof(struct sg_header));
+ buf+=sizeof(struct sg_header);
+ if (count>device->header.pack_len)
+ count=device->header.pack_len;
+ if (count > sizeof(struct sg_header)) {
+ copy_to_user(buf,device->buff,count-sizeof(struct sg_header));
+ }
+ }
+ else
+ count= device->header.result==0 ? 0 : -EIO;
+
+ /*
+ * Clean up, and release the device so that we can send another
+ * command.
+ */
+ sg_free(device->buff,device->buff_len);
+ device->buff = NULL;
+ device->pending=0;
+ wake_up(&device->write_wait);
+ return count;
+}
+
+/*
+ * This function is called by the interrupt handler when we
+ * actually have a command that is complete. Change the
+ * flags to indicate that we have a result.
+ */
static void sg_command_done(Scsi_Cmnd * SCpnt)
- {
- int dev=SCpnt->request.dev;
- struct scsi_generic *device=&scsi_generics[dev];
- if (!device->pending)
- {
- printk("unexpected done for sg %d\n",dev);
- SCpnt->request.dev=-1;
- return;
- }
- memcpy(device->header.sense_buffer, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
- if (SCpnt->sense_buffer[0])
- {
- device->header.result=EIO;
- }
- else
- device->header.result=SCpnt->result;
- device->complete=1;
- SCpnt->request.dev=-1;
- wake_up(&scsi_generics[dev].read_wait);
- }
-
-static int sg_write(struct inode *inode,struct file *filp,char *buf,int count)
- {
- int dev=MINOR(inode->i_rdev);
- Scsi_Cmnd *SCpnt;
- int bsize,size,amt,i;
- unsigned char opcode;
- unsigned char cmnd[MAX_COMMAND_SIZE];
- struct scsi_generic *device=&scsi_generics[dev];
-
- if ((i=verify_area(VERIFY_READ,buf,count)))
- return i;
- /*
- * The minimum scsi command length is 6 bytes. If we get anything less than this,
- * it is clearly bogus.
- */
- if (count<(sizeof(struct sg_header) + 6))
- return -EIO;
- /* make sure we can fit */
- while(device->pending)
- {
- if (filp->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
+{
+ int dev = MINOR(SCpnt->request.rq_dev);
+ struct scsi_generic *device = &scsi_generics[dev];
+ if (!device->pending)
+ {
+ printk("unexpected done for sg %d\n",dev);
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ return;
+ }
+
+ /*
+ * See if the command completed normally, or whether something went
+ * wrong.
+ */
+ memcpy(device->header.sense_buffer, SCpnt->sense_buffer,
+ sizeof(SCpnt->sense_buffer));
+ switch (host_byte(SCpnt->result)) {
+ case DID_OK:
+ device->header.result = 0;
+ break;
+ case DID_NO_CONNECT:
+ case DID_BUS_BUSY:
+ case DID_TIME_OUT:
+ device->header.result = EBUSY;
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ case DID_PARITY:
+ case DID_RESET:
+ case DID_BAD_INTR:
+ device->header.result = EIO;
+ break;
+ case DID_ERROR:
+ /*
+ * There really should be DID_UNDERRUN and DID_OVERRUN error values,
+ * and a means for callers of scsi_do_cmd to indicate whether an
+ * underrun or overrun should signal an error. Until that can be
+ * implemented, this kludge allows for returning useful error values
+ * except in cases that return DID_ERROR that might be due to an
+ * underrun.
+ */
+ if (SCpnt->sense_buffer[0] == 0 &&
+ status_byte(SCpnt->result) == GOOD)
+ device->header.result = 0;
+ else device->header.result = EIO;
+ break;
+ }
+
+ /*
+ * Now wake up the process that is waiting for the
+ * result.
+ */
+ device->complete=1;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ wake_up(&scsi_generics[dev].read_wait);
+}
+
+static long sg_write(struct inode *inode,struct file *filp,const char *buf,unsigned long count)
+{
+ int bsize,size,amt,i;
+ unsigned char cmnd[MAX_COMMAND_SIZE];
+ kdev_t devt = inode->i_rdev;
+ int dev = MINOR(devt);
+ struct scsi_generic * device=&scsi_generics[dev];
+ int input_size;
+ unsigned char opcode;
+ Scsi_Cmnd * SCpnt;
+
+ if ((i=verify_area(VERIFY_READ,buf,count)))
+ return i;
+ /*
+ * The minimum scsi command length is 6 bytes. If we get anything
+ * less than this, it is clearly bogus.
+ */
+ if (count<(sizeof(struct sg_header) + 6))
+ return -EIO;
+
+ /*
+ * If we still have a result pending from a previous command,
+ * wait until the result has been read by the user before sending
+ * another command.
+ */
+ while(device->pending)
+ {
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN;
#ifdef DEBUG
- printk("sg_write: sleeping on pending request\n");
+ printk("sg_write: sleeping on pending request\n");
#endif
- interruptible_sleep_on(&device->write_wait);
- if (current->signal & ~current->blocked)
- return -ERESTARTSYS;
- }
- device->pending=1;
- device->complete=0;
- memcpy_fromfs(&device->header,buf,sizeof(struct sg_header));
- /* fix input size */
- device->header.pack_len=count;
- buf+=sizeof(struct sg_header);
- bsize=(device->header.pack_len>device->header.reply_len) ? device->header.pack_len : device->header.reply_len;
- bsize-=sizeof(struct sg_header);
- amt=bsize;
- if (!bsize)
- bsize++;
- bsize=(bsize+511) & ~511;
- if ((bsize<0) || !(device->buff=sg_malloc(device->buff_len=bsize)))
- {
- device->pending=0;
- wake_up(&device->write_wait);
- return -ENOMEM;
- }
+ interruptible_sleep_on(&device->write_wait);
+ if (current->signal & ~current->blocked)
+ return -ERESTARTSYS;
+ }
+
+ /*
+ * Mark the device flags for the new state.
+ */
+ device->pending=1;
+ device->complete=0;
+ copy_from_user(&device->header,buf,sizeof(struct sg_header));
+
+ device->header.pack_len=count;
+ buf+=sizeof(struct sg_header);
+
+ /*
+ * Now we need to grab the command itself from the user's buffer.
+ */
+ get_user(opcode, buf);
+ size=COMMAND_SIZE(opcode);
+ if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
+
+ /*
+ * Determine buffer size.
+ */
+ input_size = device->header.pack_len - size;
+ if( input_size > device->header.reply_len)
+ {
+ bsize = input_size;
+ } else {
+ bsize = device->header.reply_len;
+ }
+
+ /*
+ * Don't include the command header itself in the size.
+ */
+ bsize-=sizeof(struct sg_header);
+ input_size-=sizeof(struct sg_header);
+
+ /*
+ * Verify that the user has actually passed enough bytes for this command.
+ */
+ if( input_size < 0 )
+ {
+ device->pending=0;
+ wake_up( &device->write_wait );
+ return -EIO;
+ }
+
+ /*
+ * Allocate a buffer that is large enough to hold the data
+ * that has been requested. Round up to an even number of sectors,
+ * since scsi_malloc allocates in chunks of 512 bytes.
+ */
+ amt=bsize;
+ if (!bsize)
+ bsize++;
+ bsize=(bsize+511) & ~511;
+
+ /*
+ * If we cannot allocate the buffer, report an error.
+ */
+ if ((bsize<0) || !(device->buff=sg_malloc(device->buff_len=bsize)))
+ {
+ device->pending=0;
+ wake_up(&device->write_wait);
+ return -ENOMEM;
+ }
+
#ifdef DEBUG
- printk("allocating device\n");
+ printk("allocating device\n");
#endif
- if (!(SCpnt=allocate_device(NULL,device->device, !(filp->f_flags & O_NONBLOCK))))
- {
- device->pending=0;
- wake_up(&device->write_wait);
- sg_free(device->buff,device->buff_len);
- device->buff = NULL;
- return -EWOULDBLOCK;
- }
+
+ /*
+ * Grab a device pointer for the device we want to talk to. If we
+ * don't want to block, just return with the appropriate message.
+ */
+ if (!(SCpnt=allocate_device(NULL,device->device, !(filp->f_flags & O_NONBLOCK))))
+ {
+ device->pending=0;
+ wake_up(&device->write_wait);
+ sg_free(device->buff,device->buff_len);
+ device->buff = NULL;
+ return -EAGAIN;
+ }
#ifdef DEBUG
- printk("device allocated\n");
+ printk("device allocated\n");
#endif
- /* now issue command */
- SCpnt->request.dev=dev;
- SCpnt->sense_buffer[0]=0;
- opcode = get_fs_byte(buf);
- size=COMMAND_SIZE(opcode);
- if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
- SCpnt->cmd_len = size;
- /*
- * Verify that the user has actually passed enough bytes for this command.
- */
- if (count<(sizeof(struct sg_header) + size))
- {
- device->pending=0;
- wake_up(&device->write_wait);
- sg_free(device->buff,device->buff_len);
- device->buff = NULL;
- return -EIO;
- }
- memcpy_fromfs(cmnd,buf,size);
- buf+=size;
- memcpy_fromfs(device->buff,buf,device->header.pack_len-size-sizeof(struct sg_header));
- cmnd[1]=(cmnd[1] & 0x1f) | (device->device->lun<<5);
+ SCpnt->request.rq_dev = devt;
+ SCpnt->request.rq_status = RQ_ACTIVE;
+ SCpnt->sense_buffer[0]=0;
+ SCpnt->cmd_len = size;
+
+ /*
+ * Now copy the SCSI command from the user's address space.
+ */
+ copy_from_user(cmnd,buf,size);
+ buf+=size;
+
+ /*
+ * If we are writing data, copy the data we are writing. The pack_len
+ * field also includes the length of the header and the command,
+ * so we need to subtract these off.
+ */
+ if (input_size > 0) copy_from_user(device->buff, buf, input_size);
+
+ /*
+ * Set the LUN field in the command structure.
+ */
+ cmnd[1]= (cmnd[1] & 0x1f) | (device->device->lun<<5);
+
#ifdef DEBUG
- printk("do cmd\n");
+ printk("do cmd\n");
#endif
- scsi_do_cmd (SCpnt,(void *) cmnd,
- (void *) device->buff,amt,
- sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
+
+ /*
+ * Now pass the actual command down to the low-level driver. We
+ * do not do any more here - when the interrupt arrives, we will
+ * then do the post-processing.
+ */
+ scsi_do_cmd (SCpnt,(void *) cmnd,
+ (void *) device->buff,amt,
+ sg_command_done,device->timeout,SG_DEFAULT_RETRIES);
+
#ifdef DEBUG
- printk("done cmd\n");
+ printk("done cmd\n");
#endif
- return count;
- }
+
+ return count;
+}
+
+static int sg_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
+{
+ int dev=MINOR(inode->i_rdev);
+ int r = 0;
+ struct scsi_generic *device=&scsi_generics[dev];
+
+ if (sel_type == SEL_IN) {
+ if(device->pending && device->complete)
+ {
+ r = 1;
+ } else {
+ select_wait(&scsi_generics[dev].read_wait, wait);
+ }
+ }
+ if (sel_type == SEL_OUT) {
+ if(!device->pending){
+ r = 1;
+ }
+ else
+ {
+ select_wait(&scsi_generics[dev].write_wait, wait);
+ }
+ }
+
+ return(r);
+}
static struct file_operations sg_fops = {
- NULL, /* lseek */
- sg_read, /* read */
- sg_write, /* write */
- NULL, /* readdir */
- NULL, /* select */
- sg_ioctl, /* ioctl */
- NULL, /* mmap */
- sg_open, /* open */
- sg_close, /* release */
- NULL /* fsync */
+ NULL, /* lseek */
+ sg_read, /* read */
+ sg_write, /* write */
+ NULL, /* readdir */
+ sg_select, /* select */
+ sg_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sg_open, /* open */
+ sg_close, /* release */
+ NULL /* fsync */
};
static int sg_detect(Scsi_Device * SDp){
- ++sg_template.dev_noticed;
- return 1;
+
+ switch (SDp->type) {
+ case TYPE_DISK:
+ case TYPE_MOD:
+ case TYPE_ROM:
+ case TYPE_WORM:
+ case TYPE_TAPE: break;
+ default:
+ printk("Detected scsi generic sg%c at scsi%d, channel %d, id %d, lun %d\n",
+ 'a'+sg_template.dev_noticed,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+ }
+ sg_template.dev_noticed++;
+ return 1;
}
/* Driver initialization */
-static void sg_init()
- {
- static int sg_registered = 0;
-
- if (sg_template.dev_noticed == 0) return;
-
- if(!sg_registered) {
- if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops))
- {
- printk("Unable to get major %d for generic SCSI device\n",
- SCSI_GENERIC_MAJOR);
- return;
- }
- sg_registered++;
- }
-
- /* If we have already been through here, return */
- if(scsi_generics) return;
-
+static int sg_init()
+{
+ static int sg_registered = 0;
+
+ if (sg_template.dev_noticed == 0) return 0;
+
+ if(!sg_registered) {
+ if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops))
+ {
+ printk("Unable to get major %d for generic SCSI device\n",
+ SCSI_GENERIC_MAJOR);
+ return 1;
+ }
+ sg_registered++;
+ }
+
+ /* If we have already been through here, return */
+ if(scsi_generics) return 0;
+
#ifdef DEBUG
- printk("sg: Init generic device.\n");
+ printk("sg: Init generic device.\n");
#endif
-
+
#ifdef SG_BIG_BUFF
- big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
+ big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
#endif
-
- scsi_generics = (struct scsi_generic *)
- scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS)
- * sizeof(struct scsi_generic), GFP_ATOMIC);
- memset(scsi_generics, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
- * sizeof(struct scsi_generic));
-
- sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
- }
+
+ scsi_generics = (struct scsi_generic *)
+ scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS)
+ * sizeof(struct scsi_generic), GFP_ATOMIC);
+ memset(scsi_generics, 0, (sg_template.dev_noticed + SG_EXTRA_DEVS)
+ * sizeof(struct scsi_generic));
+
+ sg_template.dev_max = sg_template.dev_noticed + SG_EXTRA_DEVS;
+ return 0;
+}
static int sg_attach(Scsi_Device * SDp)
- {
- struct scsi_generic * gpnt;
- int i;
-
- if(sg_template.nr_dev >= sg_template.dev_max)
- {
- SDp->attached--;
- return 1;
- }
+{
+ struct scsi_generic * gpnt;
+ int i;
+
+ if(sg_template.nr_dev >= sg_template.dev_max)
+ {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
+ if(!gpnt->device) break;
+
+ if(i >= sg_template.dev_max) panic ("scsi_devices corrupt (sg)");
+
+ scsi_generics[i].device=SDp;
+ scsi_generics[i].users=0;
+ scsi_generics[i].generic_wait=NULL;
+ scsi_generics[i].read_wait=NULL;
+ scsi_generics[i].write_wait=NULL;
+ scsi_generics[i].buff=NULL;
+ scsi_generics[i].exclude=0;
+ scsi_generics[i].pending=0;
+ scsi_generics[i].timeout=SG_DEFAULT_TIMEOUT;
+ sg_template.nr_dev++;
+ return 0;
+};
- for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
- if(!gpnt->device) break;
- if(i >= sg_template.dev_max) panic ("scsi_devices corrupt (sg)");
- scsi_generics[i].device=SDp;
- scsi_generics[i].users=0;
- scsi_generics[i].generic_wait=NULL;
- scsi_generics[i].read_wait=NULL;
- scsi_generics[i].write_wait=NULL;
- scsi_generics[i].buff=NULL;
- scsi_generics[i].exclude=0;
- scsi_generics[i].pending=0;
- scsi_generics[i].timeout=SG_DEFAULT_TIMEOUT;
- sg_template.nr_dev++;
- return 0;
- };
+static void sg_detach(Scsi_Device * SDp)
+{
+ struct scsi_generic * gpnt;
+ int i;
+
+ for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
+ if(gpnt->device == SDp) {
+ gpnt->device = NULL;
+ SDp->attached--;
+ sg_template.nr_dev--;
+ /*
+ * avoid associated device /dev/sg? bying incremented
+ * each time module is inserted/removed , <dan@lectra.fr>
+ */
+ sg_template.dev_noticed--;
+ return;
+ }
+ return;
+}
+#ifdef MODULE
+int init_module(void) {
+ sg_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sg_template);
+}
-static void sg_detach(Scsi_Device * SDp)
+void cleanup_module( void)
{
- struct scsi_generic * gpnt;
- int i;
-
- for(gpnt = scsi_generics, i=0; i<sg_template.dev_max; i++, gpnt++)
- if(gpnt->device == SDp) {
- gpnt->device = NULL;
- SDp->attached--;
- sg_template.nr_dev--;
- return;
+ scsi_unregister_module(MODULE_SCSI_DEV, &sg_template);
+ unregister_chrdev(SCSI_GENERIC_MAJOR, "sg");
+
+ if(scsi_generics != NULL) {
+ scsi_init_free((char *) scsi_generics,
+ (sg_template.dev_noticed + SG_EXTRA_DEVS)
+ * sizeof(struct scsi_generic));
}
- return;
+ sg_template.dev_max = 0;
+#ifdef SG_BIG_BUFF
+ if(big_buff != NULL)
+ scsi_init_free(big_buff, SG_BIG_BUFF);
+#endif
}
+#endif /* MODULE */
/*
- * Overrides for Emacs so that we 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
* adjust the settings for this buffer only. This must remain at the end
* of the file.
* ---------------------------------------------------------------------------
* Local variables:
- * c-indent-level: 8
+ * c-indent-level: 4
* c-brace-imaginary-offset: 0
- * c-brace-offset: -8
- * c-argdecl-indent: 8
- * c-label-offset: -8
- * c-continued-statement-offset: 8
+ * 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: 8
* End:
*/