patch-1.3.9 linux/drivers/scsi/sg.c
Next file: linux/drivers/scsi/sr.c
Previous file: linux/drivers/scsi/sd.c
Back to the patch index
Back to the overall index
- Lines: 325
- Date:
Tue Jul 11 07:56:04 1995
- Orig file:
v1.3.8/linux/drivers/scsi/sg.c
- Orig date:
Fri Jul 7 08:54:51 1995
diff -u --recursive --new-file v1.3.8/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
@@ -70,12 +70,16 @@
static int sg_ioctl(struct inode * inode,struct file * file,
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:
+ result = verify_area(VERIFY_READ, arg, sizeof(long));
+ if (result) return result;
+
scsi_generics[dev].timeout=get_user((int *) arg);
return 0;
case SG_GET_TIMEOUT:
@@ -93,6 +97,11 @@
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)
{
while(scsi_generics[dev].users)
@@ -106,6 +115,10 @@
scsi_generics[dev].exclude=1;
}
else
+ /*
+ * Wait until nobody has an exclusive open on
+ * this device.
+ */
while(scsi_generics[dev].exclude)
{
if (flags & O_NONBLOCK)
@@ -114,7 +127,15 @@
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
}
- if (!scsi_generics[dev].users && scsi_generics[dev].pending && scsi_generics[dev].complete)
+
+ /*
+ * 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);
@@ -174,6 +195,11 @@
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 int sg_read(struct inode *inode,struct file *filp,char *buf,int count)
{
int dev=MINOR(inode->i_rdev);
@@ -181,6 +207,10 @@
struct scsi_generic *device=&scsi_generics[dev];
if ((i=verify_area(VERIFY_WRITE,buf,count)))
return i;
+
+ /*
+ * Wait until the command is actually done.
+ */
while(!device->pending || !device->complete)
{
if (filp->f_flags & O_NONBLOCK)
@@ -189,6 +219,10 @@
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
}
+
+ /*
+ * Now copy the result back to the user buffer.
+ */
device->header.pack_len=device->header.reply_len;
device->header.result=0;
if (count>=sizeof(struct sg_header))
@@ -203,6 +237,11 @@
}
else
count=0;
+
+ /*
+ * 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;
@@ -210,6 +249,11 @@
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;
@@ -220,6 +264,11 @@
SCpnt->request.dev=-1;
return;
}
+
+ /*
+ * See if the command completed normally, or whether something went
+ * wrong.
+ */
memcpy(device->header.sense_buffer, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer));
if (SCpnt->sense_buffer[0])
{
@@ -227,29 +276,44 @@
}
else
device->header.result=SCpnt->result;
+
+ /*
+ * Now wake up the process that is waiting for the
+ * result.
+ */
device->complete=1;
SCpnt->request.dev=-1;
wake_up(&scsi_generics[dev].read_wait);
}
+#define SG_SEND 0
+#define SG_REC 1
+
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];
+ int bsize,size,amt,i;
+ unsigned char cmnd[MAX_COMMAND_SIZE];
+ int dev=MINOR(inode->i_rdev);
+ struct scsi_generic * device=&scsi_generics[dev];
+ int direction;
+ unsigned char opcode;
+ Scsi_Cmnd * SCpnt;
+ int sgcnt;
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.
+ * 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 */
+
+ /*
+ * 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)
@@ -261,27 +325,61 @@
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
}
+
+ /*
+ * Mark the device flags for the new state.
+ */
device->pending=1;
device->complete=0;
memcpy_fromfs(&device->header,buf,sizeof(struct sg_header));
- /* fix input size */
+
+ /*
+ * fix input size, and see if we are sending data.
+ */
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;
+ if( device->header.pack_len > device->header.reply_len )
+ {
+ bsize = device->header.pack_len;
+ direction = SG_SEND;
+ } else {
+ bsize = device->header.reply_len;
+ direction = SG_REC;
+ }
+
+ /*
+ * Don't include the command header itself in the size.
+ */
bsize-=sizeof(struct sg_header);
+
+ /*
+ * 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");
#endif
+
+ /*
+ * 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;
@@ -293,39 +391,72 @@
#ifdef DEBUG
printk("device allocated\n");
#endif
- /* now issue command */
+
+ /*
+ * Now we need to grab the command itself from the user's buffer.
+ */
SCpnt->request.dev=dev;
SCpnt->sense_buffer[0]=0;
opcode = get_user(buf);
size=COMMAND_SIZE(opcode);
if (opcode >= 0xc0 && device->header.twelve_byte) size = 12;
SCpnt->cmd_len = size;
- amt-=device->header.pack_len>device->header.reply_len ? size : 0;
+
+ /*
+ * If we are writing data, subtract off the size
+ * of the command itself, to get the amount of actual data
+ * that we need to send to the device.
+ */
+ if( direction == SG_SEND )
+ amt -= size;
+
/*
* Verify that the user has actually passed enough bytes for this command.
*/
- if (count<(sizeof(struct sg_header) + size))
+ if( count < (sizeof(struct sg_header) + size) )
{
device->pending=0;
- wake_up(&device->write_wait);
- sg_free(device->buff,device->buff_len);
+ wake_up( &device->write_wait );
+ sg_free( device->buff, device->buff_len );
device->buff = NULL;
return -EIO;
}
+ /*
+ * Now copy the SCSI command from the user's address space.
+ */
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);
+
+ /*
+ * 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( direction == SG_SEND ) memcpy_fromfs(device->buff,buf, amt);
+
+ /*
+ * Set the LUN field in the command structure.
+ */
+ cmnd[1]= (cmnd[1] & 0x1f) | (device->device->lun<<5);
+
#ifdef DEBUG
printk("do cmd\n");
#endif
+
+ /*
+ * 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");
#endif
+
return count;
}
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