xref: /openbsd-src/sys/dev/atapiscsi/atapiscsi.c (revision e7c2d835904c2fb4cf01d6ce18e7bae118a8dc97)
1*e7c2d835Sjsg /*      $OpenBSD: atapiscsi.c,v 1.122 2024/06/22 10:22:29 jsg Exp $     */
27481efa2Scsapuntz 
37481efa2Scsapuntz /*
47481efa2Scsapuntz  * This code is derived from code with the copyright below.
57481efa2Scsapuntz  */
67481efa2Scsapuntz 
77481efa2Scsapuntz /*
87481efa2Scsapuntz  * Copyright (c) 1996, 1998 Manuel Bouyer.
97481efa2Scsapuntz  *
107481efa2Scsapuntz  * Redistribution and use in source and binary forms, with or without
117481efa2Scsapuntz  * modification, are permitted provided that the following conditions
127481efa2Scsapuntz  * are met:
137481efa2Scsapuntz  * 1. Redistributions of source code must retain the above copyright
147481efa2Scsapuntz  *    notice, this list of conditions and the following disclaimer.
157481efa2Scsapuntz  * 2. Redistributions in binary form must reproduce the above copyright
167481efa2Scsapuntz  *    notice, this list of conditions and the following disclaimer in the
177481efa2Scsapuntz  *    documentation and/or other materials provided with the distribution.
187481efa2Scsapuntz  *
197481efa2Scsapuntz  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
207481efa2Scsapuntz  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
217481efa2Scsapuntz  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
227481efa2Scsapuntz  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
237481efa2Scsapuntz  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
247481efa2Scsapuntz  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
257481efa2Scsapuntz  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
267481efa2Scsapuntz  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
277481efa2Scsapuntz  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
287481efa2Scsapuntz  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
297481efa2Scsapuntz  * SUCH DAMAGE.
307481efa2Scsapuntz  *
317481efa2Scsapuntz  */
327481efa2Scsapuntz 
337481efa2Scsapuntz #include <sys/param.h>
347481efa2Scsapuntz #include <sys/systm.h>
357481efa2Scsapuntz #include <sys/kernel.h>
367481efa2Scsapuntz #include <sys/device.h>
379f5e13a3Sho #include <sys/timeout.h>
387481efa2Scsapuntz #include <scsi/scsi_all.h>
39ec62f1edSniklas #include <scsi/scsi_tape.h>
407481efa2Scsapuntz #include <scsi/scsiconf.h>
417481efa2Scsapuntz 
427481efa2Scsapuntz #include <machine/bus.h>
437481efa2Scsapuntz #include <machine/cpu.h>
447481efa2Scsapuntz #include <machine/intr.h>
457481efa2Scsapuntz 
467481efa2Scsapuntz #include <dev/ata/atareg.h>
477481efa2Scsapuntz #include <dev/ata/atavar.h>
487481efa2Scsapuntz #include <dev/ic/wdcreg.h>
497481efa2Scsapuntz #include <dev/ic/wdcvar.h>
5095c10403Scsapuntz #include <dev/ic/wdcevent.h>
517481efa2Scsapuntz 
520c6728ddScsapuntz /* drive states stored in ata_drive_datas */
5366bb6343Scsapuntz enum atapi_drive_states {
5466bb6343Scsapuntz 	ATAPI_RESET_BASE_STATE = 0,
5566bb6343Scsapuntz 	ATAPI_DEVICE_RESET_WAIT_STATE = 1,
562702c783Scsapuntz 	ATAPI_IDENTIFY_STATE = 2,
572702c783Scsapuntz 	ATAPI_IDENTIFY_WAIT_STATE = 3,
582702c783Scsapuntz 	ATAPI_PIOMODE_STATE = 4,
592702c783Scsapuntz 	ATAPI_PIOMODE_WAIT_STATE = 5,
602702c783Scsapuntz 	ATAPI_DMAMODE_STATE = 6,
612702c783Scsapuntz 	ATAPI_DMAMODE_WAIT_STATE = 7,
622702c783Scsapuntz 	ATAPI_READY_STATE = 8
6366bb6343Scsapuntz };
640c6728ddScsapuntz 
657481efa2Scsapuntz #define DEBUG_INTR   0x01
667481efa2Scsapuntz #define DEBUG_XFERS  0x02
677481efa2Scsapuntz #define DEBUG_STATUS 0x04
687481efa2Scsapuntz #define DEBUG_FUNCS  0x08
697481efa2Scsapuntz #define DEBUG_PROBE  0x10
70ec62f1edSniklas #define DEBUG_DSC    0x20
71ec62f1edSniklas #define DEBUG_POLL   0x40
7236acebdeScsapuntz #define DEBUG_ERRORS 0x80   /* Debug error handling code */
7336acebdeScsapuntz 
74c9eb5cb9Scsapuntz #if defined(WDCDEBUG)
7582fc695dSgrange #ifndef WDCDEBUG_ATAPI_MASK
7682fc695dSgrange #define WDCDEBUG_ATAPI_MASK 0x00
7782fc695dSgrange #endif
7882fc695dSgrange int wdcdebug_atapi_mask = WDCDEBUG_ATAPI_MASK;
79d6986e4fSgrange #define WDCDEBUG_PRINT(args, level) do {		\
80d6986e4fSgrange 	if ((wdcdebug_atapi_mask & (level)) != 0)	\
81d6986e4fSgrange 		printf args;				\
82d6986e4fSgrange } while (0)
837481efa2Scsapuntz #else
847481efa2Scsapuntz #define WDCDEBUG_PRINT(args, level)
857481efa2Scsapuntz #endif
867481efa2Scsapuntz 
87ec62f1edSniklas /* 10 ms, this is used only before sending a cmd.  */
88ec62f1edSniklas #define ATAPI_DELAY 10
892702c783Scsapuntz #define ATAPI_RESET_DELAY 1000
909ec8daebSderaadt #define ATAPI_RESET_WAIT 2000
91a24b05dfScsapuntz #define ATAPI_CTRL_WAIT 4000
92ec62f1edSniklas 
93ec62f1edSniklas /* When polling, let the exponential backoff max out at 1 second's interval. */
94ec62f1edSniklas #define ATAPI_POLL_MAXTIC (hz)
957481efa2Scsapuntz 
96c4071fd1Smillert void  wdc_atapi_start(struct channel_softc *,struct wdc_xfer *);
97a24b05dfScsapuntz 
98c4071fd1Smillert void  wdc_atapi_timer_handler(void *);
99a24b05dfScsapuntz 
100c4071fd1Smillert void  wdc_atapi_real_start(struct channel_softc *, struct wdc_xfer *,
101c4071fd1Smillert     int, struct atapi_return_args *);
102c4071fd1Smillert void  wdc_atapi_real_start_2(struct channel_softc *, struct wdc_xfer *,
103c4071fd1Smillert     int, struct atapi_return_args *);
104c4071fd1Smillert void  wdc_atapi_intr_command(struct channel_softc *, struct wdc_xfer *,
105c4071fd1Smillert     int, struct atapi_return_args *);
106c4071fd1Smillert void  wdc_atapi_intr_data(struct channel_softc *, struct wdc_xfer *,
107c4071fd1Smillert     int, struct atapi_return_args *);
108c4071fd1Smillert void  wdc_atapi_intr_complete(struct channel_softc *, struct wdc_xfer *,
109c4071fd1Smillert     int, struct atapi_return_args *);
110c4071fd1Smillert void  wdc_atapi_pio_intr(struct channel_softc *, struct wdc_xfer *,
111c4071fd1Smillert     int, struct atapi_return_args *);
112c4071fd1Smillert void  wdc_atapi_send_packet(struct channel_softc *, struct wdc_xfer *,
113c4071fd1Smillert     int, struct atapi_return_args *);
114c4071fd1Smillert void  wdc_atapi_ctrl(struct channel_softc *, struct wdc_xfer *,
115c4071fd1Smillert     int, struct atapi_return_args *);
116a24b05dfScsapuntz 
117c4071fd1Smillert char  *wdc_atapi_in_data_phase(struct wdc_xfer *, int, int);
118a24b05dfScsapuntz 
119c4071fd1Smillert int   wdc_atapi_intr(struct channel_softc *, struct wdc_xfer *, int);
120c4071fd1Smillert void  wdc_atapi_done(struct channel_softc *, struct wdc_xfer *,
121c4071fd1Smillert 	int, struct atapi_return_args *);
122c4071fd1Smillert void  wdc_atapi_reset(struct channel_softc *, struct wdc_xfer *,
123c4071fd1Smillert 	int, struct atapi_return_args *);
124c4071fd1Smillert void  wdc_atapi_reset_2(struct channel_softc *, struct wdc_xfer *,
125c4071fd1Smillert 	int, struct atapi_return_args *);
1267481efa2Scsapuntz 
127c4071fd1Smillert void  wdc_atapi_tape_done(struct channel_softc *, struct wdc_xfer *,
128c4071fd1Smillert 	int, struct atapi_return_args *);
1297481efa2Scsapuntz 
130c4071fd1Smillert int	atapiscsi_match(struct device *, void *, void *);
131c4071fd1Smillert void	atapiscsi_attach(struct device *, struct device *, void *);
1329a379db1Sderaadt int	atapiscsi_activate(struct device *, int);
133a3f240fdSmiod int	atapiscsi_detach(struct device *, int);
134c4071fd1Smillert int     atapi_to_scsi_sense(struct scsi_xfer *, u_int8_t);
1357481efa2Scsapuntz 
1360de5a53dSespie enum atapi_state { as_none, as_data, as_completed };
1370de5a53dSespie 
1387481efa2Scsapuntz struct atapiscsi_softc {
1397481efa2Scsapuntz 	struct device  sc_dev;
14048d11ec3Scsapuntz 	struct channel_softc *chp;
141e61c843cScsapuntz 	enum atapi_state protocol_phase;
1429ec8daebSderaadt 
1435252c952Scsapuntz 	int drive;
1447481efa2Scsapuntz };
1457481efa2Scsapuntz 
146fdca2419Sdlg int   wdc_atapi_ioctl(struct scsi_link *, u_long, caddr_t, int);
147bae03be6Skrw void  wdc_atapi_send_cmd(struct scsi_xfer *sc_xfer);
14866bb6343Scsapuntz 
149a454aff3Snaddy static const struct scsi_adapter atapiscsi_switch = {
150f4b58f5aSkrw 	wdc_atapi_send_cmd, NULL, NULL, NULL, wdc_atapi_ioctl
1517481efa2Scsapuntz };
1527481efa2Scsapuntz 
1534b1a56afSjsg /* Initial version shares bus_link structure so it can easily
1547481efa2Scsapuntz    be "attached to current" wdc driver */
1557481efa2Scsapuntz 
156471aeecfSnaddy const struct cfattach atapiscsi_ca = {
157a3f240fdSmiod 	sizeof(struct atapiscsi_softc), atapiscsi_match, atapiscsi_attach,
1589a379db1Sderaadt 	    atapiscsi_detach, atapiscsi_activate
1597481efa2Scsapuntz };
1607481efa2Scsapuntz 
1617481efa2Scsapuntz struct cfdriver atapiscsi_cd = {
1627481efa2Scsapuntz 	NULL, "atapiscsi", DV_DULL
1637481efa2Scsapuntz };
1647481efa2Scsapuntz 
1657481efa2Scsapuntz 
1669ec8daebSderaadt int
atapiscsi_match(struct device * parent,void * match,void * aux)167c28b5ab2Smatthew atapiscsi_match(struct device *parent, void *match, void *aux)
1687481efa2Scsapuntz {
1697481efa2Scsapuntz 	struct ata_atapi_attach *aa_link = aux;
1707481efa2Scsapuntz 	struct cfdata *cf = match;
1717481efa2Scsapuntz 
1727481efa2Scsapuntz 	if (aa_link == NULL)
1737481efa2Scsapuntz 		return (0);
17448d11ec3Scsapuntz 
1757481efa2Scsapuntz 	if (aa_link->aa_type != T_ATAPI)
1767481efa2Scsapuntz 		return (0);
1777481efa2Scsapuntz 
1787481efa2Scsapuntz 	if (cf->cf_loc[0] != aa_link->aa_channel &&
1797481efa2Scsapuntz 	    cf->cf_loc[0] != -1)
1807481efa2Scsapuntz 		return (0);
1817481efa2Scsapuntz 
1827481efa2Scsapuntz 	return (1);
1837481efa2Scsapuntz }
1847481efa2Scsapuntz 
1857481efa2Scsapuntz void
atapiscsi_attach(struct device * parent,struct device * self,void * aux)186c28b5ab2Smatthew atapiscsi_attach(struct device *parent, struct device *self, void *aux)
1877481efa2Scsapuntz {
1887481efa2Scsapuntz 	struct atapiscsi_softc *as = (struct atapiscsi_softc *)self;
1897481efa2Scsapuntz 	struct ata_atapi_attach *aa_link = aux;
19073d09fc5Sdlg 	struct scsibus_attach_args saa;
1915252c952Scsapuntz 	struct ata_drive_datas *drvp = aa_link->aa_drv_data;
1925252c952Scsapuntz 	struct channel_softc *chp = drvp->chnl_softc;
1935252c952Scsapuntz 	struct ataparams *id = &drvp->id;
194cb45a5fdSdlg 	struct device *child;
1957481efa2Scsapuntz 
196a1f52200Sdlg 	extern struct scsi_iopool wdc_xfer_iopool;
197a1f52200Sdlg 
198a0a14a4eSderaadt 	printf("\n");
199a0a14a4eSderaadt 
200731fd99bSfgsch 	/* Initialize shared data. */
201731fd99bSfgsch 	scsi_init();
202731fd99bSfgsch 
203551bdc0fSderaadt #ifdef WDCDEBUG
204d348db70Scsapuntz 	if (chp->wdc->sc_dev.dv_cfdata->cf_flags & WDC_OPTION_PROBE_VERBOSE)
205d348db70Scsapuntz 		wdcdebug_atapi_mask |= DEBUG_PROBE;
206551bdc0fSderaadt #endif
207d348db70Scsapuntz 
20848d11ec3Scsapuntz 	as->chp = chp;
2095252c952Scsapuntz 	as->drive = drvp->drive;
2107481efa2Scsapuntz 
211f981035cSdlg 	strlcpy(drvp->drive_name, as->sc_dev.dv_xname,
212f981035cSdlg 	    sizeof(drvp->drive_name));
213f97ca3e4Scsapuntz 	drvp->cf_flags = as->sc_dev.dv_cfdata->cf_flags;
214f97ca3e4Scsapuntz 
21548d11ec3Scsapuntz 	wdc_probe_caps(drvp, id);
2167481efa2Scsapuntz 
217551bdc0fSderaadt 	WDCDEBUG_PRINT(
218ec62f1edSniklas 		("general config %04x capabilities %04x ",
219551bdc0fSderaadt 		    id->atap_config, id->atap_capabilities1),
220551bdc0fSderaadt 		    DEBUG_PROBE);
221ec62f1edSniklas 
222ec8802a4Scsapuntz 	if ((NERRS_MAX - 2) > 0)
223ec8802a4Scsapuntz 		drvp->n_dmaerrs = NERRS_MAX - 2;
224ec8802a4Scsapuntz 	else
225ec8802a4Scsapuntz 		drvp->n_dmaerrs = 0;
22666bb6343Scsapuntz 	drvp->drive_flags |= DRIVE_DEVICE_RESET;
22766bb6343Scsapuntz 
228ec62f1edSniklas 	/* Tape drives do funny DSC stuff */
229ec62f1edSniklas 	if (ATAPI_CFG_TYPE(id->atap_config) ==
230ec62f1edSniklas 	    ATAPI_CFG_TYPE_SEQUENTIAL)
231ec62f1edSniklas 		drvp->atapi_cap |= ACAP_DSC;
232ec62f1edSniklas 
233ec62f1edSniklas 	if ((id->atap_config & ATAPI_CFG_CMD_MASK) ==
234ec62f1edSniklas 	    ATAPI_CFG_CMD_16)
235a077a3e9Scsapuntz 		drvp->atapi_cap |= ACAP_LEN;
236a077a3e9Scsapuntz 
237ec62f1edSniklas 	drvp->atapi_cap |=
238ec62f1edSniklas 	    (id->atap_config & ATAPI_CFG_DRQ_MASK);
239ec62f1edSniklas 
240551bdc0fSderaadt 	WDCDEBUG_PRINT(("driver caps %04x\n", drvp->atapi_cap),
241551bdc0fSderaadt 	    DEBUG_PROBE);
24248d11ec3Scsapuntz 
2433ba66839Skrw 
244ead808c4Skrw 	saa.saa_adapter_softc = as;
245ead808c4Skrw 	saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET;
246ead808c4Skrw 	saa.saa_adapter_buswidth = 2;
247ead808c4Skrw 	saa.saa_adapter = &atapiscsi_switch;
248ead808c4Skrw 	saa.saa_luns = 1;
249e5eae15dSkrw 	saa.saa_openings = 1;
250e5eae15dSkrw 	saa.saa_flags = SDEV_ATAPI;
251e5eae15dSkrw 	saa.saa_pool = &wdc_xfer_iopool;
252e5eae15dSkrw 	saa.saa_quirks = 0;
253e5eae15dSkrw 	saa.saa_wwpn = saa.saa_wwnn = 0;
25473d09fc5Sdlg 
25573d09fc5Sdlg 	child = config_found((struct device *)as, &saa, scsiprint);
256ad0c6836Scsapuntz 
257cb45a5fdSdlg 	if (child != NULL) {
258cb45a5fdSdlg 		struct scsibus_softc *scsi = (struct scsibus_softc *)child;
259a0870786Smatthew 		struct scsi_link *link = scsi_get_link(scsi, 0, 0);
26048d11ec3Scsapuntz 
261c1dfe69fSniklas 		if (link) {
262f981035cSdlg 			strlcpy(drvp->drive_name,
263f97ca3e4Scsapuntz 			    ((struct device *)(link->device_softc))->dv_xname,
264f981035cSdlg 			    sizeof(drvp->drive_name));
265f97ca3e4Scsapuntz 
26648d11ec3Scsapuntz 			wdc_print_caps(drvp);
2677481efa2Scsapuntz 		}
26848d11ec3Scsapuntz 	}
269d348db70Scsapuntz 
270551bdc0fSderaadt #ifdef WDCDEBUG
271d348db70Scsapuntz 	if (chp->wdc->sc_dev.dv_cfdata->cf_flags & WDC_OPTION_PROBE_VERBOSE)
272d348db70Scsapuntz 		wdcdebug_atapi_mask &= ~DEBUG_PROBE;
273551bdc0fSderaadt #endif
27448d11ec3Scsapuntz }
2757481efa2Scsapuntz 
276a3f240fdSmiod int
atapiscsi_activate(struct device * self,int act)2779a379db1Sderaadt atapiscsi_activate(struct device *self, int act)
2789a379db1Sderaadt {
2799a379db1Sderaadt 	struct atapiscsi_softc *as = (void *)self;
2809a379db1Sderaadt  	struct channel_softc *chp = as->chp;
2819a379db1Sderaadt 	struct ata_drive_datas *drvp = &chp->ch_drive[as->drive];
2829a379db1Sderaadt 
2839a379db1Sderaadt 	switch (act) {
2849a379db1Sderaadt 	case DVACT_SUSPEND:
2859a379db1Sderaadt 		break;
2869a379db1Sderaadt 	case DVACT_RESUME:
2879a379db1Sderaadt 		/*
2889a379db1Sderaadt 		 * Do two resets separated by a small delay. The
2899a379db1Sderaadt 		 * first wakes the controller, the second resets
2909a379db1Sderaadt 		 * the channel
2919a379db1Sderaadt 		 */
2929a379db1Sderaadt 		wdc_disable_intr(chp);
293ba8cbb4aSmiod 		wdc_reset_channel(drvp, 1);
2949a379db1Sderaadt 		delay(10000);
295ba8cbb4aSmiod 		wdc_reset_channel(drvp, 0);
2969a379db1Sderaadt 		wdc_enable_intr(chp);
2979a379db1Sderaadt 		break;
2989a379db1Sderaadt 	}
2999a379db1Sderaadt 	return (0);
3009a379db1Sderaadt }
3019a379db1Sderaadt 
3029a379db1Sderaadt int
atapiscsi_detach(struct device * dev,int flags)303c28b5ab2Smatthew atapiscsi_detach(struct device *dev, int flags)
304a3f240fdSmiod {
305a3f240fdSmiod 	return (config_detach_children(dev, flags));
306a3f240fdSmiod }
3077481efa2Scsapuntz 
308bae03be6Skrw void
wdc_atapi_send_cmd(struct scsi_xfer * sc_xfer)309c28b5ab2Smatthew wdc_atapi_send_cmd(struct scsi_xfer *sc_xfer)
3107481efa2Scsapuntz {
3110b29cb40Skrw 	struct atapiscsi_softc *as = sc_xfer->sc_link->bus->sb_adapter_softc;
31248d11ec3Scsapuntz  	struct channel_softc *chp = as->chp;
3135252c952Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[as->drive];
3147481efa2Scsapuntz 	struct wdc_xfer *xfer;
3158e703ec5Skrw 	int s;
3163f343521Scsapuntz 	int idx;
3177481efa2Scsapuntz 
3183f343521Scsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d start\n",
3194ec4f187Sho 	    chp->wdc->sc_dev.dv_xname, chp->channel, as->drive), DEBUG_XFERS);
3207481efa2Scsapuntz 
3215252c952Scsapuntz 	if (sc_xfer->sc_link->target != 0) {
3227481efa2Scsapuntz 		sc_xfer->error = XS_DRIVER_STUFFUP;
323b19e1108Sdlg 		scsi_done(sc_xfer);
324bae03be6Skrw 		return;
3257481efa2Scsapuntz 	}
3267481efa2Scsapuntz 
327a1f52200Sdlg 	xfer = sc_xfer->io;
3280f976493Sdlg 	wdc_scrub_xfer(xfer);
3297481efa2Scsapuntz 	if (sc_xfer->flags & SCSI_POLL)
3307481efa2Scsapuntz 		xfer->c_flags |= C_POLL;
3315252c952Scsapuntz 	xfer->drive = as->drive;
3327481efa2Scsapuntz 	xfer->c_flags |= C_ATAPI;
3337481efa2Scsapuntz 	xfer->cmd = sc_xfer;
3347481efa2Scsapuntz 	xfer->databuf = sc_xfer->data;
3357481efa2Scsapuntz 	xfer->c_bcount = sc_xfer->datalen;
3367481efa2Scsapuntz 	xfer->c_start = wdc_atapi_start;
3377481efa2Scsapuntz 	xfer->c_intr = wdc_atapi_intr;
338ec62f1edSniklas 
33954f23ac8Scsapuntz 	timeout_set(&xfer->atapi_poll_to, wdc_atapi_timer_handler, chp);
3409f5e13a3Sho 
3413f343521Scsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_send_cmd %s:%d:%d ",
3423f343521Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, as->drive),
3433f343521Scsapuntz 	    DEBUG_XFERS | DEBUG_ERRORS);
3443f343521Scsapuntz 
3453f343521Scsapuntz 	for (idx = 0; idx < sc_xfer->cmdlen; idx++) {
3463f343521Scsapuntz 		WDCDEBUG_PRINT((" %02x",
347664c6166Skrw 				   ((unsigned char *)&sc_xfer->cmd)[idx]),
3483f343521Scsapuntz 		    DEBUG_XFERS | DEBUG_ERRORS);
3493f343521Scsapuntz 	}
3503f343521Scsapuntz 	WDCDEBUG_PRINT(("\n"), DEBUG_XFERS | DEBUG_ERRORS);
3513f343521Scsapuntz 
3527481efa2Scsapuntz 	s = splbio();
353ec62f1edSniklas 
354ec62f1edSniklas 	if (drvp->atapi_cap & ACAP_DSC) {
35520d226a3Sgrange 		WDCDEBUG_PRINT(("about to send cmd 0x%x ",
356664c6166Skrw 		    sc_xfer->cmd.opcode), DEBUG_DSC);
357664c6166Skrw 		switch (sc_xfer->cmd.opcode) {
358ec62f1edSniklas 		case READ:
359ec62f1edSniklas 		case WRITE:
360a24b05dfScsapuntz 			xfer->c_flags |= C_MEDIA_ACCESS;
361a24b05dfScsapuntz 
362ec62f1edSniklas 			/* If we are not in buffer availability mode,
363ec62f1edSniklas 			   we limit the first request to 0 bytes, which
364ec62f1edSniklas 			   gets us into buffer availability mode without
365ec62f1edSniklas 			   holding the bus.  */
366ec62f1edSniklas 			if (!(drvp->drive_flags & DRIVE_DSCBA)) {
367a24b05dfScsapuntz 				xfer->c_bcount = 0;
368a24b05dfScsapuntz 				xfer->transfer_len =
369a24b05dfScsapuntz 				  _3btol(((struct scsi_rw_tape *)
370664c6166Skrw 					  &sc_xfer->cmd)->len);
371ec62f1edSniklas 				_lto3b(0,
372ec62f1edSniklas 				    ((struct scsi_rw_tape *)
373664c6166Skrw 				    &sc_xfer->cmd)->len);
374a24b05dfScsapuntz 				xfer->c_done = wdc_atapi_tape_done;
375ec62f1edSniklas 				WDCDEBUG_PRINT(
376ec62f1edSniklas 				    ("R/W in completion mode, do 0 blocks\n"),
377ec62f1edSniklas 				    DEBUG_DSC);
378ec62f1edSniklas 			} else
379ec62f1edSniklas 				WDCDEBUG_PRINT(("R/W %d blocks %d bytes\n",
380ec62f1edSniklas 				    _3btol(((struct scsi_rw_tape *)
381664c6166Skrw 					&sc_xfer->cmd)->len),
382a24b05dfScsapuntz 				    sc_xfer->datalen),
383ec62f1edSniklas 				    DEBUG_DSC);
384ec62f1edSniklas 
385ec62f1edSniklas 			/* DSC will change to buffer availability mode.
386ec62f1edSniklas 			   We reflect this in wdc_atapi_intr.  */
387ec62f1edSniklas 			break;
388ec62f1edSniklas 
389ec62f1edSniklas 		case ERASE:		/* Media access commands */
390ec62f1edSniklas 		case LOAD:
391ec62f1edSniklas 		case REWIND:
392ec62f1edSniklas 		case SPACE:
393a24b05dfScsapuntz 		case WRITE_FILEMARKS:
394ec62f1edSniklas #if 0
395ec62f1edSniklas 		case LOCATE:
396ec62f1edSniklas 		case READ_POSITION:
397ec62f1edSniklas #endif
398a24b05dfScsapuntz 
399a24b05dfScsapuntz 			xfer->c_flags |= C_MEDIA_ACCESS;
400ec62f1edSniklas 			break;
401ec62f1edSniklas 
402ec62f1edSniklas 		default:
403ec62f1edSniklas 			WDCDEBUG_PRINT(("no media access\n"), DEBUG_DSC);
404ec62f1edSniklas 		}
405ec62f1edSniklas 	}
406ec62f1edSniklas 
40748d11ec3Scsapuntz 	wdc_exec_xfer(chp, xfer);
4087481efa2Scsapuntz 	splx(s);
4097481efa2Scsapuntz }
4107481efa2Scsapuntz 
41166bb6343Scsapuntz int
wdc_atapi_ioctl(struct scsi_link * sc_link,u_long cmd,caddr_t addr,int flag)412c28b5ab2Smatthew wdc_atapi_ioctl(struct scsi_link *sc_link, u_long cmd, caddr_t addr, int flag)
41366bb6343Scsapuntz {
4140b29cb40Skrw 	struct atapiscsi_softc *as = sc_link->bus->sb_adapter_softc;
41566bb6343Scsapuntz 	struct channel_softc *chp = as->chp;
4165252c952Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[as->drive];
41766bb6343Scsapuntz 
4185252c952Scsapuntz 	if (sc_link->target != 0)
41966bb6343Scsapuntz 		return ENOTTY;
42066bb6343Scsapuntz 
421fdca2419Sdlg 	return (wdc_ioctl(drvp, cmd, addr, flag, curproc));
42266bb6343Scsapuntz }
42366bb6343Scsapuntz 
424ab72a97eScsapuntz 
425ab72a97eScsapuntz /*
426ab72a97eScsapuntz  * Returns 1 if we experienced an ATA-level abort command
427ab72a97eScsapuntz  *           (ABRT bit set but no additional sense)
428ab72a97eScsapuntz  *         0 if normal command processing
429ab72a97eScsapuntz  */
430ab72a97eScsapuntz int
atapi_to_scsi_sense(struct scsi_xfer * xfer,u_int8_t flags)431c28b5ab2Smatthew atapi_to_scsi_sense(struct scsi_xfer *xfer, u_int8_t flags)
43236acebdeScsapuntz {
43336acebdeScsapuntz 	struct scsi_sense_data *sense = &xfer->sense;
434ab72a97eScsapuntz 	int ret = 0;
435ab72a97eScsapuntz 
436ab72a97eScsapuntz 	xfer->error = XS_SHORTSENSE;
43736acebdeScsapuntz 
4387806ac8dSkrw 	sense->error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT;
43936acebdeScsapuntz 	sense->flags = (flags >> 4);
44036acebdeScsapuntz 
44136acebdeScsapuntz 	WDCDEBUG_PRINT(("Atapi error: %d ", (flags >> 4)), DEBUG_ERRORS);
44236acebdeScsapuntz 
443ab72a97eScsapuntz 	if ((flags & 4) && (sense->flags == 0)) {
44436acebdeScsapuntz 		sense->flags = SKEY_ABORTED_COMMAND;
44536acebdeScsapuntz 		WDCDEBUG_PRINT(("ABRT "), DEBUG_ERRORS);
446ab72a97eScsapuntz 		ret = 1;
44736acebdeScsapuntz 	}
44836acebdeScsapuntz 
44936acebdeScsapuntz 	if (flags & 0x1) {
45036acebdeScsapuntz 		sense->flags |= SSD_ILI;
45136acebdeScsapuntz 		WDCDEBUG_PRINT(("ILI "), DEBUG_ERRORS);
45236acebdeScsapuntz 	}
45336acebdeScsapuntz 
45436acebdeScsapuntz 	if (flags & 0x2) {
45536acebdeScsapuntz 		sense->flags |= SSD_EOM;
45636acebdeScsapuntz 		WDCDEBUG_PRINT(("EOM "), DEBUG_ERRORS);
45736acebdeScsapuntz 	}
45836acebdeScsapuntz 
45936acebdeScsapuntz 	/* Media change requested */
46036acebdeScsapuntz 	/* Let's ignore these in version 1 */
46136acebdeScsapuntz 	if (flags & 0x8) {
46236acebdeScsapuntz 		WDCDEBUG_PRINT(("MCR "), DEBUG_ERRORS);
463ab72a97eScsapuntz 		if (sense->flags == 0)
464ab72a97eScsapuntz 			xfer->error = XS_NOERROR;
46536acebdeScsapuntz 	}
46636acebdeScsapuntz 
46736acebdeScsapuntz 	WDCDEBUG_PRINT(("\n"), DEBUG_ERRORS);
468ab72a97eScsapuntz 	return (ret);
46936acebdeScsapuntz }
47036acebdeScsapuntz 
471c4071fd1Smillert int wdc_atapi_drive_selected(struct channel_softc *, int);
472ad0c6836Scsapuntz 
473ad0c6836Scsapuntz int
wdc_atapi_drive_selected(struct channel_softc * chp,int drive)474c28b5ab2Smatthew wdc_atapi_drive_selected(struct channel_softc *chp, int drive)
475ad0c6836Scsapuntz {
476ad0c6836Scsapuntz 	u_int8_t reg = CHP_READ_REG(chp, wdr_sdh);
477ad0c6836Scsapuntz 
47895c10403Scsapuntz 	WDC_LOG_REG(chp, wdr_sdh, reg);
47995c10403Scsapuntz 
480ad0c6836Scsapuntz 	return ((reg & 0x10) == (drive << 4));
481ad0c6836Scsapuntz }
48236acebdeScsapuntz 
483a24b05dfScsapuntz enum atapi_context {
484a24b05dfScsapuntz 	ctxt_process = 0,
485a24b05dfScsapuntz 	ctxt_timer = 1,
486a24b05dfScsapuntz 	ctxt_interrupt = 2
487a24b05dfScsapuntz };
488a24b05dfScsapuntz 
489c4071fd1Smillert void wdc_atapi_the_machine(struct channel_softc *, struct wdc_xfer *,
490c4071fd1Smillert     enum atapi_context);
491a24b05dfScsapuntz 
492c4071fd1Smillert void wdc_atapi_the_poll_machine(struct channel_softc *, struct wdc_xfer *);
493a24b05dfScsapuntz 
49436acebdeScsapuntz void
wdc_atapi_start(struct channel_softc * chp,struct wdc_xfer * xfer)495c28b5ab2Smatthew wdc_atapi_start(struct channel_softc *chp, struct wdc_xfer *xfer)
4967481efa2Scsapuntz {
497a24b05dfScsapuntz 	xfer->next = wdc_atapi_real_start;
498a24b05dfScsapuntz 
499a24b05dfScsapuntz 	wdc_atapi_the_machine(chp, xfer, ctxt_process);
500a24b05dfScsapuntz }
501a24b05dfScsapuntz 
502a24b05dfScsapuntz 
503a24b05dfScsapuntz void
wdc_atapi_timer_handler(void * arg)504c28b5ab2Smatthew wdc_atapi_timer_handler(void *arg)
505a24b05dfScsapuntz {
50654f23ac8Scsapuntz 	struct channel_softc *chp = arg;
50754f23ac8Scsapuntz 	struct wdc_xfer *xfer;
508a24b05dfScsapuntz 	int s;
509a24b05dfScsapuntz 
510a24b05dfScsapuntz 	s = splbio();
51154f23ac8Scsapuntz 	xfer = TAILQ_FIRST(&chp->ch_queue->sc_xfer);
51254f23ac8Scsapuntz 	if (xfer == NULL ||
51354f23ac8Scsapuntz 	    !timeout_triggered(&xfer->atapi_poll_to)) {
51454f23ac8Scsapuntz 		splx(s);
51554f23ac8Scsapuntz 		return;
51654f23ac8Scsapuntz 	}
51754f23ac8Scsapuntz 	xfer->c_flags &= ~C_POLL_MACHINE;
51854f23ac8Scsapuntz 	timeout_del(&xfer->atapi_poll_to);
519fb74e841Sniklas 	chp->ch_flags &= ~WDCF_IRQ_WAIT;
520a24b05dfScsapuntz 	wdc_atapi_the_machine(chp, xfer, ctxt_timer);
521a24b05dfScsapuntz 	splx(s);
522a24b05dfScsapuntz }
523a24b05dfScsapuntz 
524a24b05dfScsapuntz 
525a24b05dfScsapuntz int
wdc_atapi_intr(struct channel_softc * chp,struct wdc_xfer * xfer,int irq)526c28b5ab2Smatthew wdc_atapi_intr(struct channel_softc *chp, struct wdc_xfer *xfer, int irq)
527a24b05dfScsapuntz {
528b78a0069Scsapuntz 	timeout_del(&chp->ch_timo);
529b78a0069Scsapuntz 
530a24b05dfScsapuntz 	/* XXX we should consider an alternate signaling regime here */
531a24b05dfScsapuntz 	if (xfer->c_flags & C_TIMEOU) {
532a24b05dfScsapuntz 		xfer->c_flags &= ~C_TIMEOU;
5333f343521Scsapuntz 		wdc_atapi_the_machine(chp, xfer, ctxt_timer);
5343f343521Scsapuntz 		return (0);
535a24b05dfScsapuntz 	}
536a24b05dfScsapuntz 
5373f343521Scsapuntz 	wdc_atapi_the_machine(chp, xfer, ctxt_interrupt);
5383f343521Scsapuntz 
5393f343521Scsapuntz 	return (-1);
540a24b05dfScsapuntz }
541a24b05dfScsapuntz 
54254f23ac8Scsapuntz struct atapi_return_args {
54354f23ac8Scsapuntz 	int timeout;
54454f23ac8Scsapuntz 	int delay;
54554f23ac8Scsapuntz 	int expect_irq;
54654f23ac8Scsapuntz };
54754f23ac8Scsapuntz 
5483f343521Scsapuntz #define ARGS_INIT {-1, 0, 0}
549a24b05dfScsapuntz 
5503f343521Scsapuntz void
wdc_atapi_the_poll_machine(struct channel_softc * chp,struct wdc_xfer * xfer)551c28b5ab2Smatthew wdc_atapi_the_poll_machine(struct channel_softc *chp, struct wdc_xfer *xfer)
552a24b05dfScsapuntz {
55354f23ac8Scsapuntz 	int  idx = 0;
554e61c843cScsapuntz 	int  current_timeout = 10;
555a24b05dfScsapuntz 
556a24b05dfScsapuntz 
557a24b05dfScsapuntz 	while (1) {
55854f23ac8Scsapuntz 		struct atapi_return_args retargs = ARGS_INIT;
559a24b05dfScsapuntz 		idx++;
560a24b05dfScsapuntz 
56154f23ac8Scsapuntz 		(xfer->next)(chp, xfer, (current_timeout * 1000 <= idx),
56254f23ac8Scsapuntz 		    &retargs);
563a24b05dfScsapuntz 
56454f23ac8Scsapuntz 		if (xfer->next == NULL) {
565a24b05dfScsapuntz 			wdc_free_xfer(chp, xfer);
566a24b05dfScsapuntz 			wdcstart(chp);
5673f343521Scsapuntz 			return;
568a24b05dfScsapuntz 		}
56954f23ac8Scsapuntz 
57054f23ac8Scsapuntz 		if (retargs.timeout != -1) {
57154f23ac8Scsapuntz 			current_timeout = retargs.timeout;
57254f23ac8Scsapuntz 			idx = 0;
57354f23ac8Scsapuntz 		}
57454f23ac8Scsapuntz 
57554f23ac8Scsapuntz 		if (retargs.delay != 0) {
57654f23ac8Scsapuntz 			delay (1000 * retargs.delay);
57754f23ac8Scsapuntz 			idx += 1000 * retargs.delay;
57854f23ac8Scsapuntz 		}
57954f23ac8Scsapuntz 
58054f23ac8Scsapuntz 		DELAY(1);
581a24b05dfScsapuntz 	}
582a24b05dfScsapuntz }
583a24b05dfScsapuntz 
58454f23ac8Scsapuntz 
5853f343521Scsapuntz void
wdc_atapi_the_machine(struct channel_softc * chp,struct wdc_xfer * xfer,enum atapi_context ctxt)586c28b5ab2Smatthew wdc_atapi_the_machine(struct channel_softc *chp, struct wdc_xfer *xfer,
587c28b5ab2Smatthew     enum atapi_context ctxt)
588a24b05dfScsapuntz {
58954f23ac8Scsapuntz 	int idx = 0;
590ab72a97eScsapuntz 	extern int ticks;
591a24b05dfScsapuntz 	int timeout_delay = hz / 10;
592a24b05dfScsapuntz 
593a24b05dfScsapuntz 	if (xfer->c_flags & C_POLL) {
594e7f35eb1Sgrange 		wdc_disable_intr(chp);
595e7f35eb1Sgrange 
5961d4420b3Scsapuntz 		if (ctxt != ctxt_process) {
5971d4420b3Scsapuntz 			if (ctxt == ctxt_interrupt)
5981d4420b3Scsapuntz 				xfer->endticks = 1;
5991d4420b3Scsapuntz 
6003f343521Scsapuntz 			return;
6011d4420b3Scsapuntz 		}
602a24b05dfScsapuntz 
603a24b05dfScsapuntz 		wdc_atapi_the_poll_machine(chp, xfer);
6043f343521Scsapuntz 		return;
605a24b05dfScsapuntz 	}
606a24b05dfScsapuntz 
60754f23ac8Scsapuntz 	/* Don't go through more than 50 state machine steps
60854f23ac8Scsapuntz 	   before yielding. This tries to limit the amount of time
60954f23ac8Scsapuntz 	   spent at high SPL */
61054f23ac8Scsapuntz 	for (idx = 0; idx < 50; idx++) {
61154f23ac8Scsapuntz 		struct atapi_return_args retargs = ARGS_INIT;
612a24b05dfScsapuntz 
61354f23ac8Scsapuntz 		(xfer->next)(chp, xfer,
61454f23ac8Scsapuntz 		    xfer->endticks && (ticks - xfer->endticks >= 0),
61554f23ac8Scsapuntz 		    &retargs);
616a24b05dfScsapuntz 
61754f23ac8Scsapuntz 		if (retargs.timeout != -1)
618fb74e841Sniklas 			/*
61954f23ac8Scsapuntz 			 * Add 1 tick to compensate for the fact that we
62054f23ac8Scsapuntz 			 * can be just microseconds before the tick changes.
621fb74e841Sniklas 			 */
622fb74e841Sniklas 			xfer->endticks =
62354f23ac8Scsapuntz 			    max((retargs.timeout * hz) / 1000, 1) + 1 + ticks;
624a24b05dfScsapuntz 
62554f23ac8Scsapuntz 		if (xfer->next == NULL) {
626a24b05dfScsapuntz 			if (xfer->c_flags & C_POLL_MACHINE)
6279f5e13a3Sho 				timeout_del(&xfer->atapi_poll_to);
628a24b05dfScsapuntz 
629a24b05dfScsapuntz 			wdc_free_xfer(chp, xfer);
630a24b05dfScsapuntz 			wdcstart(chp);
631a24b05dfScsapuntz 
6323f343521Scsapuntz 			return;
633a24b05dfScsapuntz 		}
634a24b05dfScsapuntz 
63554f23ac8Scsapuntz 		if (retargs.expect_irq) {
636fee6ff40Sdrahn 			int timeout_period;
63754f23ac8Scsapuntz 			chp->ch_flags |= WDCF_IRQ_WAIT;
638fee6ff40Sdrahn 			timeout_period =  xfer->endticks - ticks;
639fee6ff40Sdrahn 			if (timeout_period < 1)
640fee6ff40Sdrahn 				timeout_period = 1;
641fee6ff40Sdrahn 			timeout_add(&chp->ch_timo, timeout_period);
6423f343521Scsapuntz 			return;
64354f23ac8Scsapuntz 		}
64454f23ac8Scsapuntz 
6451d4420b3Scsapuntz 		if (retargs.delay != 0) {
6461d4420b3Scsapuntz 			timeout_delay = max(retargs.delay * hz / 1000, 1);
64754f23ac8Scsapuntz 			break;
6481d4420b3Scsapuntz 		}
64954f23ac8Scsapuntz 
65054f23ac8Scsapuntz 		DELAY(1);
65154f23ac8Scsapuntz 	}
65254f23ac8Scsapuntz 
6539f5e13a3Sho 	timeout_add(&xfer->atapi_poll_to, timeout_delay);
654a24b05dfScsapuntz 	xfer->c_flags |= C_POLL_MACHINE;
655caa5053fScsapuntz 
6563f343521Scsapuntz 	return;
657a24b05dfScsapuntz }
658a24b05dfScsapuntz 
659a24b05dfScsapuntz 
660c4071fd1Smillert void wdc_atapi_update_status(struct channel_softc *);
661a24b05dfScsapuntz 
662a24b05dfScsapuntz void
wdc_atapi_update_status(struct channel_softc * chp)663c28b5ab2Smatthew wdc_atapi_update_status(struct channel_softc *chp)
664a24b05dfScsapuntz {
665a24b05dfScsapuntz 	chp->ch_status = CHP_READ_REG(chp, wdr_status);
666a24b05dfScsapuntz 
66795c10403Scsapuntz 	WDC_LOG_STATUS(chp, chp->ch_status);
66895c10403Scsapuntz 
669a24b05dfScsapuntz 	if (chp->ch_status == 0xff && (chp->ch_flags & WDCF_ONESLAVE)) {
67095c10403Scsapuntz 		wdc_set_drive(chp, 1);
671a24b05dfScsapuntz 
672a24b05dfScsapuntz 		chp->ch_status = CHP_READ_REG(chp, wdr_status);
67395c10403Scsapuntz 		WDC_LOG_STATUS(chp, chp->ch_status);
674a24b05dfScsapuntz 	}
675a24b05dfScsapuntz 
67695c10403Scsapuntz 	if ((chp->ch_status & (WDCS_BSY | WDCS_ERR)) == WDCS_ERR) {
677a24b05dfScsapuntz 		chp->ch_error = CHP_READ_REG(chp, wdr_error);
67895c10403Scsapuntz 		WDC_LOG_ERROR(chp, chp->ch_error);
67995c10403Scsapuntz 	}
680a24b05dfScsapuntz }
681a24b05dfScsapuntz 
68254f23ac8Scsapuntz void
wdc_atapi_real_start(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)683c28b5ab2Smatthew wdc_atapi_real_start(struct channel_softc *chp, struct wdc_xfer *xfer,
684c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
685a24b05dfScsapuntz {
686e61c843cScsapuntz #ifdef WDCDEBUG
6877481efa2Scsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
688e61c843cScsapuntz #endif
6897481efa2Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
6907481efa2Scsapuntz 
691bf07dfc9Scsapuntz 	/*
692bf07dfc9Scsapuntz 	 * Only set the DMA flag if the transfer is reasonably large.
693bf07dfc9Scsapuntz 	 * At least one older drive failed to complete a 4 byte DMA transfer.
694bf07dfc9Scsapuntz 	 */
695bf07dfc9Scsapuntz 
696bf07dfc9Scsapuntz 	/* Turn off DMA flag on REQUEST SENSE */
697bf07dfc9Scsapuntz 
6983f343521Scsapuntz 	if (!(xfer->c_flags & (C_POLL | C_SENSE | C_MEDIA_ACCESS)) &&
69936acebdeScsapuntz 	    (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) &&
700c2572690Scsapuntz 	    (xfer->c_bcount > 100))
7017481efa2Scsapuntz 		xfer->c_flags |= C_DMA;
7027481efa2Scsapuntz 	else
7037481efa2Scsapuntz 		xfer->c_flags &= ~C_DMA;
7049ec8daebSderaadt 
705a24b05dfScsapuntz 
70695c10403Scsapuntz 	wdc_set_drive(chp, xfer->drive);
707a24b05dfScsapuntz 
708a24b05dfScsapuntz 	DELAY(1);
709a24b05dfScsapuntz 
710a24b05dfScsapuntz 	xfer->next = wdc_atapi_real_start_2;
71154f23ac8Scsapuntz 	ret->timeout = ATAPI_DELAY;
712a24b05dfScsapuntz 
71320d226a3Sgrange 	WDCDEBUG_PRINT(("wdc_atapi_start %s:%d:%d, scsi flags 0x%x, "
71420d226a3Sgrange 	    "ATA flags 0x%x\n",
7153f343521Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive,
7163f343521Scsapuntz 	    sc_xfer->flags, xfer->c_flags), DEBUG_XFERS);
7173f343521Scsapuntz 
7183f343521Scsapuntz 
71954f23ac8Scsapuntz 	return;
720a24b05dfScsapuntz }
721a24b05dfScsapuntz 
722a24b05dfScsapuntz 
72354f23ac8Scsapuntz void
wdc_atapi_real_start_2(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)724c28b5ab2Smatthew wdc_atapi_real_start_2(struct channel_softc *chp, struct wdc_xfer *xfer,
725c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
726a24b05dfScsapuntz {
727a24b05dfScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
728a24b05dfScsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
729a24b05dfScsapuntz 
730a24b05dfScsapuntz 	if (timeout) {
7319ec8daebSderaadt 		printf("wdc_atapi_start: not ready, st = %02x\n",
7329ec8daebSderaadt 		    chp->ch_status);
733a24b05dfScsapuntz 
7349ec8daebSderaadt 		sc_xfer->error = XS_TIMEOUT;
735a24b05dfScsapuntz 		xfer->next = wdc_atapi_reset;
73654f23ac8Scsapuntz 		return;
737a24b05dfScsapuntz 	} else {
738a24b05dfScsapuntz 		wdc_atapi_update_status(chp);
739a24b05dfScsapuntz 
74066bb6343Scsapuntz 		if (chp->ch_status & (WDCS_BSY | WDCS_DRQ))
74154f23ac8Scsapuntz 			return;
7429ec8daebSderaadt 	}
7439ec8daebSderaadt 
7447481efa2Scsapuntz 	/* Do control operations specially. */
74566bb6343Scsapuntz 	if (drvp->state < ATAPI_READY_STATE) {
746a24b05dfScsapuntz 		xfer->next = wdc_atapi_ctrl;
74754f23ac8Scsapuntz 		return;
7487481efa2Scsapuntz 	}
749a077a3e9Scsapuntz 
750a24b05dfScsapuntz 	xfer->next = wdc_atapi_send_packet;
75154f23ac8Scsapuntz 	return;
752ec62f1edSniklas }
753a24b05dfScsapuntz 
754a24b05dfScsapuntz 
75554f23ac8Scsapuntz void
wdc_atapi_send_packet(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)756c28b5ab2Smatthew wdc_atapi_send_packet(struct channel_softc *chp, struct wdc_xfer *xfer,
757c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
758a24b05dfScsapuntz {
759a24b05dfScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
760a24b05dfScsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
7613f343521Scsapuntz 
7627481efa2Scsapuntz 	/*
763775cc6c0Ssobrado 	 * Even with WDCS_ERR, the device should accept a command packet.
7647481efa2Scsapuntz 	 * Limit length to what can be stuffed into the cylinder register
7657481efa2Scsapuntz 	 * (16 bits).  Some CD-ROMs seem to interpret '0' as 65536,
7667481efa2Scsapuntz 	 * but not all devices do that and it's not obvious from the
767a2fd75e8Stom 	 * ATAPI spec that this behaviour should be expected.  If more
7687481efa2Scsapuntz 	 * data is necessary, multiple data transfer phases will be done.
7697481efa2Scsapuntz 	 */
7707481efa2Scsapuntz 
7717481efa2Scsapuntz 	wdccommand(chp, xfer->drive, ATAPI_PKT_CMD,
7729b25e7c1Scsapuntz 	    xfer->c_bcount <= 0xfffe ? xfer->c_bcount : 0xfffe,
7737481efa2Scsapuntz 	    0, 0, 0,
7747481efa2Scsapuntz 	    (xfer->c_flags & C_DMA) ? ATAPI_PKT_CMD_FTRE_DMA : 0);
7757481efa2Scsapuntz 
776ec8802a4Scsapuntz 	if (xfer->c_flags & C_DMA)
777ec8802a4Scsapuntz 		drvp->n_xfers++;
7789ec8daebSderaadt 
7797481efa2Scsapuntz 	DELAY(1);
7809ec8daebSderaadt 
7813f343521Scsapuntz 	xfer->next = wdc_atapi_intr_command;
78254f23ac8Scsapuntz 	ret->timeout = sc_xfer->timeout;
783a24b05dfScsapuntz 
784a24b05dfScsapuntz 	if ((drvp->atapi_cap & ATAPI_CFG_DRQ_MASK) == ATAPI_CFG_IRQ_DRQ) {
785a24b05dfScsapuntz 		/* We expect an IRQ to tell us of the next state */
78654f23ac8Scsapuntz 		ret->expect_irq = 1;
7877481efa2Scsapuntz 	}
7883f343521Scsapuntz 
7893f343521Scsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_send_packet %s:%d:%d command sent\n",
7903f343521Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive
7913f343521Scsapuntz 	    ), DEBUG_XFERS);
79254f23ac8Scsapuntz 	return;
7937481efa2Scsapuntz }
7947481efa2Scsapuntz 
79554f23ac8Scsapuntz void
wdc_atapi_intr_command(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)796c28b5ab2Smatthew wdc_atapi_intr_command(struct channel_softc *chp, struct wdc_xfer *xfer,
797c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
7989ec8daebSderaadt {
7999ec8daebSderaadt 	struct scsi_xfer *sc_xfer = xfer->cmd;
8009ec8daebSderaadt 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
8010b29cb40Skrw 	struct atapiscsi_softc *as = sc_xfer->sc_link->bus->sb_adapter_softc;
8024b7fd08fScsapuntz 	int i;
8039ec8daebSderaadt 	u_int8_t cmd[16];
8049ec8daebSderaadt 	struct scsi_sense *cmd_reqsense;
8059ec8daebSderaadt 	int cmdlen = (drvp->atapi_cap & ACAP_LEN) ? 16 : 12;
806caa5053fScsapuntz 	int dma_flags = ((sc_xfer->flags & SCSI_DATA_IN) ||
807caa5053fScsapuntz 	    (xfer->c_flags & C_SENSE)) ?  WDC_DMA_READ : 0;
8089ec8daebSderaadt 
8093f343521Scsapuntz 	wdc_atapi_update_status(chp);
8103f343521Scsapuntz 
8113f343521Scsapuntz 	if ((chp->ch_status & WDCS_BSY) || !(chp->ch_status & WDCS_DRQ)) {
8123f343521Scsapuntz 		if (timeout)
8133f343521Scsapuntz 			goto timeout;
8143f343521Scsapuntz 
8153f343521Scsapuntz 		return;
8163f343521Scsapuntz 	}
8173f343521Scsapuntz 
8183f343521Scsapuntz 	if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
8193f343521Scsapuntz 		chp->wdc->irqack(chp);
8203f343521Scsapuntz 
8219ec8daebSderaadt 	bzero(cmd, sizeof(cmd));
8229ec8daebSderaadt 
8239ec8daebSderaadt 	if (xfer->c_flags & C_SENSE) {
8249ec8daebSderaadt 		cmd_reqsense = (struct scsi_sense *)&cmd[0];
8259ec8daebSderaadt 		cmd_reqsense->opcode = REQUEST_SENSE;
8269ec8daebSderaadt 		cmd_reqsense->length = xfer->c_bcount;
8279ec8daebSderaadt 	} else
828664c6166Skrw 		bcopy(&sc_xfer->cmd, cmd, sc_xfer->cmdlen);
8299ec8daebSderaadt 
83095c10403Scsapuntz 	WDC_LOG_ATAPI_CMD(chp, xfer->drive, xfer->c_flags,
8316e645156Scsapuntz 	    cmdlen, cmd);
83295c10403Scsapuntz 
8339ec8daebSderaadt 	for (i = 0; i < 12; i++)
8349ec8daebSderaadt 		WDCDEBUG_PRINT(("%02x ", cmd[i]), DEBUG_INTR);
8359ec8daebSderaadt 	WDCDEBUG_PRINT((": PHASE_CMDOUT\n"), DEBUG_INTR);
8369ec8daebSderaadt 
8379ec8daebSderaadt 	/* Init the DMA channel if necessary */
8389ec8daebSderaadt 	if (xfer->c_flags & C_DMA) {
8399ec8daebSderaadt 		if ((*chp->wdc->dma_init)(chp->wdc->dma_arg,
8409ec8daebSderaadt 		    chp->channel, xfer->drive, xfer->databuf,
8419ec8daebSderaadt 		    xfer->c_bcount, dma_flags) != 0) {
8429ec8daebSderaadt 			sc_xfer->error = XS_DRIVER_STUFFUP;
843a24b05dfScsapuntz 
8443f343521Scsapuntz 			xfer->next = wdc_atapi_reset;
84554f23ac8Scsapuntz 			return;
8469ec8daebSderaadt 		}
8479ec8daebSderaadt 	}
8489ec8daebSderaadt 
8499ec8daebSderaadt 	wdc_output_bytes(drvp, cmd, cmdlen);
8509ec8daebSderaadt 
8519ec8daebSderaadt 	/* Start the DMA channel if necessary */
8529ec8daebSderaadt 	if (xfer->c_flags & C_DMA) {
8539ec8daebSderaadt 		(*chp->wdc->dma_start)(chp->wdc->dma_arg,
854caa5053fScsapuntz 		    chp->channel, xfer->drive);
8553f343521Scsapuntz 		xfer->next = wdc_atapi_intr_complete;
8563f343521Scsapuntz 	} else {
8573f343521Scsapuntz 		if (xfer->c_bcount == 0)
8580c6728ddScsapuntz 			as->protocol_phase = as_completed;
8590c6728ddScsapuntz 		else
8600c6728ddScsapuntz 			as->protocol_phase = as_data;
8610c6728ddScsapuntz 
8623f343521Scsapuntz 		xfer->next = wdc_atapi_pio_intr;
8633f343521Scsapuntz 	}
8643f343521Scsapuntz 
86554f23ac8Scsapuntz 	ret->expect_irq = 1;
8669ec8daebSderaadt 
8679ec8daebSderaadt 	/* If we read/write to a tape we will get into buffer
8689ec8daebSderaadt 	   availability mode.  */
8699ec8daebSderaadt 	if (drvp->atapi_cap & ACAP_DSC) {
870664c6166Skrw 		if ((sc_xfer->cmd.opcode == READ ||
871664c6166Skrw 		       sc_xfer->cmd.opcode == WRITE)) {
8729ec8daebSderaadt 			drvp->drive_flags |= DRIVE_DSCBA;
8739ec8daebSderaadt 			WDCDEBUG_PRINT(("set DSCBA\n"), DEBUG_DSC);
874a24b05dfScsapuntz 		} else if ((xfer->c_flags & C_MEDIA_ACCESS) &&
875a24b05dfScsapuntz 		    (drvp->drive_flags & DRIVE_DSCBA)) {
876a24b05dfScsapuntz 			/* Clause 3.2.4 of QIC-157 D.
8774b7fd08fScsapuntz 
878a24b05dfScsapuntz 			   Any media access command other than read or
879a24b05dfScsapuntz 			   write will switch DSC back to completion
880a24b05dfScsapuntz 			   mode */
881a24b05dfScsapuntz 			drvp->drive_flags &= ~DRIVE_DSCBA;
882a24b05dfScsapuntz 			WDCDEBUG_PRINT(("clear DCSBA\n"), DEBUG_DSC);
8839ec8daebSderaadt 		}
884a24b05dfScsapuntz 	}
885a24b05dfScsapuntz 
88654f23ac8Scsapuntz 	return;
8873f343521Scsapuntz 
8883f343521Scsapuntz  timeout:
8893f343521Scsapuntz 	printf ("%s:%d:%d: device timeout waiting to send SCSI packet\n",
8903f343521Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive);
8913f343521Scsapuntz 
8923f343521Scsapuntz 	sc_xfer->error = XS_TIMEOUT;
8933f343521Scsapuntz 	xfer->next = wdc_atapi_reset;
8943f343521Scsapuntz 	return;
895a24b05dfScsapuntz }
896a24b05dfScsapuntz 
8979ec8daebSderaadt 
898e61c843cScsapuntz char *
wdc_atapi_in_data_phase(struct wdc_xfer * xfer,int len,int ire)899c28b5ab2Smatthew wdc_atapi_in_data_phase(struct wdc_xfer *xfer, int len, int ire)
9004b7fd08fScsapuntz {
9014b7fd08fScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
9020b29cb40Skrw 	struct atapiscsi_softc *as = sc_xfer->sc_link->bus->sb_adapter_softc;
903a24b05dfScsapuntz 	char *message;
9049ec8daebSderaadt 
905e61c843cScsapuntz 	if (as->protocol_phase != as_data) {
906e61c843cScsapuntz 		message = "unexpected data phase";
907e61c843cScsapuntz 		goto unexpected_state;
908e61c843cScsapuntz 	}
909e61c843cScsapuntz 
9109ec8daebSderaadt 	if (ire & WDCI_CMD) {
9114b7fd08fScsapuntz 		message = "unexpectedly in command phase";
9124b7fd08fScsapuntz 		goto unexpected_state;
9139ec8daebSderaadt 	}
9144b7fd08fScsapuntz 
9154b7fd08fScsapuntz 	if (!(xfer->c_flags & C_SENSE)) {
9164b7fd08fScsapuntz 		if (!(sc_xfer->flags & (SCSI_DATA_IN | SCSI_DATA_OUT))) {
9174b7fd08fScsapuntz 			message = "data phase where none expected";
9184b7fd08fScsapuntz 			goto unexpected_state;
9199ec8daebSderaadt 		}
9209ec8daebSderaadt 
9219ec8daebSderaadt 		/* Make sure polarities match */
9229ec8daebSderaadt 		if (((ire & WDCI_IN) == WDCI_IN) ==
9239ec8daebSderaadt 		    ((sc_xfer->flags & SCSI_DATA_OUT) == SCSI_DATA_OUT)) {
9244b7fd08fScsapuntz 			message = "data transfer direction disagreement";
9254b7fd08fScsapuntz 			goto unexpected_state;
9269ec8daebSderaadt 		}
9274b7fd08fScsapuntz 	} else {
9284b7fd08fScsapuntz 		if (!(ire & WDCI_IN)) {
9294b7fd08fScsapuntz 			message = "data transfer direction disagreement during sense";
9304b7fd08fScsapuntz 			goto unexpected_state;
9319ec8daebSderaadt 		}
9324b7fd08fScsapuntz 	}
9339ec8daebSderaadt 
934fd3c9972Scsapuntz 	if (len == 0) {
9354b7fd08fScsapuntz 		message = "zero length transfer requested in data phase";
9364b7fd08fScsapuntz 		goto unexpected_state;
937fd3c9972Scsapuntz 	}
9389ec8daebSderaadt 
939a24b05dfScsapuntz 
940e61c843cScsapuntz 	return (0);
941a24b05dfScsapuntz 
942a24b05dfScsapuntz  unexpected_state:
943a24b05dfScsapuntz 
944e61c843cScsapuntz 	return (message);
945a24b05dfScsapuntz }
946a24b05dfScsapuntz 
94754f23ac8Scsapuntz void
wdc_atapi_intr_data(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)948c28b5ab2Smatthew wdc_atapi_intr_data(struct channel_softc *chp, struct wdc_xfer *xfer,
949c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
950a24b05dfScsapuntz {
951a24b05dfScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
952a24b05dfScsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
953a24b05dfScsapuntz 	int len, ire;
954e61c843cScsapuntz 	char *message;
955fea489f6Sgrange 	int tohost;
956a24b05dfScsapuntz 
957a24b05dfScsapuntz 	len = (CHP_READ_REG(chp, wdr_cyl_hi) << 8) |
958a24b05dfScsapuntz 	    CHP_READ_REG(chp, wdr_cyl_lo);
95995c10403Scsapuntz 	WDC_LOG_REG(chp, wdr_cyl_lo, len);
96095c10403Scsapuntz 
961a24b05dfScsapuntz 	ire = CHP_READ_REG(chp, wdr_ireason);
96295c10403Scsapuntz 	WDC_LOG_REG(chp, wdr_ireason, ire);
963a24b05dfScsapuntz 
964e61c843cScsapuntz 	if ((message = wdc_atapi_in_data_phase(xfer, len, ire))) {
9651d2f5473Scsapuntz 		/* The drive has dropped BSY before setting up the
9661d2f5473Scsapuntz 		   registers correctly for DATA phase. This drive is
9671d2f5473Scsapuntz 		   not compliant with ATA/ATAPI-4.
9681d2f5473Scsapuntz 
9691d2f5473Scsapuntz 		   Give the drive 100ms to get its house in order
9701d2f5473Scsapuntz 		   before we try again.  */
9713f343521Scsapuntz 		WDCDEBUG_PRINT(("wdc_atapi_intr: %s\n", message),
9723f343521Scsapuntz 		    DEBUG_ERRORS);
9733f343521Scsapuntz 
9741d2f5473Scsapuntz 		if (!timeout) {
97554f23ac8Scsapuntz 			ret->delay = 100;
97654f23ac8Scsapuntz 			return;
977e61c843cScsapuntz 		}
9781d2f5473Scsapuntz 	}
979e61c843cScsapuntz 
980fea489f6Sgrange 	tohost = ((sc_xfer->flags & SCSI_DATA_IN) != 0 ||
981fea489f6Sgrange 	    (xfer->c_flags & C_SENSE) != 0);
982a24b05dfScsapuntz 
9839ec8daebSderaadt 	if (xfer->c_bcount >= len) {
984ab72a97eScsapuntz 		WDCDEBUG_PRINT(("wdc_atapi_intr: c_bcount %d len %d "
98520d226a3Sgrange 		    "st 0x%b err 0x%x "
986a24b05dfScsapuntz 		    "ire 0x%x\n", xfer->c_bcount,
98720d226a3Sgrange 		    len, chp->ch_status, WDCS_BITS, chp->ch_error, ire),
98820d226a3Sgrange 		    DEBUG_INTR);
989a24b05dfScsapuntz 
9909ec8daebSderaadt 		/* Common case */
991fea489f6Sgrange 		if (!tohost)
9929ec8daebSderaadt 			wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf +
9939ec8daebSderaadt 			    xfer->c_skip, len);
9949ec8daebSderaadt 		else
9959ec8daebSderaadt 			wdc_input_bytes(drvp, (u_int8_t *)xfer->databuf +
9969ec8daebSderaadt 			    xfer->c_skip, len);
9979ec8daebSderaadt 
9989ec8daebSderaadt 		xfer->c_skip += len;
9999ec8daebSderaadt 		xfer->c_bcount -= len;
10009ec8daebSderaadt 	} else {
10010c6728ddScsapuntz 		/* Exceptional case - drive want to transfer more
10020c6728ddScsapuntz 		   data than we have buffer for */
1003fea489f6Sgrange 		if (!tohost) {
10040c6728ddScsapuntz 			/* Wouldn't it be better to just abort here rather
10050c6728ddScsapuntz 			   than to write random stuff to drive? */
10060c6728ddScsapuntz 			printf("wdc_atapi_intr: warning: device requesting "
10070c6728ddScsapuntz 			    "%d bytes, only %d left in buffer\n", len, xfer->c_bcount);
10089ec8daebSderaadt 
10099ec8daebSderaadt 			wdc_output_bytes(drvp, (u_int8_t *)xfer->databuf +
10109ec8daebSderaadt 			    xfer->c_skip, xfer->c_bcount);
10119ec8daebSderaadt 
10124b7fd08fScsapuntz 			CHP_WRITE_RAW_MULTI_2(chp, NULL,
10134b7fd08fScsapuntz 			    len - xfer->c_bcount);
10149ec8daebSderaadt 		} else {
10159ec8daebSderaadt 			printf("wdc_atapi_intr: warning: reading only "
10169ec8daebSderaadt 			    "%d of %d bytes\n", xfer->c_bcount, len);
10179ec8daebSderaadt 
10189ec8daebSderaadt 			wdc_input_bytes(drvp,
10199ec8daebSderaadt 			    (char *)xfer->databuf + xfer->c_skip,
10209ec8daebSderaadt 			    xfer->c_bcount);
10219ec8daebSderaadt 			wdcbit_bucket(chp, len - xfer->c_bcount);
10229ec8daebSderaadt 		}
10239ec8daebSderaadt 
10249ec8daebSderaadt 		xfer->c_skip += xfer->c_bcount;
10259ec8daebSderaadt 		xfer->c_bcount = 0;
10269ec8daebSderaadt 	}
10279ec8daebSderaadt 
102854f23ac8Scsapuntz 	ret->expect_irq = 1;
10293f343521Scsapuntz 	xfer->next = wdc_atapi_pio_intr;
10309ec8daebSderaadt 
103154f23ac8Scsapuntz 	return;
10329ec8daebSderaadt }
10339ec8daebSderaadt 
103454f23ac8Scsapuntz void
wdc_atapi_intr_complete(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1035c28b5ab2Smatthew wdc_atapi_intr_complete(struct channel_softc *chp, struct wdc_xfer *xfer,
1036c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
10377481efa2Scsapuntz {
10387481efa2Scsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
10397481efa2Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
10400b29cb40Skrw 	struct atapiscsi_softc *as = sc_xfer->sc_link->bus->sb_adapter_softc;
10419ec8daebSderaadt 
10429ec8daebSderaadt 	WDCDEBUG_PRINT(("PHASE_COMPLETED\n"), DEBUG_INTR);
10439ec8daebSderaadt 
10440c6728ddScsapuntz 	if (xfer->c_flags & C_DMA) {
10453f343521Scsapuntz 		int retry;
10463f343521Scsapuntz 
10473f343521Scsapuntz 		if (timeout) {
10483f343521Scsapuntz 			sc_xfer->error = XS_TIMEOUT;
1049ec8802a4Scsapuntz 			ata_dmaerr(drvp);
10503f343521Scsapuntz 
10513f343521Scsapuntz 			xfer->next = wdc_atapi_reset;
10523f343521Scsapuntz 			return;
10533f343521Scsapuntz 		}
10543f343521Scsapuntz 
10553f343521Scsapuntz 		for (retry = 5; retry > 0; retry--) {
10563f343521Scsapuntz 			wdc_atapi_update_status(chp);
10573f343521Scsapuntz 			if ((chp->ch_status & (WDCS_BSY | WDCS_DRQ)) == 0)
10583f343521Scsapuntz 				break;
10593f343521Scsapuntz 			DELAY(5);
10603f343521Scsapuntz 		}
10613f343521Scsapuntz 		if (retry == 0) {
10623f343521Scsapuntz 			ret->expect_irq = 1;
10633f343521Scsapuntz 			return;
10643f343521Scsapuntz 		}
10653f343521Scsapuntz 
10663f343521Scsapuntz 		chp->wdc->dma_status =
10673f343521Scsapuntz 		    (*chp->wdc->dma_finish)
10683f343521Scsapuntz 		    (chp->wdc->dma_arg, chp->channel,
106914235d69Sgrange 			xfer->drive, 1);
10709ec8daebSderaadt 
1071caa5053fScsapuntz 		if (chp->wdc->dma_status & WDC_DMAST_UNDER)
1072caa5053fScsapuntz 			xfer->c_bcount = 1;
1073caa5053fScsapuntz 		else
1074a24b05dfScsapuntz 			xfer->c_bcount = 0;
10759ec8daebSderaadt 	}
10769ec8daebSderaadt 
10779ec8daebSderaadt 	as->protocol_phase = as_none;
10789ec8daebSderaadt 
10799ec8daebSderaadt 	if (xfer->c_flags & C_SENSE) {
10809ec8daebSderaadt 		if (chp->ch_status & WDCS_ERR) {
10819ec8daebSderaadt 			if (chp->ch_error & WDCE_ABRT) {
10827481efa2Scsapuntz 				WDCDEBUG_PRINT(("wdc_atapi_intr: request_sense aborted, "
10837481efa2Scsapuntz 						"calling wdc_atapi_done()"
10847481efa2Scsapuntz 					), DEBUG_INTR);
1085a24b05dfScsapuntz 				xfer->next = wdc_atapi_done;
108654f23ac8Scsapuntz 				return;
10877481efa2Scsapuntz 			}
10887481efa2Scsapuntz 
10897481efa2Scsapuntz 			/*
109059a52720Sjmc 			 * request sense failed ! it's not supposed
10917481efa2Scsapuntz  			 * to be possible
10927481efa2Scsapuntz 			 */
109366bb6343Scsapuntz 			sc_xfer->error = XS_SHORTSENSE;
10949ec8daebSderaadt 		} else if (xfer->c_bcount < sizeof(sc_xfer->sense)) {
10957481efa2Scsapuntz 			/* use the sense we just read */
10967481efa2Scsapuntz 			sc_xfer->error = XS_SENSE;
10977481efa2Scsapuntz 		} else {
10987481efa2Scsapuntz 			/*
10997481efa2Scsapuntz 			 * command completed, but no data was read.
110059a52720Sjmc 			 * use the short sense we saved previously.
11017481efa2Scsapuntz 			 */
11027481efa2Scsapuntz 			sc_xfer->error = XS_SHORTSENSE;
11037481efa2Scsapuntz 		}
11047481efa2Scsapuntz 	} else {
11057481efa2Scsapuntz 		sc_xfer->resid = xfer->c_bcount;
11067481efa2Scsapuntz 		if (chp->ch_status & WDCS_ERR) {
1107ab72a97eScsapuntz 			if (!atapi_to_scsi_sense(sc_xfer, chp->ch_error) &&
1108ab72a97eScsapuntz 			    (sc_xfer->sc_link->quirks &
110976fd3256Scsapuntz 			     ADEV_NOSENSE) == 0) {
11107481efa2Scsapuntz 				/*
11117481efa2Scsapuntz 				 * let the driver issue a
11127481efa2Scsapuntz 				 * 'request sense'
11137481efa2Scsapuntz 				 */
11147481efa2Scsapuntz 				xfer->databuf = &sc_xfer->sense;
1115a24b05dfScsapuntz 				xfer->c_bcount = sizeof(sc_xfer->sense);
11167481efa2Scsapuntz 				xfer->c_skip = 0;
1117a24b05dfScsapuntz 				xfer->c_done = NULL;
11187481efa2Scsapuntz 				xfer->c_flags |= C_SENSE;
1119a24b05dfScsapuntz 				xfer->next = wdc_atapi_real_start;
112054f23ac8Scsapuntz 				return;
11217481efa2Scsapuntz 			}
11229ec8daebSderaadt 		}
11239ec8daebSderaadt 	}
11249ec8daebSderaadt 
11254ba73d8bScsapuntz         if ((xfer->c_flags & C_DMA) &&
11264ba73d8bScsapuntz 	    (chp->wdc->dma_status & ~WDC_DMAST_UNDER)) {
1127ec8802a4Scsapuntz 		ata_dmaerr(drvp);
11287481efa2Scsapuntz 		sc_xfer->error = XS_RESET;
1129a24b05dfScsapuntz 
1130a24b05dfScsapuntz 		xfer->next = wdc_atapi_reset;
113154f23ac8Scsapuntz 		return;
11327481efa2Scsapuntz 	}
11339ec8daebSderaadt 
11349ec8daebSderaadt 
11357481efa2Scsapuntz 	if (xfer->c_bcount != 0) {
11367481efa2Scsapuntz 		WDCDEBUG_PRINT(("wdc_atapi_intr: bcount value is "
11377481efa2Scsapuntz 				"%d after io\n", xfer->c_bcount), DEBUG_XFERS);
11387481efa2Scsapuntz 	}
11397481efa2Scsapuntz #ifdef DIAGNOSTIC
11407481efa2Scsapuntz 	if (xfer->c_bcount < 0) {
11417481efa2Scsapuntz 		printf("wdc_atapi_intr warning: bcount value "
11427481efa2Scsapuntz 		       "is %d after io\n", xfer->c_bcount);
11437481efa2Scsapuntz 	}
11447481efa2Scsapuntz #endif
11457481efa2Scsapuntz 
11467481efa2Scsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_intr: wdc_atapi_done() (end), error 0x%x "
11477481efa2Scsapuntz 			"\n", sc_xfer->error),
11487481efa2Scsapuntz 		       DEBUG_INTR);
1149a24b05dfScsapuntz 
1150a24b05dfScsapuntz 
1151a24b05dfScsapuntz 	if (xfer->c_done)
1152a24b05dfScsapuntz 		xfer->next = xfer->c_done;
1153a24b05dfScsapuntz 	else
1154a24b05dfScsapuntz 		xfer->next = wdc_atapi_done;
1155a24b05dfScsapuntz 
115654f23ac8Scsapuntz 	return;
11577481efa2Scsapuntz }
11587481efa2Scsapuntz 
115954f23ac8Scsapuntz void
wdc_atapi_pio_intr(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1160c28b5ab2Smatthew wdc_atapi_pio_intr(struct channel_softc *chp, struct wdc_xfer *xfer,
1161c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
11624b7fd08fScsapuntz {
11634b7fd08fScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
11640b29cb40Skrw 	struct atapiscsi_softc *as = sc_xfer->sc_link->bus->sb_adapter_softc;
11650c6728ddScsapuntz 	u_int8_t ireason;
11663f343521Scsapuntz 
1167ab72a97eScsapuntz 	wdc_atapi_update_status(chp);
1168ab72a97eScsapuntz 
1169caa5053fScsapuntz 	if (chp->ch_status & WDCS_BSY) {
1170caa5053fScsapuntz 		if (timeout)
1171caa5053fScsapuntz 			goto timeout;
1172a24b05dfScsapuntz 
117354f23ac8Scsapuntz 		return;
1174e61c843cScsapuntz 	}
1175e61c843cScsapuntz 
117695c10403Scsapuntz 	if (!wdc_atapi_drive_selected(chp, xfer->drive)) {
11773f343521Scsapuntz 		WDCDEBUG_PRINT(("wdc_atapi_intr_for_us: wrong drive selected\n"), DEBUG_INTR);
117895c10403Scsapuntz 		wdc_set_drive(chp, xfer->drive);
1179ad0c6836Scsapuntz 		delay (1);
1180ad0c6836Scsapuntz 
1181caa5053fScsapuntz 		if (!timeout)
118254f23ac8Scsapuntz 			return;
1183ad0c6836Scsapuntz 	}
1184ad0c6836Scsapuntz 
11853f343521Scsapuntz 	if ((xfer->c_flags & C_MEDIA_ACCESS) &&
11860c6728ddScsapuntz 	    !(chp->ch_status & (WDCS_DSC | WDCS_DRQ))) {
1187caa5053fScsapuntz 		if (timeout)
1188caa5053fScsapuntz 			goto timeout;
1189caa5053fScsapuntz 
119054f23ac8Scsapuntz 		ret->delay = 100;
119154f23ac8Scsapuntz 		return;
1192a24b05dfScsapuntz 	}
1193a24b05dfScsapuntz 
11943f343521Scsapuntz 	if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
11953f343521Scsapuntz 		chp->wdc->irqack(chp);
1196a24b05dfScsapuntz 
11970c6728ddScsapuntz 	ireason = CHP_READ_REG(chp, wdr_ireason);
119895c10403Scsapuntz 	WDC_LOG_REG(chp, wdr_ireason, ireason);
119995c10403Scsapuntz 
120020d226a3Sgrange 	WDCDEBUG_PRINT(("Phase %d, (0x%b, 0x%x) ", as->protocol_phase,
120120d226a3Sgrange 	    chp->ch_status, WDCS_BITS, ireason), DEBUG_INTR );
12020c6728ddScsapuntz 
12030c6728ddScsapuntz 	switch (as->protocol_phase) {
12040c6728ddScsapuntz 	case as_data:
12050c6728ddScsapuntz 		if ((chp->ch_status & WDCS_DRQ) ||
120654f23ac8Scsapuntz 		    (ireason & 3) != 3) {
1207caa5053fScsapuntz 			if (timeout)
1208caa5053fScsapuntz 				goto timeout;
1209caa5053fScsapuntz 
121054f23ac8Scsapuntz 			wdc_atapi_intr_data(chp, xfer, timeout, ret);
121154f23ac8Scsapuntz 			return;
121254f23ac8Scsapuntz 		}
12130c6728ddScsapuntz 
1214e7ff6ceeSmpi 		wdc_atapi_intr_complete(chp, xfer, timeout, ret);
12156836a844Smestre 		return;
12166836a844Smestre 
12170c6728ddScsapuntz 	case as_completed:
12180c6728ddScsapuntz 		if ((chp->ch_status & WDCS_DRQ) ||
12190c6728ddScsapuntz 		    (ireason & 3) != 3) {
1220caa5053fScsapuntz 			if (timeout)
1221caa5053fScsapuntz 				goto timeout;
1222caa5053fScsapuntz 
122354f23ac8Scsapuntz 			ret->delay = 100;
122454f23ac8Scsapuntz 			return;
1225a24b05dfScsapuntz 		}
1226a24b05dfScsapuntz 
122754f23ac8Scsapuntz 		wdc_atapi_intr_complete(chp, xfer, timeout, ret);
122854f23ac8Scsapuntz 		return;
12290c6728ddScsapuntz 
12300c6728ddScsapuntz 	default:
12310c6728ddScsapuntz 		printf ("atapiscsi: Shouldn't get here\n");
12320c6728ddScsapuntz 		sc_xfer->error = XS_DRIVER_STUFFUP;
12330c6728ddScsapuntz 		xfer->next = wdc_atapi_reset;
123454f23ac8Scsapuntz 		return;
12350c6728ddScsapuntz 	}
1236caa5053fScsapuntz 
1237caa5053fScsapuntz 	return;
1238caa5053fScsapuntz timeout:
123966bb6343Scsapuntz 	ireason = CHP_READ_REG(chp, wdr_ireason);
124095c10403Scsapuntz 	WDC_LOG_REG(chp, wdr_ireason, ireason);
124166bb6343Scsapuntz 
12423f343521Scsapuntz 	printf("%s:%d:%d: device timeout, c_bcount=%d, c_skip=%d, "
124320d226a3Sgrange 	    "status=0x%b, ireason=0x%x\n",
12443f343521Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
124520d226a3Sgrange 	    xfer->c_bcount, xfer->c_skip, chp->ch_status, WDCS_BITS, ireason);
1246caa5053fScsapuntz 
1247caa5053fScsapuntz 	sc_xfer->error = XS_TIMEOUT;
1248caa5053fScsapuntz 	xfer->next = wdc_atapi_reset;
1249caa5053fScsapuntz 	return;
12504b7fd08fScsapuntz }
12514b7fd08fScsapuntz 
125254f23ac8Scsapuntz void
wdc_atapi_ctrl(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1253c28b5ab2Smatthew wdc_atapi_ctrl(struct channel_softc *chp, struct wdc_xfer *xfer,
1254c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
12557481efa2Scsapuntz {
12567481efa2Scsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
12577481efa2Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
12587481efa2Scsapuntz 	char *errstring = NULL;
12597481efa2Scsapuntz 
1260a24b05dfScsapuntz  	wdc_atapi_update_status(chp);
1261a24b05dfScsapuntz 
12622702c783Scsapuntz 	if (!timeout) {
12632702c783Scsapuntz 		switch (drvp->state) {
12642702c783Scsapuntz 		case ATAPI_IDENTIFY_WAIT_STATE:
12652702c783Scsapuntz 			if (chp->ch_status & WDCS_BSY)
126654f23ac8Scsapuntz 				return;
12672702c783Scsapuntz 			break;
12682702c783Scsapuntz 		default:
12692702c783Scsapuntz 			if (chp->ch_status & (WDCS_BSY | WDCS_DRQ))
12702702c783Scsapuntz 				return;
12712702c783Scsapuntz 			break;
12722702c783Scsapuntz 		}
12732702c783Scsapuntz 	}
1274a24b05dfScsapuntz 
1275ad0c6836Scsapuntz 	if (!wdc_atapi_drive_selected(chp, xfer->drive))
1276ad0c6836Scsapuntz 	{
127795c10403Scsapuntz 		wdc_set_drive(chp, xfer->drive);
1278ad0c6836Scsapuntz 		delay (1);
1279ad0c6836Scsapuntz 	}
1280ad0c6836Scsapuntz 
128166bb6343Scsapuntz 	if (timeout) {
128266bb6343Scsapuntz 		int trigger_timeout = 1;
1283ad0c6836Scsapuntz 
128466bb6343Scsapuntz 		switch (drvp->state) {
128566bb6343Scsapuntz 		case ATAPI_DEVICE_RESET_WAIT_STATE:
128666bb6343Scsapuntz 			errstring = "Device Reset Wait";
128766bb6343Scsapuntz 			drvp->drive_flags &= ~DRIVE_DEVICE_RESET;
128866bb6343Scsapuntz 			break;
128966bb6343Scsapuntz 
12902702c783Scsapuntz 		case ATAPI_IDENTIFY_WAIT_STATE:
12912702c783Scsapuntz 			errstring = "Identify";
12922702c783Scsapuntz 			if (!(chp->ch_status & WDCS_BSY) &&
12932702c783Scsapuntz 			    (chp->ch_status & (WDCS_DRQ | WDCS_ERR)))
12942702c783Scsapuntz 				trigger_timeout = 0;
12952702c783Scsapuntz 
12962702c783Scsapuntz 			break;
12972702c783Scsapuntz 
12987e610aa1Scsapuntz 		case ATAPI_PIOMODE_STATE:
12997e610aa1Scsapuntz 			errstring = "Post-Identify";
13007e610aa1Scsapuntz 			if (!(chp->ch_status & (WDCS_BSY | WDCS_DRQ)))
13017e610aa1Scsapuntz 				trigger_timeout = 0;
13027e610aa1Scsapuntz 			break;
13037e610aa1Scsapuntz 
130466bb6343Scsapuntz 		case ATAPI_PIOMODE_WAIT_STATE:
130566bb6343Scsapuntz 			errstring = "PIOMODE";
13062702c783Scsapuntz 			if (chp->ch_status & (WDCS_BSY | WDCS_DRQ))
130766bb6343Scsapuntz 				drvp->drive_flags &= ~DRIVE_MODE;
130866bb6343Scsapuntz 			else
130966bb6343Scsapuntz 				trigger_timeout = 0;
131066bb6343Scsapuntz 			break;
131166bb6343Scsapuntz 		case ATAPI_DMAMODE_WAIT_STATE:
131266bb6343Scsapuntz 			errstring = "dmamode";
13132702c783Scsapuntz 			if (chp->ch_status & (WDCS_BSY | WDCS_DRQ))
131466bb6343Scsapuntz 				drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA);
131566bb6343Scsapuntz 			else
131666bb6343Scsapuntz 				trigger_timeout = 0;
131766bb6343Scsapuntz 			break;
131866bb6343Scsapuntz 
131966bb6343Scsapuntz 		default:
132066bb6343Scsapuntz 			errstring = "unknown state";
132166bb6343Scsapuntz 			break;
132266bb6343Scsapuntz 		}
132366bb6343Scsapuntz 
132466bb6343Scsapuntz 		if (trigger_timeout)
132566bb6343Scsapuntz 			goto timeout;
132666bb6343Scsapuntz 	}
1327a24b05dfScsapuntz 
13287481efa2Scsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_ctrl %s:%d:%d state %d\n",
13297481efa2Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive, drvp->state),
13307481efa2Scsapuntz 	    DEBUG_INTR | DEBUG_FUNCS);
1331a077a3e9Scsapuntz 
13327481efa2Scsapuntz 	switch (drvp->state) {
133366bb6343Scsapuntz 		/* My ATAPI slave device likes to assert DASP-/PDIAG- until
133466bb6343Scsapuntz 		   it is DEVICE RESET. This causes the LED to stay on.
1335a077a3e9Scsapuntz 
133666bb6343Scsapuntz 		   There is a trade-off here. This drive will cause any
133766bb6343Scsapuntz 		   play-back or seeks happening to be interrupted.
1338a077a3e9Scsapuntz 
133966bb6343Scsapuntz 		   Note that the bus reset that triggered this state
134066bb6343Scsapuntz 		   (which may have been caused by the other drive on
134166bb6343Scsapuntz 		   the chain) need not interrupt this playback. It happens
134266bb6343Scsapuntz 		   to on my Smart & Friendly CD burner.
134366bb6343Scsapuntz 
134466bb6343Scsapuntz 		   - csapuntz@
134566bb6343Scsapuntz 		*/
134666bb6343Scsapuntz 	case ATAPI_RESET_BASE_STATE:
134766bb6343Scsapuntz 		if ((drvp->drive_flags & DRIVE_DEVICE_RESET) == 0) {
13482702c783Scsapuntz 			drvp->state = ATAPI_IDENTIFY_STATE;
1349a24b05dfScsapuntz 			break;
135048d11ec3Scsapuntz 		}
135136acebdeScsapuntz 
135266bb6343Scsapuntz 		wdccommandshort(chp, drvp->drive, ATAPI_DEVICE_RESET);
135366bb6343Scsapuntz 		drvp->state = ATAPI_DEVICE_RESET_WAIT_STATE;
13542702c783Scsapuntz 		ret->delay = ATAPI_RESET_DELAY;
135566bb6343Scsapuntz 		ret->timeout = ATAPI_RESET_WAIT;
135666bb6343Scsapuntz 		break;
1357a24b05dfScsapuntz 
135866bb6343Scsapuntz 	case ATAPI_DEVICE_RESET_WAIT_STATE:
135983344e41Sjsg 		/* FALLTHROUGH */
13602702c783Scsapuntz 
13612702c783Scsapuntz 	case ATAPI_IDENTIFY_STATE:
13622702c783Scsapuntz 		wdccommandshort(chp, drvp->drive, ATAPI_IDENTIFY_DEVICE);
13632702c783Scsapuntz 		drvp->state = ATAPI_IDENTIFY_WAIT_STATE;
13642702c783Scsapuntz 		ret->delay = 10;
136566bb6343Scsapuntz 		ret->timeout = ATAPI_RESET_WAIT;
136666bb6343Scsapuntz 		break;
136766bb6343Scsapuntz 
13682702c783Scsapuntz 	case ATAPI_IDENTIFY_WAIT_STATE: {
13692702c783Scsapuntz 		int idx = 0;
13702702c783Scsapuntz 
13712702c783Scsapuntz 		while ((chp->ch_status & WDCS_DRQ) &&
13722702c783Scsapuntz 		    idx++ < 20) {
13732702c783Scsapuntz 			wdcbit_bucket(chp, 512);
13742702c783Scsapuntz 
13752702c783Scsapuntz 			DELAY(1);
13762702c783Scsapuntz 			wdc_atapi_update_status(chp);
13772702c783Scsapuntz 		}
13782702c783Scsapuntz 
13792702c783Scsapuntz 		drvp->state = ATAPI_PIOMODE_STATE;
13807e610aa1Scsapuntz 		/*
13817e610aa1Scsapuntz 		 * Note, we can't go directly to set PIO mode
13827e610aa1Scsapuntz 		 * because the drive is free to assert BSY
13837e610aa1Scsapuntz 		 * after the transfer
13847e610aa1Scsapuntz 		 */
13857e610aa1Scsapuntz 		break;
13862702c783Scsapuntz 	}
13872702c783Scsapuntz 
138866bb6343Scsapuntz 	case ATAPI_PIOMODE_STATE:
13897481efa2Scsapuntz 		/* Don't try to set mode if controller can't be adjusted */
13907481efa2Scsapuntz 		if ((chp->wdc->cap & WDC_CAPABILITY_MODE) == 0)
13917481efa2Scsapuntz 			goto ready;
13927481efa2Scsapuntz 		/* Also don't try if the drive didn't report its mode */
13937481efa2Scsapuntz 		if ((drvp->drive_flags & DRIVE_MODE) == 0)
1394ec62f1edSniklas 			goto ready;
1395d9aa2812Sgrange 		/* SET FEATURES 0x08 is only for PIO mode > 2 */
1396d9aa2812Sgrange 		if (drvp->PIO_mode <= 2)
1397d9aa2812Sgrange 			goto ready;
13987481efa2Scsapuntz 		wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
13997481efa2Scsapuntz 		    0x08 | drvp->PIO_mode, WDSF_SET_MODE);
140066bb6343Scsapuntz 		drvp->state = ATAPI_PIOMODE_WAIT_STATE;
140154f23ac8Scsapuntz 		ret->timeout = ATAPI_CTRL_WAIT;
140254f23ac8Scsapuntz 		ret->expect_irq = 1;
14037481efa2Scsapuntz 		break;
140466bb6343Scsapuntz 	case ATAPI_PIOMODE_WAIT_STATE:
1405caa5053fScsapuntz 		if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
1406caa5053fScsapuntz 			chp->wdc->irqack(chp);
14077481efa2Scsapuntz 		if (chp->ch_status & WDCS_ERR) {
1408d9aa2812Sgrange 			/* Downgrade straight to PIO mode 3 */
140953b549e4Scsapuntz 			drvp->PIO_mode = 3;
141066bb6343Scsapuntz 			chp->wdc->set_modes(chp);
14117481efa2Scsapuntz 		}
141283344e41Sjsg 	/* FALLTHROUGH */
14137481efa2Scsapuntz 
141466bb6343Scsapuntz 	case ATAPI_DMAMODE_STATE:
14157481efa2Scsapuntz 		if (drvp->drive_flags & DRIVE_UDMA) {
14167481efa2Scsapuntz 			wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
14177481efa2Scsapuntz 			    0x40 | drvp->UDMA_mode, WDSF_SET_MODE);
14187481efa2Scsapuntz 		} else if (drvp->drive_flags & DRIVE_DMA) {
14197481efa2Scsapuntz 			wdccommand(chp, drvp->drive, SET_FEATURES, 0, 0, 0,
14207481efa2Scsapuntz 			    0x20 | drvp->DMA_mode, WDSF_SET_MODE);
14217481efa2Scsapuntz 		} else {
14227481efa2Scsapuntz 			goto ready;
14237481efa2Scsapuntz 		}
142466bb6343Scsapuntz 		drvp->state = ATAPI_DMAMODE_WAIT_STATE;
1425a24b05dfScsapuntz 
142654f23ac8Scsapuntz 		ret->timeout = ATAPI_CTRL_WAIT;
142754f23ac8Scsapuntz 		ret->expect_irq = 1;
14287481efa2Scsapuntz 		break;
1429a24b05dfScsapuntz 
143066bb6343Scsapuntz 	case ATAPI_DMAMODE_WAIT_STATE:
1431caa5053fScsapuntz 		if (chp->wdc->cap & WDC_CAPABILITY_IRQACK)
1432caa5053fScsapuntz 			chp->wdc->irqack(chp);
14337481efa2Scsapuntz 		if (chp->ch_status & WDCS_ERR)
1434ab72a97eScsapuntz 			drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA);
143583344e41Sjsg 	/* FALLTHROUGH */
14367481efa2Scsapuntz 
143766bb6343Scsapuntz 	case ATAPI_READY_STATE:
14387481efa2Scsapuntz 	ready:
143966bb6343Scsapuntz 		drvp->state = ATAPI_READY_STATE;
1440a24b05dfScsapuntz 		xfer->next = wdc_atapi_real_start;
1441a24b05dfScsapuntz 		break;
14427481efa2Scsapuntz 	}
144354f23ac8Scsapuntz 	return;
14447481efa2Scsapuntz 
14457481efa2Scsapuntz timeout:
14467481efa2Scsapuntz 	printf("%s:%d:%d: %s timed out\n",
14477481efa2Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, errstring);
14487481efa2Scsapuntz 	sc_xfer->error = XS_TIMEOUT;
1449a24b05dfScsapuntz 	xfer->next = wdc_atapi_reset;
145054f23ac8Scsapuntz 	return;
1451a24b05dfScsapuntz 
14527481efa2Scsapuntz }
14537481efa2Scsapuntz 
145454f23ac8Scsapuntz void
wdc_atapi_tape_done(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1455c28b5ab2Smatthew wdc_atapi_tape_done(struct channel_softc *chp, struct wdc_xfer *xfer,
1456c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
1457a24b05dfScsapuntz {
1458a24b05dfScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
1459a24b05dfScsapuntz 
1460a24b05dfScsapuntz 	if (sc_xfer->error != XS_NOERROR) {
1461a24b05dfScsapuntz 		xfer->next = wdc_atapi_done;
146254f23ac8Scsapuntz 		return;
1463a24b05dfScsapuntz 	}
1464a24b05dfScsapuntz 
1465a24b05dfScsapuntz 	_lto3b(xfer->transfer_len,
1466a24b05dfScsapuntz 	    ((struct scsi_rw_tape *)
1467664c6166Skrw 		&sc_xfer->cmd)->len);
1468a24b05dfScsapuntz 
1469a24b05dfScsapuntz 	xfer->c_bcount = sc_xfer->datalen;
1470a24b05dfScsapuntz 	xfer->c_done = NULL;
1471a24b05dfScsapuntz 	xfer->c_skip = 0;
1472a24b05dfScsapuntz 
1473a24b05dfScsapuntz 	xfer->next = wdc_atapi_real_start;
147454f23ac8Scsapuntz 	return;
1475a24b05dfScsapuntz }
1476a24b05dfScsapuntz 
1477a24b05dfScsapuntz 
147854f23ac8Scsapuntz void
wdc_atapi_done(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1479c28b5ab2Smatthew wdc_atapi_done(struct channel_softc *chp, struct wdc_xfer *xfer,
1480c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
14817481efa2Scsapuntz {
14827481efa2Scsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
14837481efa2Scsapuntz 
14844ba73d8bScsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_done %s:%d:%d: flags 0x%x error 0x%x\n",
14857481efa2Scsapuntz 	    chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive,
14864ba73d8bScsapuntz 	    (u_int)xfer->c_flags, sc_xfer->error), DEBUG_XFERS);
148795c10403Scsapuntz 	WDC_LOG_ATAPI_DONE(chp, xfer->drive, xfer->c_flags, sc_xfer->error);
1488a24b05dfScsapuntz 
14898e703ec5Skrw 	if (xfer->c_flags & C_POLL)
1490e7f35eb1Sgrange 		wdc_enable_intr(chp);
14918e703ec5Skrw 
14927481efa2Scsapuntz 	scsi_done(sc_xfer);
1493a24b05dfScsapuntz 
149454f23ac8Scsapuntz 	xfer->next = NULL;
149554f23ac8Scsapuntz 	return;
14967481efa2Scsapuntz }
14977481efa2Scsapuntz 
1498ec62f1edSniklas 
149954f23ac8Scsapuntz void
wdc_atapi_reset(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1500c28b5ab2Smatthew wdc_atapi_reset(struct channel_softc *chp, struct wdc_xfer *xfer,
1501c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
15027481efa2Scsapuntz {
15037481efa2Scsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
15047481efa2Scsapuntz 
1505ec8802a4Scsapuntz 	if (drvp->state == 0) {
1506ec8802a4Scsapuntz 		xfer->next = wdc_atapi_done;
1507ec8802a4Scsapuntz 		return;
1508ec8802a4Scsapuntz 	}
1509ec8802a4Scsapuntz 
15104ba73d8bScsapuntz 	WDCDEBUG_PRINT(("wdc_atapi_reset\n"), DEBUG_XFERS);
15117481efa2Scsapuntz 	wdccommandshort(chp, xfer->drive, ATAPI_SOFT_RESET);
15122702c783Scsapuntz 	drvp->state = ATAPI_IDENTIFY_STATE;
1513a077a3e9Scsapuntz 
151466bb6343Scsapuntz 	drvp->n_resets++;
1515a077a3e9Scsapuntz 	/* Some ATAPI devices need extra time to find their
1516a077a3e9Scsapuntz 	   brains after a reset
1517a077a3e9Scsapuntz 	 */
1518a24b05dfScsapuntz 	xfer->next = wdc_atapi_reset_2;
15192702c783Scsapuntz 	ret->delay = ATAPI_RESET_DELAY;
152054f23ac8Scsapuntz 	ret->timeout = ATAPI_RESET_WAIT;
152154f23ac8Scsapuntz 	return;
1522a24b05dfScsapuntz }
1523a077a3e9Scsapuntz 
152454f23ac8Scsapuntz void
wdc_atapi_reset_2(struct channel_softc * chp,struct wdc_xfer * xfer,int timeout,struct atapi_return_args * ret)1525c28b5ab2Smatthew wdc_atapi_reset_2(struct channel_softc *chp, struct wdc_xfer *xfer,
1526c28b5ab2Smatthew     int timeout, struct atapi_return_args *ret)
1527a24b05dfScsapuntz {
1528a24b05dfScsapuntz 	struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive];
1529a24b05dfScsapuntz 	struct scsi_xfer *sc_xfer = xfer->cmd;
1530a24b05dfScsapuntz 
1531a24b05dfScsapuntz 	if (timeout) {
1532bf07dfc9Scsapuntz 		printf("%s:%d:%d: soft reset failed\n",
15337481efa2Scsapuntz 		    chp->wdc->sc_dev.dv_xname, chp->channel,
15347481efa2Scsapuntz 		    xfer->drive);
15357481efa2Scsapuntz 		sc_xfer->error = XS_SELTIMEOUT;
1536ba8cbb4aSmiod 		wdc_reset_channel(drvp, 0);
1537a24b05dfScsapuntz 
1538a24b05dfScsapuntz 		xfer->next = wdc_atapi_done;
153954f23ac8Scsapuntz 		return;
15407481efa2Scsapuntz 	}
1541a077a3e9Scsapuntz 
1542a24b05dfScsapuntz 	wdc_atapi_update_status(chp);
1543a24b05dfScsapuntz 
1544a24b05dfScsapuntz 	if (chp->ch_status & (WDCS_BSY | WDCS_DRQ)) {
154554f23ac8Scsapuntz 		return;
15467481efa2Scsapuntz 	}
1547a24b05dfScsapuntz 
1548a24b05dfScsapuntz 	xfer->next = wdc_atapi_done;
154954f23ac8Scsapuntz 	return;
1550a24b05dfScsapuntz }
1551