📄 esp.c
字号:
esp_handle_msg_out(esp)register struct esp *esp;{ register i; register struct scsi_cmd *sp = CURRENT_CMD(esp); register struct espreg *ep = esp->e_reg; /* * Check to make *sure* that we are really * in MESSAGE OUT phase. If the last state * was ACTS_MSG_OUT_DONE, then we are trying * to resend a message that the target stated * had a parity error in it. * * If this is the case, and mark completion reason as CMD_NOMSGOUT. * XXX: Right now, we just *drive* on. Should we abort the command? */ if ((esp->e_stat & ESP_PHASE_MASK) != ESP_PHASE_MSG_OUT && esp->e_laststate == ACTS_MSG_OUT_DONE) { esplog(esp, LOG_WARNING, "Target %d refused message resend", Tgt(sp)); sp->cmd_pkt.pkt_reason = CMD_NOMSGOUT; New_state(esp, ACTS_UNKNOWN); return (ACTION_PHASEMANAGE); } /* * Clean the fifo. */ ep->esp_cmd = CMD_FLUSH; /* * See if we have a valid message to send */ if (esp->e_omsglen == 0) { /* * no valid message? Send a no-op anyway... */ esp->e_cur_msgout[0] = MSG_NOP; esp->e_omsglen = 1; } for (i = 0; i < esp->e_omsglen; i++) { ep->esp_fifo_data = esp->e_cur_msgout[i]; } ep->esp_cmd = CMD_TRAN_INFO;#ifdef ESPDEBUG if (DEBUGGING) { eprintf(esp, "msg out: %s", scsi_mname(esp->e_cur_msgout[0])); for (i = 1; i < esp->e_omsglen; i++) printf(" %x", esp->e_cur_msgout[i]); printf("\n"); }#endif /* ESPDEBUG */ New_state(esp, ACTS_MSG_OUT_DONE); return (ACTION_RETURN);}static intesp_handle_msg_out_done(esp)register struct esp *esp;{ register struct scsi_cmd *sp = CURRENT_CMD(esp); register struct espreg *ep = esp->e_reg; register u_char msgout, phase, fifocnt; register int target = Tgt(sp); msgout = esp->e_cur_msgout[0]; /* * If the ESP disconnected, then the message we sent caused * the target to decide to drop BSY* and clear the bus. */ if (esp->e_intr == ESP_INT_DISCON) { if (msgout == MSG_DEVICE_RESET || msgout == MSG_ABORT) { esp_chip_disconnect(esp); if (msgout == MSG_DEVICE_RESET) { esp->e_offset[target] = 0; esp->e_sync_known &= ~(1<<target); } EPRINTF2 ("Succesful %s message to target %d\n", scsi_mname(msgout), target); sp->cmd_pkt.pkt_reason = CMD_CMPLT; if (sp->cmd_flags & CFLAG_CMDPROXY) { sp->cmd_cdb[ESP_PROXY_RESULT] = TRUE; } return (ACTION_FINISH); } /* * If the target dropped busy on any other message, it * wasn't expected. We will let the code in esp_phasemanage() * handle this unexpected bus free event. */ goto out; } /* * What phase have we transitioned to? */ phase = esp->e_stat & ESP_PHASE_MASK; /* * Save current fifo count */ fifocnt = FIFO_CNT(ep); /* * As per the ESP errata sheets, this must be done for * all ESP chip variants. * * This releases the FIFO counter from its latched state. * Note that we read the fifo counter above prior to doing * this. */ ep->esp_cmd = CMD_NOP; /* * Clean the fifo? Yes, if and only if we haven't * transitioned to Synchronous DATA IN phase. * The ESP chip manual notes that in the case * that the target has shifted to Synchronous * DATA IN phase, that while the FIFO count * register stays latched up with the number * of bytes not transferred out, that the fifo * itself is cleared and will contain only * the incoming data bytes. * * The manual doesn't state what happens in * other receive cases (transition to STATUS, * MESSAGE IN, or asynchronous DATA IN phase), * but I'll assume that there is probably * a single-byte pad between the fifo and * the SCSI bus which the ESP uses to hold * the currently asserted data on the bus * (known valid by a true REQ* signal). In * the case of synchronous data in, up to * 15 bytes of data could arrive, so the * ESP must have to make room for by clearing * the fifo, but in other cases it can just * hold the current byte until the next * ESP chip command that would cause a * data transfer. */ if (fifocnt != 0 && (phase != ESP_PHASE_DATA_IN || esp->e_offset[target] == 0)) { ep->esp_cmd = CMD_FLUSH; } /* * If we finish sending a message out, and we are * still in message out phase, then the target has * detected one or more parity errors in the message * we just sent and it is asking us to resend the * previous message. */ if ((esp->e_intr & ESP_INT_BUS) && phase == ESP_PHASE_MSG_OUT) { /* * As per SCSI-2 specification, if the message to * be re-sent is greater than one byte, then we * have to set ATN*. */ if (esp->e_omsglen > 1) { ep->esp_cmd = CMD_SET_ATN; } esplog(esp, LOG_ERR, "SCSI bus MESSAGE OUT phase parity error"); sp->cmd_pkt.pkt_statistics |= STAT_PERR; New_state(esp, ACTS_MSG_OUT); return (ACTION_PHASEMANAGE); } /* * Count that we sent a SYNCHRONOUS DATA TRANSFER message. */ if (esp->e_omsglen == 5 && msgout == MSG_EXTENDED && esp->e_cur_msgout[2] == MSG_SYNCHRONOUS) { esp->e_sdtr++; }out: esp->e_last_msgout = msgout; esp->e_omsglen = 0; New_state(esp, ACTS_UNKNOWN); return (ACTION_PHASEMANAGE);}static intesp_handle_clearing(esp)register struct esp *esp;{ register struct scsi_cmd *sp = CURRENT_CMD(esp); register action; register u_char lmsg = esp->e_last_msgin; if (esp->e_intr != ESP_INT_DISCON) { if (lmsg != MSG_LINK_CMPLT && lmsg != MSG_LINK_CMPLT_FLAG) { /* * If the chip/target didn't disconnect from the * bus, that is a gross fatal error. */ esplog (esp, LOG_WARNING, "Target %d didn't disconnect after sending %s", Tgt(sp), scsi_mname(lmsg)); sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; return (ACTION_ABORT_CURCMD); } else { /* * In this case, the last message in was a 'linked * command complete' message and the target stays * connected. We don't fiddle with any of the * settings on the ESP chip. We return state * such that the completing command is finished * up and depend upon the finish routine to * handle the case that a new command has to * be available to start right away for this target. */ esp->e_last_msgout = 0xff; esp->e_omsglen = 0; return (ACTION_FINISH); } } /* * At this point the ESP chip has disconnected. The bus should * be either quiet or someone may be attempting a reselection * of us (or somebody else). Call the routine the sets the * chip back to a correct and known state. */ esp_chip_disconnect(esp); /* * If the last message in was a disconnect, search * for new work to do, else return to call esp_finish() */ if (lmsg == MSG_DISCONNECT) { sp->cmd_pkt.pkt_statistics |= STAT_DISCON; sp->cmd_flags |= CFLAG_CMDDISC; esp->e_disconnects++; esp->e_ndisc++; New_state(esp, STATE_FREE); esp->e_last_slot = esp->e_cur_slot; esp->e_cur_slot = UNDEFINED; action = ACTION_SEARCH; EPRINTF2 ("disconnecting %d.%d\n", Tgt(sp), Lun(sp)); } else { action = ACTION_FINISH; } esp->e_last_msgout = 0xff; esp->e_omsglen = 0; return (action);}static intesp_handle_data(esp)struct esp *esp;{ register i; struct espreg *ep = esp->e_reg; struct dmaga *dmar = esp->e_dma; struct scsi_cmd *sp = CURRENT_CMD(esp); register int sending; if (IS_53C90(esp)) { ep->esp_cmd = CMD_NOP; /* per ESP errata sheet */ } if ((sp->cmd_flags & CFLAG_DMAVALID) == 0) { esp_printstate(esp, "unexpected data phase"); /* * XXX: This isn't the right reason */ sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; return (ACTION_ABORT_CURCMD); } else { sending = (sp->cmd_flags & CFLAG_DMASEND)? 1 : 0; } if (sp->cmd_flags & CFLAG_NEEDSEG) { /* * We can currently handle truncating the current * subsegment (in case a restore pointers is followed * by a re-entry into data phase). XXX Else we die * horribly XXX */ register struct dataseg *segp = sp->cmd_cursubseg; IPRINTF3 ("data new seg: datap 0x%x base 0x%x count 0x%x\n", sp->cmd_data, segp->d_base, segp->d_count); if (sp->cmd_data >= segp->d_base && (sp->cmd_data < (segp->d_base + segp->d_count))) { /* * We are backing up within the current * segment. Adjust the count field of the * current segment to reflect that we * only got 'this far' with it to date. */ segp->d_count = sp->cmd_data - segp->d_base; sp->cmd_flags ^= CFLAG_NEEDSEG; } else { /* * XXX: bad bad bad... */ panic("need new seg in esp"); /* NOTREACHED */ } } i = scsi_chkdma(sp, ESP_MAX_DMACOUNT); if (i == 0) { esp_printstate(esp, "data transfer overrun"); sp->cmd_pkt.pkt_reason = CMD_DATA_OVR; /* * A fix for bug id 1048141- if we get data transfer * overruns, assume we have a weak scsi bus. Note that * this won't catch consistent underruns or other * noise related syndromes. */ esp_sync_backoff(esp, sp); return (ACTION_ABORT_CURCMD); } esp->e_lastcount = i;#ifdef ESPDEBUG esp->e_xfer = i;#endif /* ESPDEBUG */#ifdef sun4m if (sp->cmd_flags & CFLAG_DMAKEEP) { dmar->dmaga_addr = esp->e_lastdma = (btop(sp->cmd_data) < dvmasize) ? (sp->cmd_data | esp->e_dma_base) : sp->cmd_data; } else { dmar->dmaga_addr = esp->e_lastdma = (btop(sp->cmd_data) < BIGSBUSMAP_SIZE) ? (sp->cmd_data | BIGSBUSDVMA_BASE) : sp->cmd_data; }#else /* sun4m */ dmar->dmaga_addr = esp->e_lastdma = (btop(sp->cmd_data) < dvmasize) ? (sp->cmd_data | esp->e_dma_base) : sp->cmd_data;#endif /* sun4m */ SET_ESP_COUNT(ep, i); if (DMAGA_REV(dmar) == ESC1_REV1) { SET_DMAESC_COUNT(dmar, i); } EPRINTF4 ("%d.%d cmd 0x%x to xfer %x\n", Tgt(sp), Lun(sp), sp->cmd_pkt.pkt_cdbp[0], i); if ((esp->e_stat & ESP_PHASE_MASK) == ESP_PHASE_DATA_OUT) { if (!sending) { esplog(esp, LOG_ERR, "unwanted data out for Target %d", Tgt(sp)); sp->cmd_pkt.pkt_reason = CMD_DMA_DERR; return (ACTION_ABORT_CURCMD); } LOG_STATE(ACTS_DATAOUT, esp->e_stat, i, -1); } else { if (sending) { esplog (esp, LOG_ERR, "unwanted data in for Target %d", Tgt(sp)); sp->cmd_pkt.pkt_reason = CMD_DMA_DERR; return (ACTION_ABORT_CURCMD); } dmar->dmaga_csr |= DMAGA_WRITE; LOG_STATE(ACTS_DATAIN, esp->e_stat, i, -1); }#ifdef ESP_TEST_PARITY if (!sending && (esp_ptest_data_in & (1<<Tgt(sp)))) { ep->esp_cmd = CMD_SET_ATN; }#endif /* ESP_TEST_PARITY */ dmar->dmaga_csr |= DMAGA_ENDVMA|DMAGA_INTEN; ep->esp_cmd = CMD_TRAN_INFO|CMD_DMA; New_state(esp, ACTS_DATA_DONE); return (ACTION_RETURN);}static intesp_handle_data_done(esp)register struct esp *esp;{ register struct espreg *ep = esp->e_reg; register struct dmaga *dmar = esp->e_dma; register struct scsi_cmd *sp = CURRENT_CMD(esp); register u_long xfer_amt; char spurious_data, do_drain_fifo, was_sending; register u_char stat, tgt, fifoamt; tgt = Tgt(sp); stat = esp->e_stat; was_sending = (sp->cmd_flags & CFLAG_DMASEND) ? 1 : 0; spurious_data = do_drain_fifo = 0; /* * Check for DMAGA errors (parity or memory fault) */ if (dmar->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 %s", (was_sending) ? "send" : "receive"); sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; return (ACTION_RESET); } /* * Data Receive conditions: * * Check for parity errors. If we have a parity error upon * receive, the ESP chip has asserted ATN* for us already. * * For Rev-1 and Rev-2 dma gate arrayts, * make sure the last bytes have flushed. */ if (!was_sending) {#ifdef ESP_TEST_PARITY if (esp_ptest_data_in & (1<<tgt)) { esp_ptest_data_in = 0; stat |= ESP_STAT_PERR; }#endif /* ESP_TEST_PARITY */ if (stat & ESP_STAT_PERR) { esplog (esp, LOG_ERR, "SCSI bus DATA IN phase parity error"); esp->e_cur_msgout[0] = MSG_INITIATOR_ERROR; esp->e_omsglen = 1; sp->cmd_pkt.pkt_statistics |= STAT_PERR; } xfer_amt = 0; /* * For DMA gate arrays, the PACKCNT field of the DMA * CSR register indicates how many bytes are still * latched up and need to be drained to memory. * * For the DMA+ CSR, the PACKCNT field will either * be zero or non-zero, indicating a empty/non-empty * D_CACHE. The DRAIN bit has no effect. * * The DMA2 CSR (as of right now- 6/1/90) actually * requires a write to the CSR register to force * the drain of the D_CACHE, so the practice of * writing the otherwise null-effect DRAIN bit is * still required. */ while (DMAGA_NPACKED(dmar) != 0 && xfer_amt < 100) { if (DMAGA_REV(dmar) != ESC1_REV1) { dmar->dmaga_csr |= DMAGA_DRAIN; } DELAY(200); xfer_amt++; } if (xfer_amt >= 100 && DMAGA_NPACKED(dmar)) { esplog (esp, LOG_ERR, "DMA gate array won't drain"); /* * This isn't quite right... */ sp->cmd_pkt.pkt_reason = CMD_TRAN_ERR; return (ACTION_ABORT_CURCMD); } } /* * clear state of dma gate array */ dmar->dmaga_csr |= DMAGA_FLUSH; dmar->dmaga_csr &= ~(DMAGA_ENDVMA|DMAGA_WRITE|DMAGA_ENATC); /* /* * Check to make sure we're still connected to the target. * If the target dropped the bus, that is a fatal error. * We don't even attempt to count what we were transferring * here. Let esp_handle_unknown clean up for us. */ if (esp->e_intr != ESP_INT_BUS) { New_state(esp, ACTS_UNKNOWN); return (ACTION_PHASEMANAGE); } /* * Figure out how far we got. * Latch up fifo amount first. */ fifoamt = FIFO_CNT(ep); if (stat & ESP_STAT_XZERO) { xfer_amt = esp->e_lastcount; } else { GET_ESP_COUNT(ep, xfer_amt); xfer_amt = esp->e_lastcount - xfer_amt; } /* * Unconditionally knock off by the amount left * in the fifo if we were sending out the SCSI bus. * * If we were receiving from the SCSI bus, believe * what the chip told us (either XZERO or by the * value calculated from the counter register). * The reason we don't look at the fifo for * incoming data is that in synchronous mode * the fifo may have further data bytes, and * for async mode we assume that all data in * the fifo will have been transferred before * the esp asserts an interrupt. */ if (was_sending) { xfer_amt -= fifoamt; } /* * If this was a synchronous transfer, flag it. * Also check for the errata condition of long *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -