📄 fas216.c
字号:
* result - driver byte of result */static voidfas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result){ info->stats.fins += 1; SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | info->scsi.SCp.Status;#ifdef DEBUG_CONNECT printk("scsi%d.%c: command complete, result=%08X, command=", info->host->host_no, '0' + SCpnt->target, SCpnt->result); print_command(SCpnt->cmnd);#endif /* * If the driver detected an error, or the command * was request sense, then we're all done. */ if (result != DID_OK || SCpnt->cmnd[0] == REQUEST_SENSE) goto done; /* * If the command returned CHECK_CONDITION status, * request the sense information. */ if (info->scsi.SCp.Status == CHECK_CONDITION) goto request_sense; /* * If the command did not complete with GOOD status, * we are all done here. */ if (info->scsi.SCp.Status != GOOD) goto done; /* * We have successfully completed a command. Make sure that * we do not have any buffers left to transfer. The world * is not perfect, and we seem to occasionally hit this. * It can be indicative of a buggy driver, target or the upper * levels of the SCSI code. */ if (info->scsi.SCp.ptr) { switch (SCpnt->cmnd[0]) { case INQUIRY: case START_STOP:// case READ_CAPACITY: case MODE_SENSE: break; default: printk(KERN_ERR "scsi%d.%c: incomplete data transfer " "detected: res=%08X ptr=%p len=%X command=", info->host->host_no, '0' + SCpnt->target, SCpnt->result, info->scsi.SCp.ptr, info->scsi.SCp.this_residual); print_command(SCpnt->cmnd); } }done: SCpnt->scsi_done(SCpnt); return;request_sense: memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd)); SCpnt->cmnd[0] = REQUEST_SENSE; SCpnt->cmnd[1] = SCpnt->lun << 5; SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer); SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); SCpnt->SCp.buffer = NULL; SCpnt->SCp.buffers_residual = 0; SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); SCpnt->SCp.Message = 0; SCpnt->SCp.Status = 0; SCpnt->sc_data_direction = SCSI_DATA_READ; SCpnt->use_sg = 0; SCpnt->tag = 0; SCpnt->host_scribble = (void *)fas216_rq_sns_done; /* * Place this command into the high priority "request * sense" slot. This will be the very next command * executed, unless a target connects to us. */ if (info->reqSCpnt) printk(KERN_WARNING "scsi%d.%c: loosing request command\n", info->host->host_no, '0' + SCpnt->target); info->reqSCpnt = SCpnt;}/* Function: void fas216_done(FAS216_Info *info, unsigned int result) * Purpose : complete processing for current command * Params : info - interface that completed * result - driver byte of result */static void fas216_done(FAS216_Info *info, unsigned int result){ void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int); Scsi_Cmnd *SCpnt; fas216_checkmagic(info); if (!info->SCpnt) goto no_command; SCpnt = info->SCpnt; info->SCpnt = NULL; info->scsi.phase = PHASE_IDLE; if (!SCpnt->scsi_done) goto no_done; if (info->scsi.aborting) { printk("scsi%d.%c: uncaught abort - returning DID_ABORT\n", info->host->host_no, fas216_target(info)); result = DID_ABORT; info->scsi.aborting = 0; } /* * Sanity check the completion - if we have zero bytes left * to transfer, we should not have a valid pointer. */ if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) { printk("scsi%d.%c: zero bytes left to transfer, but " "buffer pointer still valid: ptr=%p len=%08x command=", info->host->host_no, '0' + SCpnt->target, info->scsi.SCp.ptr, info->scsi.SCp.this_residual); info->scsi.SCp.ptr = NULL; print_command(SCpnt->cmnd); } /* * Clear down this command as completed. If we need to request * the sense information, fas216_kick will re-assert the busy * status. */ clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble; fn(info, SCpnt, result); if (info->scsi.irq != NO_IRQ) fas216_kick(info); return;no_command: panic("scsi%d.H: null command in fas216_done", info->host->host_no);no_done: panic("scsi%d.H: null scsi_done function in fas216_done", info->host->host_no);}/* Function: int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) * Purpose : queue a command for adapter to process. * Params : SCpnt - Command to queue * done - done function to call once command is complete * Returns : 0 - success, else error * Notes : io_request_lock is held, interrupts are disabled. */int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; int result; fas216_checkmagic(info);#ifdef DEBUG_CONNECT printk("scsi%d.%c: received queuable command (%p) %02X\n", SCpnt->host->host_no, '0' + SCpnt->target, SCpnt, SCpnt->cmnd[0]);#endif SCpnt->scsi_done = done; SCpnt->host_scribble = (void *)fas216_std_done; SCpnt->result = 0; SCpnt->SCp.Message = 0; SCpnt->SCp.Status = 0; if (SCpnt->use_sg) { unsigned long len = 0; int buf; SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->address; SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; /* * Calculate correct buffer length. Some commands * come in with the wrong request_bufflen. */ for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) len += SCpnt->SCp.buffer[buf].length; if (SCpnt->request_bufflen != len) printk(KERN_WARNING "scsi%d.%c: bad request buffer " "length %d, should be %ld\n", info->host->host_no, '0' + SCpnt->target, SCpnt->request_bufflen, len); SCpnt->request_bufflen = len; } else { SCpnt->SCp.buffer = NULL; SCpnt->SCp.buffers_residual = 0; SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; SCpnt->SCp.this_residual = SCpnt->request_bufflen; } /* * If the upper SCSI layers pass a buffer, but zero length, * we aren't interested in the buffer pointer. */ if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) {#if 0 printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for " "command ", info->host->host_no, '0' + SCpnt->target); print_command(SCpnt->cmnd);#endif SCpnt->SCp.ptr = NULL; } info->stats.queues += 1; SCpnt->tag = 0; /* * Add command into execute queue and let it complete under * whatever scheme we're using. */ result = !queue_add_cmd_ordered(&info->queues.issue, SCpnt); /* * If we successfully added the command, * kick the interface to get it moving. */ if (result == 0 && (!info->SCpnt || info->scsi.disconnectable)) fas216_kick(info); return result;}/* Function: void fas216_internal_done(Scsi_Cmnd *SCpnt) * Purpose : trigger restart of a waiting thread in fas216_command * Params : SCpnt - Command to wake */static void fas216_internal_done(Scsi_Cmnd *SCpnt){ FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; fas216_checkmagic(info); info->internal_done = 1;}/* Function: int fas216_command(Scsi_Cmnd *SCpnt) * Purpose : queue a command for adapter to process. * Params : SCpnt - Command to queue * Returns : scsi result code * Notes : io_request_lock is held, interrupts are disabled. */int fas216_command(Scsi_Cmnd *SCpnt){ FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; fas216_checkmagic(info); /* * We should only be using this if we don't have an interrupt. * Provide some "incentive" to use the queueing code. */ if (info->scsi.irq != NO_IRQ) BUG(); info->internal_done = 0; fas216_queue_command(SCpnt, fas216_internal_done); /* * This wastes time, since we can't return until the command is * complete. We can't sleep either since we may get re-entered! * However, we must re-enable interrupts, or else we'll be * waiting forever. */ spin_unlock_irq(&io_request_lock); while (!info->internal_done) { /* * If we don't have an IRQ, then we must poll the card for * it's interrupt, and use that to call this driver's * interrupt routine. That way, we keep the command * progressing. Maybe we can add some inteligence here * and go to sleep if we know that the device is going * to be some time (eg, disconnected). */ if (inb(REG_STAT(info)) & STAT_INT) { spin_lock_irq(&io_request_lock); fas216_intr(info->host); spin_unlock_irq(&io_request_lock); } } spin_lock_irq(&io_request_lock); return SCpnt->result;}enum res_abort { res_failed, /* unable to abort */ res_success, /* command on issue queue */ res_success_clear, /* command marked tgt/lun busy */ res_hw_abort /* command on disconnected dev */};/* * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt) * Purpose : decide how to abort a command * Params : SCpnt - command to abort * Returns : abort status */static enum res_abortfas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt){ enum res_abort res = res_failed; if (queue_remove_cmd(&info->queues.issue, SCpnt)) { /* * The command was on the issue queue, and has not been * issued yet. We can remove the command from the queue, * and acknowledge the abort. Neither the device nor the * interface know about the command. */ printk("on issue queue "); res = res_success; } else if (queue_remove_cmd(&info->queues.disconnected, SCpnt)) { /* * The command was on the disconnected queue. We must * reconnect with the device if possible, and send it * an abort message. */ printk("on disconnected queue "); res = res_hw_abort; } else if (info->SCpnt == SCpnt) { printk("executing "); switch (info->scsi.phase) { /* * If the interface is idle, and the command is 'disconnectable', * then it is the same as on the disconnected queue. */ case PHASE_IDLE: if (info->scsi.disconnectable) { info->scsi.disconnectable = 0; info->SCpnt = NULL; res = res_hw_abort; } break; default: break; } } else if (info->origSCpnt == SCpnt) { /* * The command will be executed next, but a command * is currently using the interface. This is similar to * being on the issue queue, except the busylun bit has * been set. */ info->origSCpnt = NULL; printk("waiting for execution "); res = res_success_clear; } else printk("unknown "); return res;}/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt) * Purpose : abort this command * Params : SCpnt - command to abort * Returns : FAILED if unable to abort * Notes : io_request_lock is taken, and irqs are disabled */int fas216_eh_abort(Scsi_Cmnd *SCpnt){ FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; int result = FAILED; fas216_checkmagic(info); info->stats.aborts += 1; print_debug_list(); fas216_dumpstate(info); fas216_dumpinfo(info); printk(KERN_WARNING "scsi%d: abort ", info->host->host_no); switch (fas216_do_abort(info, SCpnt)) { /* * We managed to find the command and cleared it out. * We do not expect the command to be executing on the * target, but we have set the busylun bit. */ case res_success_clear: printk("clear "); clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); /* * We found the command, and cleared it out. Either * the command is still known to be executing on the * target, or the busylun bit is not set. */ case res_success: printk("success\n"); result = SUCCESS; break; /* * We need to reconnect to the target and send it an * ABORT or ABORT_TAG message. We can only do this * if the bus is free. */ case res_hw_abort: /* * We are unable to abort the command for some reason. */ default: case res_failed: printk("failed\n"); break; } return result;}/* Function: void fas216_reset_state(FAS216_Info *info) * Purpose : Initialise driver internal state * Params : info - state to initialise */static void fas216_reset_state(FAS216_Info *info){ neg_t sync_state, wide_state; int i; fas216_checkmagic(info); /* * Clear out all stale info in our state structure */ memset(info->busyluns, 0, sizeof(info->busyluns)); msgqueue_flush(&info->scsi.msgs); info->scsi.reconnected.target = 0; info->scsi.reconnected.lun = 0; info->scsi.reconnected.tag = 0; info->scsi.disconnectable = 0; info->scsi.aborting = 0; info->scsi.phase = PHASE_IDLE; info->scsi.async_stp = fas216_
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -