1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/conf.h>
27 #include <sys/file.h>
28 #include <sys/ddi.h>
29 #include <sys/sunddi.h>
30 #include <sys/modctl.h>
31 #include <sys/scsi/scsi.h>
32 #include <sys/scsi/impl/scsi_reset_notify.h>
33 #include <sys/disp.h>
34 #include <sys/byteorder.h>
35 #include <sys/varargs.h>
36 #include <sys/atomic.h>
37 #include <sys/sdt.h>
38 
39 #include <stmf.h>
40 #include <stmf_ioctl.h>
41 #include <portif.h>
42 #include <fct.h>
43 #include <fctio.h>
44 #include <fct_impl.h>
45 #include <discovery.h>
46 
47 static int fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
48 static int fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
49 static int fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
50     void **result);
51 static int fct_open(dev_t *devp, int flag, int otype, cred_t *credp);
52 static int fct_close(dev_t dev, int flag, int otype, cred_t *credp);
53 static int fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
54     cred_t *credp, int *rval);
55 static int fct_fctiocmd(intptr_t data, int mode);
56 
57 static dev_info_t *fct_dip;
58 static struct cb_ops fct_cb_ops = {
59 	fct_open,			/* open */
60 	fct_close,			/* close */
61 	nodev,				/* strategy */
62 	nodev,				/* print */
63 	nodev,				/* dump */
64 	nodev,				/* read */
65 	nodev,				/* write */
66 	fct_ioctl,			/* ioctl */
67 	nodev,				/* devmap */
68 	nodev,				/* mmap */
69 	nodev,				/* segmap */
70 	nochpoll,			/* chpoll */
71 	ddi_prop_op,			/* cb_prop_op */
72 	0,				/* streamtab */
73 	D_NEW | D_MP,			/* cb_flag */
74 	CB_REV,				/* rev */
75 	nodev,				/* aread */
76 	nodev				/* awrite */
77 };
78 
79 static struct dev_ops fct_ops = {
80 	DEVO_REV,
81 	0,
82 	fct_getinfo,
83 	nulldev,		/* identify */
84 	nulldev,		/* probe */
85 	fct_attach,
86 	fct_detach,
87 	nodev,			/* reset */
88 	&fct_cb_ops,
89 	NULL,			/* bus_ops */
90 	NULL			/* power */
91 };
92 
93 #define	FCT_NAME	"COMSTAR FCT"
94 
95 extern struct mod_ops mod_driverops;
96 static struct modldrv modldrv = {
97 	&mod_driverops,
98 	FCT_NAME,
99 	&fct_ops
100 };
101 
102 static struct modlinkage modlinkage = {
103 	MODREV_1,
104 	&modldrv,
105 	NULL
106 };
107 
108 static uint32_t	rportid_table_size = FCT_HASH_TABLE_SIZE;
109 static int max_cached_ncmds = FCT_MAX_CACHED_CMDS;
110 static fct_i_local_port_t *fct_iport_list = NULL;
111 static kmutex_t fct_global_mutex;
112 uint32_t fct_rscn_options = RSCN_OPTION_VERIFY;
113 
114 int
115 _init(void)
116 {
117 	int ret;
118 
119 	ret = mod_install(&modlinkage);
120 	if (ret)
121 		return (ret);
122 	/* XXX */
123 	mutex_init(&fct_global_mutex, NULL, MUTEX_DRIVER, NULL);
124 	return (ret);
125 }
126 
127 int
128 _fini(void)
129 {
130 	int ret;
131 
132 	ret = mod_remove(&modlinkage);
133 	if (ret)
134 		return (ret);
135 	/* XXX */
136 	mutex_destroy(&fct_global_mutex);
137 	return (ret);
138 }
139 
140 int
141 _info(struct modinfo *modinfop)
142 {
143 	return (mod_info(&modlinkage, modinfop));
144 }
145 
146 /* ARGSUSED */
147 static int
148 fct_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
149 {
150 	switch (cmd) {
151 	case DDI_INFO_DEVT2DEVINFO:
152 		*result = fct_dip;
153 		break;
154 	case DDI_INFO_DEVT2INSTANCE:
155 		*result = (void *)(uintptr_t)ddi_get_instance(fct_dip);
156 		break;
157 	default:
158 		return (DDI_FAILURE);
159 	}
160 
161 	return (DDI_SUCCESS);
162 }
163 
164 static int
165 fct_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
166 {
167 	switch (cmd) {
168 	case DDI_ATTACH:
169 		fct_dip = dip;
170 
171 		if (ddi_create_minor_node(dip, "admin", S_IFCHR, 0,
172 		    DDI_NT_STMF_PP, 0) != DDI_SUCCESS) {
173 			break;
174 		}
175 		ddi_report_dev(dip);
176 		return (DDI_SUCCESS);
177 	}
178 
179 	return (DDI_FAILURE);
180 }
181 
182 static int
183 fct_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
184 {
185 	switch (cmd) {
186 	case DDI_DETACH:
187 		ddi_remove_minor_node(dip, 0);
188 		return (DDI_SUCCESS);
189 	}
190 
191 	return (DDI_FAILURE);
192 }
193 
194 /* ARGSUSED */
195 static int
196 fct_open(dev_t *devp, int flag, int otype, cred_t *credp)
197 {
198 	if (otype != OTYP_CHR)
199 		return (EINVAL);
200 	return (0);
201 }
202 
203 /* ARGSUSED */
204 static int
205 fct_close(dev_t dev, int flag, int otype, cred_t *credp)
206 {
207 	return (0);
208 }
209 
210 /* ARGSUSED */
211 static int
212 fct_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
213     cred_t *credp, int *rval)
214 {
215 	int		ret = 0;
216 
217 	if ((cmd & 0xff000000) != FCT_IOCTL) {
218 		return (ENOTTY);
219 	}
220 
221 	if (drv_priv(credp) != 0) {
222 		return (EPERM);
223 	}
224 
225 	switch (cmd) {
226 	case FCTIO_CMD:
227 		ret = fct_fctiocmd(data, mode);
228 		break;
229 	default:
230 		ret = ENOTTY;
231 		break;
232 	}
233 
234 	return (ret);
235 }
236 
237 int
238 fct_copyin_iocdata(intptr_t data, int mode, fctio_t **fctio,
239     void **ibuf, void **abuf, void **obuf)
240 {
241 	int ret = 0;
242 
243 	*ibuf = NULL;
244 	*abuf = NULL;
245 	*obuf = NULL;
246 	*fctio = kmem_zalloc(sizeof (fctio_t), KM_SLEEP);
247 	if (ddi_copyin((void *)data, *fctio, sizeof (fctio_t), mode)) {
248 		ret = EFAULT;
249 		goto copyin_iocdata_done;
250 	}
251 
252 	if ((*fctio)->fctio_ilen) {
253 		*ibuf = kmem_zalloc((*fctio)->fctio_ilen, KM_SLEEP);
254 		if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_ibuf,
255 		    *ibuf, (*fctio)->fctio_ilen, mode)) {
256 			ret = EFAULT;
257 			goto copyin_iocdata_done;
258 		}
259 	}
260 	if ((*fctio)->fctio_alen) {
261 		*abuf = kmem_zalloc((*fctio)->fctio_alen, KM_SLEEP);
262 		if (ddi_copyin((void *)(unsigned long)(*fctio)->fctio_abuf,
263 		    *abuf, (*fctio)->fctio_alen, mode)) {
264 			ret = EFAULT;
265 			goto copyin_iocdata_done;
266 		}
267 	}
268 	if ((*fctio)->fctio_olen)
269 		*obuf = kmem_zalloc((*fctio)->fctio_olen, KM_SLEEP);
270 	if (ret == 0)
271 		return (0);
272 	ret = EFAULT;
273 copyin_iocdata_done:
274 	if (*obuf) {
275 		kmem_free(*obuf, (*fctio)->fctio_olen);
276 		*obuf = NULL;
277 	}
278 	if (*abuf) {
279 		kmem_free(*abuf, (*fctio)->fctio_alen);
280 		*abuf = NULL;
281 	}
282 	if (*ibuf) {
283 		kmem_free(*ibuf, (*fctio)->fctio_ilen);
284 		*ibuf = NULL;
285 	}
286 	kmem_free(*fctio, sizeof (fctio_t));
287 	return (ret);
288 }
289 
290 int
291 fct_copyout_iocdata(intptr_t data, int mode, fctio_t *fctio, void *obuf)
292 {
293 	int ret = 0;
294 
295 	if (fctio->fctio_olen) {
296 		ret = ddi_copyout(obuf,
297 		    (void *)(unsigned long)fctio->fctio_obuf, fctio->fctio_olen,
298 		    mode);
299 		if (ret) {
300 			return (EFAULT);
301 		}
302 	}
303 	ret = ddi_copyout(fctio, (void *)data, sizeof (fctio_t), mode);
304 	if (ret) {
305 		return (EFAULT);
306 	}
307 	return (0);
308 }
309 
310 int
311 fct_get_port_list(char *pathList, int count)
312 {
313 	fct_i_local_port_t *iport;
314 	int	i = 0, maxPorts = 0;
315 
316 	ASSERT(pathList != NULL);
317 
318 	mutex_enter(&fct_global_mutex);
319 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
320 		if (i < count)
321 			bcopy(iport->iport_port->port_pwwn,
322 			    pathList + 8 * i, 8);
323 		maxPorts ++;
324 		i++;
325 	}
326 	mutex_exit(&fct_global_mutex);
327 	return (maxPorts);
328 }
329 
330 /* invoked with fct_global_mutex locked */
331 fct_i_local_port_t *
332 fct_get_iport_per_wwn(uint8_t *pwwn)
333 {
334 	fct_i_local_port_t *iport;
335 
336 	ASSERT(mutex_owned(&fct_global_mutex));
337 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
338 		if (bcmp(iport->iport_port->port_pwwn, pwwn, 8) == 0)
339 			return (iport);
340 	}
341 	return (NULL);
342 }
343 
344 int
345 fct_get_adapter_attr(uint8_t *pwwn, fc_tgt_hba_adapter_attributes_t *hba_attr,
346     uint32_t *err_detail)
347 {
348 	fct_i_local_port_t *iport;
349 	fct_port_attrs_t *attr;
350 
351 	hba_attr->version = FCT_HBA_ADAPTER_ATTRIBUTES_VERSION;
352 	iport = fct_get_iport_per_wwn(pwwn);
353 	if (!iport) {
354 		*err_detail = FCTIO_BADWWN;
355 		return (ENXIO);
356 	}
357 
358 	attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
359 	    KM_SLEEP);
360 	mutex_exit(&fct_global_mutex);
361 	iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
362 	mutex_enter(&fct_global_mutex);
363 
364 	bcopy(attr->manufacturer, hba_attr->Manufacturer,
365 	    sizeof (hba_attr->Manufacturer));
366 	bcopy(attr->serial_number, hba_attr->SerialNumber,
367 	    sizeof (hba_attr->SerialNumber));
368 	bcopy(attr->model, hba_attr->Model, sizeof (hba_attr->Model));
369 	bcopy(attr->model_description, hba_attr->ModelDescription,
370 	    sizeof (hba_attr->ModelDescription));
371 	if (iport->iport_port->port_sym_node_name)
372 		bcopy(iport->iport_port->port_sym_node_name,
373 		    hba_attr->NodeSymbolicName,
374 		    strlen(iport->iport_port->port_sym_node_name));
375 	else
376 		bcopy(utsname.nodename, hba_attr->NodeSymbolicName,
377 		    strlen(utsname.nodename));
378 	bcopy(attr->hardware_version, hba_attr->HardwareVersion,
379 	    sizeof (hba_attr->HardwareVersion));
380 	bcopy(attr->option_rom_version, hba_attr->OptionROMVersion,
381 	    sizeof (hba_attr->OptionROMVersion));
382 	bcopy(attr->firmware_version, hba_attr->FirmwareVersion,
383 	    sizeof (hba_attr->FirmwareVersion));
384 	hba_attr->VendorSpecificID = attr->vendor_specific_id;
385 	bcopy(iport->iport_port->port_nwwn, hba_attr->NodeWWN,
386 	    sizeof (hba_attr->NodeWWN));
387 
388 	bcopy(attr->driver_name, hba_attr->DriverName,
389 	    sizeof (hba_attr->DriverName));
390 	bcopy(attr->driver_version, hba_attr->DriverVersion,
391 	    sizeof (hba_attr->DriverVersion));
392 
393 
394 	/* hba_attr->NumberOfPorts = fct_count_fru_ports(iport); */
395 	hba_attr->NumberOfPorts = 1;
396 
397 	kmem_free(attr, sizeof (fct_port_attrs_t));
398 	return (0);
399 }
400 
401 int
402 fct_get_adapter_port_attr(fct_i_local_port_t *ilport, uint8_t *pwwn,
403     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *err_detail)
404 {
405 	fct_i_local_port_t *iport = ilport;
406 	fct_i_remote_port_t *irp = NULL;
407 	fct_port_attrs_t *attr;
408 	int i = 0;
409 
410 	port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
411 
412 	if (!ilport) {
413 		iport = fct_get_iport_per_wwn(pwwn);
414 		if (!iport) {
415 			*err_detail = FCTIO_BADWWN;
416 			return (ENXIO);
417 		}
418 	}
419 
420 	attr = (fct_port_attrs_t *)kmem_zalloc(sizeof (fct_port_attrs_t),
421 	    KM_SLEEP);
422 	mutex_exit(&fct_global_mutex);
423 	iport->iport_port->port_populate_hba_details(iport->iport_port, attr);
424 	mutex_enter(&fct_global_mutex);
425 
426 	port_attr->lastChange = iport->iport_last_change;
427 	bcopy(iport->iport_port->port_nwwn, port_attr->NodeWWN,
428 	    sizeof (port_attr->NodeWWN));
429 	bcopy(iport->iport_port->port_pwwn, port_attr->PortWWN,
430 	    sizeof (port_attr->PortWWN));
431 	bzero(port_attr->FabricName, sizeof (port_attr->FabricName));
432 	port_attr->PortFcId = iport->iport_link_info.portid;
433 	if ((iport->iport_link_state & S_LINK_ONLINE) ||
434 	    (iport->iport_link_state & S_RCVD_LINK_UP)) {
435 		port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
436 	} else {
437 		port_attr->PortState = FC_HBA_PORTSTATE_OFFLINE;
438 	}
439 	switch (iport->iport_link_info.port_topology) {
440 		case PORT_TOPOLOGY_PT_TO_PT:
441 			port_attr->PortType = FC_HBA_PORTTYPE_PTP;
442 			break;
443 		case PORT_TOPOLOGY_PRIVATE_LOOP:
444 			port_attr->PortType = FC_HBA_PORTTYPE_LPORT;
445 			break;
446 		case PORT_TOPOLOGY_PUBLIC_LOOP:
447 			port_attr->PortType = FC_HBA_PORTTYPE_NLPORT;
448 			break;
449 		case PORT_TOPOLOGY_FABRIC_PT_TO_PT:
450 			port_attr->PortType = FC_HBA_PORTTYPE_FPORT;
451 			break;
452 		default:
453 			port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
454 			break;
455 	}
456 	port_attr->PortSupportedClassofService = attr->supported_cos;
457 	port_attr->PortSupportedFc4Types[0] = 0;
458 	port_attr->PortActiveFc4Types[2] = 1;
459 	if (iport->iport_port->port_sym_port_name)
460 		bcopy(iport->iport_port->port_sym_port_name,
461 		    port_attr->PortSymbolicName,
462 		    strlen(iport->iport_port->port_sym_port_name));
463 	else if (iport->iport_port->port_default_alias)
464 		bcopy(iport->iport_port->port_default_alias,
465 		    port_attr->PortSymbolicName,
466 		    strlen(iport->iport_port->port_default_alias));
467 	else
468 		port_attr->PortSymbolicName[0] = 0;
469 	/* the definition is different so need to translate */
470 	if (attr->supported_speed & PORT_SPEED_1G)
471 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_1GBIT;
472 	if (attr->supported_speed & PORT_SPEED_2G)
473 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_2GBIT;
474 	if (attr->supported_speed & PORT_SPEED_4G)
475 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_4GBIT;
476 	if (attr->supported_speed & PORT_SPEED_8G)
477 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_8GBIT;
478 	if (attr->supported_speed & PORT_SPEED_10G)
479 		port_attr->PortSupportedSpeed |= FC_HBA_PORTSPEED_10GBIT;
480 	switch (iport->iport_link_info.port_speed) {
481 		case PORT_SPEED_1G:
482 			port_attr->PortSpeed = FC_HBA_PORTSPEED_1GBIT;
483 			break;
484 		case PORT_SPEED_2G:
485 			port_attr->PortSpeed = FC_HBA_PORTSPEED_2GBIT;
486 			break;
487 		case PORT_SPEED_4G:
488 			port_attr->PortSpeed = FC_HBA_PORTSPEED_4GBIT;
489 			break;
490 		case PORT_SPEED_8G:
491 			port_attr->PortSpeed = FC_HBA_PORTSPEED_8GBIT;
492 			break;
493 		case PORT_SPEED_10G:
494 			port_attr->PortSpeed = FC_HBA_PORTSPEED_10GBIT;
495 			break;
496 		default:
497 			port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
498 			break;
499 	}
500 	port_attr->PortMaxFrameSize = attr->max_frame_size;
501 	rw_enter(&iport->iport_lock, RW_READER);
502 	port_attr->NumberofDiscoveredPorts = iport->iport_nrps_login;
503 	for (; i < iport->iport_port->port_max_logins; i++) {
504 		irp = iport->iport_rp_slots[i];
505 		if (irp && irp->irp_flags & IRP_PLOGI_DONE) {
506 			if (FC_WELL_KNOWN_ADDR(irp->irp_portid))
507 				port_attr->NumberofDiscoveredPorts --;
508 		}
509 	}
510 	rw_exit(&iport->iport_lock);
511 
512 	kmem_free(attr, sizeof (fct_port_attrs_t));
513 
514 	return (0);
515 }
516 
517 int
518 fct_get_discovered_port_attr(fct_i_remote_port_t *remote_port,
519     uint8_t *port_wwn, uint32_t index, fc_tgt_hba_port_attributes_t *port_attr,
520     uint32_t *error_detail)
521 {
522 	fct_i_local_port_t *iport;
523 	fct_i_remote_port_t *irp = remote_port;
524 	int	count = 0, i = 0;
525 
526 	port_attr->version = FCT_HBA_PORT_ATTRIBUTES_VERSION;
527 	if (!remote_port) {
528 		iport = fct_get_iport_per_wwn(port_wwn);
529 		if (!iport) {
530 			*error_detail = FCTIO_BADWWN;
531 			return (ENXIO);
532 		}
533 
534 		rw_enter(&iport->iport_lock, RW_READER);
535 
536 		if (index >= iport->iport_nrps_login) {
537 			rw_exit(&iport->iport_lock);
538 			*error_detail = FCTIO_OUTOFBOUNDS;
539 			return (EINVAL);
540 		}
541 		for (; i < iport->iport_port->port_max_logins; i++) {
542 			irp = iport->iport_rp_slots[i];
543 			if (irp && irp->irp_flags & IRP_PLOGI_DONE &&
544 			    !FC_WELL_KNOWN_ADDR(irp->irp_portid)) {
545 				count ++;
546 				if ((index + 1) <= count)
547 					break;
548 			}
549 		}
550 		if (i >= iport->iport_port->port_max_logins) {
551 			rw_exit(&iport->iport_lock);
552 			*error_detail = FCTIO_OUTOFBOUNDS;
553 			return (EINVAL);
554 		}
555 		ASSERT(irp);
556 	} else {
557 		iport = (fct_i_local_port_t *)
558 		    irp->irp_rp->rp_port->port_fct_private;
559 	}
560 	port_attr->lastChange = iport->iport_last_change;
561 	rw_enter(&irp->irp_lock, RW_READER);
562 	bcopy(irp->irp_rp->rp_pwwn, port_attr->PortWWN,
563 	    sizeof (port_attr->PortWWN));
564 	bcopy(irp->irp_rp->rp_nwwn, port_attr->NodeWWN,
565 	    sizeof (port_attr->NodeWWN));
566 	port_attr->PortFcId = irp->irp_portid;
567 	if (irp->irp_spn)
568 		(void) strncpy(port_attr->PortSymbolicName, irp->irp_spn,
569 		    strlen(irp->irp_spn));
570 	else
571 		port_attr->PortSymbolicName[0] = '\0';
572 	port_attr->PortSupportedClassofService = irp->irp_cos;
573 	bcopy((caddr_t)irp->irp_fc4types, port_attr->PortActiveFc4Types,
574 	    sizeof (irp->irp_fc4types));
575 	bcopy((caddr_t)irp->irp_fc4types, port_attr->PortSupportedFc4Types,
576 	    sizeof (irp->irp_fc4types));
577 	if (irp->irp_flags & IRP_PLOGI_DONE)
578 		port_attr->PortState = FC_HBA_PORTSTATE_ONLINE;
579 	else
580 		port_attr->PortState = FC_HBA_PORTSTATE_UNKNOWN;
581 
582 	port_attr->PortType = FC_HBA_PORTTYPE_UNKNOWN;
583 	port_attr->PortSupportedSpeed = FC_HBA_PORTSPEED_UNKNOWN;
584 	port_attr->PortSpeed = FC_HBA_PORTSPEED_UNKNOWN;
585 	port_attr->PortMaxFrameSize = 0;
586 	port_attr->NumberofDiscoveredPorts = 0;
587 	rw_exit(&irp->irp_lock);
588 	if (!remote_port) {
589 		rw_exit(&iport->iport_lock);
590 	}
591 	return (0);
592 }
593 
594 int
595 fct_get_port_attr(uint8_t *port_wwn,
596     fc_tgt_hba_port_attributes_t *port_attr, uint32_t *error_detail)
597 {
598 	fct_i_local_port_t *iport;
599 	fct_i_remote_port_t *irp;
600 	int i, ret;
601 
602 	iport = fct_get_iport_per_wwn(port_wwn);
603 	if (iport) {
604 		return (fct_get_adapter_port_attr(iport, port_wwn,
605 		    port_attr, error_detail));
606 	}
607 	/* else */
608 	for (iport = fct_iport_list; iport; iport = iport->iport_next) {
609 		rw_enter(&iport->iport_lock, RW_READER);
610 		for (i = 0; i < rportid_table_size; i++) {
611 			irp = iport->iport_rp_tb[i];
612 			while (irp) {
613 				if (bcmp(irp->irp_rp->rp_pwwn,
614 				    port_wwn, 8) == 0 &&
615 				    irp->irp_flags & IRP_PLOGI_DONE) {
616 					ret = fct_get_discovered_port_attr(
617 					    irp, NULL, 0, port_attr,
618 					    error_detail);
619 					rw_exit(&iport->iport_lock);
620 					return (ret);
621 				}
622 				irp = irp->irp_next;
623 			}
624 		}
625 		rw_exit(&iport->iport_lock);
626 	}
627 	*error_detail = FCTIO_BADWWN;
628 	return (ENXIO);
629 }
630 
631 /* ARGSUSED */
632 int
633 fct_get_port_stats(uint8_t *port_wwn,
634     fc_tgt_hba_adapter_port_stats_t *port_stats, uint32_t *error_detail)
635 {
636 	fct_i_local_port_t *iport = fct_get_iport_per_wwn(port_wwn);
637 
638 	if (!iport)
639 		return (ENXIO);
640 	port_stats->version = FCT_HBA_ADAPTER_PORT_STATS_VERSION;
641 	/*
642 	 * port_stats->SecondsSinceLastReset = ;
643 	 * port_stats->TxFrames = ;
644 	 * port_stats->TxWords = ;
645 	 * port_stats->RxFrames = ;
646 	 * port_stats->RxWords = ;
647 	 * port_stats->LIPCount = ;
648 	 * port_stats->NOSCount = ;
649 	 * port_stats->ErrorFrames = ;
650 	 * port_stats->DumpedFrames = ;
651 	 * port_stats->LinkFailureCount = ;
652 	 * port_stats->LossOfSyncCount = ;
653 	 * port_stats->LossOfSignalCount = ;
654 	 * port_stats->PrimitiveSeqProtocol = ;
655 	 * port_stats->InvalidTxWordCount = ;
656 	 * port_stats->InvalidCRCCount = ;
657 	 */
658 	return (0);
659 }
660 
661 static int
662 fct_fctiocmd(intptr_t data, int mode)
663 {
664 	int ret	 = 0;
665 	void		*ibuf = NULL;
666 	void		*obuf = NULL;
667 	void		*abuf = NULL;
668 	fctio_t		*fctio;
669 	uint32_t	attr_length;
670 
671 	ret = fct_copyin_iocdata(data, mode, &fctio, &ibuf, &abuf, &obuf);
672 	if (ret) {
673 		return (ret);
674 	}
675 
676 	switch (fctio->fctio_cmd) {
677 	case FCTIO_ADAPTER_LIST: {
678 		fc_tgt_hba_list_t *list = (fc_tgt_hba_list_t *)obuf;
679 		int		count;
680 
681 		if (fctio->fctio_olen < sizeof (fc_tgt_hba_list_t)) {
682 			ret = EINVAL;
683 			break;
684 		}
685 		list->numPorts = (fctio->fctio_olen -
686 		    sizeof (fc_tgt_hba_list_t))/8 + 1;
687 
688 		list->version = FCT_HBA_LIST_VERSION;
689 		count = fct_get_port_list((char *)list->port_wwn,
690 		    list->numPorts);
691 		if (count < 0) {
692 			ret = ENXIO;
693 			break;
694 		}
695 		if (count > list->numPorts) {
696 			fctio->fctio_errno = FCTIO_MOREDATA;
697 			ret = ENOSPC;
698 		}
699 		list->numPorts = count;
700 		break;
701 		}
702 	case FCTIO_GET_ADAPTER_ATTRIBUTES: {
703 		fc_tgt_hba_adapter_attributes_t *hba_attr;
704 		uint8_t	*port_wwn = (uint8_t *)ibuf;
705 
706 		attr_length = sizeof (fc_tgt_hba_adapter_attributes_t);
707 		if (fctio->fctio_olen < attr_length ||
708 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
709 			ret = EINVAL;
710 			break;
711 		}
712 		hba_attr = (fc_tgt_hba_adapter_attributes_t *)obuf;
713 
714 		mutex_enter(&fct_global_mutex);
715 		ret = fct_get_adapter_attr(port_wwn, hba_attr,
716 		    &fctio->fctio_errno);
717 		mutex_exit(&fct_global_mutex);
718 
719 		break;
720 		}
721 	case FCTIO_GET_ADAPTER_PORT_ATTRIBUTES: {
722 		fc_tgt_hba_port_attributes_t *port_attr;
723 
724 		uint8_t *port_wwn = (uint8_t *)ibuf;
725 
726 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
727 		if (fctio->fctio_olen < attr_length ||
728 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
729 			ret = EINVAL;
730 			break;
731 		}
732 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
733 
734 		mutex_enter(&fct_global_mutex);
735 		ret = fct_get_adapter_port_attr(NULL, port_wwn, port_attr,
736 		    &fctio->fctio_errno);
737 		mutex_exit(&fct_global_mutex);
738 
739 		break;
740 		}
741 	case FCTIO_GET_DISCOVERED_PORT_ATTRIBUTES: {
742 		uint8_t *port_wwn = (uint8_t *)ibuf;
743 		uint32_t *port_index = (uint32_t *)abuf;
744 		fc_tgt_hba_port_attributes_t *port_attr;
745 
746 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
747 		if (fctio->fctio_olen < attr_length ||
748 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
749 			ret = EINVAL;
750 			break;
751 		}
752 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
753 
754 		mutex_enter(&fct_global_mutex);
755 		ret = fct_get_discovered_port_attr(NULL, port_wwn,
756 		    *port_index, port_attr, &fctio->fctio_errno);
757 		mutex_exit(&fct_global_mutex);
758 
759 		break;
760 		}
761 	case FCTIO_GET_PORT_ATTRIBUTES: {
762 		uint8_t *port_wwn = (uint8_t *)ibuf;
763 		fc_tgt_hba_port_attributes_t *port_attr;
764 
765 		attr_length = sizeof (fc_tgt_hba_port_attributes_t);
766 		if (fctio->fctio_olen < attr_length ||
767 		    fctio->fctio_xfer != FCTIO_XFER_READ) {
768 			ret = EINVAL;
769 			break;
770 		}
771 
772 		port_attr = (fc_tgt_hba_port_attributes_t *)obuf;
773 
774 		mutex_enter(&fct_global_mutex);
775 		ret = fct_get_port_attr(port_wwn, port_attr,
776 		    &fctio->fctio_errno);
777 		mutex_exit(&fct_global_mutex);
778 
779 		break;
780 		}
781 	case FCTIO_GET_ADAPTER_PORT_STATS: {
782 		uint8_t *port_wwn = (uint8_t *)ibuf;
783 		fc_tgt_hba_adapter_port_stats_t *port_stats =
784 		    (fc_tgt_hba_adapter_port_stats_t *)obuf;
785 		mutex_enter(&fct_global_mutex);
786 		ret = fct_get_port_stats(port_wwn, port_stats,
787 		    &fctio->fctio_errno);
788 		mutex_exit(&fct_global_mutex);
789 		break;
790 		}
791 	default:
792 		break;
793 	}
794 	if (ret == 0) {
795 		ret = fct_copyout_iocdata(data, mode, fctio, obuf);
796 	} else if (fctio->fctio_errno) {
797 		(void) fct_copyout_iocdata(data, mode, fctio, obuf);
798 	}
799 
800 	if (obuf) {
801 		kmem_free(obuf, fctio->fctio_olen);
802 		obuf = NULL;
803 	}
804 	if (abuf) {
805 		kmem_free(abuf, fctio->fctio_alen);
806 		abuf = NULL;
807 	}
808 
809 	if (ibuf) {
810 		kmem_free(ibuf, fctio->fctio_ilen);
811 		ibuf = NULL;
812 	}
813 	kmem_free(fctio, sizeof (fctio_t));
814 	return (ret);
815 }
816 
817 typedef struct {
818 	void	*bp;	/* back pointer from internal struct to main struct */
819 	int	alloc_size;
820 	fct_struct_id_t struct_id;
821 } __ifct_t;
822 
823 typedef struct {
824 	__ifct_t	*fp;	/* Framework private */
825 	void		*cp;	/* Caller private */
826 	void		*ss;	/* struct specific */
827 } __fct_t;
828 
829 static struct {
830 	int shared;
831 	int fw_private;
832 	int struct_specific;
833 } fct_sizes[] = { { 0, 0, 0 },
834 	{ GET_STRUCT_SIZE(fct_local_port_t),
835 		GET_STRUCT_SIZE(fct_i_local_port_t), 0 },
836 	{ GET_STRUCT_SIZE(fct_remote_port_t),
837 		GET_STRUCT_SIZE(fct_i_remote_port_t), 0 },
838 	{ GET_STRUCT_SIZE(fct_cmd_t),
839 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
840 	{ GET_STRUCT_SIZE(fct_cmd_t),
841 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_els_t) },
842 	{ GET_STRUCT_SIZE(fct_cmd_t),
843 		GET_STRUCT_SIZE(fct_i_cmd_t), GET_STRUCT_SIZE(fct_sol_ct_t) },
844 	{ GET_STRUCT_SIZE(fct_cmd_t), GET_STRUCT_SIZE(fct_i_cmd_t),
845 		GET_STRUCT_SIZE(fct_rcvd_abts_t) },
846 	{ GET_STRUCT_SIZE(fct_cmd_t),	/* FCT_STRUCT_CMD_FCP_XCHG */
847 		GET_STRUCT_SIZE(fct_i_cmd_t), 0 },
848 	{ GET_STRUCT_SIZE(fct_dbuf_store_t),
849 		GET_STRUCT_SIZE(__ifct_t), 0 }
850 };
851 
852 void *
853 fct_alloc(fct_struct_id_t struct_id, int additional_size, int flags)
854 {
855 	int fct_size;
856 	int kmem_flag;
857 	__fct_t *sh;
858 
859 	if ((struct_id == 0) || (struct_id >= FCT_MAX_STRUCT_IDS))
860 		return (NULL);
861 
862 	if ((curthread->t_flag & T_INTR_THREAD) || (flags & AF_FORCE_NOSLEEP)) {
863 		kmem_flag = KM_NOSLEEP;
864 	} else {
865 		kmem_flag = KM_SLEEP;
866 	}
867 
868 	additional_size = (additional_size + 7) & (~7);
869 	fct_size = fct_sizes[struct_id].shared +
870 	    fct_sizes[struct_id].fw_private +
871 	    fct_sizes[struct_id].struct_specific + additional_size;
872 
873 	if (struct_id == FCT_STRUCT_LOCAL_PORT) {
874 		stmf_local_port_t *lport;
875 
876 		lport = (stmf_local_port_t *)stmf_alloc(
877 		    STMF_STRUCT_STMF_LOCAL_PORT, fct_size, flags);
878 		if (lport) {
879 			sh = (__fct_t *)lport->lport_port_private;
880 			sh->ss = lport;
881 		} else {
882 			return (NULL);
883 		}
884 	} else if (struct_id == FCT_STRUCT_DBUF_STORE) {
885 		stmf_dbuf_store_t *ds;
886 
887 		ds = (stmf_dbuf_store_t *)stmf_alloc(STMF_STRUCT_DBUF_STORE,
888 		    fct_size, flags);
889 		if (ds) {
890 			sh = (__fct_t *)ds->ds_port_private;
891 			sh->ss = ds;
892 		} else {
893 			return (NULL);
894 		}
895 	} else {
896 		sh = (__fct_t *)kmem_zalloc(fct_size, kmem_flag);
897 	}
898 
899 	if (sh == NULL)
900 		return (NULL);
901 
902 	sh->fp = (__ifct_t *)GET_BYTE_OFFSET(sh, fct_sizes[struct_id].shared);
903 	sh->cp = GET_BYTE_OFFSET(sh->fp, fct_sizes[struct_id].fw_private);
904 	if (fct_sizes[struct_id].struct_specific)
905 		sh->ss = GET_BYTE_OFFSET(sh->cp, additional_size);
906 
907 	sh->fp->bp = sh;
908 	sh->fp->alloc_size = fct_size;
909 	sh->fp->struct_id = struct_id;
910 
911 	if (struct_id == FCT_STRUCT_CMD_FCP_XCHG) {
912 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_FCP_XCHG;
913 	} else if (struct_id == FCT_STRUCT_CMD_RCVD_ELS) {
914 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ELS;
915 	} else if (struct_id == FCT_STRUCT_CMD_SOL_ELS) {
916 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_ELS;
917 	} else if (struct_id == FCT_STRUCT_CMD_RCVD_ABTS) {
918 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_RCVD_ABTS;
919 	} else if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
920 		((fct_cmd_t *)sh)->cmd_type = FCT_CMD_SOL_CT;
921 	}
922 
923 	return (sh);
924 }
925 
926 void
927 fct_free(void *ptr)
928 {
929 	__fct_t *sh = (__fct_t *)ptr;
930 	fct_struct_id_t struct_id = sh->fp->struct_id;
931 
932 	if (struct_id == FCT_STRUCT_CMD_SOL_CT) {
933 		fct_sol_ct_t *ct = (fct_sol_ct_t *)
934 		    ((fct_cmd_t *)ptr)->cmd_specific;
935 
936 		if (ct->ct_req_alloc_size) {
937 			kmem_free(ct->ct_req_payload, ct->ct_req_alloc_size);
938 		}
939 		if (ct->ct_resp_alloc_size) {
940 			kmem_free(ct->ct_resp_payload, ct->ct_resp_alloc_size);
941 		}
942 	} else if ((struct_id == FCT_STRUCT_CMD_RCVD_ELS) ||
943 	    (struct_id == FCT_STRUCT_CMD_SOL_ELS)) {
944 		fct_els_t *els = (fct_els_t *)
945 			((fct_cmd_t *)ptr)->cmd_specific;
946 		if (els->els_req_alloc_size)
947 			kmem_free(els->els_req_payload,
948 				els->els_req_alloc_size);
949 		if (els->els_resp_alloc_size)
950 			kmem_free(els->els_resp_payload,
951 				els->els_resp_alloc_size);
952 	}
953 
954 	if (struct_id == FCT_STRUCT_LOCAL_PORT) {
955 		stmf_free(((fct_local_port_t *)ptr)->port_lport);
956 	} else if (struct_id == FCT_STRUCT_DBUF_STORE) {
957 		stmf_free(((fct_dbuf_store_t *)ptr)->fds_ds);
958 	} else {
959 		kmem_free(ptr, sh->fp->alloc_size);
960 	}
961 }
962 
963 stmf_data_buf_t *
964 fct_alloc_dbuf(scsi_task_t *task, uint32_t size, uint32_t *pminsize,
965     uint32_t flags)
966 {
967 	fct_local_port_t *port = (fct_local_port_t *)
968 	    task->task_lport->lport_port_private;
969 
970 	return (port->port_fds->fds_alloc_data_buf(port, size,
971 	    pminsize, flags));
972 }
973 
974 void
975 fct_free_dbuf(stmf_dbuf_store_t *ds, stmf_data_buf_t *dbuf)
976 {
977 	fct_dbuf_store_t *fds;
978 
979 	fds = (fct_dbuf_store_t *)ds->ds_port_private;
980 
981 	fds->fds_free_data_buf(fds, dbuf);
982 }
983 
984 static uint32_t taskq_cntr = 0;
985 
986 fct_status_t
987 fct_register_local_port(fct_local_port_t *port)
988 {
989 	fct_i_local_port_t	*iport;
990 	stmf_local_port_t	*lport;
991 	fct_cmd_slot_t		*slot;
992 	int			i;
993 	char			taskq_name[24];
994 
995 	iport = (fct_i_local_port_t *)port->port_fct_private;
996 	if (port->port_default_alias) {
997 		int l = strlen(port->port_default_alias);
998 
999 		if (l < 16) {
1000 			iport->iport_alias = iport->iport_alias_mem;
1001 		} else {
1002 			iport->iport_alias =
1003 			    (char *)kmem_zalloc(l+1, KM_SLEEP);
1004 		}
1005 		(void) strcpy(iport->iport_alias, port->port_default_alias);
1006 	} else {
1007 		iport->iport_alias = NULL;
1008 	}
1009 	stmf_wwn_to_devid_desc((scsi_devid_desc_t *)iport->iport_id,
1010 	    port->port_pwwn, PROTOCOL_FIBRE_CHANNEL);
1011 	(void) snprintf(taskq_name, 24, "stmf_fct_taskq_%d",
1012 	    atomic_add_32_nv(&taskq_cntr, 1));
1013 	taskq_name[23] = 0;
1014 	if ((iport->iport_worker_taskq = ddi_taskq_create(NULL,
1015 	    taskq_name, 1, TASKQ_DEFAULTPRI, 0)) == NULL) {
1016 		return (FCT_FAILURE);
1017 	}
1018 	mutex_init(&iport->iport_worker_lock, NULL, MUTEX_DRIVER, NULL);
1019 	cv_init(&iport->iport_worker_cv, NULL, CV_DRIVER, NULL);
1020 	rw_init(&iport->iport_lock, NULL, RW_DRIVER, NULL);
1021 
1022 	/* Remote port mgmt */
1023 	iport->iport_rp_slots = (fct_i_remote_port_t **)kmem_zalloc(
1024 	    port->port_max_logins * sizeof (fct_i_remote_port_t *), KM_SLEEP);
1025 	iport->iport_rp_tb = kmem_zalloc(rportid_table_size *
1026 	    sizeof (fct_i_remote_port_t *), KM_SLEEP);
1027 
1028 	/* fct_cmds for SCSI traffic */
1029 	iport->iport_total_alloced_ncmds = 0;
1030 	iport->iport_cached_ncmds = 0;
1031 	port->port_fca_fcp_cmd_size =
1032 	    (port->port_fca_fcp_cmd_size + 7) & ~7;
1033 	iport->iport_cached_cmdlist = NULL;
1034 	mutex_init(&iport->iport_cached_cmd_lock, NULL, MUTEX_DRIVER, NULL);
1035 
1036 	/* Initialize cmd slots */
1037 	iport->iport_cmd_slots = (fct_cmd_slot_t *)kmem_zalloc(
1038 	    port->port_max_xchges * sizeof (fct_cmd_slot_t), KM_SLEEP);
1039 	iport->iport_next_free_slot = 0;
1040 	for (i = 0; i < port->port_max_xchges; ) {
1041 		slot = &iport->iport_cmd_slots[i];
1042 		slot->slot_no = (uint16_t)i;
1043 		slot->slot_next = (uint16_t)(++i);
1044 	}
1045 	slot->slot_next = FCT_SLOT_EOL;
1046 	iport->iport_nslots_free = port->port_max_xchges;
1047 
1048 	iport->iport_task_green_limit =
1049 	    (port->port_max_xchges * FCT_TASK_GREEN_LIMIT) / 100;
1050 	iport->iport_task_yellow_limit =
1051 	    (port->port_max_xchges * FCT_TASK_YELLOW_LIMIT) / 100;
1052 	iport->iport_task_red_limit =
1053 	    (port->port_max_xchges * FCT_TASK_RED_LIMIT) / 100;
1054 
1055 	/* Start worker thread */
1056 	atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1057 	(void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1058 	    fct_port_worker, port, DDI_SLEEP);
1059 	/* Wait for taskq to start */
1060 	while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1061 		delay(1);
1062 	}
1063 
1064 	lport = port->port_lport;
1065 	lport->lport_id = (scsi_devid_desc_t *)iport->iport_id;
1066 	lport->lport_alias = iport->iport_alias;
1067 	lport->lport_pp = port->port_pp;
1068 	port->port_fds->fds_ds->ds_alloc_data_buf = fct_alloc_dbuf;
1069 	port->port_fds->fds_ds->ds_free_data_buf = fct_free_dbuf;
1070 	lport->lport_ds = port->port_fds->fds_ds;
1071 	lport->lport_xfer_data = fct_xfer_scsi_data;
1072 	lport->lport_send_status = fct_send_scsi_status;
1073 	lport->lport_task_free = fct_scsi_task_free;
1074 	lport->lport_abort = fct_scsi_abort;
1075 	lport->lport_ctl = fct_ctl;
1076 	lport->lport_info = fct_info;
1077 	lport->lport_event_handler = fct_event_handler;
1078 	if (stmf_register_local_port(port->port_lport) != FCT_SUCCESS) {
1079 		goto fct_regport_fail1;
1080 	}
1081 	(void) stmf_lport_add_event(lport, LPORT_EVENT_INITIAL_LUN_MAPPED);
1082 
1083 	mutex_enter(&fct_global_mutex);
1084 	iport->iport_next = fct_iport_list;
1085 	iport->iport_prev = NULL;
1086 	if (iport->iport_next)
1087 		iport->iport_next->iport_prev = iport;
1088 	fct_iport_list = iport;
1089 	mutex_exit(&fct_global_mutex);
1090 
1091 	fct_log_local_port_event(port, ESC_SUNFC_PORT_ATTACH);
1092 
1093 	return (FCT_SUCCESS);
1094 
1095 fct_regport_fail1:;
1096 	/* Stop the taskq 1st */
1097 	if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1098 		atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1099 		cv_broadcast(&iport->iport_worker_cv);
1100 		while (iport->iport_flags & IPORT_WORKER_RUNNING) {
1101 			delay(1);
1102 		}
1103 	}
1104 	ddi_taskq_destroy(iport->iport_worker_taskq);
1105 	if (iport->iport_rp_tb) {
1106 		kmem_free(iport->iport_rp_tb, rportid_table_size *
1107 		    sizeof (fct_i_remote_port_t *));
1108 	}
1109 	return (FCT_FAILURE);
1110 }
1111 
1112 fct_status_t
1113 fct_deregister_local_port(fct_local_port_t *port)
1114 {
1115 	fct_i_local_port_t	*iport;
1116 	fct_i_cmd_t		*icmd, *next_icmd;
1117 	int			ndx;
1118 
1119 	iport = (fct_i_local_port_t *)port->port_fct_private;
1120 
1121 	if ((iport->iport_state != FCT_STATE_OFFLINE) ||
1122 	    iport->iport_state_not_acked) {
1123 		return (FCT_FAILURE);
1124 	}
1125 
1126 	/* Stop the taskq 1st */
1127 	if (iport->iport_flags & IPORT_WORKER_RUNNING) {
1128 		atomic_or_32(&iport->iport_flags, IPORT_TERMINATE_WORKER);
1129 		cv_broadcast(&iport->iport_worker_cv);
1130 		for (ndx = 0; ndx < 100; ndx++) {
1131 			if ((iport->iport_flags & IPORT_WORKER_RUNNING)
1132 			    == 0) {
1133 				break;
1134 			}
1135 			delay(drv_usectohz(10000));
1136 		}
1137 		if (ndx == 100) {
1138 			atomic_and_32(&iport->iport_flags,
1139 			    ~IPORT_TERMINATE_WORKER);
1140 			return (FCT_WORKER_STUCK);
1141 		}
1142 	}
1143 
1144 	if (stmf_deregister_local_port(port->port_lport) != FCT_SUCCESS) {
1145 		goto fct_deregport_fail1;
1146 	}
1147 
1148 	mutex_enter(&fct_global_mutex);
1149 	if (iport->iport_next)
1150 		iport->iport_next->iport_prev = iport->iport_prev;
1151 	if (iport->iport_prev)
1152 		iport->iport_prev->iport_next = iport->iport_next;
1153 	else
1154 		fct_iport_list = iport->iport_next;
1155 	mutex_exit(&fct_global_mutex);
1156 	/*
1157 	 * At this time, there should be no outstanding and pending
1158 	 * I/Os, so we can just release resources.
1159 	 */
1160 	ASSERT(iport->iport_total_alloced_ncmds == iport->iport_cached_ncmds);
1161 	for (icmd = iport->iport_cached_cmdlist; icmd; icmd = next_icmd) {
1162 		next_icmd = icmd->icmd_next;
1163 		fct_free(icmd->icmd_cmd);
1164 	}
1165 	mutex_destroy(&iport->iport_cached_cmd_lock);
1166 	kmem_free(iport->iport_cmd_slots, port->port_max_xchges *
1167 	    sizeof (fct_cmd_slot_t));
1168 	kmem_free(iport->iport_rp_slots, port->port_max_logins *
1169 	    sizeof (fct_i_remote_port_t *));
1170 	rw_destroy(&iport->iport_lock);
1171 	cv_destroy(&iport->iport_worker_cv);
1172 	mutex_destroy(&iport->iport_worker_lock);
1173 	ddi_taskq_destroy(iport->iport_worker_taskq);
1174 	if (iport->iport_rp_tb) {
1175 		kmem_free(iport->iport_rp_tb, rportid_table_size *
1176 		    sizeof (fct_i_remote_port_t *));
1177 	}
1178 
1179 	fct_log_local_port_event(port, ESC_SUNFC_PORT_DETACH);
1180 	return (FCT_SUCCESS);
1181 
1182 fct_deregport_fail1:;
1183 	/* Restart the worker */
1184 	atomic_and_32(&iport->iport_flags, ~IPORT_TERMINATE_WORKER);
1185 	(void) ddi_taskq_dispatch(iport->iport_worker_taskq,
1186 	    fct_port_worker, port, DDI_SLEEP);
1187 	/* Wait for taskq to start */
1188 	while ((iport->iport_flags & IPORT_WORKER_RUNNING) == 0) {
1189 		delay(1);
1190 	}
1191 	return (FCT_FAILURE);
1192 }
1193 
1194 /* ARGSUSED */
1195 void
1196 fct_handle_event(fct_local_port_t *port, int event_id, uint32_t event_flags,
1197 		caddr_t arg)
1198 {
1199 	char			info[80];
1200 	fct_i_event_t		*e;
1201 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1202 	    port->port_fct_private;
1203 
1204 	e = kmem_zalloc(sizeof (fct_i_event_t), KM_NOSLEEP);
1205 
1206 	if (e == NULL) {
1207 		/*
1208 		 * XXX Throw HBA fatal error event
1209 		 */
1210 		(void) snprintf(info, 80,
1211 		    "fct_handle_event: iport-%p, allocation "
1212 		    "of fct_i_event failed", (void *)iport);
1213 		info[79] = 0;
1214 		(void) fct_port_shutdown(iport->iport_port,
1215 		    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1216 		return;
1217 	}
1218 	/* Just queue the event */
1219 	e->event_type = event_id;
1220 	mutex_enter(&iport->iport_worker_lock);
1221 	if (iport->iport_event_head == NULL) {
1222 		iport->iport_event_head = iport->iport_event_tail = e;
1223 	} else {
1224 		iport->iport_event_tail->event_next = e;
1225 		iport->iport_event_tail = e;
1226 	}
1227 	if (IS_WORKER_SLEEPING(iport))
1228 		cv_signal(&iport->iport_worker_cv);
1229 	mutex_exit(&iport->iport_worker_lock);
1230 }
1231 
1232 /*
1233  * Called with iport_lock held as reader.
1234  */
1235 fct_i_remote_port_t *
1236 fct_portid_to_portptr(fct_i_local_port_t *iport, uint32_t portid)
1237 {
1238 	fct_i_remote_port_t	*irp;
1239 
1240 	irp = iport->iport_rp_tb[FCT_PORTID_HASH_FUNC(portid)];
1241 	for (; irp != NULL; irp = irp->irp_next) {
1242 		if (irp->irp_portid == portid)
1243 			return (irp);
1244 	}
1245 
1246 	return (NULL);
1247 
1248 }
1249 
1250 /*
1251  * Called with irp_lock held as writer.
1252  */
1253 void
1254 fct_queue_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1255 {
1256 	int hash_key =
1257 	    FCT_PORTID_HASH_FUNC(irp->irp_portid);
1258 
1259 	irp->irp_next = iport->iport_rp_tb[hash_key];
1260 	iport->iport_rp_tb[hash_key] = irp;
1261 	iport->iport_nrps++;
1262 }
1263 
1264 /*
1265  * Called with irp_lock and iport_lock held as writer.
1266  */
1267 void
1268 fct_deque_rp(fct_i_local_port_t *iport, fct_i_remote_port_t *irp)
1269 {
1270 	fct_i_remote_port_t	*irp_next = NULL;
1271 	fct_i_remote_port_t	*irp_last = NULL;
1272 	int hash_key			  =
1273 	    FCT_PORTID_HASH_FUNC(irp->irp_portid);
1274 
1275 	irp_next = iport->iport_rp_tb[hash_key];
1276 	irp_last = NULL;
1277 	while (irp_next != NULL) {
1278 		if (irp == irp_next) {
1279 			break;
1280 		}
1281 		irp_last = irp_next;
1282 		irp_next = irp_next->irp_next;
1283 	}
1284 
1285 	if (irp_next) {
1286 		if (irp_last == NULL) {
1287 			iport->iport_rp_tb[hash_key] =
1288 			    irp->irp_next;
1289 		} else {
1290 			irp_last->irp_next = irp->irp_next;
1291 		}
1292 		irp->irp_next = NULL;
1293 		iport->iport_nrps--;
1294 	}
1295 }
1296 
1297 int
1298 fct_is_irp_logging_out(fct_i_remote_port_t *irp, int force_implicit)
1299 {
1300 	int logging_out = 0;
1301 
1302 	rw_enter(&irp->irp_lock, RW_WRITER);
1303 	if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1304 		logging_out = 0;
1305 		goto ilo_done;
1306 	}
1307 	if ((irp->irp_els_list == NULL) && (irp->irp_deregister_timer)) {
1308 		if (force_implicit && irp->irp_nonfcp_xchg_count) {
1309 			logging_out = 0;
1310 		} else {
1311 			logging_out = 1;
1312 		}
1313 		goto ilo_done;
1314 	}
1315 	if (irp->irp_els_list) {
1316 		fct_i_cmd_t *icmd;
1317 		/* Last session affecting ELS should be a LOGO */
1318 		for (icmd = irp->irp_els_list; icmd; icmd = icmd->icmd_next) {
1319 			uint8_t op = (ICMD_TO_ELS(icmd))->els_req_payload[0];
1320 			if (op == ELS_OP_LOGO) {
1321 				if (force_implicit) {
1322 					if (icmd->icmd_flags & ICMD_IMPLICIT)
1323 						logging_out = 1;
1324 					else
1325 						logging_out = 0;
1326 				} else {
1327 					logging_out = 1;
1328 				}
1329 			} else if ((op == ELS_OP_PLOGI) ||
1330 			    (op == ELS_OP_PRLI) ||
1331 			    (op == ELS_OP_PRLO) || (op == ELS_OP_TPRLO)) {
1332 				logging_out = 0;
1333 			}
1334 		}
1335 	}
1336 ilo_done:;
1337 	rw_exit(&irp->irp_lock);
1338 
1339 	return (logging_out);
1340 }
1341 
1342 /*
1343  * The force_implicit flag enforces the implicit semantics which may be
1344  * needed if a received logout got stuck e.g. a response to a received
1345  * LOGO never came back from the FCA.
1346  */
1347 int
1348 fct_implicitly_logo_all(fct_i_local_port_t *iport, int force_implicit)
1349 {
1350 	fct_i_remote_port_t	*irp = NULL;
1351 	fct_cmd_t		*cmd = NULL;
1352 	int			 i   = 0;
1353 	int			nports = 0;
1354 
1355 	if (!iport->iport_nrps) {
1356 		return (nports);
1357 	}
1358 
1359 	rw_enter(&iport->iport_lock, RW_WRITER);
1360 	for (i = 0; i < rportid_table_size; i++) {
1361 		irp = iport->iport_rp_tb[i];
1362 		while (irp) {
1363 			if ((!(irp->irp_flags & IRP_PLOGI_DONE)) &&
1364 			    (fct_is_irp_logging_out(irp, force_implicit))) {
1365 				irp = irp->irp_next;
1366 				continue;
1367 			}
1368 
1369 			cmd = fct_create_solels(iport->iport_port, irp->irp_rp,
1370 			    1, ELS_OP_LOGO, 0, fct_logo_cb);
1371 			if (cmd == NULL) {
1372 				stmf_trace(iport->iport_alias,
1373 				    "fct_implictly_logo_all: cmd null");
1374 				rw_exit(&iport->iport_lock);
1375 
1376 				return (nports);
1377 			}
1378 
1379 			fct_post_implicit_logo(cmd);
1380 			nports++;
1381 			irp = irp->irp_next;
1382 		}
1383 	}
1384 	rw_exit(&iport->iport_lock);
1385 
1386 	return (nports);
1387 }
1388 
1389 void
1390 fct_rehash(fct_i_local_port_t *iport)
1391 {
1392 	fct_i_remote_port_t **iport_rp_tb_tmp;
1393 	fct_i_remote_port_t **iport_rp_tb_new;
1394 	fct_i_remote_port_t *irp;
1395 	fct_i_remote_port_t *irp_next;
1396 	int i;
1397 
1398 	iport_rp_tb_new = kmem_zalloc(rportid_table_size *
1399 	    sizeof (fct_i_remote_port_t *), KM_SLEEP);
1400 	rw_enter(&iport->iport_lock, RW_WRITER);
1401 	/* reconstruct the hash table */
1402 	iport_rp_tb_tmp = iport->iport_rp_tb;
1403 	iport->iport_rp_tb = iport_rp_tb_new;
1404 	iport->iport_nrps = 0;
1405 	for (i = 0; i < rportid_table_size; i++) {
1406 		irp = iport_rp_tb_tmp[i];
1407 		while (irp) {
1408 			irp_next = irp->irp_next;
1409 			fct_queue_rp(iport, irp);
1410 			irp = irp_next;
1411 		}
1412 	}
1413 	rw_exit(&iport->iport_lock);
1414 	kmem_free(iport_rp_tb_tmp, rportid_table_size *
1415 	    sizeof (fct_i_remote_port_t *));
1416 
1417 }
1418 
1419 uint8_t
1420 fct_local_port_cleanup_done(fct_i_local_port_t *iport)
1421 {
1422 	fct_i_remote_port_t *irp;
1423 	int i;
1424 
1425 	if (iport->iport_nrps_login)
1426 		return (0);
1427 	/* loop all rps to check if the cmd have already been drained */
1428 	for (i = 0; i < rportid_table_size; i++) {
1429 		irp = iport->iport_rp_tb[i];
1430 		while (irp) {
1431 			if (irp->irp_fcp_xchg_count ||
1432 			    irp->irp_nonfcp_xchg_count)
1433 				return (0);
1434 			irp = irp->irp_next;
1435 		}
1436 	}
1437 	return (1);
1438 }
1439 
1440 fct_cmd_t *
1441 fct_scsi_task_alloc(fct_local_port_t *port, uint16_t rp_handle,
1442 		uint32_t rportid, uint8_t *lun, uint16_t cdb_length,
1443 		uint16_t task_ext)
1444 {
1445 	fct_cmd_t *cmd;
1446 	fct_i_cmd_t *icmd;
1447 	fct_i_local_port_t *iport =
1448 	    (fct_i_local_port_t *)port->port_fct_private;
1449 	fct_i_remote_port_t *irp;
1450 	scsi_task_t *task;
1451 	fct_remote_port_t *rp;
1452 	uint16_t cmd_slot;
1453 
1454 	rw_enter(&iport->iport_lock, RW_READER);
1455 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
1456 		rw_exit(&iport->iport_lock);
1457 		stmf_trace(iport->iport_alias, "cmd alloc called while the port"
1458 		    " was offline");
1459 		return (NULL);
1460 	}
1461 
1462 	if (rp_handle == FCT_HANDLE_NONE) {
1463 		irp = fct_portid_to_portptr(iport, rportid);
1464 		if (irp == NULL) {
1465 			rw_exit(&iport->iport_lock);
1466 			stmf_trace(iport->iport_alias, "cmd received from "
1467 			    "non existent port %x", rportid);
1468 			return (NULL);
1469 		}
1470 	} else {
1471 		if ((rp_handle >= port->port_max_logins) ||
1472 		    ((irp = iport->iport_rp_slots[rp_handle]) == NULL)) {
1473 			rw_exit(&iport->iport_lock);
1474 			stmf_trace(iport->iport_alias, "cmd received from "
1475 			    "invalid port handle %x", rp_handle);
1476 			return (NULL);
1477 		}
1478 	}
1479 	rp = irp->irp_rp;
1480 
1481 	rw_enter(&irp->irp_lock, RW_READER);
1482 	if ((irp->irp_flags & IRP_PRLI_DONE) == 0) {
1483 		rw_exit(&irp->irp_lock);
1484 		rw_exit(&iport->iport_lock);
1485 		stmf_trace(iport->iport_alias, "cmd alloc called while fcp "
1486 		    "login was not done. portid=%x, rp=%p", rp->rp_id, rp);
1487 		return (NULL);
1488 	}
1489 
1490 	mutex_enter(&iport->iport_cached_cmd_lock);
1491 	if ((icmd = iport->iport_cached_cmdlist) != NULL) {
1492 		iport->iport_cached_cmdlist = icmd->icmd_next;
1493 		iport->iport_cached_ncmds--;
1494 		cmd = icmd->icmd_cmd;
1495 	} else {
1496 		icmd = NULL;
1497 	}
1498 	mutex_exit(&iport->iport_cached_cmd_lock);
1499 	if (icmd == NULL) {
1500 		cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_FCP_XCHG,
1501 		    port->port_fca_fcp_cmd_size, 0);
1502 		if (cmd == NULL) {
1503 			rw_exit(&irp->irp_lock);
1504 			rw_exit(&iport->iport_lock);
1505 			stmf_trace(iport->iport_alias, "Ran out of "
1506 			    "memory, port=%p", port);
1507 			return (NULL);
1508 		}
1509 
1510 		icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1511 		icmd->icmd_next = NULL;
1512 		cmd->cmd_port = port;
1513 		atomic_add_32(&iport->iport_total_alloced_ncmds, 1);
1514 	}
1515 
1516 	/*
1517 	 * The accuracy of iport_max_active_ncmds is not important
1518 	 */
1519 	if ((iport->iport_total_alloced_ncmds - iport->iport_cached_ncmds) >
1520 	    iport->iport_max_active_ncmds) {
1521 		iport->iport_max_active_ncmds =
1522 		    iport->iport_total_alloced_ncmds -
1523 		    iport->iport_cached_ncmds;
1524 	}
1525 
1526 	/* Lets get a slot */
1527 	cmd_slot = fct_alloc_cmd_slot(iport, cmd);
1528 	if (cmd_slot == FCT_SLOT_EOL) {
1529 		rw_exit(&irp->irp_lock);
1530 		rw_exit(&iport->iport_lock);
1531 		stmf_trace(iport->iport_alias, "Ran out of xchg resources");
1532 		cmd->cmd_handle = 0;
1533 		fct_cmd_free(cmd);
1534 		return (NULL);
1535 	}
1536 	atomic_add_16(&irp->irp_fcp_xchg_count, 1);
1537 	cmd->cmd_rp = rp;
1538 	icmd->icmd_flags |= ICMD_IN_TRANSITION | ICMD_KNOWN_TO_FCA;
1539 	rw_exit(&irp->irp_lock);
1540 	rw_exit(&iport->iport_lock);
1541 
1542 	icmd->icmd_start_time = ddi_get_lbolt();
1543 
1544 	cmd->cmd_specific = stmf_task_alloc(port->port_lport, irp->irp_session,
1545 	    lun, cdb_length, task_ext);
1546 	if ((task = (scsi_task_t *)cmd->cmd_specific) != NULL) {
1547 		task->task_port_private = cmd;
1548 		return (cmd);
1549 	}
1550 
1551 	fct_cmd_free(cmd);
1552 
1553 	return (NULL);
1554 }
1555 
1556 void
1557 fct_scsi_task_free(scsi_task_t *task)
1558 {
1559 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1560 
1561 	cmd->cmd_comp_status = task->task_completion_status;
1562 	fct_cmd_free(cmd);
1563 }
1564 
1565 void
1566 fct_post_rcvd_cmd(fct_cmd_t *cmd, stmf_data_buf_t *dbuf)
1567 {
1568 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1569 		fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1570 		fct_i_local_port_t *iport =
1571 		    (fct_i_local_port_t *)cmd->cmd_port->port_fct_private;
1572 		fct_i_remote_port_t *irp =
1573 		    (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private;
1574 		scsi_task_t *task = (scsi_task_t *)cmd->cmd_specific;
1575 
1576 		uint16_t irp_task = irp->irp_fcp_xchg_count;
1577 		uint32_t load = iport->iport_total_alloced_ncmds -
1578 		    iport->iport_cached_ncmds;
1579 
1580 		DTRACE_FC_4(scsi__command,
1581 		    fct_cmd_t, cmd,
1582 		    fct_i_local_port_t, iport,
1583 		    scsi_task_t, task,
1584 		    fct_i_remote_port_t, irp);
1585 
1586 		if (load >= iport->iport_task_green_limit) {
1587 			if ((load < iport->iport_task_yellow_limit &&
1588 			    irp_task >= 4) ||
1589 			    (load >= iport->iport_task_yellow_limit &&
1590 			    load < iport->iport_task_red_limit &&
1591 			    irp_task >= 1) ||
1592 			    (load >= iport->iport_task_red_limit))
1593 				task->task_additional_flags |=
1594 				    TASK_AF_PORT_LOAD_HIGH;
1595 		}
1596 		stmf_post_task((scsi_task_t *)cmd->cmd_specific, dbuf);
1597 		atomic_and_32(&icmd->icmd_flags, ~ICMD_IN_TRANSITION);
1598 		return;
1599 	}
1600 	/* We dont need dbuf for other cmds */
1601 	if (dbuf) {
1602 		cmd->cmd_port->port_fds->fds_free_data_buf(
1603 		    cmd->cmd_port->port_fds, dbuf);
1604 		dbuf = NULL;
1605 	}
1606 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1607 		fct_handle_els(cmd);
1608 		return;
1609 	}
1610 	if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) {
1611 		fct_handle_rcvd_abts(cmd);
1612 		return;
1613 	}
1614 
1615 	ASSERT(0);
1616 }
1617 
1618 /*
1619  * This function bypasses fct_handle_els()
1620  */
1621 void
1622 fct_post_implicit_logo(fct_cmd_t *cmd)
1623 {
1624 	fct_local_port_t *port = cmd->cmd_port;
1625 	fct_i_local_port_t *iport =
1626 	    (fct_i_local_port_t *)port->port_fct_private;
1627 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1628 	fct_remote_port_t *rp = cmd->cmd_rp;
1629 	fct_i_remote_port_t *irp = (fct_i_remote_port_t *)rp->rp_fct_private;
1630 
1631 	icmd->icmd_start_time = ddi_get_lbolt();
1632 
1633 	rw_enter(&irp->irp_lock, RW_WRITER);
1634 	atomic_or_32(&icmd->icmd_flags, ICMD_IMPLICIT_CMD_HAS_RESOURCE);
1635 	atomic_add_16(&irp->irp_nonfcp_xchg_count, 1);
1636 	atomic_add_16(&irp->irp_sa_elses_count, 1);
1637 	/*
1638 	 * An implicit LOGO can also be posted to a irp where a PLOGI might
1639 	 * be in process. That PLOGI will reset this flag and decrement the
1640 	 * iport_nrps_login counter.
1641 	 */
1642 	if (irp->irp_flags & IRP_PLOGI_DONE) {
1643 		atomic_add_32(&iport->iport_nrps_login, -1);
1644 	}
1645 	atomic_and_32(&irp->irp_flags, ~(IRP_PLOGI_DONE | IRP_PRLI_DONE));
1646 	atomic_or_32(&icmd->icmd_flags, ICMD_SESSION_AFFECTING);
1647 	fct_post_to_discovery_queue(iport, irp, icmd);
1648 	rw_exit(&irp->irp_lock);
1649 }
1650 
1651 /*
1652  * called with iport_lock held, return the slot number
1653  */
1654 uint16_t
1655 fct_alloc_cmd_slot(fct_i_local_port_t *iport, fct_cmd_t *cmd)
1656 {
1657 	uint16_t cmd_slot;
1658 	uint32_t old, new;
1659 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1660 
1661 	do {
1662 		old = iport->iport_next_free_slot;
1663 		cmd_slot = old & 0xFFFF;
1664 		if (cmd_slot == FCT_SLOT_EOL)
1665 			return (cmd_slot);
1666 		/*
1667 		 * We use high order 16 bits as a counter which keeps on
1668 		 * incrementing to avoid ABA issues with atomic lists.
1669 		 */
1670 		new = ((old + (0x10000)) & 0xFFFF0000);
1671 		new |= iport->iport_cmd_slots[cmd_slot].slot_next;
1672 	} while (atomic_cas_32(&iport->iport_next_free_slot, old, new) != old);
1673 
1674 	atomic_add_16(&iport->iport_nslots_free, -1);
1675 	iport->iport_cmd_slots[cmd_slot].slot_cmd = icmd;
1676 	cmd->cmd_handle = (uint32_t)cmd_slot | 0x80000000 |
1677 	    (((uint32_t)(iport->iport_cmd_slots[cmd_slot].slot_uniq_cntr))
1678 	    << 24);
1679 	return (cmd_slot);
1680 }
1681 
1682 /*
1683  * If icmd is not NULL, irp_lock must be held
1684  */
1685 void
1686 fct_post_to_discovery_queue(fct_i_local_port_t *iport,
1687     fct_i_remote_port_t *irp, fct_i_cmd_t *icmd)
1688 {
1689 	fct_i_cmd_t	**p;
1690 
1691 	ASSERT(!MUTEX_HELD(&iport->iport_worker_lock));
1692 	if (icmd) {
1693 		icmd->icmd_next = NULL;
1694 		for (p = &irp->irp_els_list; *p != NULL;
1695 		    p = &((*p)->icmd_next))
1696 			;
1697 
1698 		*p = icmd;
1699 		atomic_or_32(&icmd->icmd_flags, ICMD_IN_IRP_QUEUE);
1700 	}
1701 
1702 	mutex_enter(&iport->iport_worker_lock);
1703 	if ((irp->irp_flags & IRP_IN_DISCOVERY_QUEUE) == 0) {
1704 
1705 		/*
1706 		 * CAUTION: do not grab local_port/remote_port locks after
1707 		 * grabbing the worker lock.
1708 		 */
1709 		irp->irp_discovery_next = NULL;
1710 		if (iport->iport_rpwe_tail) {
1711 			iport->iport_rpwe_tail->irp_discovery_next = irp;
1712 			iport->iport_rpwe_tail = irp;
1713 		} else {
1714 			iport->iport_rpwe_head = iport->iport_rpwe_tail = irp;
1715 		}
1716 
1717 		atomic_or_32(&irp->irp_flags, IRP_IN_DISCOVERY_QUEUE);
1718 	}
1719 
1720 	/*
1721 	 * We need always signal the port worker irrespective of the fact that
1722 	 * irp is already in discovery queue or not.
1723 	 */
1724 	if (IS_WORKER_SLEEPING(iport)) {
1725 		cv_signal(&iport->iport_worker_cv);
1726 	}
1727 	mutex_exit(&iport->iport_worker_lock);
1728 }
1729 
1730 stmf_status_t
1731 fct_xfer_scsi_data(scsi_task_t *task, stmf_data_buf_t *dbuf, uint32_t ioflags)
1732 {
1733 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1734 
1735 	DTRACE_FC_5(xfer__start,
1736 	    fct_cmd_t, cmd,
1737 	    fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1738 	    scsi_task_t, task,
1739 	    fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1740 	    stmf_data_buf_t, dbuf);
1741 
1742 	return (cmd->cmd_port->port_xfer_scsi_data(cmd, dbuf, ioflags));
1743 }
1744 
1745 void
1746 fct_scsi_data_xfer_done(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags)
1747 {
1748 	fct_i_cmd_t	*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1749 	uint32_t	old, new;
1750 	uint32_t	iof = 0;
1751 
1752 	DTRACE_FC_5(xfer__done,
1753 	    fct_cmd_t, cmd,
1754 	    fct_i_local_port_t, cmd->cmd_port->port_fct_private,
1755 	    scsi_task_t, ((scsi_task_t *)cmd->cmd_specific),
1756 	    fct_i_remote_port_t, cmd->cmd_rp->rp_fct_private,
1757 	    stmf_data_buf_t, dbuf);
1758 
1759 	if (ioflags & FCT_IOF_FCA_DONE) {
1760 		do {
1761 			old = new = icmd->icmd_flags;
1762 			if (old & ICMD_BEING_ABORTED) {
1763 				return;
1764 			}
1765 			new &= ~ICMD_KNOWN_TO_FCA;
1766 		} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1767 		iof = STMF_IOF_LPORT_DONE;
1768 		cmd->cmd_comp_status = dbuf->db_xfer_status;
1769 	}
1770 
1771 	if (icmd->icmd_flags & ICMD_BEING_ABORTED)
1772 		return;
1773 	stmf_data_xfer_done((scsi_task_t *)cmd->cmd_specific, dbuf, iof);
1774 }
1775 
1776 stmf_status_t
1777 fct_send_scsi_status(scsi_task_t *task, uint32_t ioflags)
1778 {
1779 	fct_cmd_t *cmd = (fct_cmd_t *)task->task_port_private;
1780 
1781 	DTRACE_FC_4(scsi__response,
1782 	    fct_cmd_t, cmd,
1783 	    fct_i_local_port_t,
1784 	    (fct_i_local_port_t *)cmd->cmd_port->port_fct_private,
1785 	    scsi_task_t, task,
1786 	    fct_i_remote_port_t,
1787 	    (fct_i_remote_port_t *)cmd->cmd_rp->rp_fct_private);
1788 
1789 	return (cmd->cmd_port->port_send_cmd_response(cmd, ioflags));
1790 }
1791 
1792 void
1793 fct_send_response_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
1794 {
1795 	fct_i_cmd_t	*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1796 	fct_local_port_t *port = cmd->cmd_port;
1797 	fct_i_local_port_t *iport = (fct_i_local_port_t *)
1798 	    port->port_fct_private;
1799 	uint32_t old, new;
1800 
1801 	if ((ioflags & FCT_IOF_FCA_DONE) == 0) {
1802 		/* Until we support confirmed completions, this is an error */
1803 		fct_queue_cmd_for_termination(cmd, s);
1804 		return;
1805 	}
1806 	do {
1807 		old = new = icmd->icmd_flags;
1808 		if (old & ICMD_BEING_ABORTED) {
1809 			return;
1810 		}
1811 		new &= ~ICMD_KNOWN_TO_FCA;
1812 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1813 
1814 	cmd->cmd_comp_status = s;
1815 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1816 		stmf_send_status_done((scsi_task_t *)cmd->cmd_specific, s,
1817 		    STMF_IOF_LPORT_DONE);
1818 		return;
1819 	}
1820 
1821 	if (cmd->cmd_type == FCT_CMD_RCVD_ELS) {
1822 		fct_cmd_free(cmd);
1823 		return;
1824 	} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
1825 		fct_handle_sol_els_completion(iport, icmd);
1826 	} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
1827 		/* Tell the caller that we are done */
1828 		atomic_or_32(&icmd->icmd_flags, ICMD_CMD_COMPLETE);
1829 	} else {
1830 		ASSERT(0);
1831 	}
1832 }
1833 
1834 void
1835 fct_cmd_free(fct_cmd_t *cmd)
1836 {
1837 	char			info[80];
1838 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1839 	fct_local_port_t	*port = cmd->cmd_port;
1840 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
1841 	    port->port_fct_private;
1842 	fct_i_remote_port_t	*irp = NULL;
1843 	int			do_abts_acc = 0;
1844 	uint32_t		old, new;
1845 
1846 	ASSERT(!mutex_owned(&iport->iport_worker_lock));
1847 	/* Give the slot back */
1848 	if (CMD_HANDLE_VALID(cmd->cmd_handle)) {
1849 		uint16_t n = CMD_HANDLE_SLOT_INDEX(cmd->cmd_handle);
1850 		fct_cmd_slot_t *slot;
1851 
1852 		/*
1853 		 * If anything went wrong, grab the lock as writer. This is
1854 		 * probably unnecessary.
1855 		 */
1856 		if ((cmd->cmd_comp_status != FCT_SUCCESS) ||
1857 		    (icmd->icmd_flags & ICMD_ABTS_RECEIVED)) {
1858 			rw_enter(&iport->iport_lock, RW_WRITER);
1859 		} else {
1860 			rw_enter(&iport->iport_lock, RW_READER);
1861 		}
1862 
1863 		if ((icmd->icmd_flags & ICMD_ABTS_RECEIVED) &&
1864 		    (cmd->cmd_link != NULL)) {
1865 			do_abts_acc = 1;
1866 		}
1867 
1868 		/* XXX Validate slot before freeing */
1869 
1870 		slot = &iport->iport_cmd_slots[n];
1871 		slot->slot_uniq_cntr++;
1872 		slot->slot_cmd = NULL;
1873 		do {
1874 			old = iport->iport_next_free_slot;
1875 			slot->slot_next = old & 0xFFFF;
1876 			new = (old + 0x10000) & 0xFFFF0000;
1877 			new |= slot->slot_no;
1878 		} while (atomic_cas_32(&iport->iport_next_free_slot,
1879 		    old, new) != old);
1880 		cmd->cmd_handle = 0;
1881 		atomic_add_16(&iport->iport_nslots_free, 1);
1882 		if (cmd->cmd_rp) {
1883 			irp = (fct_i_remote_port_t *)
1884 			    cmd->cmd_rp->rp_fct_private;
1885 			if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
1886 				atomic_add_16(&irp->irp_fcp_xchg_count, -1);
1887 			else
1888 				atomic_add_16(&irp->irp_nonfcp_xchg_count, -1);
1889 		}
1890 		rw_exit(&iport->iport_lock);
1891 	} else if ((icmd->icmd_flags & ICMD_IMPLICIT) &&
1892 	    (icmd->icmd_flags & ICMD_IMPLICIT_CMD_HAS_RESOURCE)) {
1893 		/* for implicit cmd, no cmd slot is used */
1894 		if (cmd->cmd_rp) {
1895 			irp = (fct_i_remote_port_t *)
1896 			    cmd->cmd_rp->rp_fct_private;
1897 			if (cmd->cmd_type == FCT_CMD_FCP_XCHG)
1898 				atomic_add_16(&irp->irp_fcp_xchg_count, -1);
1899 			else
1900 				atomic_add_16(&irp->irp_nonfcp_xchg_count, -1);
1901 		}
1902 	}
1903 
1904 	if (do_abts_acc) {
1905 		fct_cmd_t *lcmd = cmd->cmd_link;
1906 		fct_fill_abts_acc(lcmd);
1907 		if (port->port_send_cmd_response(lcmd,
1908 		    FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
1909 			/*
1910 			 * XXX Throw HBA fatal error event
1911 			 * Later shutdown svc will terminate the ABTS in the end
1912 			 */
1913 			(void) snprintf(info, 80,
1914 			    "fct_cmd_free: iport-%p, ABTS_ACC"
1915 			    " port_send_cmd_response failed", (void *)iport);
1916 			info[79] = 0;
1917 			(void) fct_port_shutdown(iport->iport_port,
1918 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
1919 			return;
1920 		} else {
1921 			fct_cmd_free(lcmd);
1922 			cmd->cmd_link = NULL;
1923 		}
1924 	}
1925 
1926 	/* Free the cmd */
1927 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
1928 		if (iport->iport_cached_ncmds < max_cached_ncmds) {
1929 			icmd->icmd_flags = 0;
1930 			mutex_enter(&iport->iport_cached_cmd_lock);
1931 			icmd->icmd_next = iport->iport_cached_cmdlist;
1932 			iport->iport_cached_cmdlist = icmd;
1933 			iport->iport_cached_ncmds++;
1934 			mutex_exit(&iport->iport_cached_cmd_lock);
1935 		} else {
1936 			atomic_add_32(&iport->iport_total_alloced_ncmds, -1);
1937 			fct_free(cmd);
1938 		}
1939 	} else {
1940 		fct_free(cmd);
1941 	}
1942 }
1943 
1944 /* ARGSUSED */
1945 stmf_status_t
1946 fct_scsi_abort(stmf_local_port_t *lport, int abort_cmd, void *arg,
1947 							uint32_t flags)
1948 {
1949 	stmf_status_t ret = STMF_SUCCESS;
1950 	scsi_task_t *task;
1951 	fct_cmd_t *cmd;
1952 	fct_i_cmd_t *icmd;
1953 	fct_local_port_t *port;
1954 	uint32_t old, new;
1955 
1956 	ASSERT(abort_cmd == STMF_LPORT_ABORT_TASK);
1957 
1958 	task = (scsi_task_t *)arg;
1959 	cmd = (fct_cmd_t *)task->task_port_private;
1960 	icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
1961 	port = (fct_local_port_t *)lport->lport_port_private;
1962 
1963 	do {
1964 		old = new = icmd->icmd_flags;
1965 		if ((old & ICMD_KNOWN_TO_FCA) == 0)
1966 			return (STMF_NOT_FOUND);
1967 		ASSERT((old & ICMD_FCA_ABORT_CALLED) == 0);
1968 		new |= ICMD_BEING_ABORTED | ICMD_FCA_ABORT_CALLED;
1969 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
1970 	ret = port->port_abort_cmd(port, cmd, 0);
1971 	if ((ret == FCT_NOT_FOUND) || (ret == FCT_ABORT_SUCCESS)) {
1972 		atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
1973 	} else if (ret == FCT_BUSY) {
1974 		atomic_and_32(&icmd->icmd_flags, ~ICMD_FCA_ABORT_CALLED);
1975 	}
1976 
1977 	return (ret);
1978 }
1979 
1980 void
1981 fct_ctl(struct stmf_local_port *lport, int cmd, void *arg)
1982 {
1983 	fct_local_port_t *port;
1984 	fct_i_local_port_t *iport;
1985 	stmf_change_status_t st;
1986 	stmf_change_status_t *pst;
1987 
1988 	ASSERT((cmd == STMF_CMD_LPORT_ONLINE) ||
1989 	    (cmd == STMF_ACK_LPORT_ONLINE_COMPLETE) ||
1990 	    (cmd == STMF_CMD_LPORT_OFFLINE) ||
1991 	    (cmd == STMF_ACK_LPORT_OFFLINE_COMPLETE) ||
1992 	    (cmd == FCT_CMD_PORT_ONLINE_COMPLETE) ||
1993 	    (cmd == FCT_CMD_PORT_OFFLINE_COMPLETE));
1994 
1995 	port = (fct_local_port_t *)lport->lport_port_private;
1996 	pst = (stmf_change_status_t *)arg;
1997 	st.st_completion_status = STMF_SUCCESS;
1998 	st.st_additional_info = NULL;
1999 
2000 	iport = (fct_i_local_port_t *)port->port_fct_private;
2001 	/*
2002 	 * We are mostly a passthrough, except during offline.
2003 	 */
2004 	switch (cmd) {
2005 	case STMF_CMD_LPORT_ONLINE:
2006 		if (iport->iport_state == FCT_STATE_ONLINE)
2007 			st.st_completion_status = STMF_ALREADY;
2008 		else if (iport->iport_state != FCT_STATE_OFFLINE)
2009 			st.st_completion_status = STMF_INVALID_ARG;
2010 		if (st.st_completion_status != STMF_SUCCESS) {
2011 			(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport,
2012 			    &st);
2013 			break;
2014 		}
2015 		iport->iport_state_not_acked = 1;
2016 		iport->iport_state = FCT_STATE_ONLINING;
2017 		port->port_ctl(port, FCT_CMD_PORT_ONLINE, arg);
2018 		break;
2019 	case FCT_CMD_PORT_ONLINE_COMPLETE:
2020 		ASSERT(iport->iport_state == FCT_STATE_ONLINING);
2021 		if (pst->st_completion_status != FCT_SUCCESS) {
2022 			iport->iport_state = FCT_STATE_OFFLINE;
2023 			iport->iport_state_not_acked = 0;
2024 		} else {
2025 			iport->iport_state = FCT_STATE_ONLINE;
2026 		}
2027 		(void) stmf_ctl(STMF_CMD_LPORT_ONLINE_COMPLETE, lport, arg);
2028 		break;
2029 	case STMF_ACK_LPORT_ONLINE_COMPLETE:
2030 		ASSERT(iport->iport_state == FCT_STATE_ONLINE);
2031 		iport->iport_state_not_acked = 0;
2032 		port->port_ctl(port, FCT_ACK_PORT_ONLINE_COMPLETE, arg);
2033 		break;
2034 
2035 	case STMF_CMD_LPORT_OFFLINE:
2036 		if (iport->iport_state == FCT_STATE_OFFLINE)
2037 			st.st_completion_status = STMF_ALREADY;
2038 		else if (iport->iport_state != FCT_STATE_ONLINE)
2039 			st.st_completion_status = STMF_INVALID_ARG;
2040 		if (st.st_completion_status != STMF_SUCCESS) {
2041 			(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2042 			    &st);
2043 			break;
2044 		}
2045 		iport->iport_state_not_acked = 1;
2046 		iport->iport_state = FCT_STATE_OFFLINING;
2047 		port->port_ctl(port, FCT_CMD_PORT_OFFLINE, arg);
2048 		break;
2049 	case FCT_CMD_PORT_OFFLINE_COMPLETE:
2050 		ASSERT(iport->iport_state == FCT_STATE_OFFLINING);
2051 		if (pst->st_completion_status != FCT_SUCCESS) {
2052 			iport->iport_state = FCT_STATE_ONLINE;
2053 			iport->iport_state_not_acked = 0;
2054 			(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE, lport,
2055 			    pst);
2056 			break;
2057 		}
2058 
2059 		/*
2060 		 * If FCA's offline was successful, we dont tell stmf yet.
2061 		 * Becasue now we have to do the cleanup before we go upto
2062 		 * stmf. That cleanup is done by the worker thread.
2063 		 */
2064 
2065 		/* FCA is offline, post a link down, its harmless anyway */
2066 		fct_handle_event(port, FCT_EVENT_LINK_DOWN, 0, 0);
2067 
2068 		/* Trigger port offline processing by the worker */
2069 		iport->iport_offline_prstate = FCT_OPR_START;
2070 		break;
2071 	case STMF_ACK_LPORT_OFFLINE_COMPLETE:
2072 		ASSERT(iport->iport_state == FCT_STATE_OFFLINE);
2073 		iport->iport_state_not_acked = 0;
2074 		port->port_ctl(port, FCT_ACK_PORT_OFFLINE_COMPLETE, arg);
2075 		break;
2076 	}
2077 }
2078 
2079 /* ARGSUSED */
2080 stmf_status_t
2081 fct_info(uint32_t cmd, stmf_local_port_t *lport, void *arg, uint8_t *buf,
2082 						uint32_t *bufsizep)
2083 {
2084 	return (STMF_NOT_SUPPORTED);
2085 }
2086 
2087 /*
2088  * implicit: if it's true, it means it will only be used in fct module, or else
2089  * it will be sent to the link.
2090  */
2091 fct_cmd_t *
2092 fct_create_solels(fct_local_port_t *port, fct_remote_port_t *rp, int implicit,
2093     uchar_t elsop, uint32_t wkdid, fct_icmd_cb_t icmdcb)
2094 {
2095 	fct_cmd_t		*cmd	= NULL;
2096 	fct_i_cmd_t		*icmd	= NULL;
2097 	fct_els_t		*els	= NULL;
2098 	fct_i_remote_port_t	*irp	= NULL;
2099 	uint8_t			*p	= NULL;
2100 	uint32_t		 ptid	= 0;
2101 
2102 	cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_SOL_ELS,
2103 	    port->port_fca_sol_els_private_size, 0);
2104 	if (!cmd) {
2105 		return (NULL);
2106 	}
2107 
2108 	if (rp) {
2109 		irp = RP_TO_IRP(rp);
2110 	} else if (((irp = fct_portid_to_portptr(PORT_TO_IPORT(port),
2111 	    wkdid)) == NULL) && (elsop != ELS_OP_PLOGI)) {
2112 		stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2113 		    "fct_create_solels: Must PLOGI to %x first", wkdid);
2114 		fct_free(cmd);
2115 		return (NULL);
2116 	}
2117 
2118 	cmd->cmd_port	= port;
2119 	cmd->cmd_oxid	= PTR2INT(cmd, uint16_t);
2120 	cmd->cmd_rxid	= 0xFFFF;
2121 	cmd->cmd_handle = 0;
2122 	icmd		= CMD_TO_ICMD(cmd);
2123 	els		= ICMD_TO_ELS(icmd);
2124 	icmd->icmd_cb	= icmdcb;
2125 	if (irp) {
2126 		cmd->cmd_rp	   = irp->irp_rp;
2127 		cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2128 		cmd->cmd_rportid   = irp->irp_rp->rp_id;
2129 	} else {
2130 		cmd->cmd_rp_handle = FCT_HANDLE_NONE;
2131 		cmd->cmd_rportid   = wkdid;
2132 	}
2133 	cmd->cmd_lportid = (PORT_TO_IPORT(port))->iport_link_info.portid;
2134 
2135 	if (implicit) {
2136 		/*
2137 		 * Since we will not send it to FCA, so we only allocate space
2138 		 */
2139 		ASSERT(elsop & (ELS_OP_LOGO | ELS_OP_PLOGI));
2140 		icmd->icmd_flags |= ICMD_IMPLICIT;
2141 		if (elsop == ELS_OP_LOGO) {
2142 			/*
2143 			 * Handling implicit LOGO should dependent on as less
2144 			 * as resources. So a trick here.
2145 			 */
2146 			els->els_req_size = 1;
2147 			els->els_req_payload = cmd->cmd_fca_private;
2148 		} else {
2149 			els->els_req_alloc_size = els->els_req_size = 116;
2150 			els->els_resp_alloc_size = els->els_resp_size = 116;
2151 			els->els_req_payload = (uint8_t *)
2152 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2153 			els->els_resp_payload = (uint8_t *)
2154 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2155 		}
2156 	} else {
2157 		/*
2158 		 * Allocate space for its request and response
2159 		 * Fill the request payload according to spec.
2160 		 */
2161 		switch (elsop) {
2162 		case ELS_OP_LOGO:
2163 			els->els_resp_alloc_size = els->els_resp_size = 4;
2164 			els->els_resp_payload = (uint8_t *)kmem_zalloc(
2165 			    els->els_resp_size, KM_SLEEP);
2166 			els->els_req_alloc_size = els->els_req_size = 16;
2167 			els->els_req_payload = (uint8_t *)kmem_zalloc(
2168 			    els->els_req_size, KM_SLEEP);
2169 			ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2170 			fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2171 			bcopy(port->port_pwwn, els->els_req_payload + 8, 8);
2172 			break;
2173 
2174 		case ELS_OP_RSCN:
2175 			els->els_resp_alloc_size = els->els_resp_size = 4;
2176 			els->els_resp_payload = (uint8_t *)kmem_zalloc(
2177 			    els->els_resp_size, KM_SLEEP);
2178 			els->els_req_size = els->els_req_alloc_size = 8;
2179 			els->els_req_payload = (uint8_t *)kmem_zalloc(
2180 			    els->els_req_size, KM_SLEEP);
2181 			els->els_req_payload[1] = 0x04;
2182 			els->els_req_payload[3] = 0x08;
2183 			els->els_req_payload[4] |= 0x80;
2184 			ptid = PORT_TO_IPORT(port)->iport_link_info.portid;
2185 			fct_value_to_netbuf(ptid, els->els_req_payload + 5, 3);
2186 			break;
2187 
2188 		case ELS_OP_PLOGI:
2189 			els->els_resp_alloc_size = els->els_resp_size = 116;
2190 			els->els_resp_payload = (uint8_t *)
2191 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2192 			els->els_req_alloc_size = els->els_req_size = 116;
2193 			p = els->els_req_payload = (uint8_t *)
2194 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2195 			bcopy(port->port_pwwn, p + 20, 8);
2196 			bcopy(port->port_nwwn, p + 28, 8);
2197 
2198 			/*
2199 			 * Common service parameters
2200 			 */
2201 			p[0x04] = 0x09;		/* high version */
2202 			p[0x05] = 0x08;		/* low version */
2203 			p[0x06] = 0x00;		/* BB credit: 0x0065 */
2204 			p[0x07] = 0x65;
2205 
2206 			/* CI0: Continuously Increasing Offset - 1 */
2207 			/* RRO: Randomly Relative Offset - 0 */
2208 			/* VVV: Vendor Version Level - 0 */
2209 			/* N-F: N or F Port Payload Sender - 0 (N) */
2210 			/* BBM: BB Credit Management - 0 (Normal) */
2211 			p[0x08] = 0x80;
2212 			p[0x09] = 0x00;
2213 
2214 			/* Max RX size */
2215 			p[0x0A] = 0x08;
2216 			p[0x0B] = 0x00;
2217 
2218 			/* NPTCS: N Port Total Concurrent Sequences - 0x0000 */
2219 			p[0x0C] = 0x00;
2220 			p[0x0D] = 0x00;
2221 
2222 			/* ROIC: Relative Offset By Info - 0xFFFF */
2223 			p[0x0E] = 0xFF;
2224 			p[0x0F] = 0xFF;
2225 
2226 			/* EDTOV: Error Detect Timeout - 0x000007D0 */
2227 			p[0x10] = 0x00;
2228 			p[0x11] = 0x00;
2229 			p[0x12] = 0x07;
2230 			p[0x13] = 0xD0;
2231 
2232 			/*
2233 			 * Class-3 Parameters
2234 			 */
2235 			/* C3-VAL: Class 3 Value - 1 */
2236 			/* C3-XID: X_ID Reassignment - 0 */
2237 			/* C3-IPA: Initial Process Assignment */
2238 			/* C3-AI-DCC: Data compression capable */
2239 			/* C3-AI-DC-HB: Data compression history buffer size */
2240 			/* C3-AI-DCE: Data encrytion capable */
2241 			/* C3-AI-CSC: Clock synchronization capable */
2242 			/* C3-ErrPol: Error pliciy */
2243 			/* C3-CatSeq: Information Cat. Per Sequence */
2244 			/* C3-AR-DCC: */
2245 			/* C3-AR-DC-HB: */
2246 			/* C3-AR-DCE: */
2247 			/* C3-AR-CSC */
2248 			p[0x44] = 0x80;
2249 			p[0x45] = 0x00;
2250 			p[0x46] = 0x00;
2251 			p[0x47] = 0x00;
2252 			p[0x48] = 0x00;
2253 			p[0x49] = 0x00;
2254 
2255 			/* C3-RxSize: Class 3 receive data size */
2256 			p[0x4A] = 0x08;
2257 			p[0x4B] = 0x00;
2258 
2259 			/* C3-ConSeq: Class 3 Concourrent sequences */
2260 			p[0x4C] = 0x00;
2261 			p[0x4D] = 0xFF;
2262 
2263 			/* C3-OSPE: Class 3 open sequence per exchange */
2264 			p[0x50] = 0x00;
2265 			p[0x51] = 0x01;
2266 
2267 			break;
2268 
2269 		case ELS_OP_SCR:
2270 			els->els_resp_alloc_size = els->els_resp_size = 4;
2271 			els->els_resp_payload = (uint8_t *)
2272 			    kmem_zalloc(els->els_resp_size, KM_SLEEP);
2273 			els->els_req_alloc_size = els->els_req_size = 8;
2274 			p = els->els_req_payload = (uint8_t *)
2275 			    kmem_zalloc(els->els_req_size, KM_SLEEP);
2276 			p[7] = FC_SCR_FULL_REGISTRATION;
2277 			break;
2278 
2279 		default:
2280 			ASSERT(0);
2281 		}
2282 	}
2283 
2284 	els->els_req_payload[0] = elsop;
2285 	return (cmd);
2286 }
2287 
2288 fct_cmd_t *
2289 fct_create_solct(fct_local_port_t *port, fct_remote_port_t *query_rp,
2290     uint16_t ctop, fct_icmd_cb_t icmdcb)
2291 {
2292 	fct_cmd_t		*cmd	 = NULL;
2293 	fct_i_cmd_t		*icmd	 = NULL;
2294 	fct_sol_ct_t		*ct	 = NULL;
2295 	uint8_t			*p	 = NULL;
2296 	fct_i_remote_port_t	*irp	 = NULL;
2297 	fct_i_local_port_t	*iport	 = NULL;
2298 	char			*nname	 = NULL;
2299 	int			 namelen = 0;
2300 
2301 	/*
2302 	 * Allocate space
2303 	 */
2304 	cmd = fct_alloc(FCT_STRUCT_CMD_SOL_CT,
2305 	    port->port_fca_sol_ct_private_size, 0);
2306 	if (!cmd) {
2307 		return (NULL);
2308 	}
2309 
2310 	/*
2311 	 * We should have PLOGIed to the name server (0xFFFFFC)
2312 	 * Caution: this irp is not query_rp->rp_fct_private.
2313 	 */
2314 	irp = fct_portid_to_portptr((fct_i_local_port_t *)
2315 	    port->port_fct_private, FS_NAME_SERVER);
2316 	if (irp == NULL) {
2317 		stmf_trace(PORT_TO_IPORT(port)->iport_alias,
2318 		    "fct_create_solct: Must PLOGI name server first");
2319 		fct_free(cmd);
2320 		return (NULL);
2321 	}
2322 
2323 	cmd->cmd_port	   = port;
2324 	cmd->cmd_rp	   = irp->irp_rp;
2325 	cmd->cmd_rp_handle = irp->irp_rp->rp_handle;
2326 	cmd->cmd_rportid   = irp->irp_rp->rp_id;
2327 	cmd->cmd_lportid   = (PORT_TO_IPORT(port))->iport_link_info.portid;
2328 	cmd->cmd_oxid	   = PTR2INT(cmd, uint16_t);
2329 	cmd->cmd_rxid	   = 0xFFFF;
2330 	cmd->cmd_handle	   = 0;
2331 	icmd		   = CMD_TO_ICMD(cmd);
2332 	ct		   = ICMD_TO_CT(icmd);
2333 	icmd->icmd_cb	   = icmdcb;
2334 	iport		   = ICMD_TO_IPORT(icmd);
2335 
2336 	switch (ctop) {
2337 	case NS_GSNN_NN:
2338 		/*
2339 		 * Allocate max space for its sybolic name
2340 		 */
2341 		ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2342 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2343 		    KM_SLEEP);
2344 
2345 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2346 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2347 		    KM_SLEEP);
2348 
2349 		bcopy(query_rp->rp_nwwn, p + 16, 8);
2350 		break;
2351 
2352 	case NS_RNN_ID:
2353 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2354 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2355 		    KM_SLEEP);
2356 		ct->ct_req_size = ct->ct_req_alloc_size = 28;
2357 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2358 		    KM_SLEEP);
2359 
2360 		/*
2361 		 * Port Identifier
2362 		 */
2363 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2364 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2365 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2366 
2367 		/*
2368 		 * Node Name
2369 		 */
2370 		bcopy(port->port_nwwn, p + 20, 8);
2371 		break;
2372 
2373 	case NS_RCS_ID:
2374 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2375 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2376 		    KM_SLEEP);
2377 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2378 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2379 		    KM_SLEEP);
2380 
2381 		/*
2382 		 * Port Identifier
2383 		 */
2384 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2385 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2386 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2387 
2388 		/*
2389 		 * Class of Service
2390 		 */
2391 		*(p + 23) = FC_NS_CLASS3;
2392 		break;
2393 
2394 	case NS_RFT_ID:
2395 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2396 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2397 		    KM_SLEEP);
2398 		ct->ct_req_size = ct->ct_req_alloc_size = 52;
2399 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2400 		    KM_SLEEP);
2401 
2402 		/*
2403 		 * Port Identifier
2404 		 */
2405 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2406 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2407 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2408 
2409 		/*
2410 		 * FC-4 Protocol Types
2411 		 */
2412 		*(p + 22) = 0x1;	/* 0x100 */
2413 		break;
2414 
2415 	case NS_RSPN_ID:
2416 		/*
2417 		 * If we get here, port->port_sym_port_name is always not NULL.
2418 		 */
2419 		ASSERT(port->port_sym_port_name);
2420 		namelen = strlen(port->port_sym_port_name);
2421 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2422 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2423 		    KM_SLEEP);
2424 		ct->ct_req_size = ct->ct_req_alloc_size =
2425 		    (21 + namelen + 3) & ~3;
2426 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2427 		    KM_SLEEP);
2428 
2429 		/*
2430 		 * Port Identifier
2431 		 */
2432 		p[17] = (iport->iport_link_info.portid >> 16) & 0xFF;
2433 		p[18] = (iport->iport_link_info.portid >>  8) & 0xFF;
2434 		p[19] = (iport->iport_link_info.portid >>  0) & 0xFF;
2435 
2436 		/*
2437 		 * String length
2438 		 */
2439 		p[20] = namelen;
2440 
2441 		/*
2442 		 * Symbolic port name
2443 		 */
2444 		bcopy(port->port_sym_port_name, p + 21, ct->ct_req_size - 21);
2445 		break;
2446 
2447 	case NS_RSNN_NN:
2448 		namelen = port->port_sym_node_name == NULL ?
2449 		    strlen(utsname.nodename) :
2450 		    strlen(port->port_sym_node_name);
2451 		nname = port->port_sym_node_name == NULL ?
2452 		    utsname.nodename : port->port_sym_node_name;
2453 
2454 		ct->ct_resp_alloc_size = ct->ct_resp_size = 16;
2455 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2456 		    KM_SLEEP);
2457 		ct->ct_req_size = ct->ct_req_alloc_size =
2458 		    (25 + namelen + 3) & ~3;
2459 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2460 		    KM_SLEEP);
2461 
2462 		/*
2463 		 * Node name
2464 		 */
2465 		bcopy(port->port_nwwn, p + 16, 8);
2466 
2467 		/*
2468 		 * String length
2469 		 */
2470 		p[24] = namelen;
2471 
2472 		/*
2473 		 * Symbolic node name
2474 		 */
2475 		bcopy(nname, p + 25, ct->ct_req_size - 25);
2476 		break;
2477 
2478 	case NS_GSPN_ID:
2479 		ct->ct_resp_alloc_size = ct->ct_resp_size = 272;
2480 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2481 		    KM_SLEEP);
2482 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2483 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2484 		    KM_SLEEP);
2485 		/*
2486 		 * Port Identifier
2487 		 */
2488 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2489 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2490 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2491 		break;
2492 
2493 	case NS_GCS_ID:
2494 		ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2495 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2496 		    KM_SLEEP);
2497 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2498 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2499 		    KM_SLEEP);
2500 		/*
2501 		 * Port Identifier
2502 		 */
2503 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2504 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2505 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2506 		break;
2507 
2508 	case NS_GFT_ID:
2509 		ct->ct_resp_alloc_size = ct->ct_resp_size = 48;
2510 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2511 		    KM_SLEEP);
2512 		ct->ct_req_size = ct->ct_req_alloc_size = 20;
2513 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2514 		    KM_SLEEP);
2515 		/*
2516 		 * Port Identifier
2517 		 */
2518 		p[17] = (query_rp->rp_id >> 16) & 0xFF;
2519 		p[18] = (query_rp->rp_id >>  8) & 0xFF;
2520 		p[19] = (query_rp->rp_id >>  0) & 0xFF;
2521 		break;
2522 
2523 	case NS_GID_PN:
2524 		ct->ct_resp_alloc_size = ct->ct_resp_size = 20;
2525 		ct->ct_resp_payload = (uint8_t *)kmem_zalloc(ct->ct_resp_size,
2526 		    KM_SLEEP);
2527 
2528 		ct->ct_req_size = ct->ct_req_alloc_size = 24;
2529 		p = ct->ct_req_payload = (uint8_t *)kmem_zalloc(ct->ct_req_size,
2530 		    KM_SLEEP);
2531 
2532 		bcopy(query_rp->rp_pwwn, p + 16, 8);
2533 		break;
2534 
2535 	default:
2536 		/* CONSTCOND */
2537 		ASSERT(0);
2538 	}
2539 
2540 	FCT_FILL_CTIU_PREAMPLE(p, ctop);
2541 	return (cmd);
2542 }
2543 
2544 /*
2545  * Cmd can only be solicited CT/ELS. They will be dispatched to the discovery
2546  * queue eventually too.
2547  * We queue solicited cmds here to track solicited cmds and to take full use
2548  * of single thread mechanism.
2549  * But in current implmentation, we don't use  this mechanism on SOL_CT, PLOGI.
2550  * To avoid to interrupt current flow, ICMD_IN_SOLCMD_QUEUE is used here.
2551  */
2552 void
2553 fct_post_to_solcmd_queue(fct_local_port_t *port, fct_cmd_t *cmd)
2554 {
2555 	fct_i_local_port_t	*iport	= (fct_i_local_port_t *)
2556 	    port->port_fct_private;
2557 	fct_i_cmd_t *icmd		= (fct_i_cmd_t *)cmd->cmd_fct_private;
2558 
2559 	mutex_enter(&iport->iport_worker_lock);
2560 	icmd->icmd_solcmd_next = iport->iport_solcmd_queue;
2561 	iport->iport_solcmd_queue = icmd;
2562 	atomic_or_32(&icmd->icmd_flags, ICMD_IN_SOLCMD_QUEUE | ICMD_SOLCMD_NEW);
2563 	if (IS_WORKER_SLEEPING(iport)) {
2564 		cv_signal(&iport->iport_worker_cv);
2565 	}
2566 	mutex_exit(&iport->iport_worker_lock);
2567 }
2568 
2569 /* ARGSUSED */
2570 void
2571 fct_event_handler(stmf_local_port_t *lport, int eventid, void *arg,
2572     uint32_t flags)
2573 {
2574 	fct_local_port_t	*port  = (fct_local_port_t *)
2575 	    lport->lport_port_private;
2576 	fct_i_local_port_t	*iport = (fct_i_local_port_t *)
2577 	    port->port_fct_private;
2578 	stmf_scsi_session_t	*ss;
2579 	fct_i_remote_port_t	*irp;
2580 
2581 	switch (eventid) {
2582 	case LPORT_EVENT_INITIAL_LUN_MAPPED:
2583 		ss = (stmf_scsi_session_t *)arg;
2584 		irp = (fct_i_remote_port_t *)ss->ss_port_private;
2585 		stmf_trace(iport->iport_alias,
2586 		    "Initial LUN mapped to session ss-%p, irp-%p", ss, irp);
2587 		break;
2588 
2589 	default:
2590 		stmf_trace(iport->iport_alias,
2591 		    "Unknown event received, %d", eventid);
2592 	}
2593 }
2594 
2595 void
2596 fct_send_cmd_done(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2597 {
2598 	/* XXX For now just call send_resp_done() */
2599 	fct_send_response_done(cmd, s, ioflags);
2600 }
2601 
2602 void
2603 fct_cmd_fca_aborted(fct_cmd_t *cmd, fct_status_t s, uint32_t ioflags)
2604 {
2605 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2606 	char			info[160];
2607 	unsigned long long	st;
2608 
2609 	st = s;	/* To make gcc happy */
2610 	ASSERT(icmd->icmd_flags & ICMD_BEING_ABORTED);
2611 	if ((((s != FCT_ABORT_SUCCESS) && (s != FCT_NOT_FOUND))) ||
2612 	    ((ioflags & FCT_IOF_FCA_DONE) == 0)) {
2613 		(void) snprintf(info, 160, "fct_cmd_fca_aborted: cmd-%p, "
2614 		    "s-%llx, iofalgs-%x", (void *)cmd, st, ioflags);
2615 		info[159] = 0;
2616 		(void) fct_port_shutdown(cmd->cmd_port,
2617 		    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2618 		return;
2619 	}
2620 
2621 	atomic_and_32(&icmd->icmd_flags, ~ICMD_KNOWN_TO_FCA);
2622 	/* For non FCP Rest of the work is done by the terminator */
2623 	/* For FCP stuff just call stmf */
2624 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2625 		stmf_task_lport_aborted((scsi_task_t *)cmd->cmd_specific,
2626 		    s, STMF_IOF_LPORT_DONE);
2627 	}
2628 }
2629 
2630 /*
2631  * FCA drivers will use it, when they want to abort some FC transactions
2632  * due to lack of resource.
2633  */
2634 uint16_t
2635 fct_get_rp_handle(fct_local_port_t *port, uint32_t rportid)
2636 {
2637 	fct_i_remote_port_t	*irp;
2638 
2639 	irp = fct_portid_to_portptr(
2640 	    (fct_i_local_port_t *)(port->port_fct_private), rportid);
2641 	if (irp == NULL) {
2642 		return (0xFFFF);
2643 	} else {
2644 		return (irp->irp_rp->rp_handle);
2645 	}
2646 }
2647 
2648 fct_cmd_t *
2649 fct_handle_to_cmd(fct_local_port_t *port, uint32_t fct_handle)
2650 {
2651 	fct_cmd_slot_t *slot;
2652 	uint16_t ndx;
2653 
2654 	if (!CMD_HANDLE_VALID(fct_handle))
2655 		return (NULL);
2656 	if ((ndx = CMD_HANDLE_SLOT_INDEX(fct_handle)) >= port->port_max_xchges)
2657 		return (NULL);
2658 
2659 	slot = &((fct_i_local_port_t *)port->port_fct_private)->iport_cmd_slots[
2660 	    ndx];
2661 
2662 	if ((slot->slot_uniq_cntr | 0x80) != (fct_handle >> 24))
2663 		return (NULL);
2664 	return (slot->slot_cmd->icmd_cmd);
2665 }
2666 
2667 void
2668 fct_queue_scsi_task_for_termination(fct_cmd_t *cmd, fct_status_t s)
2669 {
2670 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2671 
2672 	uint32_t old, new;
2673 
2674 	do {
2675 		old = icmd->icmd_flags;
2676 		if ((old & (ICMD_BEING_ABORTED | ICMD_KNOWN_TO_FCA)) !=
2677 		    ICMD_KNOWN_TO_FCA)
2678 			return;
2679 		new = old | ICMD_BEING_ABORTED;
2680 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2681 	stmf_abort(STMF_QUEUE_TASK_ABORT, (scsi_task_t *)cmd->cmd_specific,
2682 	    s, NULL);
2683 }
2684 
2685 void
2686 fct_fill_abts_acc(fct_cmd_t *cmd)
2687 {
2688 	fct_rcvd_abts_t *abts = (fct_rcvd_abts_t *)cmd->cmd_specific;
2689 	uint8_t *p;
2690 
2691 	abts->abts_resp_rctl = BLS_OP_BA_ACC;
2692 	p = abts->abts_resp_payload;
2693 	bzero(p, 12);
2694 	*((uint16_t *)(p+4)) = BE_16(cmd->cmd_oxid);
2695 	*((uint16_t *)(p+6)) = BE_16(cmd->cmd_rxid);
2696 	p[10] = p[11] = 0xff;
2697 }
2698 
2699 void
2700 fct_handle_rcvd_abts(fct_cmd_t *cmd)
2701 {
2702 	char			info[80];
2703 	fct_local_port_t	*port = cmd->cmd_port;
2704 	fct_i_local_port_t	*iport =
2705 	    (fct_i_local_port_t *)port->port_fct_private;
2706 	fct_i_cmd_t		*icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2707 	fct_i_remote_port_t	*irp;
2708 	fct_cmd_t		*c = NULL;
2709 	fct_i_cmd_t		*ic = NULL;
2710 	int			found = 0;
2711 	int			i;
2712 
2713 	icmd->icmd_start_time = ddi_get_lbolt();
2714 	icmd->icmd_flags |= ICMD_KNOWN_TO_FCA;
2715 
2716 	rw_enter(&iport->iport_lock, RW_WRITER);
2717 	/* Make sure local port is sane */
2718 	if ((iport->iport_link_state & S_LINK_ONLINE) == 0) {
2719 		rw_exit(&iport->iport_lock);
2720 		stmf_trace(iport->iport_alias, "ABTS not posted becasue"
2721 		    "port state was %x", iport->iport_link_state);
2722 		fct_queue_cmd_for_termination(cmd, FCT_LOCAL_PORT_OFFLINE);
2723 		return;
2724 	}
2725 
2726 	if (cmd->cmd_rp_handle == FCT_HANDLE_NONE)
2727 		irp = fct_portid_to_portptr(iport, cmd->cmd_rportid);
2728 	else if (cmd->cmd_rp_handle < port->port_max_logins)
2729 		irp = iport->iport_rp_slots[cmd->cmd_rp_handle];
2730 	else
2731 		irp = NULL;
2732 	if (irp == NULL) {
2733 		/* XXX Throw a logout to the initiator */
2734 		rw_exit(&iport->iport_lock);
2735 		stmf_trace(iport->iport_alias, "ABTS received from"
2736 		    " %x without a session", cmd->cmd_rportid);
2737 		fct_queue_cmd_for_termination(cmd, FCT_NOT_LOGGED_IN);
2738 		return;
2739 	}
2740 	cmd->cmd_rp = irp->irp_rp;
2741 
2742 	/*
2743 	 * No need to allocate an xchg resource. ABTSes use the same
2744 	 * xchg resource as the cmd they are aborting.
2745 	 */
2746 	rw_enter(&irp->irp_lock, RW_WRITER);
2747 	mutex_enter(&iport->iport_worker_lock);
2748 	/* Lets find the command first */
2749 	for (i = 0; i < port->port_max_xchges; i++) {
2750 		if ((ic = iport->iport_cmd_slots[i].slot_cmd) == NULL)
2751 			continue;
2752 		if ((ic->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
2753 			continue;
2754 		c = ic->icmd_cmd;
2755 		if (!CMD_HANDLE_VALID(c->cmd_handle))
2756 			continue;
2757 		if ((c->cmd_rportid != cmd->cmd_rportid) ||
2758 		    (c->cmd_oxid != cmd->cmd_oxid))
2759 			continue;
2760 		/* Found the command */
2761 		found = 1;
2762 		break;
2763 	}
2764 	if (!found) {
2765 		mutex_exit(&iport->iport_worker_lock);
2766 		rw_exit(&irp->irp_lock);
2767 		rw_exit(&iport->iport_lock);
2768 		/* Dont even bother queueing it. Just respond */
2769 		fct_fill_abts_acc(cmd);
2770 		if (port->port_send_cmd_response(cmd,
2771 		    FCT_IOF_FORCE_FCA_DONE) != FCT_SUCCESS) {
2772 			/*
2773 			 * XXX Throw HBA fatal error event
2774 			 * Later shutdown svc will terminate the ABTS in the end
2775 			 */
2776 			(void) snprintf(info, 80,
2777 			    "fct_handle_rcvd_abts: iport-%p, "
2778 			    "ABTS_ACC port_send_cmd_response failed",
2779 			    (void *)iport);
2780 			info[79] = 0;
2781 			(void) fct_port_shutdown(iport->iport_port,
2782 			    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info);
2783 		} else {
2784 			fct_cmd_free(cmd);
2785 		}
2786 		return;
2787 	}
2788 
2789 	/* Check if this an abts retry */
2790 	if (c->cmd_link && (ic->icmd_flags & ICMD_ABTS_RECEIVED)) {
2791 		/* Kill this abts. */
2792 		fct_q_for_termination_lock_held(iport, icmd, FCT_ABORTED);
2793 		if (IS_WORKER_SLEEPING(iport))
2794 			cv_signal(&iport->iport_worker_cv);
2795 		mutex_exit(&iport->iport_worker_lock);
2796 		rw_exit(&irp->irp_lock);
2797 		rw_exit(&iport->iport_lock);
2798 		return;
2799 	}
2800 	c->cmd_link = cmd;
2801 	atomic_or_32(&ic->icmd_flags, ICMD_ABTS_RECEIVED);
2802 	cmd->cmd_link = c;
2803 	mutex_exit(&iport->iport_worker_lock);
2804 	rw_exit(&irp->irp_lock);
2805 	fct_queue_cmd_for_termination(c, FCT_ABTS_RECEIVED);
2806 	rw_exit(&iport->iport_lock);
2807 }
2808 
2809 void
2810 fct_queue_cmd_for_termination(fct_cmd_t *cmd, fct_status_t s)
2811 {
2812 	fct_local_port_t *port = cmd->cmd_port;
2813 	fct_i_local_port_t *iport = (fct_i_local_port_t *)
2814 	    port->port_fct_private;
2815 	fct_i_cmd_t *icmd = (fct_i_cmd_t *)cmd->cmd_fct_private;
2816 
2817 	if (cmd->cmd_type == FCT_CMD_FCP_XCHG) {
2818 		fct_queue_scsi_task_for_termination(cmd, s);
2819 		return;
2820 	}
2821 	mutex_enter(&iport->iport_worker_lock);
2822 	fct_q_for_termination_lock_held(iport, icmd, s);
2823 	if (IS_WORKER_SLEEPING(iport))
2824 		cv_signal(&iport->iport_worker_cv);
2825 	mutex_exit(&iport->iport_worker_lock);
2826 }
2827 
2828 /*
2829  * This function will not be called for SCSI CMDS
2830  */
2831 void
2832 fct_q_for_termination_lock_held(fct_i_local_port_t *iport, fct_i_cmd_t *icmd,
2833 		fct_status_t s)
2834 {
2835 	uint32_t old, new;
2836 	fct_i_cmd_t **ppicmd;
2837 
2838 	do {
2839 		old = icmd->icmd_flags;
2840 		if (old & ICMD_BEING_ABORTED)
2841 			return;
2842 		new = old | ICMD_BEING_ABORTED;
2843 	} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2844 
2845 	icmd->icmd_start_time = ddi_get_lbolt();
2846 	icmd->icmd_cmd->cmd_comp_status = s;
2847 
2848 	icmd->icmd_next = NULL;
2849 	for (ppicmd = &(iport->iport_abort_queue); *ppicmd != NULL;
2850 	    ppicmd = &((*ppicmd)->icmd_next))
2851 		;
2852 
2853 	*ppicmd = icmd;
2854 }
2855 
2856 /*
2857  * For those cmds, for which we called fca_abort but it has not yet completed,
2858  * reset the FCA_ABORT_CALLED flag, so that abort can be called again.
2859  * This is done after a FCA offline. The reason is that after offline, the
2860  * firmware is not running so abort will never complete. But if we call it
2861  * again, the FCA will detect that it is not offline and it will
2862  * not call the firmware at all. Most likely it will abort in a synchronous
2863  * manner i.e. return FCT_ABORT_SUCCESS or FCT_NOT_FOUND.
2864  */
2865 void
2866 fct_reset_flag_abort_called(fct_i_local_port_t *iport)
2867 {
2868 	fct_i_cmd_t *icmd;
2869 	uint32_t old, new;
2870 	int i, do_clear;
2871 
2872 	ASSERT(mutex_owned(&iport->iport_worker_lock));
2873 	mutex_exit(&iport->iport_worker_lock);
2874 	rw_enter(&iport->iport_lock, RW_WRITER);
2875 	mutex_enter(&iport->iport_worker_lock);
2876 
2877 	for (i = 0; i < iport->iport_port->port_max_xchges; i++) {
2878 		if (iport->iport_cmd_slots[i].slot_cmd == NULL)
2879 			continue;
2880 
2881 		icmd = iport->iport_cmd_slots[i].slot_cmd;
2882 
2883 		do {
2884 			old = new = icmd->icmd_flags;
2885 			if ((old & (ICMD_KNOWN_TO_FCA |
2886 			    ICMD_FCA_ABORT_CALLED)) == (ICMD_KNOWN_TO_FCA |
2887 			    ICMD_FCA_ABORT_CALLED)) {
2888 				new &= ~ICMD_FCA_ABORT_CALLED;
2889 				do_clear = 1;
2890 			} else {
2891 				do_clear = 0;
2892 				break;
2893 			}
2894 		} while (atomic_cas_32(&icmd->icmd_flags, old, new) != old);
2895 		if (do_clear &&
2896 		    (icmd->icmd_cmd->cmd_type == FCT_CMD_FCP_XCHG)) {
2897 			stmf_abort(STMF_REQUEUE_TASK_ABORT_LPORT,
2898 			    icmd->icmd_cmd->cmd_specific, 0, NULL);
2899 		}
2900 	}
2901 
2902 	rw_exit(&iport->iport_lock);
2903 }
2904 
2905 /*
2906  * Modify the irp_deregister_timer such that the ports start deregistering
2907  * quickly.
2908  */
2909 void
2910 fct_irp_deregister_speedup(fct_i_local_port_t *iport)
2911 {
2912 	fct_i_remote_port_t *irp;
2913 	int i;
2914 
2915 	if (!iport->iport_nrps)
2916 		return;
2917 
2918 	for (i = 0; i < rportid_table_size; i++) {
2919 		irp = iport->iport_rp_tb[i];
2920 		while (irp) {
2921 			irp->irp_deregister_timer = ddi_get_lbolt() - 1;
2922 			irp = irp->irp_next;
2923 		}
2924 	}
2925 }
2926 
2927 disc_action_t
2928 fct_handle_port_offline(fct_i_local_port_t *iport)
2929 {
2930 	if (iport->iport_offline_prstate == FCT_OPR_START) {
2931 		fct_reset_flag_abort_called(iport);
2932 		iport->iport_offline_prstate = FCT_OPR_CMD_CLEANUP_WAIT;
2933 		/* fct_ctl has already submitted a link offline event */
2934 		return (DISC_ACTION_DELAY_RESCAN);
2935 	}
2936 	if (iport->iport_offline_prstate == FCT_OPR_CMD_CLEANUP_WAIT) {
2937 		if (iport->iport_link_state != PORT_STATE_LINK_DOWN)
2938 			return (DISC_ACTION_DELAY_RESCAN);
2939 		/*
2940 		 * All I/Os have been killed at this time. Lets speedup
2941 		 * the port deregister process.
2942 		 */
2943 		mutex_exit(&iport->iport_worker_lock);
2944 		rw_enter(&iport->iport_lock, RW_WRITER);
2945 		fct_irp_deregister_speedup(iport);
2946 		rw_exit(&iport->iport_lock);
2947 		mutex_enter(&iport->iport_worker_lock);
2948 		iport->iport_offline_prstate = FCT_OPR_INT_CLEANUP_WAIT;
2949 		return (DISC_ACTION_RESCAN);
2950 	}
2951 	if (iport->iport_offline_prstate == FCT_OPR_INT_CLEANUP_WAIT) {
2952 		stmf_change_status_t st;
2953 
2954 		if (iport->iport_solcmd_queue) {
2955 			return (DISC_ACTION_DELAY_RESCAN);
2956 		}
2957 
2958 		if (iport->iport_nrps) {
2959 			/*
2960 			 * A port logout may have gone when implicit logo all
2961 			 * was retried. So do the port speedup again here.
2962 			 */
2963 			mutex_exit(&iport->iport_worker_lock);
2964 			rw_enter(&iport->iport_lock, RW_WRITER);
2965 			fct_irp_deregister_speedup(iport);
2966 			rw_exit(&iport->iport_lock);
2967 			mutex_enter(&iport->iport_worker_lock);
2968 			return (DISC_ACTION_DELAY_RESCAN);
2969 		}
2970 
2971 		if (iport->iport_event_head != NULL) {
2972 			return (DISC_ACTION_DELAY_RESCAN);
2973 		}
2974 
2975 		st.st_completion_status = STMF_SUCCESS;
2976 		st.st_additional_info = NULL;
2977 		iport->iport_offline_prstate = FCT_OPR_DONE;
2978 		iport->iport_state = FCT_STATE_OFFLINE;
2979 		mutex_exit(&iport->iport_worker_lock);
2980 		(void) stmf_ctl(STMF_CMD_LPORT_OFFLINE_COMPLETE,
2981 		    iport->iport_port->port_lport, &st);
2982 		mutex_enter(&iport->iport_worker_lock);
2983 		return (DISC_ACTION_DELAY_RESCAN);
2984 	}
2985 
2986 	/* NOTREACHED */
2987 	return (0);
2988 }
2989 
2990 /*
2991  * See stmf.h for information on rflags. Additional info is just a text
2992  * description of the reason for this call. Additional_info can be NULL.
2993  * Also the caller can declare additional info on the stack. stmf_ctl
2994  * makes a copy of it before returning.
2995  */
2996 fct_status_t
2997 fct_port_initialize(fct_local_port_t *port, uint32_t rflags,
2998 				char *additional_info)
2999 {
3000 	stmf_state_change_info_t st;
3001 
3002 	st.st_rflags = rflags;
3003 	st.st_additional_info = additional_info;
3004 	stmf_trace(NULL, "fct_port_initialize: port-%p, %s", port,
3005 	    additional_info? additional_info : "no more information");
3006 	return (stmf_ctl(STMF_CMD_LPORT_ONLINE, port->port_lport, &st));
3007 }
3008 
3009 fct_status_t
3010 fct_port_shutdown(fct_local_port_t *port, uint32_t rflags,
3011 				char *additional_info)
3012 {
3013 	stmf_state_change_info_t st;
3014 
3015 	st.st_rflags = rflags;
3016 	st.st_additional_info = additional_info;
3017 	stmf_trace(NULL, "fct_port_shutdown: port-%p, %s", port,
3018 	    additional_info? additional_info : "no more information");
3019 	return (stmf_ctl(STMF_CMD_LPORT_OFFLINE, port->port_lport, &st));
3020 }
3021 
3022 /*
3023  * Called by worker thread. The aim is to terminate the command
3024  * using whatever means it takes.
3025  * Called with worker lock held.
3026  */
3027 disc_action_t
3028 fct_cmd_terminator(fct_i_local_port_t *iport)
3029 {
3030 	char			info[80];
3031 	clock_t			endtime;
3032 	fct_i_cmd_t		**ppicmd;
3033 	fct_i_cmd_t		*icmd;
3034 	fct_cmd_t		*cmd;
3035 	fct_local_port_t	*port = iport->iport_port;
3036 	disc_action_t		ret = DISC_ACTION_NO_WORK;
3037 	fct_status_t		abort_ret;
3038 	int			fca_done, fct_done, cmd_implicit = 0;
3039 	int			flags;
3040 	unsigned long long	st;
3041 
3042 	/* Lets Limit each run to 20ms max. */
3043 	endtime = ddi_get_lbolt() + drv_usectohz(20000);
3044 
3045 	/* Start from where we left off last time */
3046 	if (iport->iport_ppicmd_term) {
3047 		ppicmd = iport->iport_ppicmd_term;
3048 		iport->iport_ppicmd_term = NULL;
3049 	} else {
3050 		ppicmd = &iport->iport_abort_queue;
3051 	}
3052 
3053 	/*
3054 	 * Once a command gets on discovery queue, this is the only thread
3055 	 * which can access it. So no need for the lock here.
3056 	 */
3057 	mutex_exit(&iport->iport_worker_lock);
3058 
3059 	while ((icmd = *ppicmd) != NULL) {
3060 		cmd = icmd->icmd_cmd;
3061 
3062 		/* Always remember that cmd->cmd_rp can be NULL */
3063 		if ((icmd->icmd_flags & (ICMD_KNOWN_TO_FCA |
3064 		    ICMD_FCA_ABORT_CALLED)) == ICMD_KNOWN_TO_FCA) {
3065 			atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3066 			if (CMD_HANDLE_VALID(cmd->cmd_handle))
3067 				flags = 0;
3068 			else
3069 				flags = FCT_IOF_FORCE_FCA_DONE;
3070 			abort_ret = port->port_abort_cmd(port, cmd, flags);
3071 			if ((abort_ret != FCT_SUCCESS) &&
3072 			    (abort_ret != FCT_ABORT_SUCCESS) &&
3073 			    (abort_ret != FCT_NOT_FOUND)) {
3074 				if (flags & FCT_IOF_FORCE_FCA_DONE) {
3075 					/*
3076 					 * XXX trigger port fatal,
3077 					 * Abort the termination, and shutdown
3078 					 * svc will trigger fct_cmd_termination
3079 					 * again.
3080 					 */
3081 					(void) snprintf(info, 80,
3082 					    "fct_cmd_terminator:"
3083 					    " iport-%p, port_abort_cmd with "
3084 					    "FORCE_FCA_DONE failed",
3085 					    (void *)iport);
3086 					info[79] = 0;
3087 					(void) fct_port_shutdown(
3088 					    iport->iport_port,
3089 					    STMF_RFLAG_FATAL_ERROR |
3090 					    STMF_RFLAG_RESET, info);
3091 
3092 					mutex_enter(&iport->iport_worker_lock);
3093 					iport->iport_ppicmd_term = ppicmd;
3094 					return (DISC_ACTION_DELAY_RESCAN);
3095 				}
3096 				atomic_and_32(&icmd->icmd_flags,
3097 				    ~ICMD_FCA_ABORT_CALLED);
3098 			} else if ((flags & FCT_IOF_FORCE_FCA_DONE) ||
3099 			    (abort_ret == FCT_ABORT_SUCCESS) ||
3100 			    (abort_ret == FCT_NOT_FOUND)) {
3101 				atomic_and_32(&icmd->icmd_flags,
3102 				    ~ICMD_KNOWN_TO_FCA);
3103 			}
3104 			ret |= DISC_ACTION_DELAY_RESCAN;
3105 		} else if (icmd->icmd_flags & ICMD_IMPLICIT) {
3106 			if (cmd->cmd_type == FCT_CMD_SOL_ELS)
3107 				cmd->cmd_comp_status = FCT_ABORTED;
3108 			atomic_or_32(&icmd->icmd_flags, ICMD_FCA_ABORT_CALLED);
3109 			cmd_implicit = 1;
3110 		}
3111 		if ((icmd->icmd_flags & ICMD_KNOWN_TO_FCA) == 0)
3112 			fca_done = 1;
3113 		else
3114 			fca_done = 0;
3115 		if ((icmd->icmd_flags & ICMD_IN_IRP_QUEUE) == 0)
3116 			fct_done = 1;
3117 		else
3118 			fct_done = 0;
3119 		if ((fca_done || cmd_implicit) && fct_done) {
3120 			mutex_enter(&iport->iport_worker_lock);
3121 			ASSERT(*ppicmd == icmd);
3122 			*ppicmd = (*ppicmd)->icmd_next;
3123 			mutex_exit(&iport->iport_worker_lock);
3124 			if ((cmd->cmd_type == FCT_CMD_RCVD_ELS) ||
3125 			    (cmd->cmd_type == FCT_CMD_RCVD_ABTS)) {
3126 				/* Free the cmd */
3127 				fct_cmd_free(cmd);
3128 			} else if (cmd->cmd_type == FCT_CMD_SOL_ELS) {
3129 				fct_handle_sol_els_completion(iport, icmd);
3130 				if (icmd->icmd_flags & ICMD_IMPLICIT) {
3131 					if (IS_LOGO_ELS(icmd)) {
3132 						/* IMPLICIT LOGO is special */
3133 						fct_cmd_free(cmd);
3134 					}
3135 				}
3136 			} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3137 				fct_sol_ct_t *ct = ICMD_TO_CT(icmd);
3138 
3139 				/* Tell the caller that we are done */
3140 				atomic_or_32(&icmd->icmd_flags,
3141 				    ICMD_CMD_COMPLETE);
3142 				if (fct_netbuf_to_value(
3143 				    ct->ct_req_payload + 8, 2) == NS_GID_PN) {
3144 					fct_i_remote_port_t *irp;
3145 
3146 					rw_enter(&iport->iport_lock, RW_READER);
3147 					irp = fct_lookup_irp_by_portwwn(iport,
3148 					    ct->ct_req_payload + 16);
3149 
3150 					if (irp) {
3151 						atomic_and_32(&irp->irp_flags,
3152 						    ~IRP_RSCN_QUEUED);
3153 					}
3154 					rw_exit(&iport->iport_lock);
3155 				}
3156 			} else {
3157 				ASSERT(0);
3158 			}
3159 		} else {
3160 			clock_t	timeout_ticks;
3161 			if (port->port_fca_abort_timeout)
3162 				timeout_ticks = drv_usectohz(
3163 				    port->port_fca_abort_timeout*1000);
3164 			else
3165 				/* 10 seconds by default */
3166 				timeout_ticks = drv_usectohz(10 * 1000000);
3167 			if ((ddi_get_lbolt() >
3168 			    (icmd->icmd_start_time+timeout_ticks)) &&
3169 			    iport->iport_state == FCT_STATE_ONLINE) {
3170 				/* timeout, reset the port */
3171 				char cmd_type[10];
3172 				if (cmd->cmd_type == FCT_CMD_RCVD_ELS ||
3173 				    cmd->cmd_type == FCT_CMD_SOL_ELS) {
3174 					fct_els_t *els = cmd->cmd_specific;
3175 					(void) snprintf(cmd_type,
3176 					    sizeof (cmd_type), "%x.%x",
3177 					    cmd->cmd_type,
3178 					    els->els_req_payload[0]);
3179 				} else if (cmd->cmd_type == FCT_CMD_SOL_CT) {
3180 					fct_sol_ct_t *ct = cmd->cmd_specific;
3181 					(void) snprintf(cmd_type,
3182 					    sizeof (cmd_type), "%x.%02x%02x",
3183 					    cmd->cmd_type,
3184 					    ct->ct_req_payload[8],
3185 					    ct->ct_req_payload[9]);
3186 				} else {
3187 					cmd_type[0] = 0;
3188 				}
3189 				st = cmd->cmd_comp_status;	/* gcc fix */
3190 				(void) snprintf(info, 80, "fct_cmd_terminator:"
3191 				    " iport-%p, cmd_type(0x%s),"
3192 				    " reason(%llx)", (void *)iport, cmd_type,
3193 				    st);
3194 				info[79] = 0;
3195 				(void) fct_port_shutdown(port,
3196 				    STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET,
3197 				    info);
3198 			}
3199 			ppicmd = &((*ppicmd)->icmd_next);
3200 		}
3201 
3202 		if (ddi_get_lbolt() > endtime) {
3203 			mutex_enter(&iport->iport_worker_lock);
3204 			iport->iport_ppicmd_term = ppicmd;
3205 			return (DISC_ACTION_DELAY_RESCAN);
3206 		}
3207 	}
3208 	mutex_enter(&iport->iport_worker_lock);
3209 	if (iport->iport_abort_queue)
3210 		return (DISC_ACTION_DELAY_RESCAN);
3211 	if (ret == DISC_ACTION_NO_WORK)
3212 		return (DISC_ACTION_RESCAN);
3213 	return (ret);
3214 }
3215 
3216 /*
3217  * Send a syslog event for adapter port level events.
3218  */
3219 void
3220 fct_log_local_port_event(fct_local_port_t *port, char *subclass)
3221 {
3222 	nvlist_t *attr_list;
3223 	int port_instance;
3224 
3225 	if (!fct_dip)
3226 		return;
3227 	port_instance = ddi_get_instance(fct_dip);
3228 
3229 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3230 	    KM_SLEEP) != DDI_SUCCESS) {
3231 		goto alloc_failed;
3232 	}
3233 
3234 	if (nvlist_add_uint32(attr_list, "instance", port_instance)
3235 	    != DDI_SUCCESS) {
3236 		goto error;
3237 	}
3238 
3239 	if (nvlist_add_byte_array(attr_list, "port-wwn",
3240 	    port->port_pwwn, 8) != DDI_SUCCESS) {
3241 		goto error;
3242 	}
3243 
3244 	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3245 	    subclass, attr_list, NULL, DDI_SLEEP);
3246 
3247 	nvlist_free(attr_list);
3248 	return;
3249 
3250 error:
3251 	nvlist_free(attr_list);
3252 alloc_failed:
3253 	stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3254 	    "Unable to send %s event", subclass);
3255 }
3256 
3257 void
3258 fct_log_remote_port_event(fct_local_port_t *port, char *subclass,
3259     uint8_t *rp_pwwn, uint32_t rp_id)
3260 {
3261 	nvlist_t *attr_list;
3262 	int port_instance;
3263 
3264 	if (!fct_dip)
3265 		return;
3266 	port_instance = ddi_get_instance(fct_dip);
3267 
3268 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
3269 	    KM_SLEEP) != DDI_SUCCESS) {
3270 		goto alloc_failed;
3271 	}
3272 
3273 	if (nvlist_add_uint32(attr_list, "instance", port_instance)
3274 	    != DDI_SUCCESS) {
3275 		goto error;
3276 	}
3277 
3278 	if (nvlist_add_byte_array(attr_list, "port-wwn",
3279 	    port->port_pwwn, 8) != DDI_SUCCESS) {
3280 		goto error;
3281 	}
3282 
3283 	if (nvlist_add_byte_array(attr_list, "target-port-wwn",
3284 	    rp_pwwn, 8) != DDI_SUCCESS) {
3285 		goto error;
3286 	}
3287 
3288 	if (nvlist_add_uint32(attr_list, "target-port-id",
3289 	    rp_id) != DDI_SUCCESS) {
3290 		goto error;
3291 	}
3292 
3293 	(void) ddi_log_sysevent(fct_dip, DDI_VENDOR_SUNW, EC_SUNFC,
3294 	    subclass, attr_list, NULL, DDI_SLEEP);
3295 
3296 	nvlist_free(attr_list);
3297 	return;
3298 
3299 error:
3300 	nvlist_free(attr_list);
3301 alloc_failed:
3302 	stmf_trace(((fct_i_local_port_t *)port->port_fct_private)->iport_alias,
3303 	    "Unable to send %s event", subclass);
3304 }
3305 
3306 uint64_t
3307 fct_netbuf_to_value(uint8_t *buf, uint8_t nbytes)
3308 {
3309 	uint64_t	ret = 0;
3310 	uint8_t		idx = 0;
3311 
3312 	do {
3313 		ret |= (buf[idx] << (8 * (nbytes -idx - 1)));
3314 	} while (++idx < nbytes);
3315 
3316 	return (ret);
3317 }
3318 
3319 void
3320 fct_value_to_netbuf(uint64_t value, uint8_t *buf, uint8_t nbytes)
3321 {
3322 	uint8_t		idx = 0;
3323 
3324 	for (idx = 0; idx < nbytes; idx++) {
3325 		buf[idx] = 0xFF & (value >> (8 * (nbytes - idx - 1)));
3326 	}
3327 }
3328 
3329 /*
3330  * from_ptr: ptr to uchar_t array of size WWN_SIZE
3331  * to_ptr: char ptr to string of size WWN_SIZE*2+1
3332  */
3333 void
3334 fct_wwn_to_str(char *to_ptr, const uint8_t *from_ptr)
3335 {
3336 	ASSERT(to_ptr != NULL && from_ptr != NULL);
3337 
3338 	(void) sprintf(to_ptr, "%02x%02x%02x%02x%02x%02x%02x%02x",
3339 	    from_ptr[0], from_ptr[1], from_ptr[2], from_ptr[3],
3340 	    from_ptr[4], from_ptr[5], from_ptr[6], from_ptr[7]);
3341 }
3342