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