static void siop_setuptables(struct siop_adapter *adp, struct siop_xfer *xfer, struct scsi_xfer *xs) { int msgoffset = 1; xfer->siop_tables.id = htoc32((adp->clock_div << 24) | (xs->target << 16)); memset(xfer->siop_tables.msg_out, 0, sizeof(xfer->siop_tables.msg_out)); /* request sense doesn't disconnect */ if (xs->cmd->opcode == SCSI_REQUEST_SENSE) xfer->siop_tables.msg_out[0] = MSG_IDENTIFY(xs->lun, 0); else xfer->siop_tables.msg_out[0] = MSG_IDENTIFY(xs->lun, 1); xfer->siop_tables.t_msgout.count = htoc32(msgoffset); xfer->siop_tables.status = htoc32(SCSI_SIOP_NOSTATUS); /* set invalid status */ xfer->siop_tables.cmd.count = htoc32(xs->cmdlen); xfer->siop_tables.cmd.addr = htoc32(local_to_PCI((u_long)xs->cmd)); if (xs->datalen != 0) { xfer->siop_tables.data[0].count = htoc32(xs->datalen); xfer->siop_tables.data[0].addr = htoc32(local_to_PCI((u_long)xs->data)); } }
/* prepare tables before sending a cmd */ void siop_setuptables(struct siop_common_cmd *siop_cmd) { int i; struct siop_common_softc *sc = siop_cmd->siop_sc; struct scsipi_xfer *xs = siop_cmd->xs; int target = xs->xs_periph->periph_target; int lun = xs->xs_periph->periph_lun; int msgoffset = 1; siop_cmd->siop_tables->id = siop_htoc32(sc, sc->targets[target]->id); memset(siop_cmd->siop_tables->msg_out, 0, sizeof(siop_cmd->siop_tables->msg_out)); /* request sense doesn't disconnect */ if (xs->xs_control & XS_CTL_REQSENSE) siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0); else if ((sc->features & SF_CHIP_GEBUG) && (sc->targets[target]->flags & TARF_ISWIDE) == 0) /* * 1010 bug: it seems that the 1010 has problems with reselect * when not in wide mode (generate false SCSI gross error). * The FreeBSD sym driver has comments about it but their * workaround (disable SCSI gross error reporting) doesn't * work with my adapter. So disable disconnect when not * wide. */ siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 0); else siop_cmd->siop_tables->msg_out[0] = MSG_IDENTIFY(lun, 1); if (xs->xs_tag_type != 0) { if ((sc->targets[target]->flags & TARF_TAG) == 0) { scsipi_printaddr(xs->xs_periph); printf(": tagged command type %d id %d\n", siop_cmd->xs->xs_tag_type, siop_cmd->xs->xs_tag_id); panic("tagged command for non-tagging device"); } siop_cmd->flags |= CMDFL_TAG; siop_cmd->siop_tables->msg_out[1] = siop_cmd->xs->xs_tag_type; /* * use siop_cmd->tag not xs->xs_tag_id, caller may want a * different one */ siop_cmd->siop_tables->msg_out[2] = siop_cmd->tag; msgoffset = 3; } siop_cmd->siop_tables->t_msgout.count = siop_htoc32(sc, msgoffset); if (sc->targets[target]->status == TARST_ASYNC) { if ((sc->targets[target]->flags & TARF_DT) && (sc->mode == STEST4_MODE_LVD)) { sc->targets[target]->status = TARST_PPR_NEG; siop_ppr_msg(siop_cmd, msgoffset, sc->dt_minsync, sc->maxoff); } else if (sc->targets[target]->flags & TARF_WIDE) { sc->targets[target]->status = TARST_WIDE_NEG; siop_wdtr_msg(siop_cmd, msgoffset, MSG_EXT_WDTR_BUS_16_BIT); } else if (sc->targets[target]->flags & TARF_SYNC) { sc->targets[target]->status = TARST_SYNC_NEG; siop_sdtr_msg(siop_cmd, msgoffset, sc->st_minsync, (sc->maxoff > 31) ? 31 : sc->maxoff); } else { sc->targets[target]->status = TARST_OK; siop_update_xfer_mode(sc, target); } } siop_cmd->siop_tables->status = siop_htoc32(sc, SCSI_SIOP_NOSTATUS); /* set invalid status */ siop_cmd->siop_tables->cmd.count = siop_htoc32(sc, siop_cmd->dmamap_cmd->dm_segs[0].ds_len); siop_cmd->siop_tables->cmd.addr = siop_htoc32(sc, siop_cmd->dmamap_cmd->dm_segs[0].ds_addr); if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) { for (i = 0; i < siop_cmd->dmamap_data->dm_nsegs; i++) { siop_cmd->siop_tables->data[i].count = siop_htoc32(sc, siop_cmd->dmamap_data->dm_segs[i].ds_len); siop_cmd->siop_tables->data[i].addr = siop_htoc32(sc, siop_cmd->dmamap_data->dm_segs[i].ds_addr); } } }
/* * Send the highest priority, scheduled message. */ void aic6250_msgout(struct aic6250_softc *sc) { uint8_t scsisig; int n; if (sc->sc_prevphase == PH_MSGOUT) { if (sc->sc_omp == sc->sc_omess) { /* * This is a retransmission. * * We get here if the target stayed in MESSAGE OUT * phase. Section 5.1.9.2 of the SCSI 2 spec indicates * that all of the previously transmitted messages must * be sent again, in the same order. Therefore, we * requeue all the previously transmitted messages, and * start again from the top. Our simple priority * scheme keeps the messages in the right order. */ sc->sc_msgpriq |= sc->sc_msgoutq; /* * Set ATN. If we're just sending a trivial 1-byte * message, we'll clear ATN later on anyway. */ oaic_write(sc, AIC_SCSI_SIGNAL_REG, PH_MSGOUT | AIC_SS_ATN_OUT); } else { /* This is a continuation of the previous message. */ n = sc->sc_omp - sc->sc_omess; goto nextbyte; } } /* No messages transmitted so far. */ sc->sc_msgoutq = 0; sc->sc_lastmsg = 0; nextmsg: /* Pick up highest priority message. */ sc->sc_currmsg = sc->sc_msgpriq & -sc->sc_msgpriq; sc->sc_msgpriq &= ~sc->sc_currmsg; sc->sc_msgoutq |= sc->sc_currmsg; /* Build the outgoing message data. */ switch (sc->sc_currmsg) { case SEND_IDENTIFY: sc->sc_omess[0] = MSG_IDENTIFY(sc->sc_tgtlun, 1); n = 1; break; case SEND_DEV_RESET: sc->sc_flags |= AIC_ABORTING; sc->sc_omess[0] = MSG_BUS_DEV_RESET; n = 1; break; case SEND_REJECT: sc->sc_omess[0] = MSG_MESSAGE_REJECT; n = 1; break; case SEND_PARITY_ERROR: sc->sc_omess[0] = MSG_PARITY_ERROR; n = 1; break; case SEND_INIT_DET_ERR: sc->sc_omess[0] = MSG_INITIATOR_DET_ERR; n = 1; break; case SEND_ABORT: sc->sc_flags |= AIC_ABORTING; sc->sc_omess[0] = MSG_ABORT; n = 1; break; default: printf("insc: unexpected MESSAGE OUT\n"); sc->sc_omess[0] = MSG_NOOP; n = 1; break; } sc->sc_omp = &sc->sc_omess[n]; nextbyte: /* Send message bytes. */ for (;;) { for (;;) { scsisig = oaic_read(sc, AIC_SCSI_SIGNAL_REG); if ((scsisig & PH_MASK) != PH_MSGOUT) { /* * Target left MESSAGE OUT, possibly to reject * our message. * * If this is the last message being sent, then * we deassert ATN, since either the target is * going to ignore this message, or it's going * to ask for a retransmission via MESSAGE * PARITY ERROR (in which case we reassert ATN * anyway). */ if (sc->sc_msgpriq == 0) oaic_write(sc, AIC_SCSI_SIGNAL_REG, scsisig & ~AIC_SS_ATN_OUT); return; } if ((scsisig & AIC_SS_REQ_IN) != 0) break; } /* Clear ATN before last byte if this is the last message. */ if (n == 1 && sc->sc_msgpriq == 0) oaic_write(sc, AIC_SCSI_SIGNAL_REG, scsisig & ~AIC_SS_ATN_OUT); /* Send message byte. */ oaic_write(sc, AIC_SCSI_ID_DATA, *--sc->sc_omp); --n; /* Keep track of the last message we've sent any bytes of. */ sc->sc_lastmsg = sc->sc_currmsg; aic6250_ack(sc); if (n == 0) break; } /* We get here only if the entire message has been transmitted. */ if (sc->sc_msgpriq != 0) { /* There are more outgoing messages. */ goto nextmsg; } /* * The last message has been transmitted. We need to remember the last * message transmitted (in case the target switches to MESSAGE IN phase * and sends a MESSAGE REJECT), and the list of messages transmitted * this time around (in case the target stays in MESSAGE OUT phase to * request a retransmit). */ }
int scsiicmd(char target, char lun, u_char *cbuf, int clen, char *addr, int len) { volatile caddr_t sr; int i; DPRINTF(("scsiicmd: [%x, %d] -> %d (%lx, %d)\n",*cbuf, clen, target, (long)addr, len)); sr = P_SCSI; if (sc->sc_state != SCSI_IDLE) { scsierror("scsiiscmd: bad state"); return EIO; } sc->sc_result = 0; /* select target */ sr[ESP_CMD] = ESPCMD_FLUSH; DELAY(10); sr[ESP_SELID] = target; sr[ESP_FIFO] = MSG_IDENTIFY(lun, 0); for (i=0; i<clen; i++) sr[ESP_FIFO] = cbuf[i]; sr[ESP_CMD] = ESPCMD_SELATN; sc->sc_state = SCSI_SELECTING; while(sc->sc_state != SCSI_DONE) { if (scsi_wait_for_intr()) /* maybe we'd better use real intrs ? */ return EIO; if (sc->sc_state == SCSI_DMA) { /* registers are not valid on dma intr */ sc->sc_status = sc->sc_seqstep = sc->sc_intrstatus = 0; DPRINTF(("scsiicmd: dma intr\n")); } else { /* scsi processing */ sc->sc_status = sr[ESP_STAT]; sc->sc_seqstep = sr[ESP_STEP]; sc->sc_intrstatus = sr[ESP_INTR]; DPRINTF(("scsiicmd: regs[intr=%x, stat=%x, step=%x]\n", sc->sc_intrstatus, sc->sc_status, sc->sc_seqstep)); } if (sc->sc_intrstatus & ESPINTR_SBR) { scsierror("scsi bus reset"); return EIO; } if ((sc->sc_status & ESPSTAT_GE) || (sc->sc_intrstatus & ESPINTR_ILL)) { scsierror("software error"); return EIO; } if (sc->sc_status & ESPSTAT_PE) { scsierror("parity error"); return EIO; } switch(sc->sc_state) { case SCSI_SELECTING: if (sc->sc_intrstatus & ESPINTR_DIS) { sc->sc_state = SCSI_IDLE; return EUNIT; /* device not present */ } #define ESPINTR_DONE (ESPINTR_BS | ESPINTR_FC) if ((sc->sc_intrstatus & ESPINTR_DONE) != ESPINTR_DONE) { scsierror("selection failed"); return EIO; } sc->sc_state = SCSI_HASBUS; break; case SCSI_HASBUS: if (sc->sc_intrstatus & ESPINTR_DIS) { scsierror("target disconnected"); return EIO; } break; case SCSI_DMA: if (sc->sc_intrstatus & ESPINTR_DIS) { scsierror("target disconnected"); return EIO; } if (dma_done() != 0) return EIO; continue; case SCSI_CLEANUP: if (sc->sc_intrstatus & ESPINTR_DIS) { sc->sc_state = SCSI_DONE; continue; } DPRINTF(("hmm ... no disconnect on cleanup?\n")); sc->sc_state = SCSI_DONE; /* maybe ... */ break; } /* transfer information now */ switch(sc->sc_status & ESPSTAT_PHASE) { case DATA_IN_PHASE: if (dma_start(addr, len) != 0) return EIO; break; case DATA_OUT_PHASE: scsierror("data out phase not implemented"); return EIO; case STATUS_PHASE: DPRINTF(("status phase: ")); sr[ESP_CMD] = ESPCMD_ICCS; sc->sc_result = scsi_getbyte(sr); DPRINTF(("status is 0x%x.\n", sc->sc_result)); break; case MSG_IN_PHASE: if (scsi_msgin() != 0) return EIO; break; default: DPRINTF(("phase not implemented: 0x%x.\n", sc->sc_status & ESPSTAT_PHASE)); scsierror("bad phase"); return EIO; } } sc->sc_state = SCSI_IDLE; return -sc->sc_result; }