1 /*- 2 * Copyright (c) 2000 Matthew Jacob 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification, immediately at the beginning of the file. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 18 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 32 #include <sys/conf.h> 33 #include <sys/errno.h> 34 #include <sys/fcntl.h> 35 #include <sys/kernel.h> 36 #include <sys/kthread.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/mutex.h> 40 #include <sys/queue.h> 41 #include <sys/sx.h> 42 #include <sys/systm.h> 43 #include <sys/sysctl.h> 44 #include <sys/types.h> 45 46 #include <machine/stdarg.h> 47 48 #include <cam/cam.h> 49 #include <cam/cam_ccb.h> 50 #include <cam/cam_debug.h> 51 #include <cam/cam_periph.h> 52 #include <cam/cam_xpt_periph.h> 53 54 #include <cam/scsi/scsi_all.h> 55 #include <cam/scsi/scsi_message.h> 56 #include <cam/scsi/scsi_enc.h> 57 #include <cam/scsi/scsi_enc_internal.h> 58 59 MALLOC_DEFINE(M_SCSIENC, "SCSI ENC", "SCSI ENC buffers"); 60 61 /* Enclosure type independent driver */ 62 63 static d_open_t enc_open; 64 static d_close_t enc_close; 65 static d_ioctl_t enc_ioctl; 66 static periph_init_t enc_init; 67 static periph_ctor_t enc_ctor; 68 static periph_oninv_t enc_oninvalidate; 69 static periph_dtor_t enc_dtor; 70 static periph_start_t enc_start; 71 72 static void enc_async(void *, uint32_t, struct cam_path *, void *); 73 static enctyp enc_type(struct ccb_getdev *); 74 75 SYSCTL_NODE(_kern_cam, OID_AUTO, enc, CTLFLAG_RD, 0, 76 "CAM Enclosure Services driver"); 77 78 static struct periph_driver encdriver = { 79 enc_init, "ses", 80 TAILQ_HEAD_INITIALIZER(encdriver.units), /* generation */ 0 81 }; 82 83 PERIPHDRIVER_DECLARE(enc, encdriver); 84 85 static struct cdevsw enc_cdevsw = { 86 .d_version = D_VERSION, 87 .d_open = enc_open, 88 .d_close = enc_close, 89 .d_ioctl = enc_ioctl, 90 .d_name = "ses", 91 .d_flags = D_TRACKCLOSE, 92 }; 93 94 static void 95 enc_init(void) 96 { 97 cam_status status; 98 99 /* 100 * Install a global async callback. This callback will 101 * receive async callbacks like "new device found". 102 */ 103 status = xpt_register_async(AC_FOUND_DEVICE, enc_async, NULL, NULL); 104 105 if (status != CAM_REQ_CMP) { 106 printf("enc: Failed to attach master async callback " 107 "due to status 0x%x!\n", status); 108 } 109 } 110 111 static void 112 enc_devgonecb(void *arg) 113 { 114 struct cam_sim *sim; 115 struct cam_periph *periph; 116 struct enc_softc *enc; 117 int i; 118 119 periph = (struct cam_periph *)arg; 120 sim = periph->sim; 121 enc = (struct enc_softc *)periph->softc; 122 123 mtx_lock(sim->mtx); 124 125 /* 126 * When we get this callback, we will get no more close calls from 127 * devfs. So if we have any dangling opens, we need to release the 128 * reference held for that particular context. 129 */ 130 for (i = 0; i < enc->open_count; i++) 131 cam_periph_release_locked(periph); 132 133 enc->open_count = 0; 134 135 /* 136 * Release the reference held for the device node, it is gone now. 137 */ 138 cam_periph_release_locked(periph); 139 140 /* 141 * We reference the SIM lock directly here, instead of using 142 * cam_periph_unlock(). The reason is that the final call to 143 * cam_periph_release_locked() above could result in the periph 144 * getting freed. If that is the case, dereferencing the periph 145 * with a cam_periph_unlock() call would cause a page fault. 146 */ 147 mtx_unlock(sim->mtx); 148 } 149 150 static void 151 enc_oninvalidate(struct cam_periph *periph) 152 { 153 struct enc_softc *enc; 154 155 enc = periph->softc; 156 157 enc->enc_flags |= ENC_FLAG_INVALID; 158 159 /* If the sub-driver has an invalidate routine, call it */ 160 if (enc->enc_vec.softc_invalidate != NULL) 161 enc->enc_vec.softc_invalidate(enc); 162 163 /* 164 * Unregister any async callbacks. 165 */ 166 xpt_register_async(0, enc_async, periph, periph->path); 167 168 /* 169 * Shutdown our daemon. 170 */ 171 enc->enc_flags |= ENC_FLAG_SHUTDOWN; 172 if (enc->enc_daemon != NULL) { 173 /* Signal the ses daemon to terminate. */ 174 wakeup(enc->enc_daemon); 175 } 176 callout_drain(&enc->status_updater); 177 178 destroy_dev_sched_cb(enc->enc_dev, enc_devgonecb, periph); 179 180 xpt_print(periph->path, "lost device\n"); 181 } 182 183 static void 184 enc_dtor(struct cam_periph *periph) 185 { 186 struct enc_softc *enc; 187 188 enc = periph->softc; 189 190 xpt_print(periph->path, "removing device entry\n"); 191 192 193 /* If the sub-driver has a cleanup routine, call it */ 194 if (enc->enc_vec.softc_cleanup != NULL) 195 enc->enc_vec.softc_cleanup(enc); 196 197 if (enc->enc_boot_hold_ch.ich_func != NULL) { 198 config_intrhook_disestablish(&enc->enc_boot_hold_ch); 199 enc->enc_boot_hold_ch.ich_func = NULL; 200 } 201 202 ENC_FREE(enc); 203 } 204 205 static void 206 enc_async(void *callback_arg, uint32_t code, struct cam_path *path, void *arg) 207 { 208 struct cam_periph *periph; 209 210 periph = (struct cam_periph *)callback_arg; 211 212 switch(code) { 213 case AC_FOUND_DEVICE: 214 { 215 struct ccb_getdev *cgd; 216 cam_status status; 217 path_id_t path_id; 218 219 cgd = (struct ccb_getdev *)arg; 220 if (arg == NULL) { 221 break; 222 } 223 224 if (enc_type(cgd) == ENC_NONE) { 225 /* 226 * Schedule announcement of the ENC bindings for 227 * this device if it is managed by a SEP. 228 */ 229 path_id = xpt_path_path_id(path); 230 xpt_lock_buses(); 231 TAILQ_FOREACH(periph, &encdriver.units, unit_links) { 232 struct enc_softc *softc; 233 234 softc = (struct enc_softc *)periph->softc; 235 if (xpt_path_path_id(periph->path) != path_id 236 || softc == NULL 237 || (softc->enc_flags & ENC_FLAG_INITIALIZED) 238 == 0 239 || softc->enc_vec.device_found == NULL) 240 continue; 241 242 softc->enc_vec.device_found(softc); 243 } 244 xpt_unlock_buses(); 245 return; 246 } 247 248 status = cam_periph_alloc(enc_ctor, enc_oninvalidate, 249 enc_dtor, enc_start, "ses", CAM_PERIPH_BIO, 250 cgd->ccb_h.path, enc_async, AC_FOUND_DEVICE, cgd); 251 252 if (status != CAM_REQ_CMP && status != CAM_REQ_INPROG) { 253 printf("enc_async: Unable to probe new device due to " 254 "status 0x%x\n", status); 255 } 256 break; 257 } 258 default: 259 cam_periph_async(periph, code, path, arg); 260 break; 261 } 262 } 263 264 static int 265 enc_open(struct cdev *dev, int flags, int fmt, struct thread *td) 266 { 267 struct cam_periph *periph; 268 struct enc_softc *softc; 269 int error = 0; 270 271 periph = (struct cam_periph *)dev->si_drv1; 272 if (periph == NULL) { 273 return (ENXIO); 274 } 275 276 if (cam_periph_acquire(periph) != CAM_REQ_CMP) 277 return (ENXIO); 278 279 cam_periph_lock(periph); 280 281 softc = (struct enc_softc *)periph->softc; 282 283 if ((softc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 284 error = ENXIO; 285 goto out; 286 } 287 if (softc->enc_flags & ENC_FLAG_INVALID) { 288 error = ENXIO; 289 goto out; 290 } 291 out: 292 if (error != 0) 293 cam_periph_release_locked(periph); 294 else 295 softc->open_count++; 296 297 cam_periph_unlock(periph); 298 299 return (error); 300 } 301 302 static int 303 enc_close(struct cdev *dev, int flag, int fmt, struct thread *td) 304 { 305 struct cam_sim *sim; 306 struct cam_periph *periph; 307 struct enc_softc *enc; 308 309 periph = (struct cam_periph *)dev->si_drv1; 310 if (periph == NULL) 311 return (ENXIO); 312 313 sim = periph->sim; 314 enc = periph->softc; 315 316 mtx_lock(sim->mtx); 317 318 enc->open_count--; 319 320 cam_periph_release_locked(periph); 321 322 /* 323 * We reference the SIM lock directly here, instead of using 324 * cam_periph_unlock(). The reason is that the call to 325 * cam_periph_release_locked() above could result in the periph 326 * getting freed. If that is the case, dereferencing the periph 327 * with a cam_periph_unlock() call would cause a page fault. 328 * 329 * cam_periph_release() avoids this problem using the same method, 330 * but we're manually acquiring and dropping the lock here to 331 * protect the open count and avoid another lock acquisition and 332 * release. 333 */ 334 mtx_unlock(sim->mtx); 335 336 return (0); 337 } 338 339 static void 340 enc_start(struct cam_periph *p, union ccb *sccb) 341 { 342 struct enc_softc *enc; 343 344 enc = p->softc; 345 ENC_DLOG(enc, "%s enter imm=%d prio=%d\n", 346 __func__, p->immediate_priority, p->pinfo.priority); 347 if (p->immediate_priority <= p->pinfo.priority) { 348 SLIST_INSERT_HEAD(&p->ccb_list, &sccb->ccb_h, periph_links.sle); 349 p->immediate_priority = CAM_PRIORITY_NONE; 350 wakeup(&p->ccb_list); 351 } else 352 xpt_release_ccb(sccb); 353 ENC_DLOG(enc, "%s exit\n", __func__); 354 } 355 356 void 357 enc_done(struct cam_periph *periph, union ccb *dccb) 358 { 359 wakeup(&dccb->ccb_h.cbfcnp); 360 } 361 362 int 363 enc_error(union ccb *ccb, uint32_t cflags, uint32_t sflags) 364 { 365 struct enc_softc *softc; 366 struct cam_periph *periph; 367 368 periph = xpt_path_periph(ccb->ccb_h.path); 369 softc = (struct enc_softc *)periph->softc; 370 371 return (cam_periph_error(ccb, cflags, sflags, &softc->saved_ccb)); 372 } 373 374 static int 375 enc_ioctl(struct cdev *dev, u_long cmd, caddr_t arg_addr, int flag, 376 struct thread *td) 377 { 378 struct cam_periph *periph; 379 encioc_enc_status_t tmp; 380 encioc_string_t sstr; 381 encioc_elm_status_t elms; 382 encioc_elm_desc_t elmd; 383 encioc_elm_devnames_t elmdn; 384 encioc_element_t *uelm; 385 enc_softc_t *enc; 386 enc_cache_t *cache; 387 void *addr; 388 int error, i; 389 390 391 if (arg_addr) 392 addr = *((caddr_t *) arg_addr); 393 else 394 addr = NULL; 395 396 periph = (struct cam_periph *)dev->si_drv1; 397 if (periph == NULL) 398 return (ENXIO); 399 400 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("entering encioctl\n")); 401 402 cam_periph_lock(periph); 403 enc = (struct enc_softc *)periph->softc; 404 cache = &enc->enc_cache; 405 406 /* 407 * Now check to see whether we're initialized or not. 408 * This actually should never fail as we're not supposed 409 * to get past enc_open w/o successfully initializing 410 * things. 411 */ 412 if ((enc->enc_flags & ENC_FLAG_INITIALIZED) == 0) { 413 cam_periph_unlock(periph); 414 return (ENXIO); 415 } 416 cam_periph_unlock(periph); 417 418 error = 0; 419 420 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 421 ("trying to do ioctl %#lx\n", cmd)); 422 423 /* 424 * If this command can change the device's state, 425 * we must have the device open for writing. 426 * 427 * For commands that get information about the 428 * device- we don't need to lock the peripheral 429 * if we aren't running a command. The periph 430 * also can't go away while a user process has 431 * it open. 432 */ 433 switch (cmd) { 434 case ENCIOC_GETNELM: 435 case ENCIOC_GETELMMAP: 436 case ENCIOC_GETENCSTAT: 437 case ENCIOC_GETELMSTAT: 438 case ENCIOC_GETELMDESC: 439 case ENCIOC_GETELMDEVNAMES: 440 break; 441 default: 442 if ((flag & FWRITE) == 0) { 443 return (EBADF); 444 } 445 } 446 447 /* 448 * XXX The values read here are only valid for the current 449 * configuration generation. We need these ioctls 450 * to also pass in/out a generation number. 451 */ 452 sx_slock(&enc->enc_cache_lock); 453 switch (cmd) { 454 case ENCIOC_GETNELM: 455 error = copyout(&cache->nelms, addr, sizeof (cache->nelms)); 456 break; 457 458 case ENCIOC_GETELMMAP: 459 for (uelm = addr, i = 0; i != cache->nelms; i++) { 460 encioc_element_t kelm; 461 kelm.elm_idx = i; 462 kelm.elm_subenc_id = cache->elm_map[i].subenclosure; 463 kelm.elm_type = cache->elm_map[i].enctype; 464 error = copyout(&kelm, &uelm[i], sizeof(kelm)); 465 if (error) 466 break; 467 } 468 break; 469 470 case ENCIOC_GETENCSTAT: 471 cam_periph_lock(periph); 472 error = enc->enc_vec.get_enc_status(enc, 1); 473 if (error) { 474 cam_periph_unlock(periph); 475 break; 476 } 477 tmp = cache->enc_status; 478 cam_periph_unlock(periph); 479 error = copyout(&tmp, addr, sizeof(tmp)); 480 cache->enc_status = tmp; 481 break; 482 483 case ENCIOC_SETENCSTAT: 484 error = copyin(addr, &tmp, sizeof(tmp)); 485 if (error) 486 break; 487 cam_periph_lock(periph); 488 error = enc->enc_vec.set_enc_status(enc, tmp, 1); 489 cam_periph_unlock(periph); 490 break; 491 492 case ENCIOC_GETSTRING: 493 case ENCIOC_SETSTRING: 494 if (enc->enc_vec.handle_string == NULL) { 495 error = EINVAL; 496 break; 497 } 498 error = copyin(addr, &sstr, sizeof(sstr)); 499 if (error) 500 break; 501 cam_periph_lock(periph); 502 error = enc->enc_vec.handle_string(enc, &sstr, cmd); 503 cam_periph_unlock(periph); 504 break; 505 506 case ENCIOC_GETELMSTAT: 507 error = copyin(addr, &elms, sizeof(elms)); 508 if (error) 509 break; 510 if (elms.elm_idx >= cache->nelms) { 511 error = EINVAL; 512 break; 513 } 514 cam_periph_lock(periph); 515 error = enc->enc_vec.get_elm_status(enc, &elms, 1); 516 cam_periph_unlock(periph); 517 if (error) 518 break; 519 error = copyout(&elms, addr, sizeof(elms)); 520 break; 521 522 case ENCIOC_GETELMDESC: 523 error = copyin(addr, &elmd, sizeof(elmd)); 524 if (error) 525 break; 526 if (elmd.elm_idx >= cache->nelms) { 527 error = EINVAL; 528 break; 529 } 530 if (enc->enc_vec.get_elm_desc != NULL) { 531 error = enc->enc_vec.get_elm_desc(enc, &elmd); 532 if (error) 533 break; 534 } else 535 elmd.elm_desc_len = 0; 536 error = copyout(&elmd, addr, sizeof(elmd)); 537 break; 538 539 case ENCIOC_GETELMDEVNAMES: 540 if (enc->enc_vec.get_elm_devnames == NULL) { 541 error = EINVAL; 542 break; 543 } 544 error = copyin(addr, &elmdn, sizeof(elmdn)); 545 if (error) 546 break; 547 if (elmdn.elm_idx >= cache->nelms) { 548 error = EINVAL; 549 break; 550 } 551 cam_periph_lock(periph); 552 error = (*enc->enc_vec.get_elm_devnames)(enc, &elmdn); 553 cam_periph_unlock(periph); 554 if (error) 555 break; 556 error = copyout(&elmdn, addr, sizeof(elmdn)); 557 break; 558 559 case ENCIOC_SETELMSTAT: 560 error = copyin(addr, &elms, sizeof(elms)); 561 if (error) 562 break; 563 564 if (elms.elm_idx >= cache->nelms) { 565 error = EINVAL; 566 break; 567 } 568 cam_periph_lock(periph); 569 error = enc->enc_vec.set_elm_status(enc, &elms, 1); 570 cam_periph_unlock(periph); 571 572 break; 573 574 case ENCIOC_INIT: 575 576 cam_periph_lock(periph); 577 error = enc->enc_vec.init_enc(enc); 578 cam_periph_unlock(periph); 579 break; 580 581 default: 582 cam_periph_lock(periph); 583 error = cam_periph_ioctl(periph, cmd, arg_addr, enc_error); 584 cam_periph_unlock(periph); 585 break; 586 } 587 sx_sunlock(&enc->enc_cache_lock); 588 return (error); 589 } 590 591 int 592 enc_runcmd(struct enc_softc *enc, char *cdb, int cdbl, char *dptr, int *dlenp) 593 { 594 int error, dlen, tdlen; 595 ccb_flags ddf; 596 union ccb *ccb; 597 598 CAM_DEBUG(enc->periph->path, CAM_DEBUG_TRACE, 599 ("entering enc_runcmd\n")); 600 if (dptr) { 601 if ((dlen = *dlenp) < 0) { 602 dlen = -dlen; 603 ddf = CAM_DIR_OUT; 604 } else { 605 ddf = CAM_DIR_IN; 606 } 607 } else { 608 dlen = 0; 609 ddf = CAM_DIR_NONE; 610 } 611 612 if (cdbl > IOCDBLEN) { 613 cdbl = IOCDBLEN; 614 } 615 616 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 617 if (enc->enc_type == ENC_SEMB_SES || enc->enc_type == ENC_SEMB_SAFT) { 618 tdlen = min(dlen, 1020); 619 tdlen = (tdlen + 3) & ~3; 620 cam_fill_ataio(&ccb->ataio, 0, enc_done, ddf, 0, dptr, tdlen, 621 30 * 1000); 622 if (cdb[0] == RECEIVE_DIAGNOSTIC) 623 ata_28bit_cmd(&ccb->ataio, 624 ATA_SEP_ATTN, cdb[2], 0x02, tdlen / 4); 625 else if (cdb[0] == SEND_DIAGNOSTIC) 626 ata_28bit_cmd(&ccb->ataio, 627 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 628 0x82, tdlen / 4); 629 else if (cdb[0] == READ_BUFFER) 630 ata_28bit_cmd(&ccb->ataio, 631 ATA_SEP_ATTN, cdb[2], 0x00, tdlen / 4); 632 else 633 ata_28bit_cmd(&ccb->ataio, 634 ATA_SEP_ATTN, dlen > 0 ? dptr[0] : 0, 635 0x80, tdlen / 4); 636 } else { 637 tdlen = dlen; 638 cam_fill_csio(&ccb->csio, 0, enc_done, ddf, MSG_SIMPLE_Q_TAG, 639 dptr, dlen, sizeof (struct scsi_sense_data), cdbl, 640 60 * 1000); 641 bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cdbl); 642 } 643 644 error = cam_periph_runccb(ccb, enc_error, ENC_CFLAGS, ENC_FLAGS, NULL); 645 if (error) { 646 if (dptr) { 647 *dlenp = dlen; 648 } 649 } else { 650 if (dptr) { 651 if (ccb->ccb_h.func_code == XPT_ATA_IO) 652 *dlenp = ccb->ataio.resid; 653 else 654 *dlenp = ccb->csio.resid; 655 *dlenp += tdlen - dlen; 656 } 657 } 658 xpt_release_ccb(ccb); 659 CAM_DEBUG(enc->periph->path, CAM_DEBUG_SUBTRACE, 660 ("exiting enc_runcmd: *dlenp = %d\n", *dlenp)); 661 return (error); 662 } 663 664 void 665 enc_log(struct enc_softc *enc, const char *fmt, ...) 666 { 667 va_list ap; 668 669 printf("%s%d: ", enc->periph->periph_name, enc->periph->unit_number); 670 va_start(ap, fmt); 671 vprintf(fmt, ap); 672 va_end(ap); 673 } 674 675 /* 676 * The code after this point runs on many platforms, 677 * so forgive the slightly awkward and nonconforming 678 * appearance. 679 */ 680 681 /* 682 * Is this a device that supports enclosure services? 683 * 684 * It's a pretty simple ruleset- if it is device type 685 * 0x0D (13), it's an ENCLOSURE device. 686 */ 687 688 #define SAFTE_START 44 689 #define SAFTE_END 50 690 #define SAFTE_LEN SAFTE_END-SAFTE_START 691 692 static enctyp 693 enc_type(struct ccb_getdev *cgd) 694 { 695 int buflen; 696 unsigned char *iqd; 697 698 if (cgd->protocol == PROTO_SEMB) { 699 iqd = (unsigned char *)&cgd->ident_data; 700 if (STRNCMP(iqd + 43, "S-E-S", 5) == 0) 701 return (ENC_SEMB_SES); 702 else if (STRNCMP(iqd + 43, "SAF-TE", 6) == 0) 703 return (ENC_SEMB_SAFT); 704 return (ENC_NONE); 705 706 } else if (cgd->protocol != PROTO_SCSI) 707 return (ENC_NONE); 708 709 iqd = (unsigned char *)&cgd->inq_data; 710 buflen = min(sizeof(cgd->inq_data), 711 SID_ADDITIONAL_LENGTH(&cgd->inq_data)); 712 713 if ((iqd[0] & 0x1f) == T_ENCLOSURE) { 714 if ((iqd[2] & 0x7) > 2) { 715 return (ENC_SES); 716 } else { 717 return (ENC_SES_SCSI2); 718 } 719 return (ENC_NONE); 720 } 721 722 #ifdef ENC_ENABLE_PASSTHROUGH 723 if ((iqd[6] & 0x40) && (iqd[2] & 0x7) >= 2) { 724 /* 725 * PassThrough Device. 726 */ 727 return (ENC_ENC_PASSTHROUGH); 728 } 729 #endif 730 731 /* 732 * The comparison is short for a reason- 733 * some vendors were chopping it short. 734 */ 735 736 if (buflen < SAFTE_END - 2) { 737 return (ENC_NONE); 738 } 739 740 if (STRNCMP((char *)&iqd[SAFTE_START], "SAF-TE", SAFTE_LEN - 2) == 0) { 741 return (ENC_SAFT); 742 } 743 return (ENC_NONE); 744 } 745 746 /*================== Enclosure Monitoring/Processing Daemon ==================*/ 747 /** 748 * \brief Queue an update request for a given action, if needed. 749 * 750 * \param enc SES softc to queue the request for. 751 * \param action Action requested. 752 */ 753 void 754 enc_update_request(enc_softc_t *enc, uint32_t action) 755 { 756 if ((enc->pending_actions & (0x1 << action)) == 0) { 757 enc->pending_actions |= (0x1 << action); 758 ENC_DLOG(enc, "%s: queing requested action %d\n", 759 __func__, action); 760 if (enc->current_action == ENC_UPDATE_NONE) 761 wakeup(enc->enc_daemon); 762 } else { 763 ENC_DLOG(enc, "%s: ignoring requested action %d - " 764 "Already queued\n", __func__, action); 765 } 766 } 767 768 /** 769 * \brief Invoke the handler of the highest priority pending 770 * state in the SES state machine. 771 * 772 * \param enc The SES instance invoking the state machine. 773 */ 774 static void 775 enc_fsm_step(enc_softc_t *enc) 776 { 777 union ccb *ccb; 778 uint8_t *buf; 779 struct enc_fsm_state *cur_state; 780 int error; 781 uint32_t xfer_len; 782 783 ENC_DLOG(enc, "%s enter %p\n", __func__, enc); 784 785 enc->current_action = ffs(enc->pending_actions) - 1; 786 enc->pending_actions &= ~(0x1 << enc->current_action); 787 788 cur_state = &enc->enc_fsm_states[enc->current_action]; 789 790 buf = NULL; 791 if (cur_state->buf_size != 0) { 792 cam_periph_unlock(enc->periph); 793 buf = malloc(cur_state->buf_size, M_SCSIENC, M_WAITOK|M_ZERO); 794 cam_periph_lock(enc->periph); 795 } 796 797 error = 0; 798 ccb = NULL; 799 if (cur_state->fill != NULL) { 800 ccb = cam_periph_getccb(enc->periph, CAM_PRIORITY_NORMAL); 801 802 error = cur_state->fill(enc, cur_state, ccb, buf); 803 if (error != 0) 804 goto done; 805 806 error = cam_periph_runccb(ccb, cur_state->error, 807 ENC_CFLAGS, 808 ENC_FLAGS|SF_QUIET_IR, NULL); 809 } 810 811 if (ccb != NULL) { 812 if (ccb->ccb_h.func_code == XPT_ATA_IO) 813 xfer_len = ccb->ataio.dxfer_len - ccb->ataio.resid; 814 else 815 xfer_len = ccb->csio.dxfer_len - ccb->csio.resid; 816 } else 817 xfer_len = 0; 818 819 cam_periph_unlock(enc->periph); 820 cur_state->done(enc, cur_state, ccb, &buf, error, xfer_len); 821 cam_periph_lock(enc->periph); 822 823 done: 824 ENC_DLOG(enc, "%s exit - result %d\n", __func__, error); 825 ENC_FREE_AND_NULL(buf); 826 if (ccb != NULL) 827 xpt_release_ccb(ccb); 828 } 829 830 /** 831 * \invariant Called with cam_periph mutex held. 832 */ 833 static void 834 enc_status_updater(void *arg) 835 { 836 enc_softc_t *enc; 837 838 enc = arg; 839 if (enc->enc_vec.poll_status != NULL) 840 enc->enc_vec.poll_status(enc); 841 } 842 843 static void 844 enc_daemon(void *arg) 845 { 846 enc_softc_t *enc; 847 848 enc = arg; 849 850 cam_periph_lock(enc->periph); 851 while ((enc->enc_flags & ENC_FLAG_SHUTDOWN) == 0) { 852 if (enc->pending_actions == 0) { 853 struct intr_config_hook *hook; 854 855 /* 856 * Reset callout and msleep, or 857 * issue timed task completion 858 * status command. 859 */ 860 enc->current_action = ENC_UPDATE_NONE; 861 862 /* 863 * We've been through our state machine at least 864 * once. Allow the transition to userland. 865 */ 866 hook = &enc->enc_boot_hold_ch; 867 if (hook->ich_func != NULL) { 868 config_intrhook_disestablish(hook); 869 hook->ich_func = NULL; 870 } 871 872 callout_reset(&enc->status_updater, 60*hz, 873 enc_status_updater, enc); 874 875 cam_periph_sleep(enc->periph, enc->enc_daemon, 876 PUSER, "idle", 0); 877 } else { 878 enc_fsm_step(enc); 879 } 880 } 881 enc->enc_daemon = NULL; 882 cam_periph_unlock(enc->periph); 883 cam_periph_release(enc->periph); 884 kproc_exit(0); 885 } 886 887 static int 888 enc_kproc_init(enc_softc_t *enc) 889 { 890 int result; 891 892 callout_init_mtx(&enc->status_updater, enc->periph->sim->mtx, 0); 893 894 if (cam_periph_acquire(enc->periph) != CAM_REQ_CMP) 895 return (ENXIO); 896 897 result = kproc_create(enc_daemon, enc, &enc->enc_daemon, /*flags*/0, 898 /*stackpgs*/0, "enc_daemon%d", 899 enc->periph->unit_number); 900 if (result == 0) { 901 /* Do an initial load of all page data. */ 902 cam_periph_lock(enc->periph); 903 enc->enc_vec.poll_status(enc); 904 cam_periph_unlock(enc->periph); 905 } else 906 cam_periph_release(enc->periph); 907 return (result); 908 } 909 910 /** 911 * \brief Interrupt configuration hook callback associated with 912 * enc_boot_hold_ch. 913 * 914 * Since interrupts are always functional at the time of enclosure 915 * configuration, there is nothing to be done when the callback occurs. 916 * This hook is only registered to hold up boot processing while initial 917 * eclosure processing occurs. 918 * 919 * \param arg The enclosure softc, but currently unused in this callback. 920 */ 921 static void 922 enc_nop_confighook_cb(void *arg __unused) 923 { 924 } 925 926 static cam_status 927 enc_ctor(struct cam_periph *periph, void *arg) 928 { 929 cam_status status = CAM_REQ_CMP_ERR; 930 int err; 931 enc_softc_t *enc; 932 struct ccb_getdev *cgd; 933 char *tname; 934 935 cgd = (struct ccb_getdev *)arg; 936 if (cgd == NULL) { 937 printf("enc_ctor: no getdev CCB, can't register device\n"); 938 goto out; 939 } 940 941 enc = ENC_MALLOCZ(sizeof(*enc)); 942 if (enc == NULL) { 943 printf("enc_ctor: Unable to probe new device. " 944 "Unable to allocate enc\n"); 945 goto out; 946 } 947 enc->periph = periph; 948 enc->current_action = ENC_UPDATE_INVALID; 949 950 enc->enc_type = enc_type(cgd); 951 sx_init(&enc->enc_cache_lock, "enccache"); 952 953 switch (enc->enc_type) { 954 case ENC_SES: 955 case ENC_SES_SCSI2: 956 case ENC_SES_PASSTHROUGH: 957 case ENC_SEMB_SES: 958 err = ses_softc_init(enc); 959 break; 960 case ENC_SAFT: 961 case ENC_SEMB_SAFT: 962 err = safte_softc_init(enc); 963 break; 964 case ENC_NONE: 965 default: 966 ENC_FREE(enc); 967 return (CAM_REQ_CMP_ERR); 968 } 969 970 if (err) { 971 xpt_print(periph->path, "error %d initializing\n", err); 972 goto out; 973 } 974 975 /* 976 * Hold off userland until we have made at least one pass 977 * through our state machine so that physical path data is 978 * present. 979 */ 980 if (enc->enc_vec.poll_status != NULL) { 981 enc->enc_boot_hold_ch.ich_func = enc_nop_confighook_cb; 982 enc->enc_boot_hold_ch.ich_arg = enc; 983 config_intrhook_establish(&enc->enc_boot_hold_ch); 984 } 985 986 /* 987 * The softc field is set only once the enc is fully initialized 988 * so that we can rely on this field to detect partially 989 * initialized periph objects in the AC_FOUND_DEVICE handler. 990 */ 991 periph->softc = enc; 992 993 cam_periph_unlock(periph); 994 if (enc->enc_vec.poll_status != NULL) { 995 err = enc_kproc_init(enc); 996 if (err) { 997 xpt_print(periph->path, 998 "error %d starting enc_daemon\n", err); 999 goto out; 1000 } 1001 } 1002 1003 /* 1004 * Acquire a reference to the periph before we create the devfs 1005 * instance for it. We'll release this reference once the devfs 1006 * instance has been freed. 1007 */ 1008 if (cam_periph_acquire(periph) != CAM_REQ_CMP) { 1009 xpt_print(periph->path, "%s: lost periph during " 1010 "registration!\n", __func__); 1011 cam_periph_lock(periph); 1012 1013 return (CAM_REQ_CMP_ERR); 1014 } 1015 1016 enc->enc_dev = make_dev(&enc_cdevsw, periph->unit_number, 1017 UID_ROOT, GID_OPERATOR, 0600, "%s%d", 1018 periph->periph_name, periph->unit_number); 1019 1020 cam_periph_lock(periph); 1021 enc->enc_dev->si_drv1 = periph; 1022 1023 enc->enc_flags |= ENC_FLAG_INITIALIZED; 1024 1025 /* 1026 * Add an async callback so that we get notified if this 1027 * device goes away. 1028 */ 1029 xpt_register_async(AC_LOST_DEVICE, enc_async, periph, periph->path); 1030 1031 switch (enc->enc_type) { 1032 default: 1033 case ENC_NONE: 1034 tname = "No ENC device"; 1035 break; 1036 case ENC_SES_SCSI2: 1037 tname = "SCSI-2 ENC Device"; 1038 break; 1039 case ENC_SES: 1040 tname = "SCSI-3 ENC Device"; 1041 break; 1042 case ENC_SES_PASSTHROUGH: 1043 tname = "ENC Passthrough Device"; 1044 break; 1045 case ENC_SAFT: 1046 tname = "SAF-TE Compliant Device"; 1047 break; 1048 case ENC_SEMB_SES: 1049 tname = "SEMB SES Device"; 1050 break; 1051 case ENC_SEMB_SAFT: 1052 tname = "SEMB SAF-TE Device"; 1053 break; 1054 } 1055 xpt_announce_periph(periph, tname); 1056 status = CAM_REQ_CMP; 1057 1058 out: 1059 if (status != CAM_REQ_CMP) 1060 enc_dtor(periph); 1061 return (status); 1062 } 1063 1064