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