📄 fas216.c
字号:
return; /* Error recovery rules. * These either attempt to abort or retry the operation. * TODO: we need more of these */ case STATE(STAT_COMMAND, PHASE_COMMAND):/* Command -> Command */ /* error - we've sent out all the command bytes * we have. * NOTE: we need SAVE DATA POINTERS/RESTORE DATA POINTERS * to include the command bytes sent for this to work * correctly. */ printk(KERN_ERR "scsi%d.%c: " "target trying to receive more command bytes\n", info->host->host_no, fas216_target(info)); fas216_cmd(info, CMD_SETATN); fas216_set_stc(info, 15); fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA); msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); info->scsi.phase = PHASE_MSGOUT_EXPECT; 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); fas216_cmd(info, CMD_SETATN); msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR); info->scsi.phase = PHASE_MSGOUT_EXPECT; info->scsi.aborting = 1; fas216_cmd(info, CMD_TRANSFERINFO); 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)); print_debug_list(); return;bad_is: fas216_log(info, 0, "bus service at step %d?", is & IS_BITS); fas216_dumpstate(info); print_debug_list(); fas216_done(info, DID_ERROR);}/** * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip * @info: interface which caused function done interrupt * @stat: Status register contents * @is: SCSI Status register contents * * Handle a function done interrupt from FAS216 chip */static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is){ unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF; fas216_checkmagic(info); fas216_log(info, LOG_FUNCTIONDONE, "function done: stat=%02x is=%02x phase=%02x", stat, is, info->scsi.phase); switch (info->scsi.phase) { case PHASE_STATUS: /* status phase - read status and msg */ if (fifo_len != 2) { fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len); } /* * Read status then message byte. */ info->scsi.SCp.Status = fas216_readb(info, REG_FF); info->scsi.SCp.Message = fas216_readb(info, REG_FF); info->scsi.phase = PHASE_DONE; fas216_cmd(info, CMD_MSGACCEPTED); break; case PHASE_IDLE: case PHASE_SELECTION: case PHASE_SELSTEPS: break; case PHASE_MSGIN: /* message in phase */ if ((stat & STAT_BUSMASK) == STAT_MESGIN) { info->scsi.msgin_fifo = fifo_len; fas216_message(info); break; } default: fas216_log(info, 0, "internal phase %s for function done?" " What do I do with this?", fas216_target(info), fas216_drv_phase(info)); }}static void fas216_bus_reset(FAS216_Info *info){ neg_t sync_state; int i; msgqueue_flush(&info->scsi.msgs); sync_state = neg_invalid;#ifdef SCSI2_SYNC if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA)) sync_state = neg_wait;#endif info->scsi.phase = PHASE_IDLE; info->SCpnt = NULL; /* bug! */ memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp)); for (i = 0; i < 8; i++) { info->device[i].disconnect_ok = info->ifcfg.disconnect_ok; info->device[i].sync_state = sync_state; info->device[i].period = info->ifcfg.asyncperiod / 4; info->device[i].stp = info->scsi.async_stp; info->device[i].sof = 0; info->device[i].wide_xfer = 0; } info->rst_bus_status = 1; wake_up(&info->eh_wait);}/** * fas216_intr - handle interrupts to progress a command * @info: interface to service * * Handle interrupts from the interface to progress a command */irqreturn_t fas216_intr(FAS216_Info *info){ unsigned char inst, is, stat; int handled = IRQ_NONE; fas216_checkmagic(info); stat = fas216_readb(info, REG_STAT); is = fas216_readb(info, REG_IS); inst = fas216_readb(info, REG_INST); add_debug_list(stat, is, inst, info->scsi.phase); if (stat & STAT_INT) { if (inst & INST_BUSRESET) { fas216_log(info, 0, "bus reset detected"); fas216_bus_reset(info); scsi_report_bus_reset(info->host, 0); } else if (inst & INST_ILLEGALCMD) { fas216_log(info, LOG_ERROR, "illegal command given\n"); fas216_dumpstate(info); print_debug_list(); } else if (inst & INST_DISCONNECT) fas216_disconnect_intr(info); else if (inst & INST_RESELECTED) /* reselected */ fas216_reselected_intr(info); else if (inst & INST_BUSSERVICE) /* bus service request */ fas216_busservice_intr(info, stat, is); else if (inst & INST_FUNCDONE) /* function done */ fas216_funcdone_intr(info, stat, is); else fas216_log(info, 0, "unknown interrupt received:" " phase %s inst %02X is %02X stat %02X", fas216_drv_phase(info), inst, is, stat); handled = IRQ_HANDLED; } return handled;}static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt){ int tot_msglen; /* following what the ESP driver says */ fas216_set_stc(info, 0); fas216_cmd(info, CMD_NOP | CMD_WITHDMA); /* flush FIFO */ fas216_cmd(info, CMD_FLUSHFIFO); /* load bus-id and timeout */ fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); /* synchronous transfers */ fas216_set_sync(info, SCpnt->device->id); tot_msglen = msgqueue_msglength(&info->scsi.msgs);#ifdef DEBUG_MESSAGES { struct message *msg; int msgnr = 0, i; printk("scsi%d.%c: message out: ", info->host->host_no, '0' + SCpnt->device->id); while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { printk("{ "); for (i = 0; i < msg->length; i++) printk("%02x ", msg->msg[i]); printk("} "); } printk("\n"); }#endif 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++) fas216_writeb(info, REG_FF, msg->msg[i]); msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF); } /* load command */ for (i = 0; i < SCpnt->cmd_len; i++) fas216_writeb(info, REG_FF, SCpnt->cmnd[i]); if (tot_msglen == 1) fas216_cmd(info, CMD_SELECTATN); else fas216_cmd(info, CMD_SELECTATN3); } 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); fas216_writeb(info, REG_FF, msg->msg[0]); msg->fifo = 1; fas216_cmd(info, CMD_SELECTATNSTOP); }}/* * Decide whether we need to perform a parity test on this device. * Can also be used to force parity error conditions during initial * information transfer phase (message out) for test purposes. */static int parity_test(FAS216_Info *info, int target){#if 0 if (target == 3) { info->device[target].parity_check = 0; return 1; }#endif return info->device[target].parity_check;}static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt){ int disconnect_ok; /* * claim host busy */ info->scsi.phase = PHASE_SELECTION; info->scsi.SCp = SCpnt->SCp; info->SCpnt = SCpnt; info->dma.transfer_type = fasdma_none; if (parity_test(info, SCpnt->device->id)) fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE); else fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]); /* * Don't allow request sense commands to disconnect. */ disconnect_ok = SCpnt->cmnd[0] != REQUEST_SENSE && info->device[SCpnt->device->id].disconnect_ok; /* * build outgoing message bytes */ msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(disconnect_ok, SCpnt->device->lun)); /* * add tag message if required */ if (SCpnt->tag) msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag); do {#ifdef SCSI2_SYNC if ((info->device[SCpnt->device->id].sync_state == neg_wait || info->device[SCpnt->device->id].sync_state == neg_complete) && (SCpnt->cmnd[0] == REQUEST_SENSE || SCpnt->cmnd[0] == INQUIRY)) { info->device[SCpnt->device->id].sync_state = neg_inprogress; msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, 1000 / info->ifcfg.clockrate, info->ifcfg.sync_max_depth); break; }#endif } while (0); __fas216_start_command(info, SCpnt);}static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt){#ifdef SCSI2_TAG /* * tagged queuing - allocate a new tag to this command */ if (SCpnt->device->simple_tags && 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->device->id * 8 + SCpnt->device->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; }}static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt){ struct message *msg; /* * claim host busy */ info->scsi.phase = PHASE_SELECTION; info->scsi.SCp = SCpnt->SCp; info->SCpnt = SCpnt; info->dma.transfer_type = fasdma_none; fas216_log(info, LOG_ERROR, "sending bus device reset"); msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET); /* following what the ESP driver says */ fas216_set_stc(info, 0); fas216_cmd(info, CMD_NOP | CMD_WITHDMA); /* flush FIFO */ fas216_cmd(info, CMD_FLUSHFIFO); /* load bus-id and timeout */ fas216_writeb(info, REG_SDID, BUSID(SCpnt->device->id)); fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout); /* synchronous transfers */ fas216_set_sync(info, SCpnt->device->id); msg = msgqueue_getmsg(&info->scsi.msgs, 0); fas216_writeb(info, REG_FF, BUS_DEVICE_RESET); msg->fifo = 1; fas216_cmd(info, CMD_SELECTATNSTOP);}/** * fas216_kick - kick a command to the interface * @info: our host interface to kick * * Kick a command to the interface, interface should be idle. * Notes: Interrupts are always disabled! */static void fas216_kick(FAS216_Info *info){ Scsi_Cmnd *SCpnt = NULL;#define TYPE_OTHER 0#define TYPE_RESET 1#define TYPE_QUEUE 2 int where_from = TYPE_OTHER; fas216_checkmagic(info); /* * Obtain the next command to process. */ do { if (info->rstSCpnt) { SCpnt = info->rstSCpnt; /* don't remove it */ where_from = TYPE_RESET; break; } if (info->reqSCpnt) { SCpnt = info->reqSCpnt; info->reqSCpnt = NULL; break; } if (info->origSCpnt) { SCpnt = info->origSCpnt; info->origSCpnt = NULL; break; } /* retrieve next command */ if (!SCpnt) { SCpnt = queue_remove_exclude(&info->queues.issue, info->busyluns); where_from = TYPE_QUEUE; break; } } while (0); if (!SCpnt) { /* * no command pending, so enable reselection. */ fas216_cmd(info, CMD_ENABLESEL); return; } /* * We're going to start a command, so disable reselection */ fas216_cmd(info, CMD_DISABLESEL); if (info->scsi.disconnectable && info->SCpnt) { fas216_log(info, LOG_CONNECT, "moved command for %d to disconnected queue", info->SCpnt->device->id); queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt); info->scsi.disconnectable = 0; info->SCpnt = NULL; } fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, "starting"); switch (where_from) { case TYPE_QUEUE: fas216_allocate_tag(info, SCpnt); case TYPE_OTHER: fas216_start_command(info, SCpnt); break; case TYPE_RESET: fas216_do_bus_device_reset(info, SCpnt); break; } fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]", info->scsi.SCp.ptr, info->scsi.SCp.this_residual); /* * should now get either DISCONNECT or * (FUNCTION DONE with BUS SERVICE) interrupt */}/* * Clean up from issuing a BUS DEVICE RESET message to a device. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -