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