/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scsi.h" #include "hosts.h" #include #include 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", 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 = 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 */ }; 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 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, (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; default: 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 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) { if (flags & O_NONBLOCK) return -EBUSY; interruptible_sleep_on(&scsi_generics[dev].generic_wait); if (signal_pending(current)) return -ERESTARTSYS; } 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) return -EBUSY; interruptible_sleep_on(&scsi_generics[dev].generic_wait); if (signal_pending(current)) 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->module) __MOD_INC_USE_COUNT(scsi_generics[dev].device->host->hostt->module); if (sg_template.module) __MOD_INC_USE_COUNT(sg_template.module); scsi_generics[dev].users++; return 0; } static int 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->module) __MOD_DEC_USE_COUNT(scsi_generics[dev].device->host->hostt->module); if(sg_template.module) __MOD_DEC_USE_COUNT(sg_template.module); scsi_generics[dev].exclude=0; wake_up(&scsi_generics[dev].generic_wait); return 0; } static char *sg_malloc(int 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 (signal_pending(current)) return NULL; } big_inuse=1; return big_buff; } #endif 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; } #endif 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 ssize_t sg_read(struct file *filp, char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; int dev=MINOR(inode->i_rdev); int i; unsigned long flags; struct scsi_generic *device=&scsi_generics[dev]; if (ppos != &filp->f_pos) { /* FIXME: Hmm. Seek to the right place, or fail? */ } 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 (signal_pending(current)) { restore_flags(flags); return -ERESTARTSYS; } } 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 = 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 ssize_t sg_write(struct file *filp, const char *buf, size_t count, loff_t *ppos) { struct inode *inode = filp->f_dentry->d_inode; 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 (ppos != &filp->f_pos) { /* FIXME: Hmm. Seek to the right place, or fail? */ } 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"); #endif interruptible_sleep_on(&device->write_wait); if (signal_pending(current)) 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"); #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; wake_up(&device->write_wait); sg_free(device->buff,device->buff_len); device->buff = NULL; return -EAGAIN; } #ifdef DEBUG printk("device allocated\n"); #endif 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"); #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; } static unsigned int sg_poll(struct file *file, poll_table * wait) { int dev = MINOR(file->f_dentry->d_inode->i_rdev); struct scsi_generic *device = &scsi_generics[dev]; unsigned int mask = 0; poll_wait(&scsi_generics[dev].read_wait, wait); poll_wait(&scsi_generics[dev].write_wait, wait); if(device->pending && device->complete) mask |= POLLIN | POLLRDNORM; if(!device->pending) mask |= POLLOUT | POLLWRNORM; return mask; } static struct file_operations sg_fops = { NULL, /* lseek */ sg_read, /* read */ sg_write, /* write */ NULL, /* readdir */ sg_poll, /* poll */ sg_ioctl, /* ioctl */ NULL, /* mmap */ sg_open, /* open */ sg_close, /* release */ NULL /* fsync */ }; static int sg_detect(Scsi_Device * SDp){ 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 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"); #endif #ifdef SG_BIG_BUFF 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; 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; } for(gpnt = scsi_generics, i=0; idevice) 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; idevice == SDp) { gpnt->device = NULL; SDp->attached--; sg_template.nr_dev--; /* * avoid associated device /dev/sg? bying incremented * each time module is inserted/removed , */ sg_template.dev_noticed--; return; } return; } #ifdef MODULE int init_module(void) { sg_template.module = &__this_module; return scsi_register_module(MODULE_SCSI_DEV, &sg_template); } void cleanup_module( void) { 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)); } 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 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: 8 * End: */