summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2002-07-02 15:26:20 +0000
committerConstantine Sapuntzakis <csapuntz@cvs.openbsd.org>2002-07-02 15:26:20 +0000
commit315784b1e81926130ef96ee6d5a39871c88a3b80 (patch)
tree219d5e0f85d949e20aef01840a53e0c9c0156355
parentad16a186ab94f8057bd5190ca3a2366befb04bdf (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.
-rw-r--r--sys/dev/ic/wdc.c37
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);