1 /* 2 * Implementation of SCSI Processor Target Peripheral driver for CAM. 3 * 4 * Copyright (c) 1998 Justin T. Gibbs. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions, and the following disclaimer, 12 * without modification, immediately at the beginning of the file. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/queue.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/types.h> 36 #include <sys/bio.h> 37 #include <sys/devicestat.h> 38 #include <sys/malloc.h> 39 #include <sys/conf.h> 40 #include <sys/ptio.h> 41 42 #include <cam/cam.h> 43 #include <cam/cam_ccb.h> 44 #include <cam/cam_extend.h> 45 #include <cam/cam_periph.h> 46 #include <cam/cam_xpt_periph.h> 47 #include <cam/cam_debug.h> 48 49 #include <cam/scsi/scsi_all.h> 50 #include <cam/scsi/scsi_message.h> 51 #include <cam/scsi/scsi_pt.h> 52 53 #include "opt_pt.h" 54 55 typedef enum { 56 PT_STATE_PROBE, 57 PT_STATE_NORMAL 58 } pt_state; 59 60 typedef enum { 61 PT_FLAG_NONE = 0x00, 62 PT_FLAG_OPEN = 0x01, 63 PT_FLAG_DEVICE_INVALID = 0x02, 64 PT_FLAG_RETRY_UA = 0x04 65 } pt_flags; 66 67 typedef enum { 68 PT_CCB_BUFFER_IO = 0x01, 69 PT_CCB_WAITING = 0x02, 70 PT_CCB_RETRY_UA = 0x04, 71 PT_CCB_BUFFER_IO_UA = PT_CCB_BUFFER_IO|PT_CCB_RETRY_UA 72 } pt_ccb_state; 73 74 /* Offsets into our private area for storing information */ 75 #define ccb_state ppriv_field0 76 #define ccb_bp ppriv_ptr1 77 78 struct pt_softc { 79 struct bio_queue_head bio_queue; 80 struct devstat device_stats; 81 LIST_HEAD(, ccb_hdr) pending_ccbs; 82 pt_state state; 83 pt_flags flags; 84 union ccb saved_ccb; 85 int io_timeout; 86 dev_t dev; 87 }; 88 89 static d_open_t ptopen; 90 static d_close_t ptclose; 91 static d_strategy_t ptstrategy; 92 static periph_init_t ptinit; 93 static void ptasync(void *callback_arg, u_int32_t code, 94 struct cam_path *path, void *arg); 95 static periph_ctor_t ptctor; 96 static periph_oninv_t ptoninvalidate; 97 static periph_dtor_t ptdtor; 98 static periph_start_t ptstart; 99 static void ptdone(struct cam_periph *periph, 100 union ccb *done_ccb); 101 static d_ioctl_t ptioctl; 102 static int pterror(union ccb *ccb, u_int32_t cam_flags, 103 u_int32_t sense_flags); 104 105 void scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries, 106 void (*cbfcnp)(struct cam_periph *, union ccb *), 107 u_int tag_action, int readop, u_int byte2, 108 u_int32_t xfer_len, u_int8_t *data_ptr, 109 u_int8_t sense_len, u_int32_t timeout); 110 111 static struct periph_driver ptdriver = 112 { 113 ptinit, "pt", 114 TAILQ_HEAD_INITIALIZER(ptdriver.units), /* generation */ 0 115 }; 116 117 DATA_SET(periphdriver_set, ptdriver); 118 119 #define PT_CDEV_MAJOR 61 120 121 static struct cdevsw pt_cdevsw = { 122 /* open */ ptopen, 123 /* close */ ptclose, 124 /* read */ physread, 125 /* write */ physwrite, 126 /* ioctl */ ptioctl, 127 /* poll */ nopoll, 128 /* mmap */ nommap, 129 /* strategy */ ptstrategy, 130 /* name */ "pt", 131 /* maj */ PT_CDEV_MAJOR, 132 /* dump */ nodump, 133 /* psize */ nopsize, 134 /* flags */ 0, 135 /* bmaj */ -1 136 }; 137 138 static struct extend_array *ptperiphs; 139 140 #ifndef SCSI_PT_DEFAULT_TIMEOUT 141 #define SCSI_PT_DEFAULT_TIMEOUT 60 142 #endif 143 144 static int 145 ptopen(dev_t dev, int flags, int fmt, struct proc *p) 146 { 147 struct cam_periph *periph; 148 struct pt_softc *softc; 149 int unit; 150 int error; 151 int s; 152 153 unit = minor(dev); 154 periph = cam_extend_get(ptperiphs, unit); 155 if (periph == NULL) 156 return (ENXIO); 157 158 softc = (struct pt_softc *)periph->softc; 159 160 s = splsoftcam(); 161 if (softc->flags & PT_FLAG_DEVICE_INVALID) { 162 splx(s); 163 return(ENXIO); 164 } 165 166 CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, 167 ("ptopen: dev=%s (unit %d)\n", devtoname(dev), unit)); 168 169 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) { 170 splx(s); 171 return (error); /* error code from tsleep */ 172 } 173 174 splx(s); 175 176 if ((softc->flags & PT_FLAG_OPEN) == 0) { 177 if (cam_periph_acquire(periph) != CAM_REQ_CMP) 178 error = ENXIO; 179 else 180 softc->flags |= PT_FLAG_OPEN; 181 } else 182 error = EBUSY; 183 184 cam_periph_unlock(periph); 185 return (error); 186 } 187 188 static int 189 ptclose(dev_t dev, int flag, int fmt, struct proc *p) 190 { 191 struct cam_periph *periph; 192 struct pt_softc *softc; 193 int unit; 194 int error; 195 196 unit = minor(dev); 197 periph = cam_extend_get(ptperiphs, unit); 198 if (periph == NULL) 199 return (ENXIO); 200 201 softc = (struct pt_softc *)periph->softc; 202 203 if ((error = cam_periph_lock(periph, PRIBIO)) != 0) 204 return (error); /* error code from tsleep */ 205 206 softc->flags &= ~PT_FLAG_OPEN; 207 cam_periph_unlock(periph); 208 cam_periph_release(periph); 209 return (0); 210 } 211 212 /* 213 * Actually translate the requested transfer into one the physical driver 214 * can understand. The transfer is described by a buf and will include 215 * only one physical transfer. 216 */ 217 static void 218 ptstrategy(struct bio *bp) 219 { 220 struct cam_periph *periph; 221 struct pt_softc *softc; 222 u_int unit; 223 int s; 224 225 unit = minor(bp->bio_dev); 226 periph = cam_extend_get(ptperiphs, unit); 227 if (periph == NULL) { 228 bp->bio_error = ENXIO; 229 goto bad; 230 } 231 softc = (struct pt_softc *)periph->softc; 232 233 /* 234 * Mask interrupts so that the pack cannot be invalidated until 235 * after we are in the queue. Otherwise, we might not properly 236 * clean up one of the buffers. 237 */ 238 s = splbio(); 239 240 /* 241 * If the device has been made invalid, error out 242 */ 243 if ((softc->flags & PT_FLAG_DEVICE_INVALID)) { 244 splx(s); 245 bp->bio_error = ENXIO; 246 goto bad; 247 } 248 249 /* 250 * Place it in the queue of disk activities for this disk 251 */ 252 bioq_insert_tail(&softc->bio_queue, bp); 253 254 splx(s); 255 256 /* 257 * Schedule ourselves for performing the work. 258 */ 259 xpt_schedule(periph, /* XXX priority */1); 260 261 return; 262 bad: 263 bp->bio_flags |= BIO_ERROR; 264 265 /* 266 * Correctly set the buf to indicate a completed xfer 267 */ 268 bp->bio_resid = bp->bio_bcount; 269 biodone(bp); 270 } 271 272 static void 273 ptinit(void) 274 { 275 cam_status status; 276 struct cam_path *path; 277 278 /* 279 * Create our extend array for storing the devices we attach to. 280 */ 281 ptperiphs = cam_extend_new(); 282 if (ptperiphs == NULL) { 283 printf("pt: Failed to alloc extend array!\n"); 284 return; 285 } 286 287 /* 288 * Install a global async callback. This callback will 289 * receive async callbacks like "new device found". 290 */ 291 status = xpt_create_path(&path, /*periph*/NULL, CAM_XPT_PATH_ID, 292 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD); 293 294 if (status == CAM_REQ_CMP) { 295 struct ccb_setasync csa; 296 297 xpt_setup_ccb(&csa.ccb_h, path, /*priority*/5); 298 csa.ccb_h.func_code = XPT_SASYNC_CB; 299 csa.event_enable = AC_FOUND_DEVICE; 300 csa.callback = ptasync; 301 csa.callback_arg = NULL; 302 xpt_action((union ccb *)&csa); 303 status = csa.ccb_h.status; 304 xpt_free_path(path); 305 } 306 307 if (status != CAM_REQ_CMP) { 308 printf("pt: Failed to attach master async callback " 309 "due to status 0x%x!\n", status); 310 } 311 } 312 313 static cam_status 314 ptctor(struct cam_periph *periph, void *arg) 315 { 316 struct pt_softc *softc; 317 struct ccb_setasync csa; 318 struct ccb_getdev *cgd; 319 320 cgd = (struct ccb_getdev *)arg; 321 if (periph == NULL) { 322 printf("ptregister: periph was NULL!!\n"); 323 return(CAM_REQ_CMP_ERR); 324 } 325 326 if (cgd == NULL) { 327 printf("ptregister: no getdev CCB, can't register device\n"); 328 return(CAM_REQ_CMP_ERR); 329 } 330 331 softc = (struct pt_softc *)malloc(sizeof(*softc),M_DEVBUF,M_NOWAIT); 332 333 if (softc == NULL) { 334 printf("daregister: Unable to probe new device. " 335 "Unable to allocate softc\n"); 336 return(CAM_REQ_CMP_ERR); 337 } 338 339 bzero(softc, sizeof(*softc)); 340 LIST_INIT(&softc->pending_ccbs); 341 softc->state = PT_STATE_NORMAL; 342 bioq_init(&softc->bio_queue); 343 344 softc->io_timeout = SCSI_PT_DEFAULT_TIMEOUT * 1000; 345 346 periph->softc = softc; 347 348 cam_extend_set(ptperiphs, periph->unit_number, periph); 349 350 devstat_add_entry(&softc->device_stats, "pt", 351 periph->unit_number, 0, 352 DEVSTAT_NO_BLOCKSIZE, 353 SID_TYPE(&cgd->inq_data) | DEVSTAT_TYPE_IF_SCSI, 354 DEVSTAT_PRIORITY_OTHER); 355 356 softc->dev = make_dev(&pt_cdevsw, periph->unit_number, UID_ROOT, 357 GID_OPERATOR, 0600, "%s%d", periph->periph_name, 358 periph->unit_number); 359 /* 360 * Add async callbacks for bus reset and 361 * bus device reset calls. I don't bother 362 * checking if this fails as, in most cases, 363 * the system will function just fine without 364 * them and the only alternative would be to 365 * not attach the device on failure. 366 */ 367 xpt_setup_ccb(&csa.ccb_h, periph->path, /*priority*/5); 368 csa.ccb_h.func_code = XPT_SASYNC_CB; 369 csa.event_enable = AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE; 370 csa.callback = ptasync; 371 csa.callback_arg = periph; 372 xpt_action((union ccb *)&csa); 373 374 /* Tell the user we've attached to the device */ 375 xpt_announce_periph(periph, NULL); 376 377 return(CAM_REQ_CMP); 378 } 379 380 static void 381 ptoninvalidate(struct cam_periph *periph) 382 { 383 int s; 384 struct pt_softc *softc; 385 struct bio *q_bp; 386 struct ccb_setasync csa; 387 388 softc = (struct pt_softc *)periph->softc; 389 390 /* 391 * De-register any async callbacks. 392 */ 393 xpt_setup_ccb(&csa.ccb_h, periph->path, 394 /* priority */ 5); 395 csa.ccb_h.func_code = XPT_SASYNC_CB; 396 csa.event_enable = 0; 397 csa.callback = ptasync; 398 csa.callback_arg = periph; 399 xpt_action((union ccb *)&csa); 400 401 softc->flags |= PT_FLAG_DEVICE_INVALID; 402 403 /* 404 * Although the oninvalidate() routines are always called at 405 * splsoftcam, we need to be at splbio() here to keep the buffer 406 * queue from being modified while we traverse it. 407 */ 408 s = splbio(); 409 410 /* 411 * Return all queued I/O with ENXIO. 412 * XXX Handle any transactions queued to the card 413 * with XPT_ABORT_CCB. 414 */ 415 while ((q_bp = bioq_first(&softc->bio_queue)) != NULL){ 416 bioq_remove(&softc->bio_queue, q_bp); 417 q_bp->bio_resid = q_bp->bio_bcount; 418 q_bp->bio_error = ENXIO; 419 q_bp->bio_flags |= BIO_ERROR; 420 biodone(q_bp); 421 } 422 423 splx(s); 424 425 xpt_print_path(periph->path); 426 printf("lost device\n"); 427 } 428 429 static void 430 ptdtor(struct cam_periph *periph) 431 { 432 struct pt_softc *softc; 433 434 softc = (struct pt_softc *)periph->softc; 435 436 devstat_remove_entry(&softc->device_stats); 437 438 destroy_dev(softc->dev); 439 440 cam_extend_release(ptperiphs, periph->unit_number); 441 xpt_print_path(periph->path); 442 printf("removing device entry\n"); 443 free(softc, M_DEVBUF); 444 } 445 446 static void 447 ptasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) 448 { 449 struct cam_periph *periph; 450 451 periph = (struct cam_periph *)callback_arg; 452 switch (code) { 453 case AC_FOUND_DEVICE: 454 { 455 struct ccb_getdev *cgd; 456 cam_status status; 457 458 cgd = (struct ccb_getdev *)arg; 459 460 if (SID_TYPE(&cgd->inq_data) != T_PROCESSOR) 461 break; 462 463 /* 464 * Allocate a peripheral instance for 465 * this device and start the probe 466 * process. 467 */ 468 status = cam_periph_alloc(ptctor, ptoninvalidate, ptdtor, 469 ptstart, "pt", CAM_PERIPH_BIO, 470 cgd->ccb_h.path, ptasync, 471 AC_FOUND_DEVICE, cgd); 472 473 if (status != CAM_REQ_CMP 474 && status != CAM_REQ_INPROG) 475 printf("ptasync: Unable to attach to new device " 476 "due to status 0x%x\n", status); 477 break; 478 } 479 case AC_SENT_BDR: 480 case AC_BUS_RESET: 481 { 482 struct pt_softc *softc; 483 struct ccb_hdr *ccbh; 484 int s; 485 486 softc = (struct pt_softc *)periph->softc; 487 s = splsoftcam(); 488 /* 489 * Don't fail on the expected unit attention 490 * that will occur. 491 */ 492 softc->flags |= PT_FLAG_RETRY_UA; 493 for (ccbh = LIST_FIRST(&softc->pending_ccbs); 494 ccbh != NULL; ccbh = LIST_NEXT(ccbh, periph_links.le)) 495 ccbh->ccb_state |= PT_CCB_RETRY_UA; 496 splx(s); 497 /* FALLTHROUGH */ 498 } 499 default: 500 cam_periph_async(periph, code, path, arg); 501 break; 502 } 503 } 504 505 static void 506 ptstart(struct cam_periph *periph, union ccb *start_ccb) 507 { 508 struct pt_softc *softc; 509 struct bio *bp; 510 int s; 511 512 softc = (struct pt_softc *)periph->softc; 513 514 /* 515 * See if there is a buf with work for us to do.. 516 */ 517 s = splbio(); 518 bp = bioq_first(&softc->bio_queue); 519 if (periph->immediate_priority <= periph->pinfo.priority) { 520 CAM_DEBUG_PRINT(CAM_DEBUG_SUBTRACE, 521 ("queuing for immediate ccb\n")); 522 start_ccb->ccb_h.ccb_state = PT_CCB_WAITING; 523 SLIST_INSERT_HEAD(&periph->ccb_list, &start_ccb->ccb_h, 524 periph_links.sle); 525 periph->immediate_priority = CAM_PRIORITY_NONE; 526 splx(s); 527 wakeup(&periph->ccb_list); 528 } else if (bp == NULL) { 529 splx(s); 530 xpt_release_ccb(start_ccb); 531 } else { 532 int oldspl; 533 534 bioq_remove(&softc->bio_queue, bp); 535 536 devstat_start_transaction(&softc->device_stats); 537 538 scsi_send_receive(&start_ccb->csio, 539 /*retries*/4, 540 ptdone, 541 MSG_SIMPLE_Q_TAG, 542 bp->bio_cmd == BIO_READ, 543 /*byte2*/0, 544 bp->bio_bcount, 545 bp->bio_data, 546 /*sense_len*/SSD_FULL_SIZE, 547 /*timeout*/softc->io_timeout); 548 549 start_ccb->ccb_h.ccb_state = PT_CCB_BUFFER_IO; 550 551 /* 552 * Block out any asyncronous callbacks 553 * while we touch the pending ccb list. 554 */ 555 oldspl = splcam(); 556 LIST_INSERT_HEAD(&softc->pending_ccbs, &start_ccb->ccb_h, 557 periph_links.le); 558 splx(oldspl); 559 560 start_ccb->ccb_h.ccb_bp = bp; 561 bp = bioq_first(&softc->bio_queue); 562 splx(s); 563 564 xpt_action(start_ccb); 565 566 if (bp != NULL) { 567 /* Have more work to do, so ensure we stay scheduled */ 568 xpt_schedule(periph, /* XXX priority */1); 569 } 570 } 571 } 572 573 static void 574 ptdone(struct cam_periph *periph, union ccb *done_ccb) 575 { 576 struct pt_softc *softc; 577 struct ccb_scsiio *csio; 578 579 softc = (struct pt_softc *)periph->softc; 580 csio = &done_ccb->csio; 581 switch (csio->ccb_h.ccb_state) { 582 case PT_CCB_BUFFER_IO: 583 case PT_CCB_BUFFER_IO_UA: 584 { 585 struct bio *bp; 586 int oldspl; 587 588 bp = (struct bio *)done_ccb->ccb_h.ccb_bp; 589 if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { 590 int error; 591 int s; 592 int sf; 593 594 if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0) 595 sf = SF_RETRY_UA; 596 else 597 sf = 0; 598 599 sf |= SF_RETRY_SELTO; 600 601 if ((error = pterror(done_ccb, 0, sf)) == ERESTART) { 602 /* 603 * A retry was scheuled, so 604 * just return. 605 */ 606 return; 607 } 608 if (error != 0) { 609 struct bio *q_bp; 610 611 s = splbio(); 612 613 if (error == ENXIO) { 614 /* 615 * Catastrophic error. Mark our device 616 * as invalid. 617 */ 618 xpt_print_path(periph->path); 619 printf("Invalidating device\n"); 620 softc->flags |= PT_FLAG_DEVICE_INVALID; 621 } 622 623 /* 624 * return all queued I/O with EIO, so that 625 * the client can retry these I/Os in the 626 * proper order should it attempt to recover. 627 */ 628 while ((q_bp = bioq_first(&softc->bio_queue)) 629 != NULL) { 630 bioq_remove(&softc->bio_queue, q_bp); 631 q_bp->bio_resid = q_bp->bio_bcount; 632 q_bp->bio_error = EIO; 633 q_bp->bio_flags |= BIO_ERROR; 634 biodone(q_bp); 635 } 636 splx(s); 637 bp->bio_error = error; 638 bp->bio_resid = bp->bio_bcount; 639 bp->bio_flags |= BIO_ERROR; 640 } else { 641 bp->bio_resid = csio->resid; 642 bp->bio_error = 0; 643 if (bp->bio_resid != 0) { 644 /* Short transfer ??? */ 645 bp->bio_flags |= BIO_ERROR; 646 } 647 } 648 if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) 649 cam_release_devq(done_ccb->ccb_h.path, 650 /*relsim_flags*/0, 651 /*reduction*/0, 652 /*timeout*/0, 653 /*getcount_only*/0); 654 } else { 655 bp->bio_resid = csio->resid; 656 if (bp->bio_resid != 0) 657 bp->bio_flags |= BIO_ERROR; 658 } 659 660 /* 661 * Block out any asyncronous callbacks 662 * while we touch the pending ccb list. 663 */ 664 oldspl = splcam(); 665 LIST_REMOVE(&done_ccb->ccb_h, periph_links.le); 666 splx(oldspl); 667 668 devstat_end_transaction_bio(&softc->device_stats, bp); 669 biodone(bp); 670 break; 671 } 672 case PT_CCB_WAITING: 673 /* Caller will release the CCB */ 674 wakeup(&done_ccb->ccb_h.cbfcnp); 675 return; 676 } 677 xpt_release_ccb(done_ccb); 678 } 679 680 static int 681 pterror(union ccb *ccb, u_int32_t cam_flags, u_int32_t sense_flags) 682 { 683 struct pt_softc *softc; 684 struct cam_periph *periph; 685 686 periph = xpt_path_periph(ccb->ccb_h.path); 687 softc = (struct pt_softc *)periph->softc; 688 689 return(cam_periph_error(ccb, cam_flags, sense_flags, 690 &softc->saved_ccb)); 691 } 692 693 static int 694 ptioctl(dev_t dev, u_long cmd, caddr_t addr, int flag, struct proc *p) 695 { 696 struct cam_periph *periph; 697 struct pt_softc *softc; 698 int unit; 699 int error; 700 701 unit = minor(dev); 702 periph = cam_extend_get(ptperiphs, unit); 703 704 if (periph == NULL) 705 return(ENXIO); 706 707 softc = (struct pt_softc *)periph->softc; 708 709 if ((error = cam_periph_lock(periph, PRIBIO|PCATCH)) != 0) { 710 return (error); /* error code from tsleep */ 711 } 712 713 switch(cmd) { 714 case PTIOCGETTIMEOUT: 715 if (softc->io_timeout >= 1000) 716 *(int *)addr = softc->io_timeout / 1000; 717 else 718 *(int *)addr = 0; 719 break; 720 case PTIOCSETTIMEOUT: 721 { 722 int s; 723 724 if (*(int *)addr < 1) { 725 error = EINVAL; 726 break; 727 } 728 729 s = splsoftcam(); 730 softc->io_timeout = *(int *)addr * 1000; 731 splx(s); 732 733 break; 734 } 735 default: 736 error = cam_periph_ioctl(periph, cmd, addr, pterror); 737 break; 738 } 739 740 cam_periph_unlock(periph); 741 742 return(error); 743 } 744 745 void 746 scsi_send_receive(struct ccb_scsiio *csio, u_int32_t retries, 747 void (*cbfcnp)(struct cam_periph *, union ccb *), 748 u_int tag_action, int readop, u_int byte2, 749 u_int32_t xfer_len, u_int8_t *data_ptr, u_int8_t sense_len, 750 u_int32_t timeout) 751 { 752 struct scsi_send_receive *scsi_cmd; 753 754 scsi_cmd = (struct scsi_send_receive *)&csio->cdb_io.cdb_bytes; 755 scsi_cmd->opcode = readop ? RECEIVE : SEND; 756 scsi_cmd->byte2 = byte2; 757 scsi_ulto3b(xfer_len, scsi_cmd->xfer_len); 758 scsi_cmd->control = 0; 759 760 cam_fill_csio(csio, 761 retries, 762 cbfcnp, 763 /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT, 764 tag_action, 765 data_ptr, 766 xfer_len, 767 sense_len, 768 sizeof(*scsi_cmd), 769 timeout); 770 } 771