static void wdc_atapi_reset(struct ata_channel *chp, struct ata_xfer *xfer) { struct atac_softc *atac = chp->ch_atac; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->c_drive]; struct scsipi_xfer *sc_xfer = xfer->c_cmd; wdccommandshort(chp, xfer->c_drive, ATAPI_SOFT_RESET); drvp->state = 0; if (wdc_wait_for_unbusy(chp, WDC_RESET_WAIT, AT_POLL) != 0) { printf("%s:%d:%d: reset failed\n", device_xname(atac->atac_dev), chp->ch_channel, xfer->c_drive); sc_xfer->error = XS_SELTIMEOUT; } wdc_atapi_done(chp, xfer); return; }
void wdc_atapi_reset(struct channel_softc *chp, struct wdc_xfer *xfer, int timeout, struct atapi_return_args *ret) { struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; if (drvp->state == 0) { xfer->next = wdc_atapi_done; return; } WDCDEBUG_PRINT(("wdc_atapi_reset\n"), DEBUG_XFERS); wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET); drvp->state = ATAPI_IDENTIFY_STATE; drvp->n_resets++; /* Some ATAPI devices need extra time to find their brains after a reset */ xfer->next = wdc_atapi_reset_2; ret->delay = ATAPI_RESET_DELAY; ret->timeout = ATAPI_RESET_WAIT; return; }
void wdc_atapi_ctrl(struct channel_softc *chp, struct wdc_xfer *xfer, int timeout, struct atapi_return_args *ret) { struct scsi_xfer *sc_xfer = xfer->cmd; struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; char *errstring = NULL; wdc_atapi_update_status(chp); if (!timeout) { switch (drvp->state) { case ATAPI_IDENTIFY_WAIT_STATE: if (chp->ch_status & WDCS_BSY) return; break; default: if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) return; break; } } if (!wdc_atapi_drive_selected(chp, xfer->drive)) { wdc_set_drive(chp, xfer->drive); delay (1); } if (timeout) { int trigger_timeout = 1; switch (drvp->state) { case ATAPI_DEVICE_RESET_WAIT_STATE: errstring = "Device Reset Wait"; drvp->drive_flags &= ~DRIVE_DEVICE_RESET; break; case ATAPI_IDENTIFY_WAIT_STATE: errstring = "Identify"; if (!(chp->ch_status & WDCS_BSY) && (chp->ch_status & (WDCS_DRQ | WDCS_ERR))) trigger_timeout = 0; break; case ATAPI_PIOMODE_STATE: errstring = "Post-Identify"; if (!(chp->ch_status & (WDCS_BSY | WDCS_DRQ))) trigger_timeout = 0; break; case ATAPI_PIOMODE_WAIT_STATE: errstring = "PIOMODE"; if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) drvp->drive_flags &= ~DRIVE_MODE; else trigger_timeout = 0; break; case ATAPI_DMAMODE_WAIT_STATE: errstring = "dmamode"; if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); else trigger_timeout = 0; break; default: errstring = "unknown state"; break; } if (trigger_timeout) goto timeout; } WDCDEBUG_PRINT(("wdc_atapi_ctrl %s:%d:%d state %d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state), DEBUG_INTR | DEBUG_FUNCS); switch (drvp->state) { /* My ATAPI slave device likes to assert DASP-/PDIAG- until it is DEVICE RESET. This causes the LED to stay on. There is a trade-off here. This drive will cause any play-back or seeks happening to be interrupted. Note that the bus reset that triggered this state (which may have been caused by the other drive on the chain) need not interrupt this playback. It happens to on my Smart & Friendly CD burner. - csapuntz@ */ case ATAPI_RESET_BASE_STATE: if ((drvp->drive_flags & DRIVE_DEVICE_RESET) == 0) { drvp->state = ATAPI_IDENTIFY_STATE; break; } wdccommandshort(chp, drvp->drive, ATAPI_DEVICE_RESET); drvp->state = ATAPI_DEVICE_RESET_WAIT_STATE; ret->delay = ATAPI_RESET_DELAY; ret->timeout = ATAPI_RESET_WAIT; break; case ATAPI_DEVICE_RESET_WAIT_STATE: /* FALLTHROUGH */ case ATAPI_IDENTIFY_STATE: wdccommandshort(chp, drvp->drive, ATAPI_IDENTIFY_DEVICE); drvp->state = ATAPI_IDENTIFY_WAIT_STATE; ret->delay = 10; ret->timeout = ATAPI_RESET_WAIT; break; case ATAPI_IDENTIFY_WAIT_STATE: { int idx = 0; while ((chp->ch_status & WDCS_DRQ) && idx++ < 20) { wdcbit_bucket(chp, 512); DELAY(1); wdc_atapi_update_status(chp); } drvp->state = ATAPI_PIOMODE_STATE; /* * Note, we can't go directly to set PIO mode * because the drive is free to assert BSY * after the transfer */ break; } case ATAPI_PIOMODE_STATE: /* Don't try to set mode if controller can't be adjusted */ if ((chp->wdc->cap & WDC_CAPABILITY_MODE) == 0) goto ready; /* Also don't try if the drive didn't report its mode */ if ((drvp->drive_flags & DRIVE_MODE) == 0) goto ready; /* SET FEATURES 0x08 is only for PIO mode > 2 */ if (drvp->PIO_mode <= 2) goto ready; wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x08 | drvp->PIO_mode, WDSF_SET_MODE); drvp->state = ATAPI_PIOMODE_WAIT_STATE; ret->timeout = ATAPI_CTRL_WAIT; ret->expect_irq = 1; break; case ATAPI_PIOMODE_WAIT_STATE: if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) chp->wdc->irqack(chp); if (chp->ch_status & WDCS_ERR) { /* Downgrade straight to PIO mode 3 */ drvp->PIO_mode = 3; chp->wdc->set_modes(chp); } /* FALLTHROUGH */ case ATAPI_DMAMODE_STATE: if (drvp->drive_flags & DRIVE_UDMA) { wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x40 | drvp->UDMA_mode, WDSF_SET_MODE); } else if (drvp->drive_flags & DRIVE_DMA) { wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0, 0x20 | drvp->DMA_mode, WDSF_SET_MODE); } else { goto ready; } drvp->state = ATAPI_DMAMODE_WAIT_STATE; ret->timeout = ATAPI_CTRL_WAIT; ret->expect_irq = 1; break; case ATAPI_DMAMODE_WAIT_STATE: if (chp->wdc->cap & WDC_CAPABILITY_IRQACK) chp->wdc->irqack(chp); if (chp->ch_status & WDCS_ERR) drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); /* FALLTHROUGH */ case ATAPI_READY_STATE: ready: drvp->state = ATAPI_READY_STATE; xfer->next = wdc_atapi_real_start; break; } return; timeout: printf("%s:%d:%d: %s timed out\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring); sc_xfer->error = XS_TIMEOUT; xfer->next = wdc_atapi_reset; return; }