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