/* * 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); scsi_release_command(SCpnt); SCpnt = NULL; 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; scsi_release_command(SCpnt); SCpnt = NULL; wake_up(&scsi_generics[dev].read_wait); }
/* * This interface is depreciated - users should use the scsi generics * interface instead, as this is a more flexible approach to performing * generic SCSI commands on a device. */ int scsi_ioctl_send_command(Scsi_Device *dev, Scsi_Ioctl_Command *sic) { unsigned long flags; char * buf; unsigned char cmd[12]; char * cmd_in; Scsi_Cmnd * SCpnt; Scsi_Device * SDpnt; unsigned char opcode; int inlen, outlen, cmdlen; int needed, buf_needed; int timeout, retries, result; if (!sic) return -EINVAL; /* * Verify that we can read at least this much. */ result = verify_area(VERIFY_READ, sic, sizeof (Scsi_Ioctl_Command)); if (result) return result; /* * The structure that we are passed should look like: * * struct sdata { * unsigned int inlen; * unsigned int outlen; * unsigned char cmd[]; # However many bytes are used for cmd. * unsigned char data[]; * }; */ get_user(inlen, &sic->inlen); get_user(outlen, &sic->outlen); /* * We do not transfer more than MAX_BUF with this interface. * If the user needs to transfer more data than this, they * should use scsi_generics instead. */ if( inlen > MAX_BUF ) return -EINVAL; if( outlen > MAX_BUF ) return -EINVAL; cmd_in = sic->data; get_user(opcode, cmd_in); needed = buf_needed = (inlen > outlen ? inlen : outlen); if(buf_needed){ buf_needed = (buf_needed + 511) & ~511; if (buf_needed > MAX_BUF) buf_needed = MAX_BUF; spin_lock_irqsave(&io_request_lock, flags); buf = (char *) scsi_malloc(buf_needed); spin_unlock_irqrestore(&io_request_lock, flags); if (!buf) return -ENOMEM; memset(buf, 0, buf_needed); } else buf = NULL; /* * Obtain the command from the user's address space. */ cmdlen = COMMAND_SIZE(opcode); result = verify_area(VERIFY_READ, cmd_in, cmdlen + inlen > MAX_BUF ? MAX_BUF : cmdlen + inlen); if (result) return result; copy_from_user ((void *) cmd, cmd_in, cmdlen); /* * Obtain the data to be sent to the device (if any). */ copy_from_user ((void *) buf, (void *) (cmd_in + cmdlen), inlen); /* * Set the lun field to the correct value. */ cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5); switch (opcode) { case FORMAT_UNIT: timeout = FORMAT_UNIT_TIMEOUT; retries = 1; break; case START_STOP: timeout = START_STOP_TIMEOUT; retries = NORMAL_RETRIES; break; case MOVE_MEDIUM: timeout = MOVE_MEDIUM_TIMEOUT; retries = NORMAL_RETRIES; break; case READ_ELEMENT_STATUS: timeout = READ_ELEMENT_STATUS_TIMEOUT; retries = NORMAL_RETRIES; break; default: timeout = NORMAL_TIMEOUT; retries = NORMAL_RETRIES; break; } #ifndef DEBUG_NO_CMD spin_lock_irqsave(&io_request_lock, flags); SCpnt = scsi_allocate_device(NULL, dev, 1); { struct semaphore sem = MUTEX_LOCKED; SCpnt->request.sem = &sem; scsi_do_cmd(SCpnt, cmd, buf, needed, scsi_ioctl_done, timeout, retries); spin_unlock_irqrestore(&io_request_lock, flags); down(&sem); SCpnt->request.sem = NULL; } /* * If there was an error condition, pass the info back to the user. */ if(SCpnt->result) { result = verify_area(VERIFY_WRITE, cmd_in, sizeof(SCpnt->sense_buffer)); if (result) return result; copy_to_user((void *) cmd_in, SCpnt->sense_buffer, sizeof(SCpnt->sense_buffer)); } else { result = verify_area(VERIFY_WRITE, cmd_in, outlen); if (result) return result; copy_to_user ((void *) cmd_in, buf, outlen); } result = SCpnt->result; spin_lock_irqsave(&io_request_lock, flags); wake_up(&SCpnt->device->device_wait); SDpnt = SCpnt->device; scsi_release_command(SCpnt); SCpnt = NULL; if (buf) scsi_free(buf, buf_needed); if(SDpnt->scsi_request_fn) (*SDpnt->scsi_request_fn)(); spin_unlock_irqrestore(&io_request_lock, flags); return result; #else { int i; printk("scsi_ioctl : device %d. command = ", dev->id); for (i = 0; i < 12; ++i) printk("%02x ", cmd[i]); printk("\nbuffer ="); for (i = 0; i < 20; ++i) printk("%02x ", buf[i]); printk("\n"); printk("inlen = %d, outlen = %d, cmdlen = %d\n", inlen, outlen, cmdlen); printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in); } return 0; #endif }
static int ioctl_internal_command(Scsi_Device *dev, char * cmd, int timeout, int retries) { unsigned long flags; int result; Scsi_Cmnd * SCpnt; Scsi_Device * SDpnt; spin_lock_irqsave(&io_request_lock, flags); SCSI_LOG_IOCTL(1, printk("Trying ioctl with scsi command %d\n", cmd[0])); SCpnt = scsi_allocate_device(NULL, dev, 1); { struct semaphore sem = MUTEX_LOCKED; SCpnt->request.sem = &sem; scsi_do_cmd(SCpnt, cmd, NULL, 0, scsi_ioctl_done, timeout, retries); spin_unlock_irqrestore(&io_request_lock, flags); down(&sem); spin_lock_irqsave(&io_request_lock, flags); SCpnt->request.sem = NULL; } SCSI_LOG_IOCTL(2, printk("Ioctl returned 0x%x\n", SCpnt->result)); if(driver_byte(SCpnt->result) != 0) switch(SCpnt->sense_buffer[2] & 0xf) { case ILLEGAL_REQUEST: if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0; else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n"); break; case NOT_READY: /* This happens if there is no disc in drive */ if(dev->removable && (cmd[0] != TEST_UNIT_READY)){ printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n"); break; } case UNIT_ATTENTION: if (dev->removable){ dev->changed = 1; SCpnt->result = 0; /* This is no longer considered an error */ /* gag this error, VFS will log it anyway /axboe */ /* printk(KERN_INFO "Disc change detected.\n"); */ break; }; default: /* Fall through for non-removable media */ printk("SCSI error: host %d id %d lun %d return code = %x\n", dev->host->host_no, dev->id, dev->lun, SCpnt->result); printk("\tSense class %x, sense error %x, extended sense %x\n", sense_class(SCpnt->sense_buffer[0]), sense_error(SCpnt->sense_buffer[0]), SCpnt->sense_buffer[2] & 0xf); }; result = SCpnt->result; SCSI_LOG_IOCTL(2, printk("IOCTL Releasing command\n")); SDpnt = SCpnt->device; scsi_release_command(SCpnt); SCpnt = NULL; if (!SDpnt->was_reset && SDpnt->scsi_request_fn) (*SDpnt->scsi_request_fn)(); wake_up(&SDpnt->device_wait); spin_unlock_irqrestore(&io_request_lock, flags); return result; }