📄 fas216.c
字号:
"target trying to receive more command bytes\n", info->host->host_no, fas216_target(info)); outb(CMD_SETATN, REG_CMD(info)); outb(15, REG_STCL(info)); outb(0, REG_STCM(info)); outb(0, REG_STCH(info)); outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info)); msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); info->scsi.phase = PHASE_MSGOUT_EXPECT; return; /* Selection -> Message Out */ case STATE(STAT_MESGOUT, PHASE_SELSTEPS): case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out -> Message Out */ /* If we get another message out phase, this * usually means some parity error occurred. * Resend complete set of messages. If we have * more than 1 byte to send, we need to assert * ATN again. */ if (msgqueue_msglength(&info->scsi.msgs) > 1) outb(CMD_SETATN, REG_CMD(info)); fas216_send_messageout(info, 0); return; } if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) { printk(KERN_ERR "scsi%d.%c: disconnect message received, but bus service %s?\n", info->host->host_no, fas216_target(info), fas216_bus_phase(stat)); msgqueue_flush(&info->scsi.msgs); outb(CMD_SETATN, REG_CMD(info)); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; outb(CMD_TRANSFERINFO, REG_CMD(info)); return; } printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n", info->host->host_no, fas216_target(info), fas216_bus_phase(stat), fas216_drv_phase(info)); return; default: printk("scsi%d.%c: bus service at step %d?\n", info->host->host_no, fas216_target(info), ssr & IS_BITS); }}/* Function: void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr) * Purpose : handle a function done interrupt from FAS216 chip * Params : info - interface which caused function done interrupt * stat - Status register contents * ssr - SCSI Status register contents */static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr){ int status, message; fas216_checkmagic(info, "fas216_funcdone_intr");#ifdef DEBUG_FUNCTIONDONE printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n", info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);#endif switch (info->scsi.phase) { case PHASE_STATUS: /* status phase - read status and msg */ status = inb(REG_FF(info)); message = inb(REG_FF(info)); info->scsi.SCp.Message = message; info->scsi.SCp.Status = status; info->scsi.phase = PHASE_DONE; outb(CMD_MSGACCEPTED, REG_CMD(info)); break; case PHASE_IDLE: /* reselected? */ case PHASE_MSGIN: /* message in phase */ case PHASE_RECONNECTED: /* reconnected command */ if ((stat & STAT_BUSMASK) == STAT_MESGIN) { info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF; fas216_message(info); break; } default: printk("scsi%d.%c: internal phase %s for function done?" " What do I do with this?\n", info->host->host_no, fas216_target(info), fas216_drv_phase(info)); }}/* Function: void fas216_intr(struct Scsi_Host *instance) * Purpose : handle interrupts from the interface to progress a command * Params : instance - interface to service */void fas216_intr(struct Scsi_Host *instance){ FAS216_Info *info = (FAS216_Info *)instance->hostdata; unsigned char isr, ssr, stat; fas216_checkmagic(info, "fas216_intr"); stat = inb(REG_STAT(info)); ssr = inb(REG_IS(info)); isr = inb(REG_INST(info)); add_debug_list(stat, ssr, isr, info->scsi.phase); if (stat & STAT_INT) { if (isr & INST_BUSRESET) printk("scsi%d.H: bus reset detected\n", instance->host_no); else if (isr & INST_ILLEGALCMD) { printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no); fas216_dumpstate(info); } else if (isr & INST_DISCONNECT) fas216_disconnect_intr(info); else if (isr & INST_RESELECTED) /* reselected */ fas216_reselected_intr(info); else if (isr & INST_BUSSERVICE) /* bus service request */ fas216_busservice_intr(info, stat, ssr); else if (isr & INST_FUNCDONE) /* function done */ fas216_funcdone_intr(info, stat, ssr); else printk("scsi%d.%c: unknown interrupt received:" " phase %s isr %02X ssr %02X stat %02X\n", instance->host_no, fas216_target(info), fas216_drv_phase(info), isr, ssr, stat); }}/* Function: void fas216_kick(FAS216_Info *info) * Purpose : kick a command to the interface - interface should be idle * Params : info - our host interface to kick * Notes : Interrupts are always disabled! */static void fas216_kick(FAS216_Info *info){ Scsi_Cmnd *SCpnt; int tot_msglen, from_queue = 0; fas216_checkmagic(info, "fas216_kick"); if (info->origSCpnt) { SCpnt = info->origSCpnt; info->origSCpnt = NULL; } else SCpnt = NULL; /* retrieve next command */ if (!SCpnt) { SCpnt = queue_remove_exclude(&info->queues.issue, info->busyluns); from_queue = 1; } if (!SCpnt) /* no command pending - just exit */ return; if (info->scsi.disconnectable && info->SCpnt) { queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); info->scsi.disconnectable = 0; info->SCpnt = NULL; printk("scsi%d.%c: moved command to disconnected queue\n", info->host->host_no, fas216_target(info)); } /* * tagged queuing - allocate a new tag to this command */ if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE) { SCpnt->device->current_tag += 1; if (SCpnt->device->current_tag == 0) SCpnt->device->current_tag = 1; SCpnt->tag = SCpnt->device->current_tag; } /* * claim host busy */ info->scsi.phase = PHASE_SELECTION; info->SCpnt = SCpnt; info->scsi.SCp = SCpnt->SCp; info->dma.transfer_type = fasdma_none;#ifdef DEBUG_CONNECT printk("scsi%d.%c: starting cmd %02X", info->host->host_no, '0' + SCpnt->target, SCpnt->cmnd[0]);#endif if (from_queue) {#ifdef SCSI2_TAG if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE && SCpnt->cmnd[0] != INQUIRY) { SCpnt->device->current_tag += 1; if (SCpnt->device->current_tag == 0) SCpnt->device->current_tag = 1; SCpnt->tag = SCpnt->device->current_tag; } else#endif set_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); info->stats.removes += 1; switch (SCpnt->cmnd[0]) { case WRITE_6: case WRITE_10: case WRITE_12: info->stats.writes += 1; break; case READ_6: case READ_10: case READ_12: info->stats.reads += 1; break; default: info->stats.miscs += 1; break; } } /* build outgoing message bytes */ msgqueue_flush(&info->scsi.msgs); if (info->device[SCpnt->target].disconnect_ok) msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun)); else msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(0, SCpnt->lun)); /* add tag message if required */ if (SCpnt->tag) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);#ifdef SCSI2_WIDE if (info->device[SCpnt->target].wide_state == neg_wait) { info->device[SCpnt->target].wide_state = neg_inprogress; msgqueue_addmsg(&info->scsi.msgs, 4, EXTENDED_MESSAGE, 2, EXTENDED_WDTR, info->ifcfg.wide_max_size); }#ifdef SCSI2_SYNC else#endif#endif#ifdef SCSI2_SYNC if ((info->device[SCpnt->target].sync_state == neg_wait || info->device[SCpnt->target].sync_state == neg_complete) && (SCpnt->cmnd[0] == REQUEST_SENSE || SCpnt->cmnd[0] == INQUIRY)) { info->device[SCpnt->target].sync_state = neg_inprogress; msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 1000 / info->ifcfg.clockrate, info->ifcfg.sync_max_depth); }#endif /* following what the ESP driver says */ outb(0, REG_STCL(info)); outb(0, REG_STCM(info)); outb(0, REG_STCH(info)); outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info)); /* flush FIFO */ outb(CMD_FLUSHFIFO, REG_CMD(info)); /* load bus-id and timeout */ outb(BUSID(SCpnt->target), REG_SDID(info)); outb(info->ifcfg.select_timeout, REG_STIM(info)); /* synchronous transfers */ fas216_set_sync(info, SCpnt->target); tot_msglen = msgqueue_msglength(&info->scsi.msgs); if (tot_msglen == 1 || tot_msglen == 3) { /* * We have an easy message length to send... */ struct message *msg; int msgnr = 0, i; info->scsi.phase = PHASE_SELSTEPS; /* load message bytes */ while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { for (i = 0; i < msg->length; i++) outb(msg->msg[i], REG_FF(info)); msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF); } /* load command */ for (i = 0; i < SCpnt->cmd_len; i++) outb(SCpnt->cmnd[i], REG_FF(info)); if (tot_msglen == 1) outb(CMD_SELECTATN, REG_CMD(info)); else outb(CMD_SELECTATN3, REG_CMD(info)); } else { /* * We have an unusual number of message bytes to send. * Load first byte into fifo, and issue SELECT with ATN and * stop steps. */ struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0); outb(msg->msg[0], REG_FF(info)); msg->fifo = 1; outb(CMD_SELECTATNSTOP, REG_CMD(info)); }#ifdef DEBUG_CONNECT printk(", data pointers [%p, %X]\n", info->scsi.SCp.ptr, info->scsi.SCp.this_residual);#endif /* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */}/* Function: void fas216_done(FAS216_Info *info, unsigned int result) * Purpose : complete processing for command * Params : info - interface that completed * result - driver byte of result */static void fas216_done(FAS216_Info *info, unsigned int result){ Scsi_Cmnd *SCpnt; fas216_checkmagic(info, "fas216_done"); SCpnt = info->SCpnt; 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; } info->stats.fins += 1; if (SCpnt) { info->scsi.phase = PHASE_IDLE; info->SCpnt = NULL; SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 | info->scsi.SCp.Status; /* * In theory, this should not happen, but just in case it does. */ if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual && result == DID_OK) { switch (SCpnt->cmnd[0]) { case INQUIRY: case START_STOP: case READ_CAPACITY: case TEST_UNIT_READY: case MODE_SENSE: break; default: switch (status_byte(SCpnt->result)) { case CHECK_CONDITION: case COMMAND_TERMINATED: case BUSY: case QUEUE_FULL: case RESERVATION_CONFLICT: break; default: printk(KERN_ERR "scsi%d.H: incomplete data transfer " "detected: res=%08X ptr=%p len=%X command=", info->host->host_no, SCpnt->result, info->scsi.SCp.ptr, info->scsi.SCp.this_residual); print_command(SCpnt->cmnd); } } }#ifdef DEBUG_CONNECT printk("scsi%d.%c: scsi command (%p) complete, result=%08X\n", info->host->host_no, fas216_target(info), SCpnt, SCpnt->result);#endif if (!SCpnt->scsi_done) panic("scsi%d.H: null scsi_done function in " "fas216_done", info->host->host_no); clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns); SCpnt->scsi_done(SCpnt); } else panic("scsi%d.H: null command in fas216_done", info->host->host_no); if (info->scsi.irq != NO_IRQ) fas216_kick(info);}/* 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 */int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){ FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata; fas216_checkmagic(info, "fas216_queue_command");#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 = NULL; 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 */ for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++) len += SCpnt->SCp.buffer[buf].length; 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; } info->stats.queues += 1; SCpnt->tag = 0; if (info->scsi.irq != NO_IRQ) { unsigned long flags; /* add command into execute queue and let it complete under * the drivers interrupts. */ if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -