📄 esp.c
字号:
"linked command not started by driver"); action = ACTION_RESET; } else { esp->e_cur_slot = last_slot; action = esp_link_start(esp); } } else { (*sp->cmd_pkt.pkt_comp)(sp); action = ACTION_SEARCH; } return (action);}/* * Interrupt Service Section *//* * Poll for command completion (i.e., no interrupts) * time is in usec (and will not be very accurate) */static intesp_dopoll(esp, limit)register struct esp *esp;int limit;{ register int i; /* * timeout is not very accurate since we don't know how * long the poll takes * also if the packet gets started fairly late, we may * timeout prematurely */ if (limit == 0) limit = POLL_TIMEOUT; for (i = 0; i < limit; i += 100) { if (esp_poll() == 0) DELAY(100); if (esp->e_state == STATE_FREE) break; } if (i >= limit && esp->e_state != STATE_FREE) { esp_printstate(esp, "polled command timeout"); return (-1); } return (0);}/* * Autovector Interrupt Entry Point. */static intesp_poll(){ register struct esp *esp; register int serviced = 0; register int search = TRUE; if (esp_softc == (struct esp *) 0) return (serviced); while (search == TRUE) { search = FALSE; for (esp = esp_softc; esp != (struct esp *) 0; esp = esp->e_next) { if (esp->e_tran.tran_start) { register s = splr(esp->e_tran.tran_spl);#ifdef sun4m /* * If s > e_tran.tran_spl, it must be either * boot or dump time, or a polled command, * so just do it! */ if ((s <= esp->e_tran.tran_spl) && (esp->e_npolling == 0)) { if (s != esp->e_spl) { (void) splx(s); continue; } }#endif /* sun4m */ if (INTPENDING(esp)) { espsvc(esp); serviced |= (1<<(CNUM)); search = TRUE; } (void) splx(s); } } } if (serviced) { /* * Did we service more than one host * adapter for this hardware interrupt? */ if (serviced & (serviced-1)) { esp_nmultsvc++; } /* * Record the fact that we fielded a hard interrupt */ esp_nhardints++; serviced = 1; } return (serviced);}#ifdef VECTORED_INTERRUPTS/* * Vectored interrupt entry point */static intesp_intr(esp)struct esp *esp;{ register s = splr(esp->e_tran.tran_spl); if (INTPENDING(esp)) { espsvc(esp); } (void) splx(s);}#endif /* VECTORED_INTERRUPTS *//* * General interrupt service routine. */static voidespsvc(esp)register struct esp *esp;{ static int (*evec[])() = { esp_finish_select, esp_reconnect, esp_phasemanage, esp_finish, esp_reset_recovery, esp_istart, esp_abort_curcmd, esp_abort_allcmds, esp_reset_bus, esp_handle_selection }; register int action; register u_char intr; register struct espreg *ep = esp->e_reg; register struct dmaga *dmar = esp->e_dma; /* * Make sure that the DMAGA is *not* still active. * If we don't disable further interrupts, we * get Bus timeouts in trying to access the ESP * chip for Rev-1 DMA gate arrays. */ if (DMAGA_REV(dmar) == DMA_REV1) { dmar->dmaga_csr &= ~DMAGA_INTEN; }#ifdef ESPDEBUG if (DEBUGGING) { eprintf (esp, "dma csr 0x%b addr 0x%x\n", esp->e_dma->dmaga_csr, dmaga_bits, esp->e_dma->dmaga_addr); }#endif /* ESPDEBUG */ /* * A read of ESP interrupt register clears interrupt, * so any other volatile information needs to be latched * up prior to reading the interrupt register. */ /* * Even if we aren't in STATE_SELECTING, latch * up the step register (for future handling * of being in target mode operation) */ esp->e_step = (ep->esp_step & ESP_STEP_MASK); esp->e_stat = ep->esp_stat; esp->e_intr = intr = ep->esp_intr; /* * unclear what could cause a gross error; * most of the time we get a data overrun after this. */ if (esp->e_stat & ESP_STAT_GERR) { esplog(esp, LOG_ERR, "gross error in esp status (%x)", esp->e_stat); IPRINTF5( "esp_cmd=%x, stat=%x, intr=%x, step=%x, fifoflag=%x\n", ep->esp_cmd, esp->e_stat, esp->e_intr, esp->e_step, ep->esp_fifo_flag); if (esp->e_cur_slot != UNDEFINED) { struct scsi_cmd *sp = CURRENT_CMD(esp); if (sp->cmd_pkt.pkt_reason == CMD_CMPLT) { sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; } } else { action = ACTION_ABORT_ALLCMDS; goto start_action; } } /* * was there a dmaga error that caused espsvc() to be called? */ if (esp->e_dma->dmaga_csr & DMAGA_ERRPEND) { /* * It would be desirable to set the ATN* line and attempt to * do the whole schmear of INITIATOR DETECTED ERROR here, * but that is too hard to do at present. */ esplog(esp, LOG_ERR, "Unrecoverable DMA error on dma"); if (esp->e_cur_slot != UNDEFINED) { struct scsi_cmd *sp = CURRENT_CMD(esp); if (sp->cmd_pkt.pkt_reason == CMD_CMPLT) sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; action = ACTION_RESET; goto start_action; } } /* * While some documentation claims that the * ESP100A's msb in the stat register is an * INTERRUPT PENDING bit, an errata sheet * warned that you shouldn't depend on that * being so (unless you're an ESP-236) */ if (esp->e_type != ESP236) { esp->e_stat &= ~ESP_STAT_RES; }#ifdef ESPDEBUG if (DEBUGGING) { esp_stat_int_print(esp); printf("\tState %s Laststate %s\n", esp_state_name(esp->e_state), esp_state_name(esp->e_laststate)); }#endif /* ESPDEBUG */ /* * Based upon the current state of the host adapter driver * we should be able to figure out what to do with an interrupt. * We have several possible interrupt sources, some of them * modified by various status conditions. * * Basically, we'll get an interrupt through the dma gate array * for one or more of the following three conditions: * * 1. The ESP is asserting an interrupt request. * * 2. The dma gate array counter has reached zero * and TERMINAL COUNT interrupts have been enabled. * * 3. There has been a memory exception of some kind. * * In the latter two cases we are either in one of the SCSI * DATA phases or are using dma in sending a command to a * target. We will let the various handlers for these kind * of states decode any error conditions in the gate array. * * The ESP asserts an interrupt with one or more of 8 possible * bits set in its interrupt register. These conditions are * SCSI bus reset detected, an illegal command fed to the ESP, * one of DISCONNECT, BUS SERVICE, FUNCTION COMPLETE conditions * for the ESP, a Reselection interrupt, or one of Selection * or Selection with Attention. * * Of these possible interrupts, we can deal with some right * here and now, irrespective of the current state of the driver. * */ if (intr & ESP_INT_RESET) { /* * If we detect a SCSI reset, we blow away the current * command (if there is one) and all disconnected commands * because we now don't know the state of them at all. */ action = ACTION_FINRST; } else if (intr & ESP_INT_ILLEGAL) { /* * This should not happen. The one situation where * we can get an ILLEGAL COMMAND interrupt is due to * a bug in the ESP100 during reselection which we * should be handling in esp_reconnect(). */ esp_printstate (esp, "ILLEGAL bit set"); action = ACTION_ABORT_CURCMD; } else if (intr & (ESP_INT_SEL|ESP_INT_SELATN)) { action = ACTION_SELECT; } else if (intr & ESP_INT_RESEL) { if (esp->e_state & STATE_SELECTING) { action = ACTION_FINSEL; } else if (esp->e_state != STATE_FREE) { /* * this 'cannot happen'. */ esp_printstate(esp, "illegal reselection"); action = ACTION_RESET; } else { action = ACTION_RESEL; } } else { /* * The rest of the reasons for an interrupt, including * interrupts just from the dma gate array itself, can * be handled based purely on the state that the driver * is currently in now. */ if (esp->e_state & STATE_SELECTING) { action = ACTION_FINSEL; } else if (esp->e_state & STATE_ITPHASES) { action = ACTION_PHASEMANAGE; } else {#ifdef ESPDEBUG if (INFORMATIVE) { esp_printstate(esp, "spurious interrupt"); }#endif /* ESPDEBUG */ esplog(esp, LOG_ERR, "spurious interrupt"); action = ACTION_RETURN; } }start_action: while (action != ACTION_RETURN) { action = (*evec[action])(esp); } /* * Reenable interrupts for the DMA gate array */ if (DMAGA_REV(dmar) == DMA_REV1) { dmar->dmaga_csr |= DMAGA_INTEN; }}/* * Manage phase transitions. */static intesp_phasemanage(esp)struct esp *esp;{ register u_char state; register int action; static int (*pvecs[])() = { esp_handle_cmd_start, esp_handle_cmd_done, esp_handle_msg_out, esp_handle_msg_out_done, esp_handle_msg_in, esp_handle_more_msgin, esp_handle_msg_in_done, esp_handle_clearing, esp_handle_data, esp_handle_data_done, esp_handle_c_cmplt }; do { state = esp->e_state; if (state == ACTS_UNKNOWN) { action = esp_handle_unknown(esp); } else if (state == STATE_FREE || state > ACTS_ENDVEC) { esplog(esp, LOG_ERR, "lost state in phasemanage"); action = ACTION_ABORT_ALLCMDS; } else { action = (*pvecs[state-1]) (esp); } } while (action == ACTION_PHASEMANAGE); return (action);}static intesp_handle_unknown(esp)register struct esp *esp;{ register u_char newstate; if (esp->e_intr & ESP_INT_DISCON) { /* * Okay. What to do now? Let's try (for the time being) * assuming that the target went south and dropped busy, * as a disconnect implies that either we received * a completion or a disconnect message, or that we * had sent an ABORT OPERATION or BUS DEVICE RESET * message. In either case, we expected the disconnect * and should have fielded it elsewhere. * * If we see a chip disconnect here, this is an unexpected * loss of BSY*. Clean up the state of the chip and return. * */#ifdef ESPDEBUG if (INFORMATIVE) { esp_printstate(esp, "unexpected bus free"); }#endif /* ESPDEBUG */ esp_chip_disconnect(esp); CURRENT_CMD(esp)->cmd_pkt.pkt_reason = CMD_UNX_BUS_FREE; LOG_STATE(ACTS_CMD_LOST, esp->e_stat, esp->e_xfer, -1); return (ACTION_FINISH); } switch (esp->e_stat & ESP_PHASE_MASK) { case ESP_PHASE_DATA_IN: case ESP_PHASE_DATA_OUT: newstate = ACTS_DATA; break; case ESP_PHASE_MSG_OUT: newstate = ACTS_MSG_OUT; break; case ESP_PHASE_MSG_IN: newstate = ACTS_MSG_IN; break; case ESP_PHASE_STATUS: esp->e_reg->esp_cmd = CMD_FLUSH;#ifdef ESP_TEST_PARITY if (esp_ptest_status & (1<<Tgt(CURRENT_CMD(esp)))) { esp->e_reg->esp_cmd = CMD_SET_ATN; }#endif /* ESP_TEST_PARITY */ esp->e_reg->esp_cmd = CMD_COMP_SEQ; esp->e_state = ACTS_C_CMPLT; LOG_STATE(ACTS_C_CMPLT, esp->e_stat, -1, -1); return (ACTION_RETURN); case ESP_PHASE_COMMAND: newstate = ACTS_CMD_START; break; default: esp_printstate(esp, "Unknown bus phase"); return (ACTION_RESET); } esp->e_state = newstate; return (ACTION_PHASEMANAGE);}static intesp_handle_cmd_start(esp)struct esp *esp;{ register struct espreg *ep = esp->e_reg; struct scsi_cmd *sp = CURRENT_CMD(esp); register u_long cmd_distance; /* * Check for command overflow. */ cmd_distance = (u_long) sp->cmd_cdbp - (u_long) sp->cmd_pkt.pkt_cdbp; if (cmd_distance > (u_long) sp->cmd_cdblen) { sp->cmd_pkt.pkt_reason = CMD_CMD_OVR; return (ACTION_ABORT_CURCMD); } if (cmd_distance == 0) { LOG_STATE(ACTS_CMD_START, esp->e_stat, sp->cmd_cdbp[0], -1); } /* * Stuff next command byte into fifo */ ep->esp_cmd = CMD_FLUSH; SET_ESP_COUNT(ep, 1); ep->esp_fifo_data = *(sp->cmd_cdbp++); ep->esp_cmd = CMD_TRAN_INFO; New_state(esp, ACTS_CMD_DONE); return (ACTION_RETURN);}static intesp_handle_cmd_done(esp)register struct esp *esp;{ register struct scsi_cmd *sp = CURRENT_CMD(esp); register u_char intr = esp->e_intr; /* * The NOP command is required following a COMMAND * or MESSAGE OUT phase in order to unlatch the * FIFO flags register. This is needed for all * ESP chip variants. */ esp->e_reg->esp_cmd = CMD_NOP; /* * We should have gotten a BUS SERVICE interrupt. * If it isn't that, and it isn't a DISCONNECT * interrupt, we have a "cannot happen" situation. */ if ((intr & ESP_INT_BUS) == 0) { if ((intr & ESP_INT_DISCON) == 0) { esp_printstate(esp, "cmd transmission error"); return (ACTION_ABORT_CURCMD); } } else { sp->cmd_pkt.pkt_state |= STATE_SENT_CMD; } New_state(esp, ACTS_UNKNOWN); return (ACTION_PHASEMANAGE);}/* * Begin to send a message out */static int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -