xref: /openbsd-src/sys/dev/ic/ami.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: ami.c,v 1.10 2001/07/04 22:53:25 espie Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 Michael Shalayeff
5  * All rights reserved.
6  *
7  * The SCSI emulation layer is derived from gdt(4) driver,
8  * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by Michael Shalayeff.
21  * 4. The name of the author may not be used to endorse or promote products
22  *    derived from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
28  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
34  * THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 /*
37  * American Megatrends Inc. MegaRAID controllers driver
38  *
39  * This driver was made because these ppl and organizations
40  * donated hardware and provided documentation:
41  *
42  * - 428 model card
43  *	John Kerbawy, Stephan Matis, Mark Stovall;
44  *
45  * - 467 and 475 model cards, docs
46  *	American Megatrends Inc.;
47  *
48  * - uninterruptable electric power for cvs
49  *	Theo de Raadt.
50  */
51 
52 /* #define	AMI_DEBUG */
53 
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/buf.h>
57 #include <sys/device.h>
58 #include <sys/kernel.h>
59 #include <sys/malloc.h>
60 
61 #include <machine/bus.h>
62 
63 #include <vm/vm.h>
64 #include <vm/vm_kern.h>
65 #include <uvm/uvm_extern.h>
66 
67 #include <scsi/scsi_all.h>
68 #include <scsi/scsi_disk.h>
69 #include <scsi/scsiconf.h>
70 
71 #include <dev/ic/amireg.h>
72 #include <dev/ic/amivar.h>
73 
74 #ifdef AMI_DEBUG
75 #define	AMI_DPRINTF(m,a)	if (ami_debug & (m)) printf a
76 #define	AMI_D_CMD	0x0001
77 #define	AMI_D_INTR	0x0002
78 #define	AMI_D_MISC	0x0004
79 #define	AMI_D_DMA	0x0008
80 int ami_debug = 0
81 	| AMI_D_CMD
82 	| AMI_D_INTR
83 	| AMI_D_MISC
84 	| AMI_D_DMA
85 	;
86 #else
87 #define	AMI_DPRINTF(m,a)	/* m, a */
88 #endif
89 
90 struct cfdriver ami_cd = {
91 	NULL, "ami", DV_DULL
92 };
93 
94 int	ami_scsi_cmd __P((struct scsi_xfer *xs));
95 void	amiminphys __P((struct buf *bp));
96 
97 struct scsi_adapter ami_switch = {
98 	ami_scsi_cmd, amiminphys, 0, 0,
99 };
100 
101 struct scsi_device ami_dev = {
102 	NULL, NULL, NULL, NULL
103 };
104 
105 int	ami_scsi_raw_cmd __P((struct scsi_xfer *xs));
106 
107 struct scsi_adapter ami_raw_switch = {
108 	ami_scsi_raw_cmd, amiminphys, 0, 0,
109 };
110 
111 struct scsi_device ami_raw_dev = {
112 	NULL, NULL, NULL, NULL
113 };
114 
115 static __inline struct ami_ccb *ami_get_ccb __P((struct ami_softc *sc));
116 static __inline void ami_put_ccb __P((struct ami_ccb *ccb));
117 void ami_copyhds __P((struct ami_softc *sc, const u_int32_t *sizes,
118 	const u_int8_t *props, const u_int8_t *stats));
119 void *ami_allocmem __P((bus_dma_tag_t dmat, bus_dmamap_t *map,
120 	bus_dma_segment_t *segp, size_t isize, size_t nent, const char *iname));
121 void ami_freemem __P((bus_dma_tag_t dmat, bus_dmamap_t *map,
122 	bus_dma_segment_t *segp, size_t isize, size_t nent, const char *iname));
123 void ami_dispose __P((struct ami_softc *sc));
124 void ami_stimeout __P((void *v));
125 int  ami_cmd __P((struct ami_ccb *ccb, int flags, int wait));
126 int  ami_start __P((struct ami_ccb *ccb, int wait));
127 int  ami_complete __P((struct ami_ccb *ccb));
128 int  ami_done __P((struct ami_softc *sc, int idx));
129 void ami_copy_internal_data __P((struct scsi_xfer *xs, void *v, size_t size));
130 int  ami_inquire __P((struct ami_softc *sc, u_int8_t op));
131 
132 
133 static __inline struct ami_ccb *
134 ami_get_ccb(sc)
135 	struct ami_softc *sc;
136 {
137 	struct ami_ccb *ccb;
138 
139 	ccb = TAILQ_LAST(&sc->sc_free_ccb, ami_queue_head);
140 	if (ccb) {
141 		TAILQ_REMOVE(&sc->sc_free_ccb, ccb, ccb_link);
142 		ccb->ccb_state = AMI_CCB_READY;
143 	}
144 	return ccb;
145 }
146 
147 static __inline void
148 ami_put_ccb(ccb)
149 	struct ami_ccb *ccb;
150 {
151 	struct ami_softc *sc = ccb->ccb_sc;
152 
153 	ccb->ccb_state = AMI_CCB_FREE;
154 	TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link);
155 }
156 
157 void *
158 ami_allocmem(dmat, map, segp, isize, nent, iname)
159 	bus_dma_tag_t dmat;
160 	bus_dmamap_t *map;
161 	bus_dma_segment_t *segp;
162 	size_t isize, nent;
163 	const char *iname;
164 {
165 	size_t total = isize * nent;
166 	caddr_t p;
167 	int error, rseg;
168 
169 	/* XXX this is because we might have no dmamem_load_raw */
170 	if ((error = bus_dmamem_alloc(dmat, total, PAGE_SIZE, 0, segp, 1,
171 	    &rseg, BUS_DMA_NOWAIT))) {
172 		printf(": cannot allocate %s%s (%d)\n",
173 		    iname, nent==1? "": "s", error);
174 		return (NULL);
175 	}
176 
177 	if ((error = bus_dmamem_map(dmat, segp, rseg, total, &p,
178 	    BUS_DMA_NOWAIT))) {
179 		printf(": cannot map %s%s (%d)\n",
180 		    iname, nent==1? "": "s", error);
181 		return (NULL);
182 	}
183 
184 	bzero(p, total);
185 	if ((error = bus_dmamap_create(dmat, total, 1,
186 	    total, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, map))) {
187 		printf(": cannot create %s dmamap (%d)\n", iname, error);
188 		return (NULL);
189 	}
190 	if ((error = bus_dmamap_load(dmat, *map, p, total, NULL,
191 	    BUS_DMA_NOWAIT))) {
192 		printf(": cannot load %s dma map (%d)\n", iname, error);
193 		return (NULL);
194 	}
195 
196 	return (p);
197 }
198 
199 void
200 ami_freemem(dmat, map, segp, isize, nent, iname)
201 	bus_dma_tag_t dmat;
202 	bus_dmamap_t *map;
203 	bus_dma_segment_t *segp;
204 	size_t isize, nent;
205 	const char *iname;
206 {
207 	bus_dmamem_free(dmat, segp, 1);
208 	bus_dmamap_destroy(dmat, *map);
209 	*map = NULL;
210 }
211 
212 void
213 ami_dispose(sc)
214 	struct ami_softc *sc;
215 {
216 	register struct ami_ccb *ccb;
217 
218 	/* traverse the ccbs and destroy the maps */
219 	for (ccb = &sc->sc_ccbs[AMI_MAXCMDS - 1]; ccb > sc->sc_ccbs; ccb--)
220 		if (ccb->ccb_dmamap)
221 			bus_dmamap_destroy(sc->dmat, ccb->ccb_dmamap);
222 	ami_freemem(sc->dmat, &sc->sc_sgmap, sc->sc_sgseg,
223 	    sizeof(struct ami_sgent) * AMI_SGEPERCMD, AMI_MAXCMDS, "sglist");
224 	ami_freemem(sc->dmat, &sc->sc_cmdmap, sc->sc_cmdseg,
225 	    sizeof(struct ami_iocmd), AMI_MAXCMDS + 1, "command");
226 }
227 
228 
229 void
230 ami_copyhds(sc, sizes, props, stats)
231 	struct ami_softc *sc;
232 	const u_int32_t *sizes;
233 	const u_int8_t *props, *stats;
234 {
235 	int i;
236 
237 	for (i = 0; i < sc->sc_nunits; i++) {
238 		sc->sc_hdr[i].hd_present = 1;
239 		sc->sc_hdr[i].hd_is_logdrv = 1;
240 		sc->sc_hdr[i].hd_size = letoh32(sizes[i]);
241 		sc->sc_hdr[i].hd_prop = props[i];
242 		sc->sc_hdr[i].hd_stat = stats[i];
243 		if (sc->sc_hdr[i].hd_size > 0x200000) {
244 			sc->sc_hdr[i].hd_heads = 255;
245 			sc->sc_hdr[i].hd_secs = 63;
246 		} else {
247 			sc->sc_hdr[i].hd_heads = 64;
248 			sc->sc_hdr[i].hd_secs = 32;
249 		}
250 	}
251 }
252 
253 int
254 ami_attach(sc)
255 	struct ami_softc *sc;
256 {
257 	/* struct ami_rawsoftc *rsc; */
258 	struct ami_ccb	*ccb;
259 	struct ami_iocmd *cmd;
260 	struct ami_sgent *sg;
261 	bus_dmamap_t idatamap;
262 	bus_dma_segment_t idataseg[1];
263 	const char *p;
264 	void	*idata;
265 	int	error;
266 
267 	if (!(idata = ami_allocmem(sc->dmat, &idatamap, idataseg,
268 	    NBPG, 1, "init data"))) {
269 		ami_freemem(sc->dmat, &idatamap, idataseg,
270 		    NBPG, 1, "init data");
271 		return 1;
272 	}
273 
274 	sc->sc_cmds = ami_allocmem(sc->dmat, &sc->sc_cmdmap, sc->sc_cmdseg,
275 	    sizeof(struct ami_iocmd), AMI_MAXCMDS+1, "command");
276 	if (!sc->sc_cmds) {
277 		ami_dispose(sc);
278 		ami_freemem(sc->dmat, &idatamap,
279 		    idataseg, NBPG, 1, "init data");
280 		return 1;
281 	}
282 	sc->sc_sgents = ami_allocmem(sc->dmat, &sc->sc_sgmap, sc->sc_sgseg,
283 	    sizeof(struct ami_sgent) * AMI_SGEPERCMD, AMI_MAXCMDS+1, "sglist");
284 	if (!sc->sc_sgents) {
285 		ami_dispose(sc);
286 		ami_freemem(sc->dmat, &idatamap,
287 		    idataseg, NBPG, 1, "init data");
288 		return 1;
289 	}
290 
291 	TAILQ_INIT(&sc->sc_ccbq);
292 	TAILQ_INIT(&sc->sc_ccb2q);
293 	TAILQ_INIT(&sc->sc_ccbdone);
294 	TAILQ_INIT(&sc->sc_free_ccb);
295 
296 	/* 0th command is a mailbox */
297 	for (ccb = &sc->sc_ccbs[AMI_MAXCMDS-1],
298 	     cmd = sc->sc_cmds  + sizeof(*cmd) * AMI_MAXCMDS,
299 	     sg = sc->sc_sgents + sizeof(*sg)  * AMI_MAXCMDS * AMI_SGEPERCMD;
300 	     cmd >= (struct ami_iocmd *)sc->sc_cmds;
301 	     cmd--, ccb--, sg -= AMI_SGEPERCMD) {
302 
303 		cmd->acc_id = cmd - (struct ami_iocmd *)sc->sc_cmds;
304 		if (cmd->acc_id) {
305 			error = bus_dmamap_create(sc->dmat,
306 			    AMI_MAXFER, AMI_MAXOFFSETS, AMI_MAXFER, 0,
307 			    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
308 			    &ccb->ccb_dmamap);
309 			if (error) {
310 				printf(": cannot create ccb dmamap (%d)\n",
311 				    error);
312 				ami_dispose(sc);
313 				ami_freemem(sc->dmat, &idatamap,
314 				    idataseg, NBPG, 1, "init data");
315 				return (1);
316 			}
317 			ccb->ccb_sc = sc;
318 			ccb->ccb_cmd = cmd;
319 			ccb->ccb_state = AMI_CCB_FREE;
320 			ccb->ccb_cmdpa = htole32(sc->sc_cmdseg[0].ds_addr +
321 			    cmd->acc_id * sizeof(*cmd));
322 			ccb->ccb_sglist = sg;
323 			ccb->ccb_sglistpa = htole32(sc->sc_sgseg[0].ds_addr +
324 			    cmd->acc_id * sizeof(*sg) * AMI_SGEPERCMD);
325 			TAILQ_INSERT_TAIL(&sc->sc_free_ccb, ccb, ccb_link);
326 		} else {
327 			sc->sc_mbox = cmd;
328 			sc->sc_mbox_pa = sc->sc_cmdseg[0].ds_addr;
329 		}
330 	}
331 
332 	timeout_set(&sc->sc_poll_tmo, (void (*)__P((void *)))ami_intr, sc);
333 
334 	(sc->sc_init)(sc);
335 	{
336 		paddr_t	pa = idataseg[0].ds_addr;
337 		ami_lock_t lock;
338 
339 		lock = AMI_LOCK_AMI(sc);
340 
341 		ccb = ami_get_ccb(sc);
342 		cmd = ccb->ccb_cmd;
343 
344 		/* try FC inquiry first */
345 		cmd->acc_cmd = AMI_FCOP;
346 		cmd->acc_io.aio_channel = AMI_FC_EINQ3;
347 		cmd->acc_io.aio_param = AMI_FC_EINQ3_SOLICITED_FULL;
348 		cmd->acc_io.aio_data = htole32(pa);
349 		if (ami_cmd(ccb, 0, 1) == 0) {
350 			struct ami_fc_einquiry *einq = idata;
351 			struct ami_fc_prodinfo *pi = idata;
352 
353 			sc->sc_nunits = einq->ain_nlogdrv;
354 			ami_copyhds(sc, einq->ain_ldsize, einq->ain_ldprop,
355 			    einq->ain_ldstat);
356 
357 			ccb = ami_get_ccb(sc);
358 			cmd = ccb->ccb_cmd;
359 
360 			cmd->acc_cmd = AMI_FCOP;
361 			cmd->acc_io.aio_channel = AMI_FC_PRODINF;
362 			cmd->acc_io.aio_param = 0;
363 			cmd->acc_io.aio_data = htole32(pa);
364 			if (ami_cmd(ccb, 0, 1) == 0) {
365 				sc->sc_maxunits = AMI_BIG_MAX_LDRIVES;
366 
367 				bcopy (pi->api_fwver, sc->sc_fwver, 16);
368 				sc->sc_fwver[16] = '\0';
369 				bcopy (pi->api_biosver, sc->sc_biosver, 16);
370 				sc->sc_biosver[16] = '\0';
371 				sc->sc_channels = pi->api_channels;
372 				sc->sc_targets = pi->api_fcloops;
373 				sc->sc_memory = letoh16(pi->api_ramsize);
374 				sc->sc_maxcmds = pi->api_maxcmd;
375 				p = "FC loop";
376 			}
377 		}
378 
379 		if (sc->sc_maxunits == 0) {
380 			struct ami_inquiry *inq = idata;
381 
382 			ccb = ami_get_ccb(sc);
383 			cmd = ccb->ccb_cmd;
384 
385 			cmd->acc_cmd = AMI_EINQUIRY;
386 			cmd->acc_io.aio_channel = 0;
387 			cmd->acc_io.aio_param = 0;
388 			cmd->acc_io.aio_data = htole32(pa);
389 			if (ami_cmd(ccb, 0, 1) != 0) {
390 				ccb = ami_get_ccb(sc);
391 				cmd = ccb->ccb_cmd;
392 
393 				cmd->acc_cmd = AMI_INQUIRY;
394 				cmd->acc_io.aio_channel = 0;
395 				cmd->acc_io.aio_param = 0;
396 				cmd->acc_io.aio_data = htole32(pa);
397 				if (ami_cmd(ccb, 0, 1) != 0) {
398 					AMI_UNLOCK_AMI(sc, lock);
399 					printf(": cannot do inquiry\n");
400 					ami_dispose(sc);
401 					ami_freemem(sc->dmat, &idatamap,
402 					    idataseg, NBPG, 1, "init data");
403 					return (1);
404 				}
405 			}
406 
407 			sc->sc_maxunits = AMI_MAX_LDRIVES;
408 			sc->sc_nunits = inq->ain_nlogdrv;
409 			ami_copyhds(sc, inq->ain_ldsize, inq->ain_ldprop,
410 			    inq->ain_ldstat);
411 
412 			bcopy (inq->ain_fwver, sc->sc_fwver, 4);
413 			sc->sc_fwver[4] = '\0';
414 			bcopy (inq->ain_biosver, sc->sc_biosver, 4);
415 			sc->sc_biosver[4] = '\0';
416 			sc->sc_channels = inq->ain_channels;
417 			sc->sc_targets = inq->ain_targets;
418 			sc->sc_memory = inq->ain_ramsize;
419 			sc->sc_maxcmds = inq->ain_maxcmd;
420 			p = "target";
421 		}
422 
423 		AMI_UNLOCK_AMI(sc, lock);
424 
425 		if (sc->sc_maxcmds > AMI_MAXCMDS)
426 			sc->sc_maxcmds = 1 /* AMI_MAXCMDS */;
427 	}
428 	ami_freemem(sc->dmat, &idatamap, idataseg, NBPG, 1, "init data");
429 
430 	/* hack for hp netraid version encoding */
431 	if ('A' <= sc->sc_fwver[2] && sc->sc_fwver[2] <= 'Z' &&
432 	    sc->sc_fwver[1] < ' ' && sc->sc_fwver[0] < ' ' &&
433 	    'A' <= sc->sc_biosver[2] && sc->sc_biosver[2] <= 'Z' &&
434 	    sc->sc_biosver[1] < ' ' && sc->sc_biosver[0] < ' ') {
435 
436 		sprintf(sc->sc_fwver, "%c.%02d.%02d", sc->sc_fwver[2],
437 		    sc->sc_fwver[1], sc->sc_fwver[0]);
438 		sprintf(sc->sc_biosver, "%c.%02d.%02d", sc->sc_biosver[2],
439 		    sc->sc_biosver[1], sc->sc_biosver[0]);
440 	}
441 
442 	printf(": FW %s, BIOS v%s, %dMB RAM\n"
443 	     "%s: %d channels, %d %ss, %d logical drives\n",
444 	    sc->sc_fwver, sc->sc_biosver, sc->sc_memory,
445 	    sc->sc_dev.dv_xname,
446 	    sc->sc_channels, sc->sc_targets, p, sc->sc_nunits);
447 
448 	/* TODO: fetch & print cache strategy */
449 	/* TODO: fetch & print scsi and raid info */
450 
451 	sc->sc_link.device = &ami_dev;
452 	sc->sc_link.openings = sc->sc_maxcmds;
453 	sc->sc_link.adapter_softc = sc;
454 	sc->sc_link.adapter = &ami_switch;
455 	sc->sc_link.adapter_target = sc->sc_maxunits;
456 	sc->sc_link.adapter_buswidth = sc->sc_maxunits;
457 
458 	config_found(&sc->sc_dev, &sc->sc_link, scsiprint);
459 #if 0
460 	rsc = malloc(sizeof(struct ami_rawsoftc) * sc->sc_channels,
461 	    M_DEVBUF, M_NOWAIT);
462 	if (!rsc) {
463 		printf("%s: no memory for raw interface\n",
464 		    sc->sc_dev.dv_xname);
465 		return (0);
466 	}
467 
468 	bzero(rsc, sizeof(struct ami_rawsoftc) * sc->sc_channels);
469 	for (sc->sc_rawsoftcs = rsc;
470 	     rsc < &sc->sc_rawsoftcs[sc->sc_channels]; rsc++) {
471 
472 		/* TODO fetch and print channel properties */
473 
474 		rsc->sc_softc = sc;
475 		rsc->sc_channel = rsc - sc->sc_rawsoftcs;
476 		rsc->sc_link.device = &ami_raw_dev;
477 		rsc->sc_link.openings = sc->sc_maxcmds;
478 		rsc->sc_link.adapter_softc = rsc;
479 		rsc->sc_link.adapter = &ami_raw_switch;
480 		/* TODO fetch it from the controller */
481 		rsc->sc_link.adapter_target = sc->sc_targets;
482 		rsc->sc_link.adapter_buswidth = sc->sc_targets;
483 
484 		config_found(&sc->sc_dev, &rsc->sc_link, scsiprint);
485 	}
486 #endif
487 	return 0;
488 }
489 
490 int
491 ami_quartz_init(sc)
492 	struct ami_softc *sc;
493 {
494 	return 0;
495 }
496 
497 int
498 ami_quartz_exec(sc, cmd)
499 	struct ami_softc *sc;
500 	struct ami_iocmd *cmd;
501 {
502 	u_int32_t qidb;
503 
504 	qidb = bus_space_read_4(sc->iot, sc->ioh, AMI_QIDB);
505 	if (qidb & (AMI_QIDB_EXEC | AMI_QIDB_ACK)) {
506 		AMI_DPRINTF(AMI_D_CMD, ("qidb=%x ", qidb));
507 		return (EBUSY);
508 	}
509 
510 	*sc->sc_mbox = *cmd;
511 
512 	qidb = sc->sc_mbox_pa | AMI_QIDB_EXEC;
513 	AMI_DPRINTF(AMI_D_CMD, ("qidb=%x ", qidb));
514 	bus_space_write_4(sc->iot, sc->ioh, AMI_QIDB, qidb);
515 	return (0);
516 }
517 
518 int
519 ami_quartz_done(sc, mbox)
520 	struct ami_softc *sc;
521 	struct ami_iocmd *mbox;
522 {
523 	u_int32_t qdb;
524 #if 0
525 	/* do not scramble the busy mailbox */
526 	if (sc->sc_mbox->acc_busy) {
527 		AMI_DPRINTF(AMI_D_CMD, ("mbox_busy "));
528 		return (0);
529 	}
530 #endif
531 	qdb = bus_space_read_4(sc->iot, sc->ioh, AMI_QIDB);
532 	if (qdb & (AMI_QIDB_EXEC | AMI_QIDB_ACK)) {
533 		AMI_DPRINTF(AMI_D_CMD, ("qidb=%x ", qdb));
534 		return (0);
535 	}
536 
537 	qdb = bus_space_read_4(sc->iot, sc->ioh, AMI_QODB);
538 	if (qdb == AMI_QODB_READY) {
539 
540 		bus_dmamap_sync(sc->dmat, sc->sc_cmdmap, BUS_DMASYNC_POSTREAD);
541 		*mbox = *sc->sc_mbox;
542 
543 		/* ack interrupt */
544 		bus_space_write_4(sc->iot, sc->ioh, AMI_QODB, AMI_QODB_READY);
545 
546 		qdb = sc->sc_mbox_pa | AMI_QIDB_ACK;
547 		bus_space_write_4(sc->iot, sc->ioh, AMI_QIDB, qdb);
548 		return (1);
549 	}
550 
551 	AMI_DPRINTF(AMI_D_CMD, ("qodb=%x ", qdb));
552 
553 	return (0);
554 }
555 
556 int
557 ami_schwartz_init(sc)
558 	struct ami_softc *sc;
559 {
560 	u_int32_t a = (u_int32_t)sc->sc_mbox_pa;
561 
562 	bus_space_write_4(sc->iot, sc->ioh, AMI_SMBADDR, a);
563 	/* XXX 40bit address ??? */
564 	bus_space_write_1(sc->iot, sc->ioh, AMI_SMBENA, 0);
565 
566 	bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_ACK);
567 	bus_space_write_1(sc->iot, sc->ioh, AMI_SIEM, AMI_SEIM_ENA |
568 	    bus_space_read_1(sc->iot, sc->ioh, AMI_SIEM));
569 
570 	return 0;
571 }
572 
573 int
574 ami_schwartz_exec(sc, cmd)
575 	struct ami_softc *sc;
576 	struct ami_iocmd *cmd;
577 {
578 	if (bus_space_read_1(sc->iot, sc->ioh, AMI_SMBSTAT) & AMI_SMBST_BUSY)
579 		return EBUSY;
580 
581 	*sc->sc_mbox = *cmd;
582 	bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_EXEC);
583 	return 0;
584 }
585 
586 int
587 ami_schwartz_done(sc, mbox)
588 	struct ami_softc *sc;
589 	struct ami_iocmd *mbox;
590 {
591 	u_int8_t stat;
592 #if 0
593 	/* do not scramble the busy mailbox */
594 	if (sc->sc_mbox->acc_busy)
595 		return (0);
596 #endif
597 	if (bus_space_read_1(sc->iot, sc->ioh, AMI_SMBSTAT) & AMI_SMBST_BUSY)
598 		return 0;
599 
600 	stat = bus_space_read_1(sc->iot, sc->ioh, AMI_ISTAT);
601 	if (stat & AMI_ISTAT_PEND) {
602 		bus_space_write_1(sc->iot, sc->ioh, AMI_ISTAT, stat);
603 
604 		*mbox = *sc->sc_mbox;
605 
606 		bus_space_write_1(sc->iot, sc->ioh, AMI_SCMD, AMI_SCMD_ACK);
607 
608 		return 1;
609 	}
610 
611 	return 0;
612 }
613 
614 int
615 ami_cmd(ccb, flags, wait)
616 	struct ami_ccb *ccb;
617 	int flags, wait;
618 {
619 	struct ami_softc *sc = ccb->ccb_sc;
620 	bus_dmamap_t dmap = ccb->ccb_dmamap;
621 	int error = 0, i;
622 
623 	if (ccb->ccb_data) {
624 		struct ami_iocmd *cmd = ccb->ccb_cmd;
625 		bus_dma_segment_t *sgd;
626 
627 		error = bus_dmamap_load(sc->dmat, dmap, ccb->ccb_data,
628 		    ccb->ccb_len, NULL, flags);
629 		if (error) {
630 			if (error == EFBIG)
631 				printf("more than %d dma segs\n", AMI_MAXOFFSETS);
632 			else
633 				printf("error %d loading dma map\n", error);
634 
635 			ami_put_ccb(ccb);
636 			return error;
637 		}
638 
639 		sgd = dmap->dm_segs;
640 		AMI_DPRINTF(AMI_D_DMA, ("data=%p/%u<0x%lx/%u",
641 		    ccb->ccb_data, ccb->ccb_len,
642 		    sgd->ds_addr, sgd->ds_len));
643 
644 		if(dmap->dm_nsegs > 1) {
645 			struct ami_sgent *sgl = ccb->ccb_sglist;
646 
647 			cmd->acc_mbox.amb_nsge = htole32(dmap->dm_nsegs);
648 			cmd->acc_mbox.amb_data = ccb->ccb_sglistpa;
649 
650 			for (i = 0; i < dmap->dm_nsegs; i++, sgd++) {
651 				sgl[i].asg_addr = htole32(sgd->ds_addr);
652 				sgl[i].asg_len  = htole32(sgd->ds_len);
653 				if (i)
654 					AMI_DPRINTF(AMI_D_DMA, (",0x%lx/%u",
655 					    sgd->ds_addr, sgd->ds_len));
656 			}
657 		} else {
658 			cmd->acc_mbox.amb_nsge = htole32(0);
659 			cmd->acc_mbox.amb_data = htole32(sgd->ds_addr);
660 		}
661 		AMI_DPRINTF(AMI_D_DMA, ("> "));
662 
663 		bus_dmamap_sync(sc->dmat, dmap, BUS_DMASYNC_PREWRITE);
664 	}
665 	bus_dmamap_sync(sc->dmat, sc->sc_cmdmap, BUS_DMASYNC_PREWRITE);
666 
667 	if ((error = ami_start(ccb, wait))) {
668 		AMI_DPRINTF(AMI_D_DMA, ("error=%d ", error));
669 		__asm __volatile(".globl _bpamierr\n_bpamierr:");
670 		if (ccb->ccb_data)
671 			bus_dmamap_unload(sc->dmat, dmap);
672 		ami_put_ccb(ccb);
673 	} else if (wait) {
674 		AMI_DPRINTF(AMI_D_DMA, ("waiting "));
675 		if ((error = ami_complete(ccb)))
676 			ami_put_ccb(ccb);
677 	}
678 
679 	return error;
680 }
681 
682 int
683 ami_start(ccb, wait)
684 	struct ami_ccb *ccb;
685 	int wait;
686 {
687 	struct ami_softc *sc = ccb->ccb_sc;
688 	struct ami_iocmd *cmd = ccb->ccb_cmd;
689 	struct scsi_xfer *xs = ccb->ccb_xs;
690 	volatile struct ami_iocmd *mbox = sc->sc_mbox;
691 	int i;
692 
693 	AMI_DPRINTF(AMI_D_CMD, ("start(%d) ", cmd->acc_id));
694 
695 	if (ccb->ccb_state != AMI_CCB_READY) {
696 		printf("%s: ccb %d not ready <%d>\n",
697 		    sc->sc_dev.dv_xname, cmd->acc_id, ccb->ccb_state);
698 		return EINVAL;
699 	}
700 
701 	if (xs)
702 		timeout_set(&xs->stimeout, ami_stimeout, ccb);
703 
704 	if (mbox->acc_busy) {
705 
706 		if (!wait) {
707 			AMI_DPRINTF(AMI_D_CMD, ("2queue(%d) ", cmd->acc_id));
708 			ccb->ccb_state = AMI_CCB_PREQUEUED;
709 			if (xs)
710 				timeout_add(&xs->stimeout, 1);
711 
712 			return (xs? 0 : EBUSY);
713 		}
714 
715 		for (i = 100000; i-- && mbox->acc_busy; DELAY(10));
716 
717 		if (mbox->acc_busy) {
718 			AMI_DPRINTF(AMI_D_CMD, ("mbox_busy "));
719 			return (EAGAIN);
720 		}
721 	}
722 
723 	AMI_DPRINTF(AMI_D_CMD, ("exec "));
724 
725 	cmd->acc_busy = 1;
726 	cmd->acc_poll = 0;
727 	cmd->acc_ack = 0;
728 
729 	if (!(i = (sc->sc_exec)(sc, cmd))) {
730 		ccb->ccb_state = AMI_CCB_QUEUED;
731 		TAILQ_INSERT_TAIL(&sc->sc_ccbq, ccb, ccb_link);
732 		if (!wait) {
733 #ifdef AMI_POLLING
734 			if (!timeout_pending(&sc->sc_poll_tmo))
735 				timeout_add(&sc->sc_poll_tmo, 1);
736 #endif
737 			if (xs)
738 				/*timeout_add(&xs->stimeout, hz * xs->timeout / 1000)*/;
739 		}
740 	} else if (!wait) {
741 		AMI_DPRINTF(AMI_D_CMD, ("2queue1(%d) ", cmd->acc_id));
742 		ccb->ccb_state = AMI_CCB_PREQUEUED;
743 		if (xs) {
744 			timeout_add(&xs->stimeout, 1);
745 			return (0);
746 		}
747 	}
748 
749 	return i;
750 }
751 
752 void
753 ami_stimeout(v)
754 	void *v;
755 {
756 	struct ami_ccb *ccb = v;
757 	struct ami_softc *sc = ccb->ccb_sc;
758 	struct scsi_xfer *xs = ccb->ccb_xs;
759 	struct ami_iocmd *cmd = ccb->ccb_cmd;
760 	volatile struct ami_iocmd *mbox = sc->sc_mbox;
761 	ami_lock_t lock;
762 
763 	switch (ccb->ccb_state) {
764 	case AMI_CCB_PREQUEUED:
765 		if (mbox->acc_busy) {
766 			timeout_add(&xs->stimeout, 1);
767 			return;
768 		}
769 
770 		AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) ", cmd->acc_id));
771 		ccb->ccb_state = AMI_CCB_READY;
772 
773 		lock = AMI_LOCK_AMI(sc);
774 		if (ami_start(ccb, 0)) {
775 			AMI_DPRINTF(AMI_D_CMD, ("requeue(%d) again\n", cmd->acc_id));
776 			ccb->ccb_state = AMI_CCB_PREQUEUED;
777 			timeout_add(&xs->stimeout, 1);
778 		}
779 		AMI_UNLOCK_AMI(sc, lock);
780 		break;
781 
782 	case AMI_CCB_QUEUED:
783 		/* XXX need to kill all cmds in the queue and reset the card */
784 		AMI_DPRINTF(AMI_D_CMD, ("timeout(%d) ", cmd->acc_id));
785 		lock = AMI_LOCK_AMI(sc);
786 		TAILQ_REMOVE(&sc->sc_ccbq, ccb, ccb_link);
787 		ami_put_ccb(ccb);
788 		AMI_UNLOCK_AMI(sc, lock);
789 		xs->error = XS_TIMEOUT;
790 		scsi_done(xs);
791 		break;
792 	case AMI_CCB_FREE:
793 	case AMI_CCB_READY:
794 		panic("ami_stimeout(%p) botch", cmd->acc_id);
795 	}
796 }
797 
798 int
799 ami_complete(ccb)
800 	struct ami_ccb *ccb;
801 {
802 	struct ami_softc *sc = ccb->ccb_sc;
803 	struct scsi_xfer *xs = ccb->ccb_xs;
804 	struct ami_iocmd mbox;
805 	int i, j, rv, status;
806 
807 	DELAY(10000000);
808 	for (rv = 1, status = 0, i = 1 * (xs? xs->timeout: 1000);
809 	     !status && rv && i--; DELAY(1000))
810 		if ((sc->sc_done)(sc, &mbox)) {
811 			AMI_DPRINTF(AMI_D_CMD, ("got#%d ", mbox.acc_nstat));
812 			status = mbox.acc_status;
813 			for (j = 0; j < mbox.acc_nstat; j++ ) {
814 				int ready = mbox.acc_cmplidl[j];
815 
816 				AMI_DPRINTF(AMI_D_CMD, ("ready=%x ", ready));
817 
818 				if (!ami_done(sc, ready) &&
819 				    ccb->ccb_cmd->acc_id == ready)
820 					rv = 0;
821 			}
822 		}
823 
824 	if (status) {
825 		AMI_DPRINTF(AMI_D_CMD, ("aborted\n"));
826 	} else if (!rv) {
827 		AMI_DPRINTF(AMI_D_CMD, ("complete\n"));
828 	} else if (i < 0) {
829 		AMI_DPRINTF(AMI_D_CMD, ("timeout\n"));
830 	} else
831 		AMI_DPRINTF(AMI_D_CMD, ("screwed\n"));
832 
833 	return rv? rv : status;
834 }
835 
836 int
837 ami_done(sc, idx)
838 	struct ami_softc *sc;
839 	int	idx;
840 {
841 	struct ami_ccb *ccb = &sc->sc_ccbs[idx - 1];
842 	struct scsi_xfer *xs = ccb->ccb_xs;
843 	ami_lock_t lock;
844 
845 	AMI_DPRINTF(AMI_D_CMD, ("done(%d) ", ccb->ccb_cmd->acc_id));
846 
847 	if (ccb->ccb_state != AMI_CCB_QUEUED) {
848 		printf("%s: unqueued ccb %d ready, state = %d\n",
849 		    sc->sc_dev.dv_xname, idx, ccb->ccb_state);
850 		return 1;
851 	}
852 
853 	lock = AMI_LOCK_AMI(sc);
854 	TAILQ_REMOVE(&sc->sc_ccbq, ccb, ccb_link);
855 	ccb->ccb_state = AMI_CCB_READY;
856 	AMI_UNLOCK_AMI(sc, lock);
857 
858 	if (xs) {
859 		timeout_del(&xs->stimeout);
860 		if (xs->cmd->opcode != PREVENT_ALLOW &&
861 		    xs->cmd->opcode != SYNCHRONIZE_CACHE) {
862 			bus_dmamap_sync(sc->dmat, ccb->ccb_dmamap,
863 			    (xs->flags & SCSI_DATA_IN) ?
864 			    BUS_DMASYNC_POSTREAD :
865 			    BUS_DMASYNC_POSTWRITE);
866 			bus_dmamap_unload(sc->dmat, ccb->ccb_dmamap);
867 		}
868 	} else {
869 		struct ami_iocmd *cmd = ccb->ccb_cmd;
870 
871 		switch (cmd->acc_cmd) {
872 		case AMI_INQUIRY:
873 		case AMI_EINQUIRY:
874 		case AMI_EINQUIRY3:
875 			bus_dmamap_sync(sc->dmat, ccb->ccb_dmamap,
876 			    BUS_DMASYNC_POSTREAD);
877 			bus_dmamap_unload(sc->dmat, ccb->ccb_dmamap);
878 			break;
879 		default:
880 			/* no data */
881 			break;
882 		}
883 	}
884 
885 	lock = AMI_LOCK_AMI(sc);
886 	ami_put_ccb(ccb);
887 	AMI_UNLOCK_AMI(sc, lock);
888 
889 	if (xs) {
890 		xs->resid = 0;
891 		xs->flags |= ITSDONE;
892 		AMI_DPRINTF(AMI_D_CMD, ("scsi_done(%d) ", idx));
893 		scsi_done(xs);
894 	}
895 
896 	return 0;
897 }
898 
899 void
900 amiminphys(bp)
901 	struct buf *bp;
902 {
903 	if (bp->b_bcount > AMI_MAXFER)
904 		bp->b_bcount = AMI_MAXFER;
905 	minphys(bp);
906 }
907 
908 void
909 ami_copy_internal_data(xs, v, size)
910 	struct scsi_xfer *xs;
911 	void *v;
912 	size_t size;
913 {
914 	size_t copy_cnt;
915 
916 	AMI_DPRINTF(AMI_D_MISC, ("ami_copy_internal_data "));
917 
918 	if (!xs->datalen)
919 		printf("uio move not yet supported\n");
920 	else {
921 		copy_cnt = MIN(size, xs->datalen);
922 		bcopy(v, xs->data, copy_cnt);
923 	}
924 }
925 
926 int
927 ami_scsi_raw_cmd(xs)
928 	struct scsi_xfer *xs;
929 {
930 	struct scsi_link *link = xs->sc_link;
931 	struct ami_rawsoftc *rsc = link->adapter_softc;
932 	struct ami_softc *sc = rsc->sc_softc;
933 	u_int8_t channel = rsc->sc_channel, target = link->target;
934 	struct ami_ccb *ccb, *ccb1;
935 	struct ami_iocmd *cmd;
936 	struct ami_passthrough *ps;
937 	int error;
938 	ami_lock_t lock;
939 
940 	AMI_DPRINTF(AMI_D_CMD, ("ami_scsi_raw_cmd "));
941 
942 	if (xs->cmdlen > AMI_MAX_CDB) {
943 		AMI_DPRINTF(AMI_D_CMD, ("CDB too big %p ", xs));
944 		bzero(&xs->sense, sizeof(xs->sense));
945 		xs->sense.error_code = SSD_ERRCODE_VALID | 0x70;
946 		xs->sense.flags = SKEY_ILLEGAL_REQUEST;
947 		xs->sense.add_sense_code = 0x20; /* illcmd, 0x24 illfield */
948 		xs->error = XS_SENSE;
949 		scsi_done(xs);
950 		return (COMPLETE);
951 	}
952 
953 	xs->error = XS_NOERROR;
954 
955 	lock = AMI_LOCK_AMI(sc);
956 
957 	if ((ccb = ami_get_ccb(sc)) == NULL) {
958 		AMI_UNLOCK_AMI(sc, lock);
959 		xs->error = XS_DRIVER_STUFFUP;
960 		scsi_done(xs);
961 		return (COMPLETE);
962 	}
963 
964 	if ((ccb1 = ami_get_ccb(sc)) == NULL) {
965 		ami_put_ccb(ccb);
966 		AMI_UNLOCK_AMI(sc, lock);
967 		xs->error = XS_DRIVER_STUFFUP;
968 		scsi_done(xs);
969 		return (COMPLETE);
970 	}
971 
972 	ccb->ccb_xs = xs;
973 	ccb->ccb_ccb1 = ccb1;
974 	ccb->ccb_len  = xs->datalen;
975 	ccb->ccb_data = xs->data;
976 
977 	ps = (struct ami_passthrough *)ccb1->ccb_cmd;
978 	ps->apt_param = AMI_PTPARAM(AMI_TIMEOUT_6,1,0);
979 	ps->apt_channel = channel;
980 	ps->apt_target = target;
981 	bcopy(xs->cmd, ps->apt_cdb, AMI_MAX_CDB);
982 	ps->apt_ncdb = xs->cmdlen;
983 	ps->apt_nsense = AMI_MAX_SENSE;
984 
985 	cmd = ccb->ccb_cmd;
986 	cmd->acc_cmd = AMI_PASSTHRU;
987 	cmd->acc_passthru.apt_data = ccb1->ccb_cmdpa;
988 
989 	if ((error = ami_cmd(ccb, ((xs->flags & SCSI_NOSLEEP)?
990 	    BUS_DMA_NOWAIT : BUS_DMA_WAITOK), xs->flags & SCSI_POLL))) {
991 
992 		AMI_UNLOCK_AMI(sc, lock);
993 		AMI_DPRINTF(AMI_D_CMD, ("failed %p ", xs));
994 		if (xs->flags & SCSI_POLL) {
995 			xs->error = XS_TIMEOUT;
996 			return (TRY_AGAIN_LATER);
997 		} else {
998 			xs->error = XS_DRIVER_STUFFUP;
999 			scsi_done(xs);
1000 			return (COMPLETE);
1001 		}
1002 	}
1003 
1004 	AMI_UNLOCK_AMI(sc, lock);
1005 
1006 	if (xs->flags & SCSI_POLL) {
1007 		scsi_done(xs);
1008 		return (COMPLETE);
1009 	}
1010 
1011 	return (SUCCESSFULLY_QUEUED);
1012 }
1013 
1014 int
1015 ami_scsi_cmd(xs)
1016 	struct scsi_xfer *xs;
1017 {
1018 	struct scsi_link *link = xs->sc_link;
1019 	struct ami_softc *sc = link->adapter_softc;
1020 	struct ami_ccb *ccb;
1021 	struct ami_iocmd *cmd;
1022 	struct scsi_inquiry_data inq;
1023 	struct scsi_sense_data sd;
1024 	struct {
1025 		struct scsi_mode_header hd;
1026 		struct scsi_blk_desc bd;
1027 		union scsi_disk_pages dp;
1028 	} mpd;
1029 	struct scsi_read_cap_data rcd;
1030 	u_int8_t target = link->target;
1031 	u_int32_t blockno, blockcnt;
1032 	struct scsi_rw *rw;
1033 	struct scsi_rw_big *rwb;
1034 	int error, flags;
1035 	ami_lock_t lock;
1036 
1037 	if (target >= sc->sc_nunits || !sc->sc_hdr[target].hd_present ||
1038 	    link->lun != 0) {
1039 		/* XXX should be XS_SENSE and sense filled out */
1040 		xs->error = XS_DRIVER_STUFFUP;
1041 		xs->flags |= ITSDONE;
1042 		scsi_done(xs);
1043 		return (COMPLETE);
1044 	}
1045 
1046 	AMI_DPRINTF(AMI_D_CMD, ("ami_scsi_cmd "));
1047 
1048 	error = 0;
1049 	xs->error = XS_NOERROR;
1050 
1051 	switch (xs->cmd->opcode) {
1052 	case TEST_UNIT_READY:
1053 	case START_STOP:
1054 #if 0
1055 	case VERIFY:
1056 #endif
1057 		AMI_DPRINTF(AMI_D_CMD, ("opc %d tgt %d ", xs->cmd->opcode,
1058 		    target));
1059 		break;
1060 
1061 	case REQUEST_SENSE:
1062 		AMI_DPRINTF(AMI_D_CMD, ("REQUEST SENSE tgt %d ", target));
1063 		bzero(&sd, sizeof sd);
1064 		sd.error_code = 0x70;
1065 		sd.segment = 0;
1066 		sd.flags = SKEY_NO_SENSE;
1067 		*(u_int32_t*)sd.info = htole32(0);
1068 		sd.extra_len = 0;
1069 		ami_copy_internal_data(xs, &sd, sizeof sd);
1070 		break;
1071 
1072 	case INQUIRY:
1073 		AMI_DPRINTF(AMI_D_CMD, ("INQUIRY tgt %d ", target));
1074 		bzero(&inq, sizeof inq);
1075 		inq.device = T_DIRECT;
1076 		inq.dev_qual2 = 0;
1077 		inq.version = 2;
1078 		inq.response_format = 2;
1079 		inq.additional_length = 32;
1080 		strcpy(inq.vendor, "AMI    ");
1081 		sprintf(inq.product, "Host drive  #%02d", target);
1082 		strcpy(inq.revision, "   ");
1083 		ami_copy_internal_data(xs, &inq, sizeof inq);
1084 		break;
1085 
1086 	case MODE_SENSE:
1087 		AMI_DPRINTF(AMI_D_CMD, ("MODE SENSE tgt %d ", target));
1088 
1089 		bzero(&mpd, sizeof mpd);
1090 		switch (((struct scsi_mode_sense *)xs->cmd)->page) {
1091 		case 4:
1092 			/* scsi_disk.h says this should be 0x16 */
1093 			mpd.dp.rigid_geometry.pg_length = 0x16;
1094 			mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd +
1095 			    mpd.dp.rigid_geometry.pg_length;
1096 			mpd.hd.blk_desc_len = sizeof mpd.bd;
1097 
1098 			mpd.hd.dev_spec = 0;	/* writeprotect ? XXX */
1099 			_lto3b(AMI_SECTOR_SIZE, mpd.bd.blklen);
1100 			mpd.dp.rigid_geometry.pg_code = 4;
1101 			_lto3b(sc->sc_hdr[target].hd_size /
1102 			    sc->sc_hdr[target].hd_heads /
1103 			    sc->sc_hdr[target].hd_secs,
1104 			    mpd.dp.rigid_geometry.ncyl);
1105 			mpd.dp.rigid_geometry.nheads =
1106 			    sc->sc_hdr[target].hd_heads;
1107 			ami_copy_internal_data(xs, (u_int8_t *)&mpd,
1108 			    sizeof mpd);
1109 			break;
1110 
1111 		default:
1112 			printf("%s: mode sense page %d not simulated\n",
1113 			    sc->sc_dev.dv_xname,
1114 			    ((struct scsi_mode_sense *)xs->cmd)->page);
1115 			xs->error = XS_DRIVER_STUFFUP;
1116 		}
1117 		break;
1118 
1119 	case READ_CAPACITY:
1120 		AMI_DPRINTF(AMI_D_CMD, ("READ CAPACITY tgt %d ", target));
1121 		bzero(&rcd, sizeof rcd);
1122 		_lto4b(sc->sc_hdr[target].hd_size - 1, rcd.addr);
1123 		_lto4b(AMI_SECTOR_SIZE, rcd.length);
1124 		ami_copy_internal_data(xs, &rcd, sizeof rcd);
1125 		break;
1126 
1127 	case PREVENT_ALLOW:
1128 		AMI_DPRINTF(AMI_D_CMD, ("PREVENT/ALLOW "));
1129 		return (COMPLETE);
1130 
1131 	case SYNCHRONIZE_CACHE:
1132 		AMI_DPRINTF(AMI_D_CMD, ("SYNCHRONIZE CACHE "));
1133 		error++;
1134 	case READ_COMMAND:
1135 		if (!error) {
1136 			AMI_DPRINTF(AMI_D_CMD, ("READ "));
1137 			error++;
1138 		}
1139 	case READ_BIG:
1140 		if (!error) {
1141 			AMI_DPRINTF(AMI_D_CMD, ("READ BIG "));
1142 			error++;
1143 		}
1144 	case WRITE_COMMAND:
1145 		if (!error) {
1146 			AMI_DPRINTF(AMI_D_CMD, ("WRITE "));
1147 			error++;
1148 		}
1149 	case WRITE_BIG:
1150 		if (!error) {
1151 			AMI_DPRINTF(AMI_D_CMD, ("WRITE BIG "));
1152 			error++;
1153 		}
1154 		lock = AMI_LOCK_AMI(sc);
1155 
1156 		flags = 0;
1157 		if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
1158 			/* A read or write operation. */
1159 			if (xs->cmdlen == 6) {
1160 				rw = (struct scsi_rw *)xs->cmd;
1161 				blockno = _3btol(rw->addr) &
1162 				    (SRW_TOPADDR << 16 | 0xffff);
1163 				blockcnt = rw->length ? rw->length : 0x100;
1164 			} else {
1165 				rwb = (struct scsi_rw_big *)xs->cmd;
1166 				blockno = _4btol(rwb->addr);
1167 				blockcnt = _2btol(rwb->length);
1168 				/* TODO: reflect DPO & FUA flags */
1169 				if (xs->cmd->opcode == WRITE_BIG &&
1170 				    rwb->byte2 & 0x18)
1171 					flags = 0;
1172 			}
1173 			if (blockno >= sc->sc_hdr[target].hd_size ||
1174 			    blockno + blockcnt > sc->sc_hdr[target].hd_size) {
1175 				AMI_UNLOCK_AMI(sc, lock);
1176 				printf("%s: out of bounds %u-%u >= %u\n",
1177 				    sc->sc_dev.dv_xname, blockno, blockcnt,
1178 				    sc->sc_hdr[target].hd_size);
1179 				xs->error = XS_DRIVER_STUFFUP;
1180 				break;
1181 			}
1182 		}
1183 
1184 		if ((ccb = ami_get_ccb(sc)) == NULL) {
1185 			AMI_UNLOCK_AMI(sc, lock);
1186 			AMI_DPRINTF(AMI_D_CMD, ("no more ccbs "));
1187 			xs->error = XS_DRIVER_STUFFUP;
1188 		__asm __volatile(".globl _bpamiccb\n_bpamiccb:");
1189 			break;
1190 		}
1191 
1192 		ccb->ccb_xs = xs;
1193 		ccb->ccb_ccb1 = NULL;
1194 		ccb->ccb_len  = xs->datalen;
1195 		ccb->ccb_data = xs->data;
1196 		cmd = ccb->ccb_cmd;
1197 		cmd->acc_mbox.amb_nsect = htole16(blockcnt);
1198 		cmd->acc_mbox.amb_lba = htole32(blockno);
1199 		cmd->acc_mbox.amb_ldn = target;
1200 		cmd->acc_mbox.amb_data = 0;
1201 
1202 		switch (xs->cmd->opcode) {
1203 		case SYNCHRONIZE_CACHE:
1204 			cmd->acc_cmd = AMI_FLUSH;
1205 			/* XXX do other fields matter ? */
1206 			break;
1207 		case READ_COMMAND: case READ_BIG:
1208 			cmd->acc_cmd = AMI_READ;
1209 			break;
1210 		case WRITE_COMMAND: case WRITE_BIG:
1211 			cmd->acc_cmd = AMI_WRITE;
1212 			break;
1213 		}
1214 
1215 		if ((error = ami_cmd(ccb, ((xs->flags & SCSI_NOSLEEP)?
1216 		    BUS_DMA_NOWAIT : BUS_DMA_WAITOK), xs->flags & SCSI_POLL))) {
1217 
1218 			AMI_UNLOCK_AMI(sc, lock);
1219 			AMI_DPRINTF(AMI_D_CMD, ("failed %p ", xs));
1220 		__asm __volatile(".globl _bpamifail\n_bpamifail:");
1221 			if (xs->flags & SCSI_POLL) {
1222 				xs->error = XS_TIMEOUT;
1223 				return (TRY_AGAIN_LATER);
1224 			} else {
1225 				xs->error = XS_DRIVER_STUFFUP;
1226 				break;
1227 			}
1228 		}
1229 
1230 		AMI_UNLOCK_AMI(sc, lock);
1231 		if (xs->flags & SCSI_POLL)
1232 			break;
1233 		return (SUCCESSFULLY_QUEUED);
1234 
1235 	default:
1236 		AMI_DPRINTF(AMI_D_CMD, ("unknown opc %d ", xs->cmd->opcode));
1237 		xs->error = XS_DRIVER_STUFFUP;
1238 	}
1239 
1240 	xs->flags |= ITSDONE;
1241 	scsi_done(xs);
1242 	return (xs->flags & SCSI_POLL? COMPLETE : SUCCESSFULLY_QUEUED);
1243 }
1244 
1245 int
1246 ami_intr(v)
1247 	void *v;
1248 {
1249 	struct ami_softc *sc = v;
1250 	struct ami_iocmd mbox;
1251 	int i, rv = 0;
1252 	ami_lock_t lock;
1253 
1254 	if (TAILQ_EMPTY(&sc->sc_ccbq))
1255 		return (0);
1256 
1257 	AMI_DPRINTF(AMI_D_INTR, ("intr "));
1258 
1259 	lock = AMI_LOCK_AMI(sc);
1260 	while ((sc->sc_done)(sc, &mbox)) {
1261 		AMI_UNLOCK_AMI(sc, lock);
1262 		AMI_DPRINTF(AMI_D_CMD, ("got#%d ", mbox.acc_nstat));
1263 		for (i = 0; i < mbox.acc_nstat; i++ ) {
1264 			register int ready = mbox.acc_cmplidl[i];
1265 
1266 			AMI_DPRINTF(AMI_D_CMD, ("ready=%d ", ready));
1267 
1268 			if (!ami_done(sc, ready))
1269 				rv = 1;
1270 		}
1271 		lock = AMI_LOCK_AMI(sc);
1272 	}
1273 
1274 #ifdef AMI_POLLING
1275 	if (!TAILQ_EMPTY(&sc->sc_ccbq) && !timeout_pending(&sc->sc_poll_tmo)) {
1276 		AMI_DPRINTF(AMI_D_INTR, ("tmo "));
1277 		timeout_add(&sc->sc_poll_tmo, 100);
1278 	}
1279 #endif
1280 
1281 	AMI_UNLOCK_AMI(sc, lock);
1282 	return rv;
1283 }
1284