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