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