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