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);
		}
	}
}
Exemple #3
0
/*
 * 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).
     */
}
Exemple #4
0
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;
}