1 /* $NetBSD: cac.c,v 1.45 2008/04/08 12:07:25 cegger Exp $ */ 2 3 /*- 4 * Copyright (c) 2000, 2006, 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 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 the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Driver for Compaq array controllers. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: cac.c,v 1.45 2008/04/08 12:07:25 cegger Exp $"); 45 46 #include "bio.h" 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/kernel.h> 51 #include <sys/device.h> 52 #include <sys/queue.h> 53 #include <sys/proc.h> 54 #include <sys/buf.h> 55 #include <sys/endian.h> 56 #include <sys/malloc.h> 57 #include <sys/pool.h> 58 59 #include <uvm/uvm_extern.h> 60 61 #include <sys/bswap.h> 62 #include <sys/bus.h> 63 64 #include <dev/ic/cacreg.h> 65 #include <dev/ic/cacvar.h> 66 67 #if NBIO > 0 68 #include <dev/biovar.h> 69 #endif /* NBIO > 0 */ 70 71 #include "locators.h" 72 73 static struct cac_ccb *cac_ccb_alloc(struct cac_softc *, int); 74 static void cac_ccb_done(struct cac_softc *, struct cac_ccb *); 75 static void cac_ccb_free(struct cac_softc *, struct cac_ccb *); 76 static int cac_ccb_poll(struct cac_softc *, struct cac_ccb *, int); 77 static int cac_ccb_start(struct cac_softc *, struct cac_ccb *); 78 static int cac_print(void *, const char *); 79 static void cac_shutdown(void *); 80 81 static struct cac_ccb *cac_l0_completed(struct cac_softc *); 82 static int cac_l0_fifo_full(struct cac_softc *); 83 static void cac_l0_intr_enable(struct cac_softc *, int); 84 static int cac_l0_intr_pending(struct cac_softc *); 85 static void cac_l0_submit(struct cac_softc *, struct cac_ccb *); 86 87 static void *cac_sdh; /* shutdown hook */ 88 89 #if NBIO > 0 90 int cac_ioctl(struct device *, u_long, void *); 91 int cac_ioctl_vol(struct cac_softc *, struct bioc_vol *); 92 int cac_create_sensors(struct cac_softc *); 93 void cac_sensor_refresh(struct sysmon_envsys *, envsys_data_t *); 94 #endif /* NBIO > 0 */ 95 96 const struct cac_linkage cac_l0 = { 97 cac_l0_completed, 98 cac_l0_fifo_full, 99 cac_l0_intr_enable, 100 cac_l0_intr_pending, 101 cac_l0_submit 102 }; 103 104 /* 105 * Initialise our interface to the controller. 106 */ 107 int 108 cac_init(struct cac_softc *sc, const char *intrstr, int startfw) 109 { 110 struct cac_controller_info cinfo; 111 struct cac_attach_args caca; 112 int error, rseg, size, i; 113 bus_dma_segment_t seg; 114 struct cac_ccb *ccb; 115 int locs[CACCF_NLOCS]; 116 char firm[8]; 117 118 if (intrstr != NULL) 119 aprint_normal_dev(&sc->sc_dv, "interrupting at %s\n", 120 intrstr); 121 122 SIMPLEQ_INIT(&sc->sc_ccb_free); 123 SIMPLEQ_INIT(&sc->sc_ccb_queue); 124 mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); 125 cv_init(&sc->sc_ccb_cv, "cacccb"); 126 127 size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; 128 129 if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, 130 &rseg, BUS_DMA_NOWAIT)) != 0) { 131 aprint_error_dev(&sc->sc_dv, "unable to allocate CCBs, error = %d\n", 132 error); 133 return (-1); 134 } 135 136 if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, 137 (void **)&sc->sc_ccbs, 138 BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { 139 aprint_error_dev(&sc->sc_dv, "unable to map CCBs, error = %d\n", 140 error); 141 return (-1); 142 } 143 144 if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 145 BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { 146 aprint_error_dev(&sc->sc_dv, "unable to create CCB DMA map, error = %d\n", 147 error); 148 return (-1); 149 } 150 151 if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, 152 size, NULL, BUS_DMA_NOWAIT)) != 0) { 153 aprint_error_dev(&sc->sc_dv, "unable to load CCB DMA map, error = %d\n", 154 error); 155 return (-1); 156 } 157 158 sc->sc_ccbs_paddr = sc->sc_dmamap->dm_segs[0].ds_addr; 159 memset(sc->sc_ccbs, 0, size); 160 ccb = (struct cac_ccb *)sc->sc_ccbs; 161 162 for (i = 0; i < CAC_MAX_CCBS; i++, ccb++) { 163 /* Create the DMA map for this CCB's data */ 164 error = bus_dmamap_create(sc->sc_dmat, CAC_MAX_XFER, 165 CAC_SG_SIZE, CAC_MAX_XFER, 0, 166 BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, 167 &ccb->ccb_dmamap_xfer); 168 169 if (error) { 170 aprint_error_dev(&sc->sc_dv, "can't create ccb dmamap (%d)\n", 171 error); 172 break; 173 } 174 175 ccb->ccb_flags = 0; 176 ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); 177 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); 178 } 179 180 /* Start firmware background tasks, if needed. */ 181 if (startfw) { 182 if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo), 183 0, 0, CAC_CCB_DATA_IN, NULL)) { 184 aprint_error_dev(&sc->sc_dv, "CAC_CMD_START_FIRMWARE failed\n"); 185 return (-1); 186 } 187 } 188 189 if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, 190 CAC_CCB_DATA_IN, NULL)) { 191 aprint_error_dev(&sc->sc_dv, "CAC_CMD_GET_CTRL_INFO failed\n"); 192 return (-1); 193 } 194 195 strlcpy(firm, cinfo.firm_rev, 4+1); 196 printf("%s: %d channels, firmware <%s>\n", device_xname(&sc->sc_dv), 197 cinfo.scsi_chips, firm); 198 199 sc->sc_nunits = cinfo.num_drvs; 200 for (i = 0; i < cinfo.num_drvs; i++) { 201 caca.caca_unit = i; 202 203 locs[CACCF_UNIT] = i; 204 205 config_found_sm_loc(&sc->sc_dv, "cac", locs, &caca, 206 cac_print, config_stdsubmatch); 207 } 208 209 /* Set our `shutdownhook' before we start any device activity. */ 210 if (cac_sdh == NULL) 211 cac_sdh = shutdownhook_establish(cac_shutdown, NULL); 212 213 mutex_enter(&sc->sc_mutex); 214 (*sc->sc_cl.cl_intr_enable)(sc, CAC_INTR_ENABLE); 215 mutex_exit(&sc->sc_mutex); 216 217 #if NBIO > 0 218 if (bio_register(&sc->sc_dv, cac_ioctl) != 0) 219 aprint_error_dev(&sc->sc_dv, "controller registration failed"); 220 else 221 sc->sc_ioctl = cac_ioctl; 222 if (cac_create_sensors(sc) != 0) 223 aprint_error_dev(&sc->sc_dv, "unable to create sensors\n"); 224 #endif 225 226 return (0); 227 } 228 229 /* 230 * Shut down all `cac' controllers. 231 */ 232 static void 233 cac_shutdown(void *cookie) 234 { 235 extern struct cfdriver cac_cd; 236 struct cac_softc *sc; 237 u_int8_t tbuf[512]; 238 int i; 239 240 for (i = 0; i < cac_cd.cd_ndevs; i++) { 241 if ((sc = device_lookup(&cac_cd, i)) == NULL) 242 continue; 243 memset(tbuf, 0, sizeof(tbuf)); 244 tbuf[0] = 1; 245 cac_cmd(sc, CAC_CMD_FLUSH_CACHE, tbuf, sizeof(tbuf), 0, 0, 246 CAC_CCB_DATA_OUT, NULL); 247 } 248 } 249 250 /* 251 * Print autoconfiguration message for a sub-device. 252 */ 253 static int 254 cac_print(void *aux, const char *pnp) 255 { 256 struct cac_attach_args *caca; 257 258 caca = (struct cac_attach_args *)aux; 259 260 if (pnp != NULL) 261 aprint_normal("block device at %s", pnp); 262 aprint_normal(" unit %d", caca->caca_unit); 263 return (UNCONF); 264 } 265 266 /* 267 * Handle an interrupt from the controller: process finished CCBs and 268 * dequeue any waiting CCBs. 269 */ 270 int 271 cac_intr(void *cookie) 272 { 273 struct cac_softc *sc; 274 struct cac_ccb *ccb; 275 int rv; 276 277 sc = (struct cac_softc *)cookie; 278 279 mutex_enter(&sc->sc_mutex); 280 281 if ((*sc->sc_cl.cl_intr_pending)(sc)) { 282 while ((ccb = (*sc->sc_cl.cl_completed)(sc)) != NULL) { 283 cac_ccb_done(sc, ccb); 284 cac_ccb_start(sc, NULL); 285 } 286 rv = 1; 287 } else 288 rv = 0; 289 290 mutex_exit(&sc->sc_mutex); 291 292 return (rv); 293 } 294 295 /* 296 * Execute a [polled] command. 297 */ 298 int 299 cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, 300 int drive, int blkno, int flags, struct cac_context *context) 301 { 302 struct cac_ccb *ccb; 303 struct cac_sgb *sgb; 304 int i, rv, size, nsegs; 305 306 size = 0; 307 308 if ((ccb = cac_ccb_alloc(sc, 1)) == NULL) { 309 aprint_error_dev(&sc->sc_dv, "unable to alloc CCB"); 310 return (EAGAIN); 311 } 312 313 if ((flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 314 bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap_xfer, 315 (void *)data, datasize, NULL, BUS_DMA_NOWAIT | 316 BUS_DMA_STREAMING | ((flags & CAC_CCB_DATA_IN) ? 317 BUS_DMA_READ : BUS_DMA_WRITE)); 318 319 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, datasize, 320 (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : 321 BUS_DMASYNC_PREWRITE); 322 323 sgb = ccb->ccb_seg; 324 nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); 325 326 for (i = 0; i < nsegs; i++, sgb++) { 327 size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; 328 sgb->length = 329 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); 330 sgb->addr = 331 htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); 332 } 333 } else { 334 size = datasize; 335 nsegs = 0; 336 } 337 338 ccb->ccb_hdr.drive = drive; 339 ccb->ccb_hdr.priority = 0; 340 ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + 341 sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); 342 343 ccb->ccb_req.next = 0; 344 ccb->ccb_req.error = 0; 345 ccb->ccb_req.reserved = 0; 346 ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); 347 ccb->ccb_req.command = command; 348 ccb->ccb_req.sgcount = nsegs; 349 ccb->ccb_req.blkno = htole32(blkno); 350 351 ccb->ccb_flags = flags; 352 ccb->ccb_datasize = size; 353 354 mutex_enter(&sc->sc_mutex); 355 356 if (context == NULL) { 357 memset(&ccb->ccb_context, 0, sizeof(struct cac_context)); 358 359 /* Synchronous commands musn't wait. */ 360 if ((*sc->sc_cl.cl_fifo_full)(sc)) { 361 cac_ccb_free(sc, ccb); 362 rv = EAGAIN; 363 } else { 364 #ifdef DIAGNOSTIC 365 ccb->ccb_flags |= CAC_CCB_ACTIVE; 366 #endif 367 (*sc->sc_cl.cl_submit)(sc, ccb); 368 rv = cac_ccb_poll(sc, ccb, 2000); 369 cac_ccb_free(sc, ccb); 370 } 371 } else { 372 memcpy(&ccb->ccb_context, context, sizeof(struct cac_context)); 373 (void)cac_ccb_start(sc, ccb); 374 rv = 0; 375 } 376 377 mutex_exit(&sc->sc_mutex); 378 return (rv); 379 } 380 381 /* 382 * Wait for the specified CCB to complete. 383 */ 384 static int 385 cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) 386 { 387 struct cac_ccb *ccb; 388 389 KASSERT(mutex_owned(&sc->sc_mutex)); 390 391 timo *= 1000; 392 393 do { 394 for (; timo != 0; timo--) { 395 ccb = (*sc->sc_cl.cl_completed)(sc); 396 if (ccb != NULL) 397 break; 398 DELAY(1); 399 } 400 401 if (timo == 0) { 402 printf("%s: timeout\n", device_xname(&sc->sc_dv)); 403 return (EBUSY); 404 } 405 cac_ccb_done(sc, ccb); 406 } while (ccb != wantccb); 407 408 return (0); 409 } 410 411 /* 412 * Enqueue the specified command (if any) and attempt to start all enqueued 413 * commands. 414 */ 415 static int 416 cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb) 417 { 418 419 KASSERT(mutex_owned(&sc->sc_mutex)); 420 421 if (ccb != NULL) 422 SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); 423 424 while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { 425 if ((*sc->sc_cl.cl_fifo_full)(sc)) 426 return (EAGAIN); 427 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb_chain); 428 #ifdef DIAGNOSTIC 429 ccb->ccb_flags |= CAC_CCB_ACTIVE; 430 #endif 431 (*sc->sc_cl.cl_submit)(sc, ccb); 432 } 433 434 return (0); 435 } 436 437 /* 438 * Process a finished CCB. 439 */ 440 static void 441 cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) 442 { 443 struct device *dv; 444 void *context; 445 int error; 446 447 error = 0; 448 449 KASSERT(mutex_owned(&sc->sc_mutex)); 450 451 #ifdef DIAGNOSTIC 452 if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) 453 panic("cac_ccb_done: CCB not active"); 454 ccb->ccb_flags &= ~CAC_CCB_ACTIVE; 455 #endif 456 457 if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { 458 bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, 459 ccb->ccb_datasize, 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 error = ccb->ccb_req.error; 465 if (ccb->ccb_context.cc_handler != NULL) { 466 dv = ccb->ccb_context.cc_dv; 467 context = ccb->ccb_context.cc_context; 468 cac_ccb_free(sc, ccb); 469 (*ccb->ccb_context.cc_handler)(dv, context, error); 470 } else { 471 if ((error & CAC_RET_SOFT_ERROR) != 0) 472 aprint_error_dev(&sc->sc_dv, "soft error; array may be degraded\n"); 473 if ((error & CAC_RET_HARD_ERROR) != 0) 474 aprint_error_dev(&sc->sc_dv, "hard error\n"); 475 if ((error & CAC_RET_CMD_REJECTED) != 0) { 476 error = 1; 477 aprint_error_dev(&sc->sc_dv, "invalid request\n"); 478 } 479 } 480 } 481 482 /* 483 * Allocate a CCB. 484 */ 485 static struct cac_ccb * 486 cac_ccb_alloc(struct cac_softc *sc, int nosleep) 487 { 488 struct cac_ccb *ccb; 489 490 mutex_enter(&sc->sc_mutex); 491 492 for (;;) { 493 if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) { 494 SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_chain); 495 break; 496 } 497 if (nosleep) { 498 ccb = NULL; 499 break; 500 } 501 cv_wait(&sc->sc_ccb_cv, &sc->sc_mutex); 502 } 503 504 mutex_exit(&sc->sc_mutex); 505 return (ccb); 506 } 507 508 /* 509 * Put a CCB onto the freelist. 510 */ 511 static void 512 cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb) 513 { 514 515 KASSERT(mutex_owned(&sc->sc_mutex)); 516 517 ccb->ccb_flags = 0; 518 if (SIMPLEQ_EMPTY(&sc->sc_ccb_free)) 519 cv_signal(&sc->sc_ccb_cv); 520 SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); 521 } 522 523 /* 524 * Board specific linkage shared between multiple bus types. 525 */ 526 527 static int 528 cac_l0_fifo_full(struct cac_softc *sc) 529 { 530 531 KASSERT(mutex_owned(&sc->sc_mutex)); 532 533 return (cac_inl(sc, CAC_REG_CMD_FIFO) == 0); 534 } 535 536 static void 537 cac_l0_submit(struct cac_softc *sc, struct cac_ccb *ccb) 538 { 539 540 KASSERT(mutex_owned(&sc->sc_mutex)); 541 542 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 543 (char *)ccb - (char *)sc->sc_ccbs, 544 sizeof(struct cac_ccb), BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 545 cac_outl(sc, CAC_REG_CMD_FIFO, ccb->ccb_paddr); 546 } 547 548 static struct cac_ccb * 549 cac_l0_completed(struct cac_softc *sc) 550 { 551 struct cac_ccb *ccb; 552 paddr_t off; 553 554 KASSERT(mutex_owned(&sc->sc_mutex)); 555 556 if ((off = cac_inl(sc, CAC_REG_DONE_FIFO)) == 0) 557 return (NULL); 558 559 if ((off & 3) != 0) 560 aprint_error_dev(&sc->sc_dv, "failed command list returned: %lx\n", 561 (long)off); 562 563 off = (off & ~3) - sc->sc_ccbs_paddr; 564 ccb = (struct cac_ccb *)((char *)sc->sc_ccbs + off); 565 566 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, off, sizeof(struct cac_ccb), 567 BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD); 568 569 if ((off & 3) != 0 && ccb->ccb_req.error == 0) 570 ccb->ccb_req.error = CAC_RET_CMD_REJECTED; 571 572 return (ccb); 573 } 574 575 static int 576 cac_l0_intr_pending(struct cac_softc *sc) 577 { 578 579 KASSERT(mutex_owned(&sc->sc_mutex)); 580 581 return (cac_inl(sc, CAC_REG_INTR_PENDING) & CAC_INTR_ENABLE); 582 } 583 584 static void 585 cac_l0_intr_enable(struct cac_softc *sc, int state) 586 { 587 588 KASSERT(mutex_owned(&sc->sc_mutex)); 589 590 cac_outl(sc, CAC_REG_INTR_MASK, 591 state ? CAC_INTR_ENABLE : CAC_INTR_DISABLE); 592 } 593 594 #if NBIO > 0 595 const int cac_level[] = { 0, 4, 1, 5, 51, 7 }; 596 const int cac_stat[] = { BIOC_SVONLINE, BIOC_SVOFFLINE, BIOC_SVOFFLINE, 597 BIOC_SVDEGRADED, BIOC_SVREBUILD, BIOC_SVREBUILD, BIOC_SVDEGRADED, 598 BIOC_SVDEGRADED, BIOC_SVINVALID, BIOC_SVINVALID, BIOC_SVBUILDING, 599 BIOC_SVOFFLINE, BIOC_SVBUILDING }; 600 601 int 602 cac_ioctl(struct device *dev, u_long cmd, void *addr) 603 { 604 struct cac_softc *sc = (struct cac_softc *)dev; 605 struct bioc_inq *bi; 606 struct bioc_disk *bd; 607 cac_lock_t lock; 608 int error = 0; 609 610 lock = CAC_LOCK(sc); 611 switch (cmd) { 612 case BIOCINQ: 613 bi = (struct bioc_inq *)addr; 614 strlcpy(bi->bi_dev, device_xname(&sc->sc_dv), sizeof(bi->bi_dev)); 615 bi->bi_novol = sc->sc_nunits; 616 bi->bi_nodisk = 0; 617 break; 618 619 case BIOCVOL: 620 error = cac_ioctl_vol(sc, (struct bioc_vol *)addr); 621 break; 622 623 case BIOCDISK: 624 case BIOCDISK_NOVOL: 625 bd = (struct bioc_disk *)addr; 626 if (bd->bd_volid > sc->sc_nunits) { 627 error = EINVAL; 628 break; 629 } 630 /* No disk information yet */ 631 break; 632 633 case BIOCBLINK: 634 case BIOCALARM: 635 case BIOCSETSTATE: 636 default: 637 error = EINVAL; 638 } 639 CAC_UNLOCK(sc, lock); 640 641 return (error); 642 } 643 644 int 645 cac_ioctl_vol(struct cac_softc *sc, struct bioc_vol *bv) 646 { 647 struct cac_drive_info dinfo; 648 struct cac_drive_status dstatus; 649 u_int32_t blks; 650 651 if (bv->bv_volid > sc->sc_nunits) { 652 return EINVAL; 653 } 654 if (cac_cmd(sc, CAC_CMD_GET_LOG_DRV_INFO, &dinfo, sizeof(dinfo), 655 bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { 656 return EIO; 657 } 658 if (cac_cmd(sc, CAC_CMD_SENSE_DRV_STATUS, &dstatus, sizeof(dstatus), 659 bv->bv_volid, 0, CAC_CCB_DATA_IN, NULL)) { 660 return EIO; 661 } 662 blks = CAC_GET2(dinfo.ncylinders) * CAC_GET1(dinfo.nheads) * 663 CAC_GET1(dinfo.nsectors); 664 bv->bv_size = (off_t)blks * CAC_GET2(dinfo.secsize); 665 bv->bv_level = cac_level[CAC_GET1(dinfo.mirror)]; /*XXX limit check */ 666 bv->bv_nodisk = 0; /* XXX */ 667 bv->bv_status = 0; /* XXX */ 668 bv->bv_percent = -1; 669 bv->bv_seconds = 0; 670 if (dstatus.stat < sizeof(cac_stat)/sizeof(cac_stat[0])) 671 bv->bv_status = cac_stat[dstatus.stat]; 672 if (bv->bv_status == BIOC_SVREBUILD || 673 bv->bv_status == BIOC_SVBUILDING) 674 bv->bv_percent = ((blks - CAC_GET4(dstatus.prog)) * 1000ULL) / 675 blks; 676 return 0; 677 } 678 679 int 680 cac_create_sensors(struct cac_softc *sc) 681 { 682 int i; 683 int nsensors = sc->sc_nunits; 684 685 sc->sc_sme = sysmon_envsys_create(); 686 sc->sc_sensor = malloc(sizeof(envsys_data_t) * nsensors, 687 M_DEVBUF, M_NOWAIT | M_ZERO); 688 if (sc->sc_sensor == NULL) { 689 aprint_error_dev(&sc->sc_dv, "can't allocate envsys_data_t\n"); 690 return(ENOMEM); 691 } 692 693 for (i = 0; i < nsensors; i++) { 694 sc->sc_sensor[i].units = ENVSYS_DRIVE; 695 sc->sc_sensor[i].monitor = true; 696 /* Enable monitoring for drive state changes */ 697 sc->sc_sensor[i].flags |= ENVSYS_FMONSTCHANGED; 698 /* logical drives */ 699 snprintf(sc->sc_sensor[i].desc, 700 sizeof(sc->sc_sensor[i].desc), "%s:%d", 701 device_xname(&sc->sc_dv), i); 702 if (sysmon_envsys_sensor_attach(sc->sc_sme, 703 &sc->sc_sensor[i])) 704 goto out; 705 } 706 sc->sc_sme->sme_name = device_xname(&sc->sc_dv); 707 sc->sc_sme->sme_cookie = sc; 708 sc->sc_sme->sme_refresh = cac_sensor_refresh; 709 if (sysmon_envsys_register(sc->sc_sme)) { 710 aprint_error_dev(&sc->sc_dv, "unable to register with sysmon\n"); 711 return(1); 712 } 713 return (0); 714 715 out: 716 free(sc->sc_sensor, M_DEVBUF); 717 sysmon_envsys_destroy(sc->sc_sme); 718 return EINVAL; 719 } 720 721 void 722 cac_sensor_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 723 { 724 struct cac_softc *sc = sme->sme_cookie; 725 struct bioc_vol bv; 726 int s; 727 728 if (edata->sensor >= sc->sc_nunits) 729 return; 730 731 bzero(&bv, sizeof(bv)); 732 bv.bv_volid = edata->sensor; 733 s = splbio(); 734 if (cac_ioctl_vol(sc, &bv)) { 735 splx(s); 736 return; 737 } 738 splx(s); 739 740 switch(bv.bv_status) { 741 case BIOC_SVOFFLINE: 742 edata->value_cur = ENVSYS_DRIVE_FAIL; 743 edata->state = ENVSYS_SCRITICAL; 744 break; 745 746 case BIOC_SVDEGRADED: 747 edata->value_cur = ENVSYS_DRIVE_PFAIL; 748 edata->state = ENVSYS_SCRITICAL; 749 break; 750 751 case BIOC_SVSCRUB: 752 case BIOC_SVONLINE: 753 edata->value_cur = ENVSYS_DRIVE_ONLINE; 754 edata->state = ENVSYS_SVALID; 755 break; 756 757 case BIOC_SVREBUILD: 758 case BIOC_SVBUILDING: 759 edata->value_cur = ENVSYS_DRIVE_REBUILD; 760 edata->state = ENVSYS_SVALID; 761 break; 762 763 case BIOC_SVINVALID: 764 /* FALLTRHOUGH */ 765 default: 766 edata->value_cur = 0; /* unknown */ 767 edata->state = ENVSYS_SINVALID; 768 } 769 } 770 #endif /* NBIO > 0 */ 771