diff options
Diffstat (limited to 'sys/dev/ic/isp_openbsd.c')
| -rw-r--r-- | sys/dev/ic/isp_openbsd.c | 586 |
1 files changed, 420 insertions, 166 deletions
diff --git a/sys/dev/ic/isp_openbsd.c b/sys/dev/ic/isp_openbsd.c index 7cbeaef9749..71b8f526dbe 100644 --- a/sys/dev/ic/isp_openbsd.c +++ b/sys/dev/ic/isp_openbsd.c @@ -1,5 +1,4 @@ -/* $OpenBSD: isp_openbsd.c,v 1.3 1999/03/25 22:58:38 mjacob Exp $ */ -/* release_03_25_99 */ +/* $OpenBSD: isp_openbsd.c,v 1.4 1999/11/22 12:50:53 mjacob Exp $ */ /* * Platform (OpenBSD) dependent common attachment code for Qlogic adapters. * @@ -42,29 +41,28 @@ * * Matthew Jacob * Feral Software - * 2339 3rd Street - * Suite 24 - * San Francisco, CA, 94107 + * PMB#825 + * 5214-F Diamond Heights Blvd. + * San Francisco, CA, 94131 */ #include <dev/ic/isp_openbsd.h> static void ispminphys __P((struct buf *)); +static int32_t ispcmd_slow __P((ISP_SCSI_XFER_T *)); static int32_t ispcmd __P((ISP_SCSI_XFER_T *)); static struct scsi_device isp_dev = { NULL, NULL, NULL, NULL }; static int isp_poll __P((struct ispsoftc *, ISP_SCSI_XFER_T *, int)); static void isp_watch __P((void *)); +static void isp_command_requeue(void *); +static void isp_internal_restart(void *); struct cfdriver isp_cd = { NULL, "isp", DV_DULL }; -#define FC_OPENINGS RQUEST_QUEUE_LEN / (MAX_FC_TARG-1) -#define PI_OPENINGS RQUEST_QUEUE_LEN / (MAX_TARGETS-1) -#define DTHR 1 - /* * Complete attachment of hardware, include subdevices. */ @@ -72,7 +70,6 @@ void isp_attach(isp) struct ispsoftc *isp; { - isp->isp_osinfo._adapter.scsi_cmd = ispcmd; isp->isp_osinfo._adapter.scsi_minphys = ispminphys; isp->isp_state = ISP_RUNSTATE; @@ -86,23 +83,23 @@ isp_attach(isp) isp->isp_osinfo._link.adapter_softc = isp; isp->isp_osinfo._link.device = &isp_dev; isp->isp_osinfo._link.adapter = &isp->isp_osinfo._adapter; + isp->isp_osinfo._link.openings = isp->isp_maxcmds; + isp->isp_osinfo.wqf = isp->isp_osinfo.wqt = NULL; /* XXX 2nd Bus? */ - if (isp->isp_type & ISP_HA_FC) { - isp->isp_osinfo._link.openings = FC_OPENINGS; + if (IS_FC(isp)) { + isp->isp_osinfo._adapter.scsi_cmd = ispcmd; isp->isp_osinfo._link.adapter_buswidth = MAX_FC_TARG; /* We can set max lun width here */ - isp->isp_osinfo._link.adapter_target = - ((fcparam *)isp->isp_param)->isp_loopid; + /* loopid set below */ } else { - isp->isp_osinfo.delay_throttle_count = DTHR; - isp->isp_osinfo._link.openings = PI_OPENINGS; + sdparam *sdp = isp->isp_param; + isp->isp_osinfo._adapter.scsi_cmd = ispcmd_slow; isp->isp_osinfo._link.adapter_buswidth = MAX_TARGETS; /* We can set max lun width here */ isp->isp_osinfo._link.adapter_target = - ((sdparam *)isp->isp_param)->isp_initiator_id; + sdp->isp_initiator_id; + isp->isp_osinfo.discovered[0] = 1 << sdp->isp_initiator_id; } - if (isp->isp_osinfo._link.openings < 2) - isp->isp_osinfo._link.openings = 2; /* * Send a SCSI Bus Reset (used to be done as part of attach, @@ -114,17 +111,57 @@ isp_attach(isp) * XXX: that async events happen. */ if (IS_SCSI(isp)) { - (void) isp_control(isp, ISPCTL_RESET_BUS, NULL); + int bus = 0; + (void) isp_control(isp, ISPCTL_RESET_BUS, &bus); + if (IS_12X0(isp)) { + bus++; + (void) isp_control(isp, ISPCTL_RESET_BUS, &bus); + } /* - * Wait for it to settle. + * wait for the bus to settle. */ + printf("%s: waiting 2 seconds for bus reset settling\n", + isp->isp_name); delay(2 * 1000000); + } else { + int i, j; + fcparam *fcp = isp->isp_param; + /* + * wait for the loop to settle. + */ + printf("%s: waiting 2 seconds for loop reset settling\n", + isp->isp_name); + delay(2 * 1000000); + for (j = 0; j < 5; j++) { + for (i = 0; i < 5; i++) { + if (isp_control(isp, ISPCTL_FCLINK_TEST, NULL)) + continue; +#ifdef ISP2100_FABRIC + /* + * Wait extra time to see if the f/w + * eventually completed an FLOGI that + * will allow us to know we're on a + * fabric. + */ + if (fcp->isp_onfabric == 0) { + delay(1 * 1000000); + continue; + } +#endif + break; + } + if (fcp->isp_fwstate == FW_READY && + fcp->isp_loopstate >= LOOP_PDB_RCVD) { + break; + } + } + isp->isp_osinfo._link.adapter_target = fcp->isp_loopid; } /* * Start the watchdog. * - * The wathdog will, ridiculously enough, also enable Sync negotiation. + * The watchdog will, ridiculously enough, also enable Sync negotiation. */ isp->isp_dogactive = 1; timeout(isp_watch, isp, WATCH_INTERVAL * hz); @@ -156,7 +193,87 @@ ispminphys(bp) minphys(bp); } -static int +static int32_t +ispcmd_slow(xs) + ISP_SCSI_XFER_T *xs; +{ + sdparam *sdp; + int tgt, chan; + u_int16_t f; + struct ispsoftc *isp = XS_ISP(xs); + + /* + * Have we completed discovery for this target on this adapter? + */ + sdp = isp->isp_param; + sdp += chan; + tgt = XS_TGT(xs); + chan = XS_CHANNEL(xs); + if ((xs->flags & SCSI_POLL) != 0 || + (isp->isp_osinfo.discovered[chan] & (1 << tgt)) != 0) { + return (ispcmd(xs)); + } + + f = DPARM_DEFAULT; + if (xs->sc_link->quirks & SDEV_NOSYNCWIDE) { + f ^= DPARM_SYNC; + f ^= DPARM_WIDE; +#ifdef DEBUG + } else { + printf("%s: channel %d target %d can do SYNC xfers\n", + isp->isp_name, chan, tgt); + printf("%s: channel %d target %d can do WIDE xfers\n", + isp->isp_name, chan, tgt); +#endif + } + if (xs->sc_link->quirks & SDEV_NOTAGS) { + f ^= DPARM_TQING; +#ifdef DEBUG + } else { + printf("%s: channel %d target %d can do TAGGED xfers\n", + isp->isp_name, chan, tgt); +#endif + } + + /* + * Okay, we know about this device now, + * so mark parameters to be updated for it. + */ + sdp->isp_devparam[tgt].dev_flags = f; + sdp->isp_devparam[tgt].dev_update = 1; + isp->isp_update |= 1 << chan; + + /* + * Now check to see whether we can get out of this checking mode now. + * XXX: WE CANNOT AS YET BECAUSE THERE IS NO MECHANISM TO TELL US + * XXX: WHEN WE'RE DONE DISCOVERY BECAUSE WE NEED ONE COMMAND AFTER + * XXX: DISCOVERY IS DONE FOR EACH TARGET TO TELL US THAT WE'RE DONE + * XXX: AND THAT DOESN'T HAPPEN HERE. AT BEST WE CAN MARK OURSELVES + * XXX: DONE WITH DISCOVERY FOR THIS TARGET AND SO SAVE MAYBE 20 + * XXX: LINES OF C CODE. + */ + isp->isp_osinfo.discovered[chan] |= (1 << tgt); + /* do not bother with these lines- they'll never execute correctly */ +#if 0 + sdp = isp->isp_param; + for (chan = 0; chan < (IS_12X0(isp)? 2 : 1); chan++, sdp++) { + f = 0xffff & ~(1 << sdp->isp_initiator_id); + if (isp->isp_osinfo.discovered[chan] != f) { + break; + } + } + if (chan == (IS_12X0(isp)? 2 : 1)) { + CFGPRINTF("%s: allowing sync/wide negotiation and " + "tag usage\n", isp->isp_name); + isp->isp_osinfo._adapter.scsipi_cmd = ispcmd; + if (IS_12X0(isp)) + isp->isp_update |= 2; + } +#endif + return (ispcmd(xs)); +} + +static int32_t ispcmd(xs) ISP_SCSI_XFER_T *xs; { @@ -179,34 +296,89 @@ ispcmd(xs) isp->isp_state = ISP_RUNSTATE; ENABLE_INTS(isp); } + + /* + * Check for queue blockage... + */ + if (isp->isp_osinfo.blocked) { + if (xs->flags & SCSI_POLL) { + xs->error = XS_DRIVER_STUFFUP; + splx(s); + return (TRY_AGAIN_LATER); + } + if (isp->isp_osinfo.wqf != NULL) { + isp->isp_osinfo.wqt->free_list.le_next = xs; + } else { + isp->isp_osinfo.wqf = xs; + } + isp->isp_osinfo.wqt = xs; + xs->free_list.le_next = NULL; + splx(s); + return (SUCCESSFULLY_QUEUED); + } DISABLE_INTS(isp); result = ispscsicmd(xs); ENABLE_INTS(isp); - if (result != CMD_QUEUED || (xs->flags & SCSI_POLL) == 0) { + + if ((xs->flags & SCSI_POLL) == 0) { + switch (result) { + case CMD_QUEUED: + result = SUCCESSFULLY_QUEUED; + break; + case CMD_EAGAIN: + result = TRY_AGAIN_LATER; + break; + case CMD_RQLATER: + result = SUCCESSFULLY_QUEUED; + timeout(isp_command_requeue, xs, hz); + break; + case CMD_COMPLETE: + result = COMPLETE; + break; + } (void) splx(s); return (result); } + switch (result) { + case CMD_QUEUED: + result = SUCCESSFULLY_QUEUED; + break; + case CMD_RQLATER: + case CMD_EAGAIN: + if (XS_NOERR(xs)) { + xs->error = XS_DRIVER_STUFFUP; + } + result = TRY_AGAIN_LATER; + break; + case CMD_COMPLETE: + result = COMPLETE; + break; + + } + /* - * If we can't use interrupts, poll on completion. + * We can't use interrupts so poll on completion. */ - if (isp_poll(isp, xs, xs->timeout)) { - /* - * If no other error occurred but we didn't finish, - * something bad happened. - */ - if ((xs->flags & ITSDONE) == 0) { - isp->isp_nactive--; - if (isp->isp_nactive < 0) - isp->isp_nactive = 0; - if (xs->error == XS_NOERROR) { - isp_lostcmd(isp, xs); - xs->error = XS_DRIVER_STUFFUP; + if (result == SUCCESSFULLY_QUEUED) { + if (isp_poll(isp, xs, xs->timeout)) { + /* + * If no other error occurred but we didn't finish, + * something bad happened. + */ + if (XS_IS_CMD_DONE(xs) == 0) { + if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { + isp_restart(isp); + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } } } + result = COMPLETE; } (void) splx(s); - return (COMPLETE); + return (result); } static int @@ -237,20 +409,22 @@ isp_watch(arg) { int i; struct ispsoftc *isp = arg; - ISP_SCSI_XFER_T *xs; - int s = splbio(); + struct scsi_xfer *xs; + int s; /* - * Look for completely dead commands. + * Look for completely dead commands (but not polled ones). */ - for (i = 0; i < RQUEST_QUEUE_LEN; i++) { - if ((xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[i]) == NULL) { + s = splbio(); + for (i = 0; i < isp->isp_maxcmds; i++) { + if ((xs = (struct scsi_xfer *) isp->isp_xflist[i]) == NULL) { continue; } - if (XS_TIME(xs) == 0) { + if (xs->timeout == 0 || (xs->flags & SCSI_POLL)) { continue; } - XS_TIME(xs) -= (WATCH_INTERVAL * 1000); + xs->timeout -= (WATCH_INTERVAL * 1000); + /* * Avoid later thinking that this * transaction is not being timed. @@ -262,9 +436,6 @@ isp_watch(arg) } else if (xs->timeout > -(2 * WATCH_INTERVAL * 1000)) { continue; } - if (IS_SCSI(isp)) { - isp->isp_osinfo.delay_throttle_count = DTHR; - } if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { printf("%s: isp_watch failed to abort command\n", isp->isp_name); @@ -272,21 +443,9 @@ isp_watch(arg) break; } } - - if (isp->isp_osinfo.delay_throttle_count) { - if (--isp->isp_osinfo.delay_throttle_count == 0) { - sdparam *sdp = isp->isp_param; - for (i = 0; i < MAX_TARGETS; i++) { - sdp->isp_devparam[i].dev_flags |= - DPARM_WIDE|DPARM_SYNC|DPARM_TQING; - sdp->isp_devparam[i].dev_update = 1; - } - isp->isp_update = 1; - } - } - timeout(isp_watch, isp, WATCH_INTERVAL * hz); + timeout(isp_watch, isp, WATCH_INTERVAL * hz); isp->isp_dogactive = 1; - (void) splx(s); + splx(s); } /* @@ -317,140 +476,235 @@ isp_uninit(isp) splx(s); } +/* + * Restart function for a command to be requeued later. + */ +static void +isp_command_requeue(void *arg) +{ + struct scsi_xfer *xs = arg; + struct ispsoftc *isp = XS_ISP(xs); + int s = splbio(); + switch (ispcmd_slow(xs)) { + case SUCCESSFULLY_QUEUED: + printf("%s: isp_command_reque: queued %d.%d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + case TRY_AGAIN_LATER: + printf("%s: EAGAIN for %d.%d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + /* FALLTHROUGH */ + case COMPLETE: + /* can only be an error */ + if (XS_NOERR(xs)) + XS_SETERR(xs, XS_DRIVER_STUFFUP); + XS_CMD_DONE(xs); + break; + } + (void) splx(s); +} + +/* + * Restart function after a LOOP UP event (e.g.), + * done as a timeout for some hysteresis. + */ +static void +isp_internal_restart(void *arg) +{ + struct ispsoftc *isp = arg; + int result, nrestarted = 0, s; + + s = splbio(); + if (isp->isp_osinfo.blocked == 0) { + struct scsi_xfer *xs; + while ((xs = isp->isp_osinfo.wqf) != NULL) { + isp->isp_osinfo.wqf = xs->free_list.le_next; + xs->free_list.le_next = NULL; + DISABLE_INTS(isp); + result = ispscsicmd(xs); + ENABLE_INTS(isp); + if (result != CMD_QUEUED) { + printf("%s: botched command restart (0x%x)\n", + isp->isp_name, result); + if (XS_NOERR(xs)) + XS_SETERR(xs, XS_DRIVER_STUFFUP); + XS_CMD_DONE(xs); + } + nrestarted++; + } + printf("%s: requeued %d commands\n", isp->isp_name, nrestarted); + } + (void) splx(s); +} + int isp_async(isp, cmd, arg) struct ispsoftc *isp; ispasync_t cmd; void *arg; { + int bus, tgt; int s = splbio(); switch (cmd) { case ISPASYNC_NEW_TGT_PARAMS: - if (IS_SCSI(isp)) { - sdparam *sdp = isp->isp_param; - char *wt; - int mhz, flags, tgt, period; - - tgt = *((int *) arg); - - flags = sdp->isp_devparam[tgt].cur_dflags; - period = sdp->isp_devparam[tgt].cur_period; - if ((flags & DPARM_SYNC) && period && - (sdp->isp_devparam[tgt].cur_offset) != 0) { - if (sdp->isp_lvdmode) { - switch (period) { - case 0xa: - mhz = 40; - break; - case 0xb: - mhz = 33; - break; - case 0xc: - mhz = 25; - break; - default: - mhz = 1000 / (period * 4); - break; - } - } else { + if (IS_SCSI(isp) && isp->isp_dblev) { + sdparam *sdp = isp->isp_param; + char *wt; + int mhz, flags, period; + + tgt = *((int *) arg); + bus = (tgt >> 16) & 0xffff; + tgt &= 0xffff; + + flags = sdp->isp_devparam[tgt].cur_dflags; + period = sdp->isp_devparam[tgt].cur_period; + if ((flags & DPARM_SYNC) && period && + (sdp->isp_devparam[tgt].cur_offset) != 0) { + if (sdp->isp_lvdmode) { + switch (period) { + case 0xa: + mhz = 40; + break; + case 0xb: + mhz = 33; + break; + case 0xc: + mhz = 25; + break; + default: mhz = 1000 / (period * 4); + break; } } else { - mhz = 0; - } - switch (flags & (DPARM_WIDE|DPARM_TQING)) { - case DPARM_WIDE: - wt = ", 16 bit wide\n"; - break; - case DPARM_TQING: - wt = ", Tagged Queueing Enabled\n"; - break; - case DPARM_WIDE|DPARM_TQING: - wt = ", 16 bit wide, Tagged Queueing Enabled\n"; - break; - default: - wt = "\n"; - break; - } - if (mhz) { - printf("%s: Target %d at %dMHz Max Offset %d%s", - isp->isp_name, tgt, mhz, - sdp->isp_devparam[tgt].cur_offset, wt); - } else { - printf("%s: Target %d Async Mode%s", - isp->isp_name, tgt, wt); + mhz = 1000 / (period * 4); } + } else { + mhz = 0; + } + switch (flags & (DPARM_WIDE|DPARM_TQING)) { + case DPARM_WIDE: + wt = ", 16 bit wide\n"; + break; + case DPARM_TQING: + wt = ", Tagged Queueing Enabled\n"; + break; + case DPARM_WIDE|DPARM_TQING: + wt = ", 16 bit wide, Tagged Queueing Enabled\n"; + break; + default: + wt = "\n"; + break; + } + if (mhz) { + CFGPRINTF("%s: Bus %d Target %d at %dMHz Max " + "Offset %d%s", isp->isp_name, bus, tgt, mhz, + sdp->isp_devparam[tgt].cur_offset, wt); + } else { + CFGPRINTF("%s: Bus %d Target %d Async Mode%s", + isp->isp_name, bus, tgt, wt); } break; + } case ISPASYNC_BUS_RESET: - printf("%s: SCSI bus reset detected\n", isp->isp_name); + if (arg) + bus = *((int *) arg); + else + bus = 0; + printf("%s: SCSI bus %d reset detected\n", isp->isp_name, bus); break; case ISPASYNC_LOOP_DOWN: + /* + * Hopefully we get here in time to minimize the number + * of commands we are firing off that are sure to die. + */ + isp->isp_osinfo.blocked = 1; printf("%s: Loop DOWN\n", isp->isp_name); break; - case ISPASYNC_LOOP_UP: + case ISPASYNC_LOOP_UP: + isp->isp_osinfo.blocked = 0; + timeout(isp_internal_restart, isp, 1); printf("%s: Loop UP\n", isp->isp_name); break; - case ISPASYNC_PDB_CHANGE_COMPLETE: -#if 0 - if (isp->isp_type & ISP_HA_FC) { - int i; - static char *roles[4] = { + case ISPASYNC_PDB_CHANGED: + if (IS_FC(isp) && isp->isp_dblev) { + const char *fmt = "%s: Target %d (Loop 0x%x) Port ID 0x%x " + "role %s %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x\n"; + const static char *roles[4] = { "No", "Target", "Initiator", "Target/Initiator" }; - for (i = 0; i < MAX_FC_TARG; i++) { - isp_pdb_t *pdbp = - &((fcparam *)isp->isp_param)->isp_pdb[i]; - if (pdbp->pdb_options == INVALID_PDB_OPTIONS) - continue; - printf("%s: Loop ID %d, %s role\n", - isp->isp_name, pdbp->pdb_loopid, - roles[(pdbp->pdb_prli_svc3 >> 4) & 0x3]); - printf(" Node Address 0x%x WWN 0x" - "%02x%02x%02x%02x%02x%02x%02x%02x\n", - BITS2WORD(pdbp->pdb_portid_bits), - pdbp->pdb_portname[0], pdbp->pdb_portname[1], - pdbp->pdb_portname[2], pdbp->pdb_portname[3], - pdbp->pdb_portname[4], pdbp->pdb_portname[5], - pdbp->pdb_portname[6], pdbp->pdb_portname[7]); - if (pdbp->pdb_options & PDB_OPTIONS_ADISC) - printf(" Hard Address 0x%x WWN 0x" - "%02x%02x%02x%02x%02x%02x%02x%02x\n", - BITS2WORD(pdbp->pdb_hardaddr_bits), - pdbp->pdb_nodename[0], - pdbp->pdb_nodename[1], - pdbp->pdb_nodename[2], - pdbp->pdb_nodename[3], - pdbp->pdb_nodename[4], - pdbp->pdb_nodename[5], - pdbp->pdb_nodename[6], - pdbp->pdb_nodename[7]); - switch (pdbp->pdb_prli_svc3 & SVC3_ROLE_MASK) { - case SVC3_TGT_ROLE|SVC3_INI_ROLE: - printf(" Master State=%s, Slave State=%s\n", - isp2100_pdb_statename(pdbp->pdb_mstate), - isp2100_pdb_statename(pdbp->pdb_sstate)); - break; - case SVC3_TGT_ROLE: - printf(" Master State=%s\n", - isp2100_pdb_statename(pdbp->pdb_mstate)); - break; - case SVC3_INI_ROLE: - printf(" Slave State=%s\n", - isp2100_pdb_statename(pdbp->pdb_sstate)); - break; - default: - break; - } + char *ptr; + fcparam *fcp = isp->isp_param; + int tgt = *((int *) arg); + struct lportdb *lp = &fcp->portdb[tgt]; + + if (lp->valid) { + ptr = "arrived"; + } else { + ptr = "disappeared"; } + printf(fmt, isp->isp_name, tgt, lp->loopid, lp->portid, + roles[lp->roles & 0x3], ptr, + (u_int32_t) (lp->port_wwn >> 32), + (u_int32_t) (lp->port_wwn & 0xffffffffLL), + (u_int32_t) (lp->node_wwn >> 32), + (u_int32_t) (lp->node_wwn & 0xffffffffLL)); break; } -#else - break; -#endif +#ifdef ISP2100_FABRIC case ISPASYNC_CHANGE_NOTIFY: printf("%s: Name Server Database Changed\n", isp->isp_name); break; + case ISPASYNC_FABRIC_DEV: + { + int target; + struct lportdb *lp; + sns_scrsp_t *resp = (sns_scrsp_t *) arg; + u_int32_t portid; + u_int64_t wwn; + fcparam *fcp = isp->isp_param; + + portid = + (((u_int32_t) resp->snscb_port_id[0]) << 16) | + (((u_int32_t) resp->snscb_port_id[1]) << 8) | + (((u_int32_t) resp->snscb_port_id[2])); + wwn = + (((u_int64_t)resp->snscb_portname[0]) << 56) | + (((u_int64_t)resp->snscb_portname[1]) << 48) | + (((u_int64_t)resp->snscb_portname[2]) << 40) | + (((u_int64_t)resp->snscb_portname[3]) << 32) | + (((u_int64_t)resp->snscb_portname[4]) << 24) | + (((u_int64_t)resp->snscb_portname[5]) << 16) | + (((u_int64_t)resp->snscb_portname[6]) << 8) | + (((u_int64_t)resp->snscb_portname[7])); + printf("%s: Fabric Device (Type 0x%x)@PortID 0x%x WWN " + "0x%08x%08x\n", isp->isp_name, resp->snscb_port_type, + portid, ((u_int32_t)(wwn >> 32)), + ((u_int32_t)(wwn & 0xffffffff))); + if (resp->snscb_port_type != 2) + break; + for (target = FC_SNS_ID+1; target < MAX_FC_TARG; target++) { + lp = &fcp->portdb[target]; + if (lp->port_wwn == wwn) + break; + } + if (target < MAX_FC_TARG) { + break; + } + for (target = FC_SNS_ID+1; target < MAX_FC_TARG; target++) { + lp = &fcp->portdb[target]; + if (lp->port_wwn == 0) + break; + } + if (target == MAX_FC_TARG) { + printf("%s: no more space for fabric devices\n", + isp->isp_name); + return (-1); + } + lp->port_wwn = lp->node_wwn = wwn; + lp->portid = portid; + break; + } +#endif default: break; } |
