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