1 /* 2 * Copyright (c) 1997, 1998 Justin T. Gibbs. 3 * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions, and the following disclaimer, 11 * without modification, immediately at the beginning of the file. 12 * 2. The name of the author may not be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 19 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/types.h> 34 #include <sys/bio.h> 35 #include <sys/malloc.h> 36 #include <sys/fcntl.h> 37 #include <sys/conf.h> 38 #include <sys/errno.h> 39 #include <sys/devicestat.h> 40 41 #include <cam/cam.h> 42 #include <cam/cam_ccb.h> 43 #include <cam/cam_extend.h> 44 #include <cam/cam_periph.h> 45 #include <cam/cam_xpt_periph.h> 46 #include <cam/cam_debug.h> 47 48 #include <cam/scsi/scsi_all.h> 49 #include <cam/scsi/scsi_pass.h> 50 51 typedef enum { 52 PASS_FLAG_OPEN = 0x01, 53 PASS_FLAG_LOCKED = 0x02, 54 PASS_FLAG_INVALID = 0x04 55 } pass_flags; 56 57 typedef enum { 58 PASS_STATE_NORMAL 59 } pass_state; 60 61 typedef enum { 62 PASS_CCB_BUFFER_IO, 63 PASS_CCB_WAITING 64 } pass_ccb_types; 65 66 #define ccb_type ppriv_field0 67 #define ccb_bp ppriv_ptr1 68 69 struct pass_softc { 70 pass_state state; 71 pass_flags flags; 72 u_int8_t pd_type; 73 struct bio_queue_head bio_queue; 74 union ccb saved_ccb; 75 struct devstat device_stats; 76 dev_t dev; 77 }; 78 79 #ifndef MIN 80 #define MIN(x,y) ((x<y) ? x : y) 81 #endif 82 83 #define PASS_CDEV_MAJOR 31 84 85 static d_open_t passopen; 86 static d_close_t passclose; 87 static d_ioctl_t passioctl; 88 static d_strategy_t passstrategy; 89 90 static periph_init_t passinit; 91 static periph_ctor_t passregister; 92 static periph_oninv_t passoninvalidate; 93 static periph_dtor_t passcleanup; 94 static periph_start_t passstart; 95 static void passasync(void *callback_arg, u_int32_t code, 96 struct cam_path *path, void *arg); 97 static void passdone(struct cam_periph *periph, 98 union ccb *done_ccb); 99 static int passerror(union ccb *ccb, u_int32_t cam_flags, 100 u_int32_t sense_flags); 101 static int passsendccb(struct cam_periph *periph, union ccb *ccb, 102 union ccb *inccb); 103 104 static struct periph_driver passdriver = 105 { 106 passinit, "pass", 107 TAILQ_HEAD_INITIALIZER(passdriver.units), /* generation */ 0 108 }; 109 110 DATA_SET(periphdriver_set, passdriver); 111 112 static struct cdevsw pass_cdevsw = { 113 /* open */ passopen, 114 /* close */ passclose, 115 /* read */ physread, 116 /* write */ physwrite, 117 /* ioctl */ passioctl, 118 /* poll */ nopoll, 119 /* mmap */ nommap, 120 /* strategy */ passstrategy, 121 /* name */ "pass", 122 /* maj */ PASS_CDEV_MAJOR, 123 /* dump */ nodump, 124 /* psize */ nopsize, 125 /* flags */ 0, 126 /* bmaj */ -1 127 }; 128 129 static struct extend_array *passperiphs; 130 131 static void 132 passinit(void) 133 { 134 cam_status status; 135 struct cam_path *path; 136 137 /* 138 * Create our extend array for storing the devices we attach to. 139 */ 140 passperiphs = cam_extend_new(); 141 if (passperiphs == NULL) { 142 printf("passm: Failed to alloc extend array!\n"); 143 return; 144 } 145 146 /* 147 * Install a global async callback. This callback will 148 * receive async callbacks like "new device found". 149 */ 150 status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, 151 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 152 153 if (status == CAM_REQ_CMP) { 154 struct ccb_setasync csa; 155 156 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); 157 csa.ccb_h.func_code = XPT_SASYNC_CB; 158 csa.event_enable = AC_FOUND_DEVICE; 159 csa.callback = passasync; 160 csa.callback_arg = NULL; 161 xpt_action((union ccb *)&csa); 162 status = csa.ccb_h.status; 163 xpt_free_path(path); 164 } 165 166 if (status != CAM_REQ_CMP) { 167 printf("pass: Failed to attach master async callback " 168 "due to status 0x%x!\n", status); 169 } 170 171 } 172 173 static void 174 passoninvalidate(struct cam_periph *periph) 175 { 176 int s; 177 struct pass_softc *softc; 178 struct bio *q_bp; 179 struct ccb_setasync csa; 180 181 softc = (struct pass_softc *)periph->softc; 182 183 /* 184 * De-register any async callbacks. 185 */ 186 xpt_setup_ccb(&csa.ccb_h, periph->path, 187 /* priority */ 5); 188 csa.ccb_h.func_code = XPT_SASYNC_CB; 189 csa.event_enable = 0; 190 csa.callback = passasync; 191 csa.callback_arg = periph; 192 xpt_action((union ccb *)&csa); 193 194 softc->flags |= PASS_FLAG_INVALID; 195 196 /* 197 * Although the oninvalidate() routines are always called at 198 * splsoftcam, we need to be at splbio() here to keep the buffer 199 * queue from being modified while we traverse it. 200 */ 201 s = splbio(); 202 203 /* 204 * Return all queued I/O with ENXIO. 205 * XXX Handle any transactions queued to the card 206 * with XPT_ABORT_CCB. 207 */ 208 while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){ 209 bioq_remove(&softc->bio_queue, q_bp); 210 q_bp->bio_resid = q_bp->bio_bcount; 211 q_bp->bio_error = ENXIO; 212 q_bp->bio_flags |= BIO_ERROR; 213 biodone(q_bp); 214 } 215 splx(s); 216 217 if (bootverbose) { 218 xpt_print_path(periph->path); 219 printf("lost device\n"); 220 } 221 222 } 223 224 static void 225 passcleanup(struct cam_periph *periph) 226 { 227 struct pass_softc *softc; 228 229 softc = (struct pass_softc *)periph->softc; 230 231 devstat_remove_entry(&softc->device_stats); 232 233 destroy_dev(softc->dev); 234 235 cam_extend_release(passperiphs, periph->unit_number); 236 237 if (bootverbose) { 238 xpt_print_path(periph->path); 239 printf("removing device entry\n"); 240 } 241 free(softc, M_DEVBUF); 242 } 243 244 static void 245 passasync(void *callback_arg, u_int32_t code, 246 struct cam_path *path, void *arg) 247 { 248 struct cam_periph *periph; 249 250 periph = (struct cam_periph *)callback_arg; 251 252 switch (code) { 253 case AC_FOUND_DEVICE: 254 { 255 struct ccb_getdev *cgd; 256 cam_status status; 257 258 cgd = (struct ccb_getdev *)arg; 259 260 /* 261 * Allocate a peripheral instance for 262 * this device and start the probe 263 * process. 264 */ 265 status = cam_periph_alloc(passregister, passoninvalidate, 266 passcleanup, passstart, "pass", 267 CAM_PERIPH_BIO, cgd->ccb_h.path, 268 passasync, AC_FOUND_DEVICE, cgd); 269 270 if (status != CAM_REQ_CMP 271 && status != CAM_REQ_INPROG) 272 printf("passasync: Unable to attach new device " 273 "due to status 0x%x\n", status); 274 275 break; 276 } 277 default: 278 cam_periph_async(periph, code, path, arg); 279 break; 280 } 281 } 282 283 static cam_status 284 passregister(struct cam_periph *periph, void *arg) 285 { 286 struct pass_softc *softc; 287 struct ccb_setasync csa; 288 struct ccb_getdev *cgd; 289 290 cgd = (struct ccb_getdev *)arg; 291 if (periph == NULL) { 292 printf("passregister: periph was NULL!!\n"); 293 return(CAM_REQ_CMP_ERR); 294 } 295 296 if (cgd == NULL) { 297 printf("passregister: no getdev CCB, can't register device\n"); 298 return(CAM_REQ_CMP_ERR); 299 } 300 301 softc = (struct pass_softc *)malloc(sizeof(*softc), 302 M_DEVBUF, M_NOWAIT); 303 304 if (softc == NULL) { 305 printf("passregister: Unable to probe new device. " 306 "Unable to allocate softc\n"); 307 return(CAM_REQ_CMP_ERR); 308 } 309 310 bzero(softc, sizeof(*softc)); 311 softc->state = PASS_STATE_NORMAL; 312 softc->pd_type = SID_TYPE(&cgd->inq_data); 313 bioq_init(&softc->bio_queue); 314 315 periph->softc = softc; 316 317 cam_extend_set(passperiphs, periph->unit_number, periph); 318 /* 319 * We pass in 0 for a blocksize, since we don't 320 * know what the blocksize of this device is, if 321 * it even has a blocksize. 322 */ 323 devstat_add_entry(&softc->device_stats, "pass", periph->unit_number, 324 0, DEVSTAT_NO_BLOCKSIZE | DEVSTAT_NO_ORDERED_TAGS, 325 softc->pd_type | 326 DEVSTAT_TYPE_IF_SCSI | 327 DEVSTAT_TYPE_PASS, 328 DEVSTAT_PRIORITY_PASS); 329 330 /* Register the device */ 331 softc->dev = make_dev(&pass_cdevsw, periph->unit_number, UID_ROOT, 332 GID_OPERATOR, 0600, "%s%d", periph->periph_name, 333 periph->unit_number); 334 335 /* 336 * Add an async callback so that we get 337 * notified if this device goes away. 338 */ 339 xpt_setup_ccb(&csa.ccb_h, periph->path, /* priority */ 5); 340 csa.ccb_h.func_code = XPT_SASYNC_CB; 341 csa.event_enable = AC_LOST_DEVICE; 342 csa.callback = passasync; 343 csa.callback_arg = periph; 344 xpt_action((union ccb *)&csa); 345 346 if (bootverbose) 347 xpt_announce_periph(periph, NULL); 348 349 return(CAM_REQ_CMP); 350 } 351 352 static int 353 passopen(dev_t dev, int flags, int fmt, struct proc *p) 354 { 355 struct cam_periph *periph; 356 struct pass_softc *softc; 357 int unit, error; 358 int s; 359 360 error = 0; /* default to no error */ 361 362 /* unit = dkunit(dev); */ 363 /* XXX KDM fix this */ 364 unit = minor(dev) & 0xff; 365 366 periph = cam_extend_get(passperiphs, unit); 367 368 if (periph == NULL) 369 return (ENXIO); 370 371 softc = (struct pass_softc *)periph->softc; 372 373 s = splsoftcam(); 374 if (softc->flags & PASS_FLAG_INVALID) { 375 splx(s); 376 return(ENXIO); 377 } 378 379 /* 380 * Don't allow access when we're running at a high securelvel. 381 */ 382 if (securelevel > 1) { 383 splx(s); 384 return(EPERM); 385 } 386 387 /* 388 * Only allow read-write access. 389 */ 390 if (((flags & FWRITE) == 0) || ((flags & FREAD) == 0)) { 391 splx(s); 392 return(EPERM); 393 } 394 395 /* 396 * We don't allow nonblocking access. 397 */ 398 if ((flags & O_NONBLOCK) != 0) { 399 xpt_print_path(periph->path); 400 printf("can't do nonblocking accesss\n"); 401 splx(s); 402 return(EINVAL); 403 } 404 405 if ((error = cam_periph_lock(periph, PRIBIO | PCATCH)) != 0) { 406 splx(s); 407 return (error); 408 } 409 410 splx(s); 411 412 if ((softc->flags & PASS_FLAG_OPEN) == 0) { 413 if (cam_periph_acquire(periph) != CAM_REQ_CMP) 414 return(ENXIO); 415 softc->flags |= PASS_FLAG_OPEN; 416 } 417 418 cam_periph_unlock(periph); 419 420 return (error); 421 } 422 423 static int 424 passclose(dev_t dev, int flag, int fmt, struct proc *p) 425 { 426 struct cam_periph *periph; 427 struct pass_softc *softc; 428 int unit, error; 429 430 /* unit = dkunit(dev); */ 431 /* XXX KDM fix this */ 432 unit = minor(dev) & 0xff; 433 434 periph = cam_extend_get(passperiphs, unit); 435 if (periph == NULL) 436 return (ENXIO); 437 438 softc = (struct pass_softc *)periph->softc; 439 440 if ((error = cam_periph_lock(periph, PRIBIO)) != 0) 441 return (error); 442 443 softc->flags &= ~PASS_FLAG_OPEN; 444 445 cam_periph_unlock(periph); 446 cam_periph_release(periph); 447 448 return (0); 449 } 450 451 /* 452 * Actually translate the requested transfer into one the physical driver 453 * can understand. The transfer is described by a buf and will include 454 * only one physical transfer. 455 */ 456 static void 457 passstrategy(struct bio *bp) 458 { 459 struct cam_periph *periph; 460 struct pass_softc *softc; 461 u_int unit; 462 int s; 463 464 /* 465 * The read/write interface for the passthrough driver doesn't 466 * really work right now. So, we just pass back EINVAL to tell the 467 * user to go away. 468 */ 469 bp->bio_error = EINVAL; 470 goto bad; 471 472 /* unit = dkunit(bp->bio_dev); */ 473 /* XXX KDM fix this */ 474 unit = minor(bp->bio_dev) & 0xff; 475 476 periph = cam_extend_get(passperiphs, unit); 477 if (periph == NULL) { 478 bp->bio_error = ENXIO; 479 goto bad; 480 } 481 softc = (struct pass_softc *)periph->softc; 482 483 /* 484 * Odd number of bytes or negative offset 485 */ 486 /* valid request? */ 487 if (bp->bio_blkno < 0) { 488 bp->bio_error = EINVAL; 489 goto bad; 490 } 491 492 /* 493 * Mask interrupts so that the pack cannot be invalidated until 494 * after we are in the queue. Otherwise, we might not properly 495 * clean up one of the buffers. 496 */ 497 s = splbio(); 498 499 bioq_insert_tail(&softc->bio_queue, bp); 500 501 splx(s); 502 503 /* 504 * Schedule ourselves for performing the work. 505 */ 506 xpt_schedule(periph, /* XXX priority */1); 507 508 return; 509 bad: 510 bp->bio_flags |= BIO_ERROR; 511 512 /* 513 * Correctly set the buf to indicate a completed xfer 514 */ 515 bp->bio_resid = bp->bio_bcount; 516 biodone(bp); 517 return; 518 } 519 520 static void 521 passstart(struct cam_periph *periph, union ccb *start_ccb) 522 { 523 struct pass_softc *softc; 524 int s; 525 526 softc = (struct pass_softc *)periph->softc; 527 528 switch (softc->state) { 529 case PASS_STATE_NORMAL: 530 { 531 struct bio *bp; 532 533 s = splbio(); 534 bp = bioq_first(&softc->bio_queue); 535 if (periph->immediate_priority <= periph->pinfo.priority) { 536 start_ccb->ccb_h.ccb_type = PASS_CCB_WAITING; 537 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, 538 periph_links.sle); 539 periph->immediate_priority = CAM_PRIORITY_NONE; 540 splx(s); 541 wakeup(&periph->ccb_list); 542 } else if (bp == NULL) { 543 splx(s); 544 xpt_release_ccb(start_ccb); 545 } else { 546 547 bioq_remove(&softc->bio_queue, bp); 548 549 devstat_start_transaction(&softc->device_stats); 550 551 /* 552 * XXX JGibbs - 553 * Interpret the contents of the bp as a CCB 554 * and pass it to a routine shared by our ioctl 555 * code and passtart. 556 * For now, just biodone it with EIO so we don't 557 * hang. 558 */ 559 bp->bio_error = EIO; 560 bp->bio_flags |= BIO_ERROR; 561 bp->bio_resid = bp->bio_bcount; 562 biodone(bp); 563 bp = bioq_first(&softc->bio_queue); 564 splx(s); 565 566 xpt_action(start_ccb); 567 568 } 569 if (bp != NULL) { 570 /* Have more work to do, so ensure we stay scheduled */ 571 xpt_schedule(periph, /* XXX priority */1); 572 } 573 break; 574 } 575 } 576 } 577 static void 578 passdone(struct cam_periph *periph, union ccb *done_ccb) 579 { 580 struct pass_softc *softc; 581 struct ccb_scsiio *csio; 582 583 softc = (struct pass_softc *)periph->softc; 584 csio = &done_ccb->csio; 585 switch (csio->ccb_h.ccb_type) { 586 case PASS_CCB_BUFFER_IO: 587 { 588 struct bio *bp; 589 cam_status status; 590 u_int8_t scsi_status; 591 devstat_trans_flags ds_flags; 592 593 status = done_ccb->ccb_h.status; 594 scsi_status = done_ccb->csio.scsi_status; 595 bp = (struct bio *)done_ccb->ccb_h.ccb_bp; 596 /* XXX handle errors */ 597 if (!(((status & CAM_STATUS_MASK) == CAM_REQ_CMP) 598 && (scsi_status == SCSI_STATUS_OK))) { 599 int error; 600 601 if ((error = passerror(done_ccb, 0, 0)) == ERESTART) { 602 /* 603 * A retry was scheuled, so 604 * just return. 605 */ 606 return; 607 } 608 609 /* 610 * XXX unfreeze the queue after we complete 611 * the abort process 612 */ 613 bp->bio_error = error; 614 bp->bio_flags |= BIO_ERROR; 615 } 616 617 if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) 618 ds_flags = DEVSTAT_READ; 619 else if ((done_ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT) 620 ds_flags = DEVSTAT_WRITE; 621 else 622 ds_flags = DEVSTAT_NO_DATA; 623 624 devstat_end_transaction_bio(&softc->device_stats, bp); 625 biodone(bp); 626 break; 627 } 628 case PASS_CCB_WAITING: 629 { 630 /* Caller will release the CCB */ 631 wakeup(&done_ccb->ccb_h.cbfcnp); 632 return; 633 } 634 } 635 xpt_release_ccb(done_ccb); 636 } 637 638 static int 639 passioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 640 { 641 struct cam_periph *periph; 642 struct pass_softc *softc; 643 u_int8_t unit; 644 int error; 645 646 647 /* unit = dkunit(dev); */ 648 /* XXX KDM fix this */ 649 unit = minor(dev) & 0xff; 650 651 periph = cam_extend_get(passperiphs, unit); 652 653 if (periph == NULL) 654 return(ENXIO); 655 656 softc = (struct pass_softc *)periph->softc; 657 658 error = 0; 659 660 switch (cmd) { 661 662 case CAMIOCOMMAND: 663 { 664 union ccb *inccb; 665 union ccb *ccb; 666 int ccb_malloced; 667 668 inccb = (union ccb *)addr; 669 670 /* 671 * Some CCB types, like scan bus and scan lun can only go 672 * through the transport layer device. 673 */ 674 if (inccb->ccb_h.func_code & XPT_FC_XPT_ONLY) { 675 xpt_print_path(periph->path); 676 printf("CCB function code %#x is restricted to the " 677 "XPT device\n", inccb->ccb_h.func_code); 678 error = ENODEV; 679 break; 680 } 681 682 /* 683 * Non-immediate CCBs need a CCB from the per-device pool 684 * of CCBs, which is scheduled by the transport layer. 685 * Immediate CCBs and user-supplied CCBs should just be 686 * malloced. 687 */ 688 if ((inccb->ccb_h.func_code & XPT_FC_QUEUED) 689 && ((inccb->ccb_h.func_code & XPT_FC_USER_CCB) == 0)) { 690 ccb = cam_periph_getccb(periph, 691 inccb->ccb_h.pinfo.priority); 692 ccb_malloced = 0; 693 } else { 694 ccb = xpt_alloc_ccb(); 695 696 if (ccb != NULL) 697 xpt_setup_ccb(&ccb->ccb_h, periph->path, 698 inccb->ccb_h.pinfo.priority); 699 ccb_malloced = 1; 700 } 701 702 if (ccb == NULL) { 703 xpt_print_path(periph->path); 704 printf("unable to allocate CCB\n"); 705 error = ENOMEM; 706 break; 707 } 708 709 error = passsendccb(periph, ccb, inccb); 710 711 if (ccb_malloced) 712 xpt_free_ccb(ccb); 713 else 714 xpt_release_ccb(ccb); 715 716 break; 717 } 718 default: 719 error = cam_periph_ioctl(periph, cmd, addr, passerror); 720 break; 721 } 722 723 return(error); 724 } 725 726 /* 727 * Generally, "ccb" should be the CCB supplied by the kernel. "inccb" 728 * should be the CCB that is copied in from the user. 729 */ 730 static int 731 passsendccb(struct cam_periph *periph, union ccb *ccb, union ccb *inccb) 732 { 733 struct pass_softc *softc; 734 struct cam_periph_map_info mapinfo; 735 int error, need_unmap; 736 737 softc = (struct pass_softc *)periph->softc; 738 739 need_unmap = 0; 740 741 /* 742 * There are some fields in the CCB header that need to be 743 * preserved, the rest we get from the user. 744 */ 745 xpt_merge_ccb(ccb, inccb); 746 747 /* 748 * There's no way for the user to have a completion 749 * function, so we put our own completion function in here. 750 */ 751 ccb->ccb_h.cbfcnp = passdone; 752 753 /* 754 * We only attempt to map the user memory into kernel space 755 * if they haven't passed in a physical memory pointer, 756 * and if there is actually an I/O operation to perform. 757 * Right now cam_periph_mapmem() only supports SCSI and device 758 * match CCBs. For the SCSI CCBs, we only pass the CCB in if 759 * there's actually data to map. cam_periph_mapmem() will do the 760 * right thing, even if there isn't data to map, but since CCBs 761 * without data are a reasonably common occurance (e.g. test unit 762 * ready), it will save a few cycles if we check for it here. 763 */ 764 if (((ccb->ccb_h.flags & CAM_DATA_PHYS) == 0) 765 && (((ccb->ccb_h.func_code == XPT_SCSI_IO) 766 && ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE)) 767 || (ccb->ccb_h.func_code == XPT_DEV_MATCH))) { 768 769 bzero(&mapinfo, sizeof(mapinfo)); 770 771 error = cam_periph_mapmem(ccb, &mapinfo); 772 773 /* 774 * cam_periph_mapmem returned an error, we can't continue. 775 * Return the error to the user. 776 */ 777 if (error) 778 return(error); 779 780 /* 781 * We successfully mapped the memory in, so we need to 782 * unmap it when the transaction is done. 783 */ 784 need_unmap = 1; 785 } 786 787 /* 788 * If the user wants us to perform any error recovery, then honor 789 * that request. Otherwise, it's up to the user to perform any 790 * error recovery. 791 */ 792 error = cam_periph_runccb(ccb, 793 (ccb->ccb_h.flags & CAM_PASS_ERR_RECOVER) ? 794 passerror : NULL, 795 /* cam_flags */ 0, 796 /* sense_flags */SF_RETRY_UA | SF_RETRY_SELTO, 797 &softc->device_stats); 798 799 if (need_unmap != 0) 800 cam_periph_unmapmem(ccb, &mapinfo); 801 802 ccb->ccb_h.cbfcnp = NULL; 803 ccb->ccb_h.periph_priv = inccb->ccb_h.periph_priv; 804 bcopy(ccb, inccb, sizeof(union ccb)); 805 806 return(error); 807 } 808 809 static int 810 passerror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 811 { 812 struct cam_periph *periph; 813 struct pass_softc *softc; 814 815 periph = xpt_path_periph(ccb->ccb_h.path); 816 softc = (struct pass_softc *)periph->softc; 817 818 return(cam_periph_error(ccb, cam_flags, sense_flags, 819 &softc->saved_ccb)); 820 } 821