1 /* $FreeBSD$ */ 2 /* 3 * Platform (FreeBSD) dependent common attachment code for Qlogic adapters. 4 * 5 *--------------------------------------- 6 * Copyright (c) 1997, 1998 by Matthew Jacob 7 * NASA/Ames Research Center 8 * All rights reserved. 9 *--------------------------------------- 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice immediately at the beginning of the file, without modification, 16 * this list of conditions, and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 #include <dev/isp/isp_freebsd.h> 36 37 #if __FreeBSD_version >= 300004 38 39 static void isp_async __P((void *, u_int32_t, struct cam_path *, void *)); 40 static void isp_poll __P((struct cam_sim *)); 41 static void isp_action __P((struct cam_sim *, union ccb *)); 42 43 void 44 isp_attach(isp) 45 struct ispsoftc *isp; 46 { 47 struct ccb_setasync csa; 48 struct cam_devq *devq; 49 50 /* 51 * Create the device queue for our SIM. 52 */ 53 devq = cam_simq_alloc(MAXISPREQUEST); 54 if (devq == NULL) { 55 return; 56 } 57 58 /* 59 * Construct our SIM entry 60 */ 61 isp->isp_sim = cam_sim_alloc(isp_action, isp_poll, "isp", isp, 62 isp->isp_unit, 1, MAXISPREQUEST, devq); 63 if (isp->isp_sim == NULL) { 64 cam_simq_free(devq); 65 return; 66 } 67 if (xpt_bus_register(isp->isp_sim, 0) != CAM_SUCCESS) { 68 cam_sim_free(isp->isp_sim, TRUE); 69 return; 70 } 71 72 if (xpt_create_path(&isp->isp_path, NULL, cam_sim_path(isp->isp_sim), 73 CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { 74 xpt_bus_deregister(cam_sim_path(isp->isp_sim)); 75 cam_sim_free(isp->isp_sim, TRUE); 76 return; 77 } 78 79 xpt_setup_ccb(&csa.ccb_h, isp->isp_path, 5); 80 csa.ccb_h.func_code = XPT_SASYNC_CB; 81 csa.event_enable = AC_LOST_DEVICE; 82 csa.callback = isp_async; 83 csa.callback_arg = isp->isp_sim; 84 xpt_action((union ccb *)&csa); 85 86 /* 87 * Set base transfer capabilities for Fibre Channel. 88 * Technically not correct because we don't know 89 * what media we're running on top of- but we'll 90 * look good if we always say 100MB/s. 91 */ 92 if (isp->isp_type & ISP_HA_FC) { 93 isp->isp_sim->base_transfer_speed = 100000; 94 } 95 isp->isp_state = ISP_RUNSTATE; 96 } 97 98 static void 99 isp_async(cbarg, code, path, arg) 100 void *cbarg; 101 u_int32_t code; 102 struct cam_path *path; 103 void *arg; 104 { 105 struct cam_sim *sim; 106 struct ispsoftc *isp; 107 108 sim = (struct cam_sim *)cbarg; 109 isp = (struct ispsoftc *) cam_sim_softc(sim); 110 switch (code) { 111 case AC_LOST_DEVICE: 112 if (isp->isp_type & ISP_HA_SCSI) { 113 u_int16_t oflags, nflags; 114 sdparam *sdp = isp->isp_param; 115 int s, tgt = xpt_path_target_id(path); 116 117 nflags = DPARM_SAFE_DFLT; 118 if (isp->isp_fwrev >= ISP_FW_REV(7, 55)) { 119 nflags |= DPARM_NARROW | DPARM_ASYNC; 120 } 121 oflags = sdp->isp_devparam[tgt].dev_flags; 122 sdp->isp_devparam[tgt].dev_flags = nflags; 123 sdp->isp_devparam[tgt].dev_update = 1; 124 125 s = splcam(); 126 (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL); 127 (void) splx(s); 128 sdp->isp_devparam[tgt].dev_flags = oflags; 129 } 130 break; 131 default: 132 break; 133 } 134 } 135 136 static void 137 isp_poll(sim) 138 struct cam_sim *sim; 139 { 140 isp_intr((struct ispsoftc *) cam_sim_softc(sim)); 141 } 142 143 144 static void 145 isp_action(sim, ccb) 146 struct cam_sim *sim; 147 union ccb *ccb; 148 { 149 int s, tgt, error; 150 struct ispsoftc *isp; 151 struct ccb_trans_settings *cts; 152 153 CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("isp_action\n")); 154 155 isp = (struct ispsoftc *)cam_sim_softc(sim); 156 ccb->ccb_h.sim_priv.entries[0].field = 0; 157 ccb->ccb_h.sim_priv.entries[1].ptr = isp; 158 159 IDPRINTF(4, ("%s: isp_action code %x\n", isp->isp_name, 160 ccb->ccb_h.func_code)); 161 162 switch (ccb->ccb_h.func_code) { 163 case XPT_SCSI_IO: /* Execute the requested I/O operation */ 164 /* 165 * Do a couple of preliminary checks... 166 */ 167 if ((ccb->ccb_h.flags & CAM_CDB_POINTER) != 0) { 168 if ((ccb->ccb_h.flags & CAM_CDB_PHYS) != 0) { 169 ccb->ccb_h.status = CAM_REQ_INVALID; 170 xpt_done(ccb); 171 break; 172 } 173 } 174 175 176 if (isp->isp_type & ISP_HA_SCSI) { 177 if (ccb->ccb_h.target_id > (MAX_TARGETS-1)) { 178 ccb->ccb_h.status = CAM_PATH_INVALID; 179 } else if (isp->isp_fwrev >= ISP_FW_REV(7, 55)) { 180 /* 181 * Too much breakage. 182 */ 183 #if 0 184 if (ccb->ccb_h.target_lun > 31) { 185 ccb->ccb_h.status = CAM_PATH_INVALID; 186 } 187 #else 188 if (ccb->ccb_h.target_lun > 7) { 189 ccb->ccb_h.status = CAM_PATH_INVALID; 190 } 191 #endif 192 } else if (ccb->ccb_h.target_lun > 7) { 193 ccb->ccb_h.status = CAM_PATH_INVALID; 194 } 195 } else { 196 if (ccb->ccb_h.target_id > (MAX_FC_TARG-1)) { 197 ccb->ccb_h.status = CAM_PATH_INVALID; 198 } else if (ccb->ccb_h.target_lun > 15) { 199 ccb->ccb_h.status = CAM_PATH_INVALID; 200 } 201 } 202 if (ccb->ccb_h.status == CAM_PATH_INVALID) { 203 printf("%s: invalid tgt/lun (%d.%d) in XPT_SCSI_IO\n", 204 isp->isp_name, ccb->ccb_h.target_id, 205 ccb->ccb_h.target_lun); 206 xpt_done(ccb); 207 break; 208 } 209 210 s = splcam(); 211 switch (ispscsicmd((ISP_SCSI_XFER_T *) ccb)) { 212 case CMD_QUEUED: 213 ccb->ccb_h.status |= CAM_SIM_QUEUED; 214 break; 215 case CMD_EAGAIN: 216 if (isp->isp_osinfo.simqfrozen == 0) { 217 xpt_freeze_simq(sim, 1); 218 isp->isp_osinfo.simqfrozen = 1; 219 } 220 ccb->ccb_h.status &= ~CAM_STATUS_MASK; 221 ccb->ccb_h.status |= CAM_REQUEUE_REQ; 222 xpt_done(ccb); 223 break; 224 case CMD_COMPLETE: 225 /* 226 * Just make sure that we didn't get it returned 227 * as completed, but with the request still in 228 * progress. In theory, 'cannot happen'. 229 */ 230 if ((ccb->ccb_h.status & CAM_STATUS_MASK) == 231 CAM_REQ_INPROG) { 232 ccb->ccb_h.status &= ~CAM_STATUS_MASK; 233 ccb->ccb_h.status |= CAM_REQ_CMP_ERR; 234 } 235 xpt_done(ccb); 236 break; 237 } 238 splx(s); 239 break; 240 241 case XPT_EN_LUN: /* Enable LUN as a target */ 242 case XPT_TARGET_IO: /* Execute target I/O request */ 243 case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ 244 case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ 245 ccb->ccb_h.status = CAM_REQ_INVALID; 246 xpt_done(ccb); 247 break; 248 249 case XPT_RESET_DEV: /* BDR the specified SCSI device */ 250 tgt = ccb->ccb_h.target_id; 251 s = splcam(); 252 error = 253 isp_control(isp, ISPCTL_RESET_DEV, (void *)(intptr_t) tgt); 254 (void) splx(s); 255 if (error) { 256 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 257 } else { 258 ccb->ccb_h.status = CAM_REQ_CMP; 259 } 260 xpt_done(ccb); 261 break; 262 case XPT_ABORT: /* Abort the specified CCB */ 263 s = splcam(); 264 error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); 265 (void) splx(s); 266 if (error) { 267 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 268 } else { 269 ccb->ccb_h.status = CAM_REQ_CMP; 270 } 271 xpt_done(ccb); 272 break; 273 274 case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ 275 276 cts = &ccb->cts; 277 tgt = cts->ccb_h.target_id; 278 s = splcam(); 279 if (isp->isp_type & ISP_HA_FC) { 280 ; /* nothing to change */ 281 } else { 282 sdparam *sdp = isp->isp_param; 283 u_int16_t *dptr; 284 285 #if 0 286 if (cts->flags & CCB_TRANS_CURRENT_SETTINGS) 287 dptr = &sdp->isp_devparam[tgt].cur_dflags; 288 else 289 dptr = &sdp->isp_devparam[tgt].dev_flags; 290 #else 291 /* 292 * We always update (internally) from dev_flags 293 * so any request to change settings just gets 294 * vectored to that location. 295 */ 296 dptr = &sdp->isp_devparam[tgt].dev_flags; 297 #endif 298 299 /* 300 * Note that these operations affect the 301 * the permanent flags (dev_flags)- not 302 * the current state flags. Then we mark 303 * things so that the next operation to 304 * this HBA will cause the update to occur. 305 */ 306 if (cts->valid & CCB_TRANS_DISC_VALID) { 307 if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) { 308 *dptr |= DPARM_DISC; 309 } else { 310 *dptr &= ~DPARM_DISC; 311 } 312 } 313 if (cts->valid & CCB_TRANS_TQ_VALID) { 314 if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) { 315 *dptr |= DPARM_TQING; 316 } else { 317 *dptr &= ~DPARM_TQING; 318 } 319 } 320 if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) { 321 switch (cts->bus_width) { 322 case MSG_EXT_WDTR_BUS_16_BIT: 323 *dptr |= DPARM_WIDE; 324 break; 325 default: 326 *dptr &= ~DPARM_WIDE; 327 } 328 } 329 /* 330 * Any SYNC RATE of nonzero and SYNC_OFFSET 331 * of nonzero will cause us to go to the 332 * selected (from NVRAM) maximum value for 333 * this device. At a later point, we'll 334 * allow finer control. 335 */ 336 if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) && 337 (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) && 338 (cts->sync_offset > 0)) { 339 *dptr |= DPARM_SYNC; 340 } else { 341 *dptr &= ~DPARM_SYNC; 342 } 343 IDPRINTF(3, ("%s: target %d new dev_flags 0x%x\n", 344 isp->isp_name, tgt, 345 sdp->isp_devparam[tgt].dev_flags)); 346 s = splcam(); 347 sdp->isp_devparam[tgt].dev_update = 1; 348 isp->isp_update = 1; 349 (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL); 350 (void) splx(s); 351 } 352 (void) splx(s); 353 ccb->ccb_h.status = CAM_REQ_CMP; 354 xpt_done(ccb); 355 break; 356 357 case XPT_GET_TRAN_SETTINGS: 358 359 cts = &ccb->cts; 360 tgt = cts->ccb_h.target_id; 361 if (isp->isp_type & ISP_HA_FC) { 362 /* 363 * a lot of normal SCSI things don't make sense. 364 */ 365 cts->flags = CCB_TRANS_TAG_ENB | CCB_TRANS_DISC_ENB; 366 cts->valid = CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; 367 /* 368 * How do you measure the width of a high 369 * speed serial bus? Well, in bytes. 370 * 371 * Offset and period make no sense, though, so we set 372 * (above) a 'base' transfer speed to be gigabit. 373 */ 374 cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; 375 } else { 376 sdparam *sdp = isp->isp_param; 377 u_int16_t dval; 378 379 if (cts->flags & CCB_TRANS_CURRENT_SETTINGS) 380 dval = sdp->isp_devparam[tgt].cur_dflags; 381 else 382 dval = sdp->isp_devparam[tgt].dev_flags; 383 384 s = splcam(); 385 cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); 386 387 if (dval & DPARM_DISC) { 388 cts->flags |= CCB_TRANS_DISC_ENB; 389 } 390 if (dval & DPARM_TQING) { 391 cts->flags |= CCB_TRANS_TAG_ENB; 392 } 393 if (dval & DPARM_WIDE) { 394 cts->bus_width = MSG_EXT_WDTR_BUS_16_BIT; 395 } else { 396 cts->bus_width = MSG_EXT_WDTR_BUS_8_BIT; 397 } 398 cts->valid = CCB_TRANS_BUS_WIDTH_VALID | 399 CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID; 400 401 if ((dval & DPARM_SYNC) && 402 (sdp->isp_devparam[tgt].sync_offset)) { 403 cts->sync_period = 404 sdp->isp_devparam[tgt].sync_period; 405 cts->sync_offset = 406 sdp->isp_devparam[tgt].sync_offset; 407 cts->valid |= 408 CCB_TRANS_SYNC_RATE_VALID | 409 CCB_TRANS_SYNC_OFFSET_VALID; 410 } 411 splx(s); 412 } 413 ccb->ccb_h.status = CAM_REQ_CMP; 414 xpt_done(ccb); 415 break; 416 417 case XPT_CALC_GEOMETRY: 418 { 419 struct ccb_calc_geometry *ccg; 420 u_int32_t secs_per_cylinder; 421 u_int32_t size_mb; 422 423 ccg = &ccb->ccg; 424 if (ccg->block_size == 0) { 425 printf("%s: %d.%d XPT_CALC_GEOMETRY block size 0?\n", 426 isp->isp_name, ccg->ccb_h.target_id, 427 ccg->ccb_h.target_lun); 428 ccb->ccb_h.status = CAM_REQ_INVALID; 429 xpt_done(ccb); 430 break; 431 } 432 size_mb = ccg->volume_size /((1024L * 1024L) / ccg->block_size); 433 if (size_mb > 1024) { 434 ccg->heads = 255; 435 ccg->secs_per_track = 63; 436 } else { 437 ccg->heads = 64; 438 ccg->secs_per_track = 32; 439 } 440 secs_per_cylinder = ccg->heads * ccg->secs_per_track; 441 ccg->cylinders = ccg->volume_size / secs_per_cylinder; 442 ccb->ccb_h.status = CAM_REQ_CMP; 443 xpt_done(ccb); 444 break; 445 } 446 case XPT_RESET_BUS: /* Reset the specified bus */ 447 if (isp->isp_type & ISP_HA_FC) { 448 ccb->ccb_h.status = CAM_REQ_CMP; 449 xpt_done(ccb); 450 break; 451 } 452 s = splcam(); 453 error = isp_control(isp, ISPCTL_RESET_BUS, NULL); 454 (void) splx(s); 455 if (error) 456 ccb->ccb_h.status = CAM_REQ_CMP_ERR; 457 else 458 ccb->ccb_h.status = CAM_REQ_CMP; 459 xpt_done(ccb); 460 break; 461 462 case XPT_TERM_IO: /* Terminate the I/O process */ 463 /* Does this need to be implemented? */ 464 ccb->ccb_h.status = CAM_REQ_INVALID; 465 xpt_done(ccb); 466 break; 467 468 case XPT_PATH_INQ: /* Path routing inquiry */ 469 { 470 struct ccb_pathinq *cpi = &ccb->cpi; 471 472 cpi->version_num = 1; 473 cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; 474 cpi->target_sprt = 0; 475 cpi->hba_misc = 0; 476 cpi->hba_eng_cnt = 0; 477 if (isp->isp_type & ISP_HA_FC) { 478 cpi->max_target = MAX_FC_TARG-1; 479 cpi->initiator_id = 480 ((fcparam *)isp->isp_param)->isp_loopid; 481 /* 482 * XXX: actually, this is not right if we have 483 * XXX: 1.14 F/W and the second level lun addressing 484 * XXX: in place. It's also probably not right 485 * XXX: even for 1.13 f/w. 486 */ 487 cpi->max_lun = 15; 488 } else { 489 cpi->initiator_id = 490 ((sdparam *)isp->isp_param)->isp_initiator_id; 491 cpi->max_target = MAX_TARGETS-1; 492 if (isp->isp_fwrev >= ISP_FW_REV(7, 55)) { 493 /* 494 * Too much breakage. 495 */ 496 #if 0 497 cpi->max_lun = 31; 498 #else 499 cpi->max_lun = 7; 500 #endif 501 } else { 502 cpi->max_lun = 7; 503 } 504 } 505 506 cpi->bus_id = cam_sim_bus(sim); 507 strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 508 strncpy(cpi->hba_vid, "Qlogic", HBA_IDLEN); 509 strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 510 cpi->unit_number = cam_sim_unit(sim); 511 cpi->ccb_h.status = CAM_REQ_CMP; 512 xpt_done(ccb); 513 break; 514 } 515 default: 516 ccb->ccb_h.status = CAM_REQ_INVALID; 517 xpt_done(ccb); 518 break; 519 } 520 } 521 #else 522 523 static void ispminphys __P((struct buf *)); 524 static u_int32_t isp_adapter_info __P((int)); 525 static int ispcmd __P((ISP_SCSI_XFER_T *)); 526 527 static struct scsi_adapter isp_switch = { 528 ispcmd, ispminphys, 0, 0, isp_adapter_info, "isp", { 0, 0 } 529 }; 530 static struct scsi_device isp_dev = { 531 NULL, NULL, NULL, NULL, "isp", 0, { 0, 0 } 532 }; 533 static int isp_poll __P((struct ispsoftc *, ISP_SCSI_XFER_T *, int)); 534 535 536 /* 537 * Complete attachment of hardware, include subdevices. 538 */ 539 void 540 isp_attach(isp) 541 struct ispsoftc *isp; 542 { 543 struct scsibus_data *scbus; 544 545 scbus = scsi_alloc_bus(); 546 if(!scbus) { 547 return; 548 } 549 isp->isp_state = ISP_RUNSTATE; 550 551 isp->isp_osinfo._link.adapter_unit = isp->isp_osinfo.unit; 552 isp->isp_osinfo._link.adapter_softc = isp; 553 isp->isp_osinfo._link.adapter = &isp_switch; 554 isp->isp_osinfo._link.device = &isp_dev; 555 isp->isp_osinfo._link.flags = 0; 556 if (isp->isp_type & ISP_HA_FC) { 557 isp->isp_osinfo._link.adapter_targ = 558 ((fcparam *)isp->isp_param)->isp_loopid; 559 scbus->maxtarg = MAX_FC_TARG-1; 560 } else { 561 isp->isp_osinfo._link.adapter_targ = 562 ((sdparam *)isp->isp_param)->isp_initiator_id; 563 scbus->maxtarg = MAX_TARGETS-1; 564 } 565 /* 566 * Prepare the scsibus_data area for the upperlevel scsi code. 567 */ 568 scbus->adapter_link = &isp->isp_osinfo._link; 569 570 /* 571 * ask the adapter what subunits are present 572 */ 573 scsi_attachdevs(scbus); 574 } 575 576 577 /* 578 * minphys our xfers 579 * 580 * Unfortunately, the buffer pointer describes the target device- not the 581 * adapter device, so we can't use the pointer to find out what kind of 582 * adapter we are and adjust accordingly. 583 */ 584 585 static void 586 ispminphys(bp) 587 struct buf *bp; 588 { 589 /* 590 * Only the 10X0 has a 24 bit limit. 591 */ 592 if (bp->b_bcount >= (1 << 24)) { 593 bp->b_bcount = (1 << 24); 594 } 595 } 596 597 static u_int32_t 598 isp_adapter_info(unit) 599 int unit; 600 { 601 /* 602 * XXX: FIND ISP BASED UPON UNIT AND GET REAL QUEUE LIMIT FROM THAT 603 */ 604 return (2); 605 } 606 607 static int 608 ispcmd(xs) 609 ISP_SCSI_XFER_T *xs; 610 { 611 struct ispsoftc *isp; 612 int r; 613 ISP_LOCKVAL_DECL; 614 615 isp = XS_ISP(xs); 616 ISP_LOCK; 617 r = ispscsicmd(xs); 618 if (r != CMD_QUEUED || (xs->flags & SCSI_NOMASK) == 0) { 619 ISP_UNLOCK; 620 return (r); 621 } 622 623 /* 624 * If we can't use interrupts, poll on completion. 625 */ 626 if (isp_poll(isp, xs, XS_TIME(xs))) { 627 /* 628 * If no other error occurred but we didn't finish, 629 * something bad happened. 630 */ 631 if (XS_IS_CMD_DONE(xs) == 0) { 632 isp->isp_nactive--; 633 if (isp->isp_nactive < 0) 634 isp->isp_nactive = 0; 635 if (XS_NOERR(xs)) { 636 isp_lostcmd(isp, xs); 637 XS_SETERR(xs, HBA_BOTCH); 638 } 639 } 640 } 641 ISP_UNLOCK; 642 return (CMD_COMPLETE); 643 } 644 645 static int 646 isp_poll(isp, xs, mswait) 647 struct ispsoftc *isp; 648 ISP_SCSI_XFER_T *xs; 649 int mswait; 650 { 651 652 while (mswait) { 653 /* Try the interrupt handling routine */ 654 (void)isp_intr((void *)isp); 655 656 /* See if the xs is now done */ 657 if (XS_IS_CMD_DONE(xs)) 658 return (0); 659 SYS_DELAY(1000); /* wait one millisecond */ 660 mswait--; 661 } 662 return (1); 663 } 664 #endif 665