From 60df250ae113af544441103df2d1fa5491c43c1b Mon Sep 17 00:00:00 2001 From: Marius Strobl Date: Mon, 6 Aug 2012 08:58:54 +0000 Subject: [PATCH] - Merge from NetBSD: When issuing a non-DMA command, make sure to set the "remaining length of command to be transferred via DMA" (sc_cmdlen) to zero up-front, otherwise we might get confused on command competition interrupt (no DMA active but still data left to transfer). - Implement handling of MSG_IGN_WIDE_RESIDUE which some targets produce, as just rejecting these leads to a resend and disconnect loop. Reported and tested by: mjacob MFC after: 3 days --- sys/dev/esp/ncr53c9x.c | 60 ++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/sys/dev/esp/ncr53c9x.c b/sys/dev/esp/ncr53c9x.c index 0cc4387bf8ce..b21ea41bc8de 100644 --- a/sys/dev/esp/ncr53c9x.c +++ b/sys/dev/esp/ncr53c9x.c @@ -26,7 +26,7 @@ * */ -/* $NetBSD: ncr53c9x.c,v 1.143 2011/07/31 18:39:00 jakllsch Exp $ */ +/* $NetBSD: ncr53c9x.c,v 1.145 2012/06/18 21:23:56 martin Exp $ */ /*- * Copyright (c) 1998, 2002 The NetBSD Foundation, Inc. @@ -256,7 +256,7 @@ ncr53c9x_attach(struct ncr53c9x_softc *sc) return (EINVAL); } - device_printf(sc->sc_dev, "%s, %dMHz, SCSI ID %d\n", + device_printf(sc->sc_dev, "%s, %d MHz, SCSI ID %d\n", ncr53c9x_variant_names[sc->sc_rev], sc->sc_freq, sc->sc_id); sc->sc_ntarg = (sc->sc_rev == NCR_VARIANT_FAS366) ? 16 : 8; @@ -890,11 +890,8 @@ ncr53c9x_select(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) sc->sc_cmdp = cmd; error = NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &dmasize); - if (error != 0) { - sc->sc_cmdlen = 0; - sc->sc_cmdp = NULL; + if (error != 0) goto cmd; - } /* Program the SCSI counter. */ NCR_SET_COUNT(sc, dmasize); @@ -920,6 +917,7 @@ ncr53c9x_select(struct ncr53c9x_softc *sc, struct ncr53c9x_ecb *ecb) */ /* Now get the command into the FIFO. */ + sc->sc_cmdlen = 0; ncr53c9x_wrfifo(sc, cmd, clen); /* And get the target's attention. */ @@ -1771,7 +1769,7 @@ ncr53c9x_msgin(struct ncr53c9x_softc *sc) struct ncr53c9x_linfo *li; struct ncr53c9x_tinfo *ti; uint8_t *pb; - int lun, plen; + int len, lun; NCR_LOCK_ASSERT(sc, MA_OWNED); @@ -1818,15 +1816,15 @@ ncr53c9x_msgin(struct ncr53c9x_softc *sc) */ case NCR_RESELECTED: pb = sc->sc_imess + 1; - plen = sc->sc_imlen - 1; + len = sc->sc_imlen - 1; break; default: pb = sc->sc_imess; - plen = sc->sc_imlen; + len = sc->sc_imlen; } - if (__verify_msg_format(pb, plen)) + if (__verify_msg_format(pb, len)) goto gotit; } @@ -1963,6 +1961,29 @@ ncr53c9x_msgin(struct ncr53c9x_softc *sc) sc->sc_dleft = ecb->dleft; break; + case MSG_IGN_WIDE_RESIDUE: + NCR_MSGS(("ignore wide residue (%d bytes)", + sc->sc_imess[1])); + if (sc->sc_imess[1] != 1) { + xpt_print_path(ecb->ccb->ccb_h.path); + printf("unexpected MESSAGE IGNORE WIDE " + "RESIDUE (%d bytes); sending REJECT\n", + sc->sc_imess[1]); + goto reject; + } + /* + * If there was a last transfer of an even number of + * bytes, wipe the "done" memory and adjust by one + * byte (sc->sc_imess[1]). + */ + len = sc->sc_dleft - ecb->dleft; + if (len != 0 && (len & 1) == 0) { + ecb->flags &= ~ECB_TENTATIVE_DONE; + sc->sc_dp = (char *)sc->sc_dp - 1; + sc->sc_dleft--; + } + break; + case MSG_EXTENDED: NCR_MSGS(("extended(%x) ", sc->sc_imess[2])); switch (sc->sc_imess[2]) { @@ -2272,6 +2293,7 @@ ncr53c9x_msgout(struct ncr53c9x_softc *sc) /* * XXX FIFO size */ + sc->sc_cmdlen = 0; ncr53c9x_flushfifo(sc); ncr53c9x_wrfifo(sc, sc->sc_omp, sc->sc_omlen); NCRCMD(sc, NCRCMD_TRANS); @@ -2811,9 +2833,10 @@ ncr53c9x_intr1(struct ncr53c9x_softc *sc) * (Timing problems?) */ if (sc->sc_features & NCR_F_DMASELECT) { - if (sc->sc_cmdlen == 0) + if (sc->sc_cmdlen == 0) { /* Hope for the best... */ break; + } } else if ((NCR_READ_REG(sc, NCR_FFLAG) & NCRFIFO_FF) == 0) { /* Hope for the best... */ @@ -2986,11 +3009,8 @@ ncr53c9x_intr1(struct ncr53c9x_softc *sc) sc->sc_cmdp = (void *)&ecb->cmd.cmd; error = NCRDMA_SETUP(sc, &sc->sc_cmdp, &sc->sc_cmdlen, 0, &size); - if (error != 0) { - sc->sc_cmdlen = 0; - sc->sc_cmdp = NULL; + if (error != 0) goto cmd; - } /* Program the SCSI counter. */ NCR_SET_COUNT(sc, size); @@ -3005,6 +3025,7 @@ ncr53c9x_intr1(struct ncr53c9x_softc *sc) break; } cmd: + sc->sc_cmdlen = 0; ncr53c9x_wrfifo(sc, (uint8_t *)&ecb->cmd.cmd, ecb->clen); NCRCMD(sc, NCRCMD_TRANS); sc->sc_prevphase = COMMAND_PHASE; @@ -3046,7 +3067,7 @@ ncr53c9x_intr1(struct ncr53c9x_softc *sc) goto finish; } - /* Target returned to data phase: wipe "done" memory */ + /* Target returned to data phase: wipe "done" memory. */ ecb->flags &= ~ECB_TENTATIVE_DONE; /* Program the SCSI counter. */ @@ -3105,8 +3126,8 @@ ncr53c9x_intr1(struct ncr53c9x_softc *sc) * overhead to pay. For example, selecting, sending a message * and command and then doing some work can be done in one "pass". * - * The delay is a heuristic. It is 2 when at 20MHz, 2 at 25MHz and 1 - * at 40MHz. This needs testing. + * The delay is a heuristic. It is 2 when at 20 MHz, 2 at 25 MHz and + * 1 at 40 MHz. This needs testing. */ microtime(&wait); wait.tv_usec += 50 / sc->sc_freq; @@ -3191,8 +3212,7 @@ ncr53c9x_callout(void *arg) ncr53c9x_abort(sc, ecb); /* Disable sync mode if stuck in a data phase. */ - if (ecb == sc->sc_nexus && - ti->curr.offset != 0 && + if (ecb == sc->sc_nexus && ti->curr.offset != 0 && (sc->sc_phase & (MSGI | CDI)) == 0) { /* XXX ASYNC CALLBACK! */ ti->goal.offset = 0;