xref: /openbsd-src/sys/dev/ic/cac.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: cac.c,v 1.5 2001/04/23 10:47:56 art Exp $	*/
2 /*	$NetBSD: cac.c,v 1.15 2000/11/08 19:20:35 ad Exp $	*/
3 
4 /*
5  * Copyright (c) 2000 Michael Shalayeff
6  * All rights reserved.
7  *
8  * The SCSI emulation layer is derived from gdt(4) driver,
9  * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by Michael Shalayeff.
22  * 4. The name of the author may not be used to endorse or promote products
23  *    derived from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
34  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
35  * THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 /*-
38  * Copyright (c) 2000 The NetBSD Foundation, Inc.
39  * All rights reserved.
40  *
41  * This code is derived from software contributed to The NetBSD Foundation
42  * by Andrew Doran.
43  *
44  * Redistribution and use in source and binary forms, with or without
45  * modification, are permitted provided that the following conditions
46  * are met:
47  * 1. Redistributions of source code must retain the above copyright
48  *    notice, this list of conditions and the following disclaimer.
49  * 2. Redistributions in binary form must reproduce the above copyright
50  *    notice, this list of conditions and the following disclaimer in the
51  *    documentation and/or other materials provided with the distribution.
52  * 3. All advertising materials mentioning features or use of this software
53  *    must display the following acknowledgement:
54  *        This product includes software developed by the NetBSD
55  *        Foundation, Inc. and its contributors.
56  * 4. Neither the name of The NetBSD Foundation nor the names of its
57  *    contributors may be used to endorse or promote products derived
58  *    from this software without specific prior written permission.
59  *
60  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
61  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
62  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
63  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
64  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
65  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
66  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
67  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
68  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
69  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
70  * POSSIBILITY OF SUCH DAMAGE.
71  */
72 
73 /*
74  * Driver for Compaq array controllers.
75  */
76 #undef CAC_DEBUG
77 
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/kernel.h>
81 #include <sys/device.h>
82 #include <sys/queue.h>
83 #include <sys/proc.h>
84 #include <sys/buf.h>
85 #include <sys/endian.h>
86 #include <sys/malloc.h>
87 #include <sys/pool.h>
88 
89 #include <vm/vm.h>
90 #include <uvm/uvm_extern.h>
91 
92 #include <machine/bus.h>
93 
94 #include <scsi/scsi_all.h>
95 #include <scsi/scsi_disk.h>
96 #include <scsi/scsiconf.h>
97 
98 #include <dev/ic/cacreg.h>
99 #include <dev/ic/cacvar.h>
100 
101 struct cfdriver cac_cd = {
102 	NULL, "cac", DV_DULL
103 };
104 
105 int     cac_scsi_cmd __P((struct scsi_xfer *));
106 void	cacminphys __P((struct buf *bp));
107 
108 struct scsi_adapter cac_switch = {
109 	cac_scsi_cmd, cacminphys, 0, 0,
110 };
111 
112 struct scsi_device cac_dev = {
113 	NULL, NULL, NULL, NULL
114 };
115 
116 struct	cac_ccb *cac_ccb_alloc(struct cac_softc *, int);
117 void	cac_ccb_done(struct cac_softc *, struct cac_ccb *);
118 void	cac_ccb_free(struct cac_softc *, struct cac_ccb *);
119 int	cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int);
120 int	cac_ccb_start(struct cac_softc *, struct cac_ccb *);
121 int	cac_cmd(struct cac_softc *sc, int command, void *data, int datasize,
122 	int drive, int blkno, int flags, struct scsi_xfer *xs);
123 int	cac_get_dinfo __P((struct cac_softc *sc, int target));
124 int	cac_flush __P((struct cac_softc *sc));
125 void	cac_shutdown __P((void *));
126 void	cac_copy_internal_data __P((struct scsi_xfer *xs, void *v, size_t size));
127 
128 struct	cac_ccb *cac_l0_completed(struct cac_softc *);
129 int	cac_l0_fifo_full(struct cac_softc *);
130 void	cac_l0_intr_enable(struct cac_softc *, int);
131 int	cac_l0_intr_pending(struct cac_softc *);
132 void	cac_l0_submit(struct cac_softc *, struct cac_ccb *);
133 
134 void	*cac_sdh;	/* shutdown hook */
135 
136 const
137 struct cac_linkage cac_l0 = {
138 	cac_l0_completed,
139 	cac_l0_fifo_full,
140 	cac_l0_intr_enable,
141 	cac_l0_intr_pending,
142 	cac_l0_submit
143 };
144 
145 /*
146  * Initialise our interface to the controller.
147  */
148 int
149 cac_init(struct cac_softc *sc, int startfw)
150 {
151 	struct cac_controller_info cinfo;
152 	int error, rseg, size, i;
153 	bus_dma_segment_t seg;
154 	struct cac_ccb *ccb;
155 
156 	SIMPLEQ_INIT(&sc->sc_ccb_free);
157 	SIMPLEQ_INIT(&sc->sc_ccb_queue);
158 
159         size = sizeof(struct cac_ccb) * CAC_MAX_CCBS;
160 
161 	if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1,
162 	    &rseg, BUS_DMA_NOWAIT)) != 0) {
163 		printf("%s: unable to allocate CCBs, error = %d\n",
164 		    sc->sc_dv.dv_xname, error);
165 		return (-1);
166 	}
167 
168 	if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size,
169 	    (caddr_t *)&sc->sc_ccbs,
170 	    BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) {
171 		printf("%s: unable to map CCBs, error = %d\n",
172 		    sc->sc_dv.dv_xname, error);
173 		return (-1);
174 	}
175 
176 	if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
177 	    BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
178 		printf("%s: unable to create CCB DMA map, error = %d\n",
179 		    sc->sc_dv.dv_xname, error);
180 		return (-1);
181 	}
182 
183 	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs,
184 	    size, NULL, BUS_DMA_NOWAIT)) != 0) {
185 		printf("%s: unable to load CCB DMA map, error = %d\n",
186 		    sc->sc_dv.dv_xname, error);
187 		return (-1);
188 	}
189 
190 	sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr;
191 	memset(sc->sc_ccbs, 0, size);
192 	ccb = (struct cac_ccb *)sc->sc_ccbs;
193 
194 	for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) {
195 		/* Create the DMA map for this CCB's data */
196 		error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER,
197 		    CAC_SG_SIZE, CAC_MAX_XFER, 0,
198 		    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
199 		    &ccb->ccb_dmamap_xfer);
200 
201 		if (error) {
202 			printf("%s: can't create ccb dmamap (%d)\n",
203 			    sc->sc_dv.dv_xname, error);
204 			break;
205 		}
206 
207 		ccb->ccb_flags = 0;
208 		ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb);
209 		SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain);
210 	}
211 
212 	/* Start firmware background tasks, if needed. */
213 	if (startfw) {
214 		if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo),
215 		    0, 0, CAC_CCB_DATA_IN, NULL)) {
216 			printf("%s: CAC_CMD_START_FIRMWARE failed\n",
217 			    sc->sc_dv.dv_xname);
218 			return (-1);
219 		}
220 	}
221 
222 	if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0,
223 	    CAC_CCB_DATA_IN, NULL)) {
224 		printf("%s: CAC_CMD_GET_CTRL_INFO failed\n",
225 		    sc->sc_dv.dv_xname);
226 		return (-1);
227 	}
228 
229 	if (!cinfo.num_drvs) {
230 		printf("%s: no volumes defined\n", sc->sc_dv.dv_xname);
231 		return (-1);
232 	}
233 
234 	sc->sc_nunits = cinfo.num_drvs;
235 	sc->sc_dinfos = malloc(cinfo.num_drvs * sizeof(struct cac_drive_info),
236 	    M_DEVBUF, M_NOWAIT);
237 	if (sc->sc_dinfos == NULL) {
238 		printf("%s: cannot allocate memory for drive_info\n",
239 		    sc->sc_dv.dv_xname);
240 		return (-1);
241 	}
242 	bzero(sc->sc_dinfos, cinfo.num_drvs * sizeof(struct cac_drive_info));
243 
244 	sc->sc_link.adapter_softc = sc;
245 	sc->sc_link.adapter = &cac_switch;
246 	sc->sc_link.adapter_target = cinfo.num_drvs;
247 	sc->sc_link.adapter_buswidth = cinfo.num_drvs;
248 	sc->sc_link.device = &cac_dev;
249 	sc->sc_link.openings = CAC_MAX_CCBS / sc->sc_nunits;
250 	if (sc->sc_link.openings < 4 )
251 		sc->sc_link.openings = 4;
252 
253 	config_found(&sc->sc_dv, &sc->sc_link, scsiprint);
254 
255 	/* Set our `shutdownhook' before we start any device activity. */
256 	if (cac_sdh == NULL)
257 		cac_sdh = shutdownhook_establish(cac_shutdown, NULL);
258 
259 	(*sc->sc_cl->cl_intr_enable)(sc, 1);
260 	return (0);
261 }
262 
263 int
264 cac_flush(sc)
265 	struct cac_softc *sc;
266 {
267 	u_int8_t buf[512];
268 
269 	memset(buf, 0, sizeof(buf));
270 	buf[0] = 1;
271 	return cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0,
272 	    CAC_CCB_DATA_OUT, NULL);
273 }
274 
275 /*
276  * Shut down all `cac' controllers.
277  */
278 void
279 cac_shutdown(void *cookie)
280 {
281 	extern struct cfdriver cac_cd;
282 	struct cac_softc *sc;
283 	int i;
284 
285 	for (i = 0; i < cac_cd.cd_ndevs; i++) {
286 		if ((sc = (struct cac_softc *)device_lookup(&cac_cd, i)) == NULL)
287 			continue;
288 		cac_flush(sc);
289 	}
290 }
291 
292 /*
293  * Handle an interrupt from the controller: process finished CCBs and
294  * dequeue any waiting CCBs.
295  */
296 int
297 cac_intr(v)
298 	void *v;
299 {
300 	struct cac_softc *sc = v;
301 	struct cac_ccb *ccb;
302 	int ret = 0;
303 
304 	if (!(sc->sc_cl->cl_intr_pending)(sc))
305 		return 0;
306 
307 	while ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) {
308 		ret++;
309 		cac_ccb_done(sc, ccb);
310 		cac_ccb_start(sc, NULL);
311 	}
312 
313 	return (ret);
314 }
315 
316 /*
317  * Execute a [polled] command.
318  */
319 int
320 cac_cmd(struct cac_softc *sc, int command, void *data, int datasize,
321 	int drive, int blkno, int flags, struct scsi_xfer *xs)
322 {
323 	struct cac_ccb *ccb;
324 	struct cac_sgb *sgb;
325 	int i, rv, size, nsegs;
326 
327 #ifdef CAC_DEBUG
328 	printf("cac_cmd op=%x drv=%d blk=%d data=%p[%x] fl=%x xs=%p ",
329 	    command, drive, blkno, data, datasize, flags, xs);
330 #endif
331 
332 	if ((ccb = cac_ccb_alloc(sc, 0)) == NULL) {
333 		printf("%s: unable to alloc CCB", sc->sc_dv.dv_xname);
334 		return (ENOMEM);
335 	}
336 
337 	if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
338 		bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer,
339 		    (void *)data, datasize, NULL, BUS_DMA_NOWAIT);
340 
341 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer,
342 		    (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD :
343 		    BUS_DMASYNC_PREWRITE);
344 
345 		sgb = ccb->ccb_seg;
346 		nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE);
347 
348 		size = 0;
349 		for (i = 0; i < nsegs; i++, sgb++) {
350 			size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len;
351 			sgb->length =
352 			    htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len);
353 			sgb->addr =
354 			    htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr);
355 		}
356 	} else {
357 		size = datasize;
358 		nsegs = 0;
359 	}
360 
361 	ccb->ccb_hdr.drive = drive;
362 	ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) +
363 	    sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2);
364 
365 	ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE));
366 	ccb->ccb_req.command = command;
367 	ccb->ccb_req.sgcount = nsegs;
368 	ccb->ccb_req.blkno = htole32(blkno);
369 
370 	ccb->ccb_flags = flags;
371 	ccb->ccb_datasize = size;
372 	ccb->ccb_xs = xs;
373 
374 	if (!xs || xs->flags & SCSI_POLL) {
375 		int s;
376 
377 		s = splbio();
378 
379 		/* Synchronous commands musn't wait. */
380 		if ((*sc->sc_cl->cl_fifo_full)(sc)) {
381 			cac_ccb_free(sc, ccb);
382 			rv = -1;
383 		} else {
384 			ccb->ccb_flags |= CAC_CCB_ACTIVE;
385 			(*sc->sc_cl->cl_submit)(sc, ccb);
386 			rv = cac_ccb_poll(sc, ccb, 2000);
387 		}
388 		splx(s);
389 	} else
390 		rv = cac_ccb_start(sc, ccb);
391 
392 	return (rv);
393 }
394 
395 /*
396  * Wait for the specified CCB to complete.  Must be called at splbio.
397  */
398 int
399 cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo)
400 {
401 	struct cac_ccb *ccb;
402 
403 	timo *= 10;
404 
405 	do {
406 		for (; timo > 0; timo--) {
407 			if ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL)
408 				break;
409 			DELAY(100);
410 		}
411 
412 		if (timo <= 0) {
413 			printf("%s: timeout\n", sc->sc_dv.dv_xname);
414 			return (EBUSY);
415 		}
416 		cac_ccb_done(sc, ccb);
417 	} while (ccb != wantccb);
418 
419 	return (0);
420 }
421 
422 /*
423  * Enqueue the specifed command (if any) and attempt to start all enqueued
424  * commands.  Must be called at splbio.
425  */
426 int
427 cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb)
428 {
429 
430 	if (ccb != NULL)
431 		SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain);
432 
433 	while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) {
434 		if ((*sc->sc_cl->cl_fifo_full)(sc))
435 			return (EBUSY);
436 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb, ccb_chain);
437 		ccb->ccb_flags |= CAC_CCB_ACTIVE;
438 		(*sc->sc_cl->cl_submit)(sc, ccb);
439 	}
440 
441 	return (0);
442 }
443 
444 /*
445  * Process a finished CCB.
446  */
447 void
448 cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb)
449 {
450 	struct scsi_xfer *xs = ccb->ccb_xs;
451 	int error = 0;
452 
453 	if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) {
454 #ifdef CAC_DEBUG
455 		printf("%s: CCB not active, xs=%p\n", sc->sc_dv.dv_xname, xs);
456 #endif
457 		if (xs) {
458 			xs->error = XS_DRIVER_STUFFUP;
459 			scsi_done(xs);
460 		}
461 		return;
462 	}
463 	ccb->ccb_flags &= ~CAC_CCB_ACTIVE;
464 
465 	if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) {
466 		bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer,
467 		    ccb->ccb_flags & CAC_CCB_DATA_IN ?
468 		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
469 		bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap_xfer);
470 	}
471 
472 	if ((ccb->ccb_req.error & CAC_RET_SOFT_ERROR) != 0)
473 		printf("%s: soft error; corrected\n", sc->sc_dv.dv_xname);
474 	if ((ccb->ccb_req.error & CAC_RET_HARD_ERROR) != 0) {
475 		error = 1;
476 		printf("%s: hard error\n", sc->sc_dv.dv_xname);
477 	}
478 	if ((ccb->ccb_req.error & CAC_RET_CMD_REJECTED) != 0) {
479 		error = 1;
480 		printf("%s: invalid request\n", sc->sc_dv.dv_xname);
481 	}
482 
483 	if (xs) {
484 		if (error)
485 			xs->error = XS_DRIVER_STUFFUP;
486 		else {
487 			xs->resid = 0;
488 			xs->flags |= ITSDONE;
489 		}
490 
491 		scsi_done(xs);
492 	}
493 	cac_ccb_free(sc, ccb);
494 }
495 
496 /*
497  * Allocate a CCB.
498  */
499 struct cac_ccb *
500 cac_ccb_alloc(struct cac_softc *sc, int nosleep)
501 {
502 	struct cac_ccb *ccb;
503 	int s;
504 
505 	s = splbio();
506 
507 	for (;;) {
508 		if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) {
509 			SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
510 			break;
511 		}
512 		if (nosleep) {
513 			ccb = NULL;
514 			break;
515 		}
516 		tsleep(&sc->sc_ccb_free, PRIBIO, "cacccb", 0);
517 	}
518 
519 	splx(s);
520 	return (ccb);
521 }
522 
523 /*
524  * Put a CCB onto the freelist.
525  */
526 void
527 cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb)
528 {
529 	int s;
530 
531 	ccb->ccb_flags = 0;
532 	s = splbio();
533 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain);
534 	if (SIMPLEQ_NEXT(ccb, ccb_chain) == NULL)
535 		wakeup(&sc->sc_ccb_free);
536 	splx(s);
537 }
538 
539 int
540 cac_get_dinfo(sc, target)
541 	struct cac_softc *sc;
542 	int target;
543 {
544 	if (sc->sc_dinfos[target].ncylinders)
545 		return (0);
546 
547 	if (cac_cmd(sc, CAC_CMD_GET_LOG_DRV_INFO, &sc->sc_dinfos[target],
548 	    sizeof(*sc->sc_dinfos), target, 0, CAC_CCB_DATA_IN, NULL)) {
549 		printf("%s: CMD_GET_LOG_DRV_INFO failed\n",
550 		    sc->sc_dv.dv_xname);
551 		return (-1);
552 	}
553 
554 	return (0);
555 }
556 
557 void
558 cacminphys(bp)
559 	struct buf *bp;
560 {
561 	if (bp->b_bcount > CAC_MAX_XFER)
562 		bp->b_bcount = CAC_MAX_XFER;
563 	minphys(bp);
564 }
565 
566 void
567 cac_copy_internal_data(xs, v, size)
568 	struct scsi_xfer *xs;
569 	void *v;
570 	size_t size;
571 {
572 	size_t copy_cnt;
573 
574 	if (!xs->datalen)
575 		printf("uio move is not yet supported\n");
576 	else {
577 		copy_cnt = MIN(size, xs->datalen);
578 		bcopy(v, xs->data, copy_cnt);
579 	}
580 }
581 
582 int
583 cac_scsi_cmd(xs)
584 	struct scsi_xfer *xs;
585 {
586 	struct scsi_link *link = xs->sc_link;
587 	struct cac_softc *sc = link->adapter_softc;
588 	struct cac_drive_info *dinfo;
589 	struct scsi_inquiry_data inq;
590 	struct scsi_sense_data sd;
591 	struct {
592 		struct scsi_mode_header hd;
593 		struct scsi_blk_desc bd;
594 		union scsi_disk_pages dp;
595 	} mpd;
596 	struct scsi_read_cap_data rcd;
597 	u_int8_t target = link->target;
598 	u_int32_t blockno, blockcnt, size;
599 	struct scsi_rw *rw;
600 	struct scsi_rw_big *rwb;
601 	int op, flags, s;
602 	const char *p;
603 
604 	if (target >= sc->sc_nunits || link->lun != 0) {
605 		xs->error = XS_DRIVER_STUFFUP;
606 		return (COMPLETE);
607 	}
608 
609 	xs->error = XS_NOERROR;
610 	dinfo = &sc->sc_dinfos[target];
611 
612 	switch (xs->cmd->opcode) {
613 	case TEST_UNIT_READY:
614 	case START_STOP:
615 #if 0
616 	case VERIFY:
617 #endif
618 		break;
619 
620 	case REQUEST_SENSE:
621 		bzero(&sd, sizeof sd);
622 		sd.error_code = 0x70;
623 		sd.segment = 0;
624 		sd.flags = SKEY_NO_SENSE;
625 		*(u_int32_t*)sd.info = htole32(0);
626 		sd.extra_len = 0;
627 		cac_copy_internal_data(xs, &sd, sizeof sd);
628 		break;
629 
630 	case INQUIRY:
631 		if (cac_get_dinfo(sc, target)) {
632 			xs->error = XS_DRIVER_STUFFUP;
633 			scsi_done(xs);
634 			return (COMPLETE);
635 		}
636 		bzero(&inq, sizeof inq);
637 		inq.device = T_DIRECT;
638 		inq.dev_qual2 = 0;
639 		inq.version = 2;
640 		inq.response_format = 2;
641 		inq.additional_length = 32;
642 		strcpy(inq.vendor, "Compaq  ");
643 		switch (CAC_GET1(dinfo->mirror)) {
644 		case 0: p = "RAID0";	break;
645 		case 1: p = "RAID4";	break;
646 		case 2: p = "RAID1";	break;
647 		case 3: p = "RAID5";	break;
648 		default:p = "<UNK>";	break;
649 		}
650 		sprintf(inq.product, "%s volume  #%02d", p, target);
651 		strcpy(inq.revision, "   ");
652 		cac_copy_internal_data(xs, &inq, sizeof inq);
653 		break;
654 
655 	case MODE_SENSE:
656 		if (cac_get_dinfo(sc, target)) {
657 			xs->error = XS_DRIVER_STUFFUP;
658 			scsi_done(xs);
659 			return (COMPLETE);
660 		}
661 		bzero(&mpd, sizeof mpd);
662 		switch (((struct scsi_mode_sense *)xs->cmd)->page) {
663 		case 4:
664 			/* scsi_disk.h says this should be 0x16 */
665 			mpd.dp.rigid_geometry.pg_length = 0x16;
666 			mpd.hd.data_length = sizeof mpd.hd + sizeof mpd.bd +
667 			    mpd.dp.rigid_geometry.pg_length;
668 			mpd.hd.blk_desc_len = sizeof mpd.bd;
669 
670 			/* XXX */
671 			mpd.hd.dev_spec = 0;
672 			_lto3b(CAC_SECTOR_SIZE, mpd.bd.blklen);
673 			mpd.dp.rigid_geometry.pg_code = 4;
674 			_lto3b(CAC_GET2(dinfo->ncylinders),
675 			    mpd.dp.rigid_geometry.ncyl);
676 			mpd.dp.rigid_geometry.nheads =
677 			    CAC_GET1(dinfo->nheads);
678 			cac_copy_internal_data(xs, (u_int8_t *)&mpd,
679 			    sizeof mpd);
680 			break;
681 
682 		default:
683 			printf("%s: mode sense page %d not simulated\n",
684 			    sc->sc_dv.dv_xname,
685 			    ((struct scsi_mode_sense *)xs->cmd)->page);
686 			xs->error = XS_DRIVER_STUFFUP;
687 			return (TRY_AGAIN_LATER);
688 		}
689 		break;
690 
691 	case READ_CAPACITY:
692 		if (cac_get_dinfo(sc, target)) {
693 			xs->error = XS_DRIVER_STUFFUP;
694 			scsi_done(xs);
695 			return (COMPLETE);
696 		}
697 		bzero(&rcd, sizeof rcd);
698 		_lto4b( CAC_GET2(dinfo->ncylinders) * CAC_GET1(dinfo->nheads) *
699 		    CAC_GET1(dinfo->nsectors), rcd.addr);
700 		_lto4b(CAC_SECTOR_SIZE, rcd.length);
701 		cac_copy_internal_data(xs, &rcd, sizeof rcd);
702 		break;
703 
704 	case PREVENT_ALLOW:
705 		return (COMPLETE);
706 
707 	case SYNCHRONIZE_CACHE:
708 		s = splbio();
709 		if (cac_flush(sc)) {
710 			splx(s);
711 			xs->error = XS_DRIVER_STUFFUP;
712 			scsi_done(xs);
713 		} else
714 			splx(s);
715 		return (COMPLETE);
716 
717 
718 	case READ_COMMAND:
719 	case READ_BIG:
720 	case WRITE_COMMAND:
721 	case WRITE_BIG:
722 		s = splbio();
723 
724 		flags = 0;
725 		if (xs->cmd->opcode != SYNCHRONIZE_CACHE) {
726 			/* A read or write operation. */
727 			if (xs->cmdlen == 6) {
728 				rw = (struct scsi_rw *)xs->cmd;
729 				blockno = _3btol(rw->addr) &
730 				    (SRW_TOPADDR << 16 | 0xffff);
731 				blockcnt = rw->length ? rw->length : 0x100;
732 			} else {
733 				rwb = (struct scsi_rw_big *)xs->cmd;
734 				blockno = _4btol(rwb->addr);
735 				blockcnt = _2btol(rwb->length);
736 			}
737 			size = CAC_GET2(dinfo->ncylinders) *
738 			    CAC_GET1(dinfo->nheads) *
739 			    CAC_GET1(dinfo->nsectors);
740 			if (blockno >= size || blockno + blockcnt > size) {
741 				splx(s);
742 				printf("%s: out of bounds %u-%u >= %u\n",
743 				    sc->sc_dv.dv_xname, blockno, blockcnt,
744 				    size);
745 				xs->error = XS_DRIVER_STUFFUP;
746 				scsi_done(xs);
747 				return (COMPLETE);
748 			}
749 		}
750 
751 		switch (xs->cmd->opcode) {
752 		case READ_COMMAND:
753 		case READ_BIG:
754 			op = CAC_CMD_READ;
755 			flags = CAC_CCB_DATA_IN;
756 			break;
757 		case WRITE_COMMAND:
758 		case WRITE_BIG:
759 			op = CAC_CMD_WRITE;
760 			flags = CAC_CCB_DATA_OUT;
761 			break;
762 		}
763 
764 		if (cac_cmd(sc, op, xs->data, blockcnt * DEV_BSIZE, target,
765 		    blockno, flags, xs)) {
766 
767 			splx(s);
768 			if (xs->flags & SCSI_POLL) {
769 				xs->error = XS_TIMEOUT;
770 				return (TRY_AGAIN_LATER);
771 			} else {
772 				xs->error = XS_DRIVER_STUFFUP;
773 				scsi_done(xs);
774 				return (COMPLETE);
775 			}
776 		}
777 
778 		splx(s);
779 
780 		if (xs->flags & SCSI_POLL)
781 			return (COMPLETE);
782 		else
783 			return (SUCCESSFULLY_QUEUED);
784 
785 	default:
786 		xs->error = XS_DRIVER_STUFFUP;
787 	}
788 
789 	return (COMPLETE);
790 }
791 
792 /*
793  * Board specific linkage shared between multiple bus types.
794  */
795 
796 int
797 cac_l0_fifo_full(struct cac_softc *sc)
798 {
799 
800 	return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0);
801 }
802 
803 void
804 cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb)
805 {
806 #ifdef CAC_DEBUG
807 	printf("submit-%x ", ccb->ccb_paddr);
808 #endif
809 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
810 	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
811 	cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr);
812 }
813 
814 struct cac_ccb *
815 cac_l0_completed(sc)
816 	struct cac_softc *sc;
817 {
818 	struct cac_ccb *ccb;
819 	paddr_t off;
820 
821 	if (!(off = cac_inl(sc, CAC_REG_DONE_FIFO)))
822 		return NULL;
823 #ifdef CAC_DEBUG
824 	printf("compl-%x ", off);
825 #endif
826 	ccb = (struct cac_ccb *)(sc->sc_ccbs +
827 	    ((off & ~3) - sc->sc_ccbs_paddr));
828 
829 	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
830 	    BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
831 
832 	return (ccb);
833 }
834 
835 int
836 cac_l0_intr_pending(struct cac_softc *sc)
837 {
838 
839 	return (cac_inl(sc, CAC_REG_INTR_PENDING));
840 }
841 
842 void
843 cac_l0_intr_enable(struct cac_softc *sc, int state)
844 {
845 
846 	cac_outl(sc, CAC_REG_INTR_MASK,
847 	    state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE);
848 }
849