diff options
author | Constantine Sapuntzakis <csapuntz@cvs.openbsd.org> | 2002-07-02 15:26:20 +0000 |
---|---|---|
committer | Constantine Sapuntzakis <csapuntz@cvs.openbsd.org> | 2002-07-02 15:26:20 +0000 |
commit | 315784b1e81926130ef96ee6d5a39871c88a3b80 (patch) | |
tree | 219d5e0f85d949e20aef01840a53e0c9c0156355 /sys | |
parent | ad16a186ab94f8057bd5190ca3a2366befb04bdf (diff) |
Fix PIO write state machine. This has been totally broken since 2.5, but since
this code path is only executed for IOCTLs, and all of our IOCTLs only read
from the device, nobody noticed.
Thanks to grange and gluk for finding this one.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/ic/wdc.c | 37 |
1 files changed, 30 insertions, 7 deletions
diff --git a/sys/dev/ic/wdc.c b/sys/dev/ic/wdc.c index 1db6186b76a..acfdd2b04d3 100644 --- a/sys/dev/ic/wdc.c +++ b/sys/dev/ic/wdc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: wdc.c,v 1.51 2002/05/24 09:33:29 art Exp $ */ +/* $OpenBSD: wdc.c,v 1.52 2002/07/02 15:26:19 csapuntz Exp $ */ /* $NetBSD: wdc.c,v 1.68 1999/06/23 19:00:17 bouyer Exp $ */ @@ -1649,26 +1649,49 @@ __wdccommand_start(chp, xfer) if (wdc_c->r_command != ATAPI_SOFT_RESET) { if (wdcwait(chp, wdc_c->r_st_bmask | WDCS_DRQ, wdc_c->r_st_bmask, wdc_c->timeout) != 0) { - wdc_c->flags |= AT_TIMEOU; - __wdccommand_done(chp, xfer); - return; + goto timeout; } } else DELAY(10); wdccommand(chp, drive, wdc_c->r_command, wdc_c->r_cyl, wdc_c->r_head, wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp); + + if ((wdc_c->flags & AT_WRITE) == AT_WRITE) { + delay(10); + if (wait_for_unbusy(chp, wdc_c->timeout) != 0) + goto timeout; + + if ((chp->ch_status & (WDCS_DRQ | WDCS_ERR)) == WDCS_ERR) { + __wdccommand_done(chp, xfer); + return; + } + + if (wait_for_drq(chp, wdc_c->timeout) != 0) + goto timeout; + + wdc_output_bytes(&chp->ch_drive[drive], + wdc_c->data, wdc_c->bcount); + } + if ((wdc_c->flags & AT_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */ timeout_add(&chp->ch_timo, wdc_c->timeout / 1000 * hz); return; } + /* * Polled command. Wait for drive ready or drq. Done in intr(). * Wait for at last 400ns for status bit to be valid. */ delay(10); __wdccommand_intr(chp, xfer, 0); + return; + + timeout: + wdc_c->flags |= AT_TIMEOU; + __wdccommand_done(chp, xfer); + return; } int @@ -1695,10 +1718,10 @@ __wdccommand_intr(chp, xfer, irq) } if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) chp->wdc->irqack(chp); - if (wdc_c->flags & AT_READ) { + + if ((wdc_c->flags & AT_READ) && (chp->ch_status & WDCS_DRQ)) { wdc_input_bytes(drvp, data, bcount); - } else if (wdc_c->flags & AT_WRITE) { - wdc_output_bytes(drvp, data, bcount); + /* Should we wait for device to indicate idle? */ } __wdccommand_done(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_intr returned\n"), DEBUG_INTR); |