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 <unistd.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <printAttrs.h>
31 #include <smhbaapi.h>
32 
33 #define	TABLEN	2
34 typedef struct inputArgs {
35 	int 		wwnCount;
36 	char 		**wwn_argv;
37 	uint64_t 	portWWN;
38 	char		*hbaName;
39 	int		pflag;
40 	int		*wwn_flag;
41 } inputArg_t;
42 
43 typedef struct tgt_mapping {
44 	SMHBA_SCSIENTRY	tgtentry;
45 	uchar_t		inq_vid[8];
46 	uchar_t		inq_pid[16];
47 	uchar_t		inq_dtype;
48 	struct tgt_mapping *next;
49 }tgt_mapping;
50 
51 /*
52  * Remote port tree node structure.
53  */
54 typedef struct smhba_rp_tree {
55 	SMHBA_PORTATTRIBUTES	portattr;
56 	SMHBA_SAS_PORT		sasattr;
57 	tgt_mapping		*first_entry;
58 	int			printed;
59 	struct smhba_rp_tree	*parent;
60 	struct smhba_rp_tree	*child;
61 	struct smhba_rp_tree	*sibling;
62 }rp_tree_t;
63 
64 /*
65  * Report LUN data structure.
66  */
67 struct lun {
68 	uchar_t	val[8];
69 };
70 
71 typedef struct rep_luns_rsp {
72 	uint32_t    length;
73 	uint32_t    rsrvd;
74 	struct lun  lun[1];
75 } rep_luns_rsp_t;
76 
77 /*
78  * The following flag is used for printing HBA header on-demand.
79  */
80 static int g_printHBA = 0;
81 
82 /*
83  * The following structure is for sorted output of HBA and HBA Port.
84  */
85 typedef struct _sas_elem {
86 	char	name[256];
87 	int	index;
88 }sas_elem_t;
89 
90 /*
91  * The following two functions are for generating hierachy of expander
92  * subcommand.
93  */
94 static int
95 sas_rp_tree_insert(rp_tree_t **rproot, rp_tree_t *rpnode);
96 static int
97 sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
98     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
99     rp_tree_t *rpnode, inputArg_t *input, int gident,
100     int *printPort);
101 static int
102 sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
103     SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
104     inputArg_t *input, int lident, int gident);
105 static int
106 sas_print_rpnode(inputArg_t *input,
107     rp_tree_t *rpnode, int lident, int gident);
108 static void sas_rp_tree_free(rp_tree_t *rproot);
109 
110 typedef int (*processPortFunc)(HBA_HANDLE handle, char *adapterName,
111     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
112     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
113 
114 static int processHBA(inputArg_t *input,
115     processPortFunc processPort);
116 
117 static int isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN);
118 static int isStringInArgv(inputArg_t *input, const char *adapterName);
119 static boolean_t compareLUName(char *cmdArg, char *osName);
120 static discoveredDevice *LUList = NULL;
121 static targetPortList_t *gTargetPortList = NULL;
122 
123 /* processes for hanlding local HBA info */
124 static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
125     int numberOfPorts, const char *adapterName);
126 static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
127     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
128     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
129 static int processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
130     SMHBA_PORTATTRIBUTES *port, int pflag);
131 static int processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex,
132     int phyIndex, PSMHBA_SAS_PHY phyattrs, int pflag);
133 
134 /* process for handling expander info */
135 static int handleExpander(HBA_HANDLE handle, char *adapterName,
136     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
137     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
138 
139 /* process for handling target port info */
140 static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
141     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
142     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
143 
144 /* process for handling logical unit info */
145 static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
146     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
147     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input);
148 
149 /* process for target port SCSI processing */
150 static int
151 searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
152     SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
153     struct targetPortConfig *configData);
154 
155 /* process for target port config processing */
156 static int searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
157     SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
158     SMHBA_SAS_PORT *sasattr, int pflag);
159 
160 /* process for logical-unit config processing */
161 static int
162 searchDevice(PSMHBA_SCSIENTRY entryP, HBA_HANDLE handle, HBA_WWN hbaPortWWN,
163     HBA_WWN domainPortWWN, char *portName, int pflag);
164 
165 /* get domain port out of hba-port phy attr. */
166 HBA_STATUS get_domainPort(HBA_HANDLE handle,
167     int portindex, PSMHBA_PORTATTRIBUTES port,
168     HBA_WWN *pdomainPort);
169 
170 static int
171 sas_name_comp(const char *name1, const char *name2);
172 static void
173 sas_elem_sort(sas_elem_t *array, int nelem);
174 
175 /*
176  * function for hba subcommand
177  *
178  * Arguments:
179  *	wwnCount - count of the number of WWNs in wwn_argv
180  *	    if wwnCount > 0, then we will only print information for
181  *		the hba ports listed in wwn_argv
182  *	    if wwnCount == 0, then we will print information on all hba ports
183  *	wwn_argv - argument array of hba port WWNs
184  *	options - any options specified by the caller
185  *
186  * returns:
187  *	0	if successful
188  *	>0	otherwise
189  */
190 int
191 sas_util_list_hba(int hbaCount, char **hba_argv, cmdOptions_t *options)
192 {
193 	HBA_STATUS		status;
194 	int			processHBA_flags = 0;
195 	inputArg_t		input;
196 	int 			err_cnt = 0;
197 
198 	/* process each of the options */
199 	for (; options->optval; options++) {
200 		switch (options->optval) {
201 		case 'v':
202 			processHBA_flags |= PRINT_VERBOSE;
203 			break;
204 		default:
205 			break;
206 		}
207 	}
208 
209 	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
210 		(void *) fprintf(stderr, "%s %s\n",
211 		    gettext("Failed to load SM-HBA libraries."
212 		    "Reason:"), getHBAStatus(status));
213 		err_cnt++;
214 		return (err_cnt);
215 	}
216 
217 	(void *) memset(&input, 0, sizeof (input));
218 	/* utilize wwnCount and wwn_argv for hbaCount and hba_argv */
219 	input.wwnCount = hbaCount;
220 	input.wwn_argv = hba_argv;
221 	input.pflag = processHBA_flags;
222 
223 	/*
224 	 * Process and filter for every local hba,
225 	 * when the hba is not specificed, print all hba(s).
226 	 */
227 	err_cnt += processHBA(&input, NULL);
228 
229 	(void) HBA_FreeLibrary();
230 
231 	return (err_cnt);
232 }
233 
234 /*
235  * function for hba-port subcommand
236  *
237  * Arguments:
238  *	wwnCount - count of the number of WWNs in wwn_argv
239  *	    if wwnCount > 0, then we will only print information for
240  *		the hba ports listed in wwn_argv
241  *	    if wwnCount == 0, then we will print information on all hba ports
242  *	wwn_argv - argument array of hba port WWNs
243  *	options - any options specified by the caller
244  *
245  * returns:
246  *	0	if successful
247  *	>0	otherwise
248  */
249 int
250 sas_util_list_hbaport(int wwnCount, char **wwn_argv, cmdOptions_t *options)
251 {
252 	HBA_STATUS		status;
253 	int			processHBA_flags = 0;
254 	inputArg_t		input;
255 	int 			err_cnt = 0;
256 	char			hbaName[256] = {'\0'};
257 
258 	/* process each of the options */
259 	for (; options->optval; options++) {
260 		switch (options->optval) {
261 		case 'a':
262 			(void *) strlcpy(hbaName,
263 			    options->optarg, sizeof (hbaName));
264 			break;
265 		case 'y':
266 			processHBA_flags |= PRINT_PHY;
267 			break;
268 		case 'l':
269 			processHBA_flags |= PRINT_PHY_LINKSTAT;
270 			break;
271 		case 'v':
272 			processHBA_flags |= PRINT_VERBOSE;
273 			break;
274 		default:
275 			break;
276 		}
277 	}
278 
279 	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
280 		(void *) fprintf(stderr, "%s %s\n",
281 		    gettext("Failed to load SM-HBA libraries."
282 		    "Reason:"), getHBAStatus(status));
283 		err_cnt++;
284 		return (err_cnt);
285 	}
286 
287 	(void *) memset(&input, 0, sizeof (input));
288 	input.wwnCount = wwnCount;
289 	input.wwn_argv = wwn_argv;
290 	input.hbaName = hbaName;
291 	input.pflag = processHBA_flags;
292 
293 	/*
294 	 * Process and filter for every local hba-port,
295 	 * when the hba-port is not specificed, print all hba-port(s).
296 	 */
297 	err_cnt += processHBA(&input, handleHBAPort);
298 
299 	(void) HBA_FreeLibrary();
300 
301 	return (err_cnt);
302 }
303 
304 /*
305  * function for expander subcommand
306  *
307  * Arguments:
308  *	wwnCount - the number of Remote Port SAS Address in wwn_argv
309  *	    if wwnCount == 0, then print information on all
310  *		expander devices.
311  *	    if wwnCount > 0, then print information for the exapnders
312  *		given in wwn_argv.
313  *	wwn_argv - array of WWNs
314  *	options - options specified by the caller
315  *
316  * returns:
317  *	0	if successful
318  *	>0	otherwise
319  */
320 int
321 sas_util_list_expander(int wwnCount, char **wwn_argv, cmdOptions_t *options)
322 {
323 	HBA_STATUS		status;
324 	int			processHBA_flags = 0;
325 	char			hbaPort[MAXPATHLEN + 1] = {0};
326 	inputArg_t		input;
327 	int			err_cnt = 0;
328 
329 	/* process each of the options */
330 	for (; options->optval; options++) {
331 		switch (options->optval) {
332 		case 'p':
333 			(void) strlcpy(hbaPort, options->optarg,
334 			    sizeof (hbaPort));
335 			break;
336 		case 't':
337 			processHBA_flags |= PRINT_TARGET_PORT;
338 			break;
339 		case 'v':
340 			processHBA_flags |= PRINT_VERBOSE;
341 			break;
342 		default:
343 			break;
344 		}
345 	}
346 
347 	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
348 		(void *) fprintf(stderr, "%s %s\n",
349 		    gettext("Failed to load SM-HBA libraries."
350 		    "Reason:"), getHBAStatus(status));
351 		err_cnt++;
352 		return (err_cnt);
353 	}
354 
355 	(void *) memset(&input, 0, sizeof (input));
356 	input.wwnCount = wwnCount;
357 	input.wwn_argv = wwn_argv;
358 	input.pflag = processHBA_flags;
359 	input.hbaName = hbaPort;
360 
361 	/*
362 	 * Process and filter for every hba-port,
363 	 * when the hba-port is not specificed, print all hba-port(s).
364 	 */
365 	err_cnt += processHBA(&input, handleExpander);
366 
367 	(void) HBA_FreeLibrary();
368 
369 	return (err_cnt);
370 }
371 
372 /*
373  * function for target-port subcommand
374  *
375  * Arguments:
376  *	wwnCount - the number of Remote Port SAS Address in wwn_argv
377  *	    if wwnCount == 0, then print information on all
378  *		target ports.
379  *	    if wwnCount > 0, then print information for the target ports
380  *		given in wwn_argv.
381  *	wwn_argv - array of WWNs
382  *	options - options specified by the caller
383  *
384  * returns:
385  *	0	if successful
386  *	>0	otherwise
387  */
388 int
389 sas_util_list_targetport(int tpCount, char **tpArgv, cmdOptions_t *options)
390 {
391 	HBA_STATUS		status;
392 	int			processHBA_flags = 0;
393 	int			tp, tpFound;
394 	inputArg_t		input;
395 	targetPortList_t	*tpListWalk;
396 	int			err_cnt = 0;
397 	uint64_t		tmpAddr;
398 
399 	/* process each of the options */
400 	for (; options->optval; options++) {
401 		switch (options->optval) {
402 		case 's':
403 			processHBA_flags |= PRINT_TARGET_SCSI;
404 			break;
405 		case 'v':
406 			processHBA_flags |= PRINT_VERBOSE;
407 			break;
408 		default:
409 			break;
410 		}
411 	}
412 
413 	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
414 		(void *) fprintf(stderr, "%s %s\n",
415 		    gettext("Failed to load SM-HBA libraries."
416 		    "Reason:"), getHBAStatus(status));
417 		err_cnt++;
418 		return (err_cnt);
419 	}
420 
421 	(void *) memset(&input, 0, sizeof (input));
422 	input.wwnCount = tpCount;
423 	input.wwn_argv = tpArgv;
424 	input.pflag = processHBA_flags;
425 
426 	/*
427 	 * Process and filter for every hba-port,
428 	 * when the hba-port is not specificed, print all hba-port(s).
429 	 */
430 	err_cnt += processHBA(&input, handleTargetPort);
431 
432 	if (tpCount == 0) {
433 		/* list all target port */
434 		for (tpListWalk = gTargetPortList; tpListWalk != NULL;
435 		    tpListWalk = tpListWalk->next) {
436 			err_cnt += printTargetPortInfo(tpListWalk, input.pflag);
437 		}
438 	} else {
439 		/*
440 		 * When operands provided, we should set the error code
441 		 * only if there are issues related with the operands.
442 		 */
443 		err_cnt = 0;
444 		/*
445 		 * list any paths not found first
446 		 * this gives the user cleaner output
447 		 */
448 		for (tp = 0; tp < tpCount; tp++) {
449 			errno = 0;
450 			tmpAddr = strtoull(tpArgv[tp], NULL, 16);
451 			if ((tmpAddr == 0) && (errno != 0)) {
452 				err_cnt++;
453 				continue;
454 			}
455 			for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
456 			    tpListWalk != NULL;
457 			    tpListWalk = tpListWalk->next) {
458 				if (wwnConversion(tpListWalk->sasattr.
459 				    LocalSASAddress.wwn) == tmpAddr) {
460 					tpFound = B_TRUE;
461 					break;
462 				}
463 			}
464 			if (tpFound == B_FALSE) {
465 				(void *) fprintf(stderr,
466 				    "Error: Target Port %s Not Found \n",
467 				    tpArgv[tp]);
468 				err_cnt++;
469 			}
470 		}
471 		/* list all paths requested in order requested */
472 		for (tp = 0; tp < tpCount; tp++) {
473 			errno = 0;
474 			tmpAddr = strtoull(tpArgv[tp], NULL, 16);
475 			if ((tmpAddr == 0) && (errno != 0)) {
476 				continue;
477 			}
478 			for (tpListWalk = gTargetPortList, tpFound = B_FALSE;
479 			    tpListWalk != NULL;
480 			    tpListWalk = tpListWalk->next) {
481 				if (wwnConversion(tpListWalk->sasattr.
482 				    LocalSASAddress.wwn) == tmpAddr) {
483 					err_cnt += printTargetPortInfo(
484 					    tpListWalk,
485 					    processHBA_flags);
486 				}
487 			}
488 		}
489 	}
490 	(void) HBA_FreeLibrary();
491 	return (err_cnt);
492 }
493 /*
494  * This function will enumerate all the hba and hba ports,
495  * call the callback function to proceed with futher process.
496  *
497  * Arguments:
498  *	input - contains all the input parameters.
499  *	processPort - a callback function when handling each port.
500  *
501  *  Return Value:
502  *	    0		sucessfully processed handle
503  *	    >0		error has occured
504  */
505 static int
506 processHBA(inputArg_t *input, processPortFunc processPort)
507 {
508 	int			numAdapters = 0;
509 	int			matchedHBAs = 0;
510 	int			matchedHBAPorts = 0;
511 	int			hbaPortExist = 0;
512 	HBA_STATUS		status;
513 	HBA_HANDLE		handle;
514 	HBA_UINT32		numberOfPorts = 0;
515 	int			portIndex = 0;
516 	HBA_PORTTYPE		porttype;
517 	SMHBA_LIBRARYATTRIBUTES libattrs;
518 	SMHBA_ADAPTERATTRIBUTES	attrs;
519 	SMHBA_PORTATTRIBUTES	port;
520 	SMHBA_SAS_PORT		sasattrs;
521 	int			i, sum, ret = 0;
522 	int			remote_avail = 0;
523 	int			local_avail = 0;
524 	sas_elem_t		*adpt_array = NULL;
525 	sas_elem_t		*port_array = NULL;
526 
527 	numAdapters = HBA_GetNumberOfAdapters();
528 	if (numAdapters == 0) {
529 		(void *) fprintf(stderr, "%s\n",
530 		    gettext("Error: No Adapters Found."));
531 		return (++ret);
532 	}
533 
534 	/*
535 	 * To deal with mismatching HBA/HBA Port/Expander Port, we need an
536 	 * array of flags for each operands.
537 	 */
538 	if (input->wwnCount && (processPort != handleTargetPort) &&
539 	    (processPort != handleLogicalUnit)) {
540 		input->wwn_flag = calloc(input->wwnCount, sizeof (int));
541 		if (input->wwn_flag == NULL) {
542 			(void *) fprintf(stderr, "%s\n",
543 			    gettext("No enough memory on heap"));
544 			return (++ret);
545 		}
546 	}
547 
548 	adpt_array = calloc(numAdapters, sizeof (sas_elem_t));
549 	if (adpt_array == NULL) {
550 		(void *) fprintf(stderr, "%s\n",
551 		    gettext("No enough memory on heap"));
552 		if (input->wwn_flag) {
553 			free(input->wwn_flag);
554 			input->wwn_flag = NULL;
555 		}
556 		return (++ret);
557 	}
558 	for (i = 0; i < numAdapters; i++) {
559 		status =
560 		    SMHBA_GetVendorLibraryAttributes(i, &libattrs);
561 		/*
562 		 * If we get SAS incompatible library warning here,
563 		 * just skip the following steps.
564 		 */
565 		if (status != 1) {
566 			continue;
567 		}
568 		status = HBA_GetAdapterName(i, adpt_array[i].name);
569 		if (status != HBA_STATUS_OK) {
570 			(void *) fprintf(stderr, "%s %d %s %s\n",
571 			    gettext("Error: Failed to get the name for"
572 			    " HBA index"),
573 			    i, gettext("Reason:"),
574 			    getHBAStatus(status));
575 			ret++;
576 			continue;
577 		}
578 		adpt_array[i].index = i;
579 	}
580 	/* Sort the HBA Name in place. */
581 	sas_elem_sort(adpt_array, numAdapters);
582 
583 	for (i = 0; i < numAdapters; i++) {
584 		int times = 0;
585 		if (adpt_array[i].name[0] != '\0') {
586 			if ((handle = HBA_OpenAdapter(adpt_array[i].name))
587 			    == 0) {
588 				(void *) fprintf(stderr, "%s %s.\n",
589 				    gettext("Error: Failed to open adapter"),
590 				    adpt_array[i].name);
591 				ret++;
592 				continue;
593 			}
594 		} else {
595 			continue;
596 		}
597 
598 		/*
599 		 * We need to support an adapter without hba port.
600 		 * So get attributes anyway.
601 		 */
602 		(void *) memset(&attrs, 0, sizeof (attrs));
603 		status = SMHBA_GetAdapterAttributes(handle, &attrs);
604 		while ((status == HBA_STATUS_ERROR_TRY_AGAIN ||
605 		    status == HBA_STATUS_ERROR_BUSY) &&
606 		    times++ < HBA_MAX_RETRIES) {
607 			(void) sleep(1);
608 			status = SMHBA_GetAdapterAttributes(handle,
609 			    &attrs);
610 		}
611 		if (status != HBA_STATUS_OK) {
612 			(void *) fprintf(stderr, "%s %s %s %s\n",
613 			    gettext("Error: Failed to get attributes"
614 			    " for HBA "), adpt_array[i].name,
615 			    gettext("Reason:"),
616 			    getHBAStatus(status));
617 
618 			HBA_CloseAdapter(handle);
619 			ret++;
620 			continue;
621 		}
622 
623 		status = SMHBA_GetNumberOfPorts(handle, &numberOfPorts);
624 		if (status != HBA_STATUS_OK) {
625 			(void *) fprintf(stderr, "%s %s %s %s\n",
626 			    gettext("Error: Failed to get number of ports "
627 			    "for HBA"), adpt_array[i].name,
628 			    gettext("Reason:"),
629 			    getHBAStatus(status));
630 			HBA_CloseAdapter(handle);
631 			ret++;
632 			continue;
633 		}
634 
635 		/*
636 		 * Deal with each subcommand for hba filter here,
637 		 * processPort is NULL for hba subcommand.
638 		 */
639 		if (processPort == NULL) {
640 			matchedHBAs += handleHBA(&attrs, input,
641 			    numberOfPorts, adpt_array[i].name);
642 			HBA_CloseAdapter(handle);
643 			continue;
644 		} else if (processPort == handleHBAPort) {
645 			if (input->hbaName[0] != '\0') {
646 				if (strcmp(input->hbaName,
647 				    adpt_array[i].name) == 0) {
648 					matchedHBAs++;
649 				} else {
650 					continue;
651 				}
652 			} else {
653 				matchedHBAs++;
654 			}
655 		} else {
656 			matchedHBAs++;
657 		}
658 
659 		/*
660 		 * In order to have a sorted output for HBA Port, we should
661 		 * do the sorting before moving on.
662 		 */
663 		if (numberOfPorts) {
664 			port_array = calloc(numberOfPorts, sizeof (sas_elem_t));
665 		}
666 		for (portIndex = 0; portIndex < numberOfPorts; portIndex++) {
667 			if ((status = SMHBA_GetPortType(handle,
668 			    portIndex, &porttype)) != HBA_STATUS_OK) {
669 				(void *) fprintf(stderr, "%s %s %s %s\n",
670 				    gettext("Failed to get adapter port type "
671 				    "for HBA"), adpt_array[i].name,
672 				    gettext("Reason:"),
673 				    getHBAStatus(status));
674 				ret++;
675 				continue;
676 			}
677 			if (porttype != HBA_PORTTYPE_SASDEVICE) {
678 				/* skip any non-sas hba port */
679 				continue;
680 			}
681 			(void *) memset(&port, 0, sizeof (port));
682 			(void *) memset(&sasattrs, 0, sizeof (sasattrs));
683 			port.PortSpecificAttribute.SASPort = &sasattrs;
684 			if ((status = SMHBA_GetAdapterPortAttributes(
685 			    handle, portIndex, &port)) != HBA_STATUS_OK) {
686 				/*
687 				 * Not able to get port attributes.
688 				 * print out error message and
689 				 * move on to the next port
690 				 */
691 				(void *) fprintf(stderr, "%s %s %s %d %s %s\n",
692 				    gettext("Error: Failed to get port "
693 				    "attributes for HBA"), adpt_array[i].name,
694 				    gettext("port index"), portIndex,
695 				    gettext("Reason:"),
696 				    getHBAStatus(status));
697 				ret++;
698 				continue;
699 			}
700 			(void) strlcpy(port_array[portIndex].name,
701 			    port.OSDeviceName,
702 			    sizeof (port_array[portIndex].name));
703 			port_array[portIndex].index = portIndex;
704 		}
705 		/* Sort the HBA Port Name here. */
706 		if (port_array) {
707 			sas_elem_sort(port_array, numberOfPorts);
708 		}
709 		/*
710 		 * Sum up the local hba ports available.
711 		 */
712 		local_avail += numberOfPorts;
713 
714 		/*
715 		 * Clear g_printHBA flag for expander subcommand.
716 		 */
717 		g_printHBA = 0;
718 
719 		/* process each port on the given adapter */
720 		for (portIndex = 0;
721 		    portIndex < numberOfPorts;
722 		    portIndex++) {
723 			/*
724 			 * We only handle the port which is valid.
725 			 */
726 			if (port_array[portIndex].name[0] == '\0') {
727 				continue;
728 			}
729 			(void *) memset(&port, 0, sizeof (port));
730 			(void *) memset(&sasattrs, 0, sizeof (sasattrs));
731 			port.PortSpecificAttribute.SASPort = &sasattrs;
732 
733 			(void) SMHBA_GetAdapterPortAttributes(handle,
734 			    port_array[portIndex].index, &port);
735 
736 			/*
737 			 * We have different things to do for the three
738 			 * sub-commands here.
739 			 */
740 			if (processPort == handleHBAPort) {
741 				/*
742 				 * For hba-port, we will check whether the
743 				 * specified hba port exist first.
744 				 * But if no hba port specified, we should
745 				 * by pass this check(just let hbaPortExist
746 				 * be 1).
747 				 */
748 				if (input->wwnCount > 0) {
749 					if (isStringInArgv(input,
750 					    port.OSDeviceName)) {
751 						hbaPortExist = 1;
752 						if (g_printHBA == 0) {
753 							(void *) fprintf(stdout,
754 							    "%s %s\n",
755 							    "HBA Name:",
756 							    adpt_array[i].name);
757 							g_printHBA = 1;
758 						}
759 					}
760 				} else {
761 					hbaPortExist = 1;
762 					if (g_printHBA == 0) {
763 						(void *) fprintf(stdout,
764 						    "%s %s\n",
765 						    "HBA Name:",
766 						    adpt_array[i].name);
767 						g_printHBA = 1;
768 					}
769 				}
770 			}
771 
772 			if (processPort == handleExpander) {
773 				/*
774 				 * For expander device, input->hbaName is
775 				 * the hba port name specified on the
776 				 * command line(with -p option).
777 				 */
778 				if (input->hbaName[0] != '\0') {
779 					if (strcmp(input->hbaName,
780 					    port.OSDeviceName) == 0)
781 						hbaPortExist = 1;
782 				} else
783 					hbaPortExist = 1;
784 			}
785 
786 			if (processPort == handleTargetPort) {
787 				/*
788 				 * For target port, we don't need to check the
789 				 * hba port address, so let it go here.
790 				 */
791 				hbaPortExist = 1;
792 			}
793 
794 			if (processPort == handleLogicalUnit) {
795 				/*
796 				 * For lu, we don't need to check the hba
797 				 * port address, so let it go here.
798 				 */
799 				hbaPortExist = 1;
800 			}
801 
802 			if (hbaPortExist) {
803 				if (port.PortSpecificAttribute.SASPort->
804 				    NumberofDiscoveredPorts) {
805 					remote_avail++;
806 				}
807 				ret += (*processPort)(handle,
808 				    adpt_array[i].name,
809 				    port_array[portIndex].index, &port,
810 				    &attrs, input);
811 				/*
812 				 * We should reset the hbaPortExist flag
813 				 * here for next round of check and count
814 				 * for the machedHBAPorts.
815 				 */
816 				hbaPortExist = 0;
817 				matchedHBAPorts++;
818 			}
819 		}
820 		if (port_array) {
821 			free(port_array);
822 			port_array = NULL;
823 		}
824 		HBA_CloseAdapter(handle);
825 	}
826 	if (adpt_array) {
827 		free(adpt_array);
828 		adpt_array = NULL;
829 	}
830 
831 	/*
832 	 * When we are here, we have traversed all the hba and hba ports.
833 	 */
834 	if (matchedHBAs == 0) {
835 		(void *) fprintf(stderr, "%s\n",
836 		    gettext("Error: Matching HBA not found."));
837 		if (input->wwn_flag) {
838 			free(input->wwn_flag);
839 			input->wwn_flag = NULL;
840 		}
841 		return (++ret);
842 	} else if (processPort == NULL) {
843 		/*
844 		 * processPort == NULL signifies hba subcommand.
845 		 * If enter here, it means we have at least one matching
846 		 * hba, we need to check if there are mismatching ones.
847 		 */
848 		for (i = 0; i < input->wwnCount; i++) {
849 			if (input->wwn_flag[i] == 0) {
850 				(void *) fprintf(stderr, "%s %s %s\n",
851 				    gettext("Error: HBA"),
852 				    input->wwn_argv[i],
853 				    gettext("not found."));
854 				ret++;
855 			}
856 		}
857 	} else {
858 		if (local_avail > 0 && matchedHBAPorts == 0) {
859 			(void *) fprintf(stderr, "%s\n",
860 			    gettext("Error: Matching HBA Port "
861 			    "not found."));
862 			if (input->wwn_flag) {
863 				free(input->wwn_flag);
864 				input->wwn_flag = NULL;
865 			}
866 			return (++ret);
867 		} else if (local_avail == 0) {
868 			(void *) fprintf(stderr, "%s\n",
869 			    gettext("Error: No HBA Port Configured."));
870 			if (input->wwn_flag) {
871 				free(input->wwn_flag);
872 				input->wwn_flag = NULL;
873 			}
874 			return (++ret);
875 		} else if (processPort == handleHBAPort) {
876 			/*
877 			 * If enter here, we have at least one HBA port
878 			 * matched. For hba-port subcommand, we shall check
879 			 * whether there are operands mismatching.
880 			 */
881 			for (i = 0; i < input->wwnCount; i++) {
882 				if (input->wwn_flag[i] == 0) {
883 					(void *) fprintf(stderr, "%s %s %s\n",
884 					    gettext("Error: HBA Port"),
885 					    input->wwn_argv[i],
886 					    gettext("not found."));
887 					ret++;
888 				}
889 			}
890 		}
891 	}
892 
893 	/*
894 	 * For expander subcommand, we need to check if the
895 	 * specified sas address(ese) exist (none/partial/all).
896 	 */
897 	if (processPort == handleExpander) {
898 		if (input->wwnCount > 0) {
899 			sum = 0;
900 			for (i = 0; i < input->wwnCount; i++) {
901 				sum += input->wwn_flag[i];
902 			}
903 			/*
904 			 * If sum is zero, it means that for all the given
905 			 * operands matching count is zero. So none of the
906 			 * specified SAS address exist actually.
907 			 */
908 			if (sum == 0) {
909 				(void *) fprintf(stderr, gettext("Error: "
910 				    "Matching SAS Address not found.\n"));
911 				free(input->wwn_flag);
912 				input->wwn_flag = NULL;
913 				return (++ret);
914 			}
915 
916 			/*
917 			 * If we get here, it means that some of the specified
918 			 * sas address exist, we will know through looping the
919 			 * wwn_flag array.
920 			 */
921 			for (i = 0; i < input->wwnCount; i++) {
922 				if (input->wwn_flag[i] == 0) {
923 					(void *) fprintf(stderr, "%s %s %s\n",
924 					    gettext("Error: SAS Address"),
925 					    input->wwn_argv[i],
926 					    gettext("not found."));
927 					ret++;
928 				}
929 			}
930 		}
931 		/* even if no remote port is found it is not an error. */
932 	}
933 	if (input->wwn_flag) {
934 		free(input->wwn_flag);
935 		input->wwn_flag = NULL;
936 	}
937 	return (ret);
938 }
939 
940 /*
941  * This function will handle the phy stuff for hba-port subcommand.
942  *
943  * Arguments:
944  *      handle - handle to hba port.
945  *      portIndex - the index of hba port currently being processed.
946  *      port - pointer to hba port attributes.
947  *      pflag - options user specified.
948  *
949  *  Return Value:
950  *	    0		sucessfully processed handle
951  *	    >0		error has occured
952  */
953 static int
954 processHBAPortPhyInfo(HBA_HANDLE handle, HBA_UINT32 portIndex,
955     SMHBA_PORTATTRIBUTES *port, int pflag)
956 {
957 	int 		phyIndex = 0, err_cnt = 0;
958 	HBA_UINT32	numphys = 0;
959 	HBA_STATUS	status = 0;
960 	SMHBA_SAS_PHY	phyattrs;
961 
962 	if (port == NULL)
963 		return (++err_cnt);
964 
965 	numphys = port->PortSpecificAttribute.SASPort->NumberofPhys;
966 	if (numphys == 0)
967 		return (0);
968 
969 	if ((pflag & PRINT_PHY) || (pflag & PRINT_PHY_LINKSTAT))
970 		(void *) fprintf(stdout, "%s\n", "    Phy Information:");
971 	else
972 		return (0);
973 
974 
975 	for (phyIndex = 0; phyIndex < numphys; phyIndex++) {
976 		(void *) memset(&phyattrs, 0, sizeof (phyattrs));
977 		status = SMHBA_GetSASPhyAttributes(
978 		    handle, portIndex, phyIndex, &phyattrs);
979 		if (status != HBA_STATUS_OK) {
980 			(void *) fprintf(stderr, "%s %d %s %s\n",
981 			    gettext("Failed to get SAS Phy attributes"
982 			    "phyIndex"), phyIndex,
983 			    gettext("Reason:"),
984 			    getHBAStatus(status));
985 			err_cnt++;
986 			continue;
987 		}
988 		if (pflag & PRINT_PHY)
989 			printHBAPortPhyInfo(&phyattrs);
990 		if (pflag & PRINT_PHY_LINKSTAT)
991 			err_cnt += processHBAPortPhyStat(handle,
992 			    portIndex, phyIndex, &phyattrs, pflag);
993 	}
994 	return (err_cnt);
995 }
996 
997 /*
998  * This function will handle the phy stuff for hba-port subcommand.
999  *
1000  * Arguments:
1001  *      handle - handle to hba port.
1002  *      portIndex - the index of hba port currently being processed.
1003  *      port - pointer to hba port attributes.
1004  *      pflag - options user specified.
1005  *
1006  *  Return Value:
1007  *	    0		sucessfully processed handle
1008  *	    >0		error has occured
1009  */
1010 static int
1011 processHBAPortPhyStat(HBA_HANDLE handle, HBA_UINT32 portIndex, int phyIndex,
1012     PSMHBA_SAS_PHY phyattrs, int pflag)
1013 {
1014 	HBA_STATUS		status = 0;
1015 	SMHBA_PHYSTATISTICS	phystat;
1016 	SMHBA_SASPHYSTATISTICS	sasphystat;
1017 
1018 	if ((pflag & PRINT_PHY) == 0) {
1019 		(void *) fprintf(stdout, "%s %d\n",
1020 		    "      Identifier:", phyattrs->PhyIdentifier);
1021 	}
1022 
1023 	(void *) memset(&phystat, 0, sizeof (phystat));
1024 	(void *) memset(&sasphystat, 0, sizeof (sasphystat));
1025 	phystat.SASPhyStatistics = &sasphystat;
1026 	status = SMHBA_GetPhyStatistics(handle, portIndex, phyIndex, &phystat);
1027 	if (status != HBA_STATUS_OK) {
1028 		(void *) fprintf(stdout, "%s\n",
1029 		    "        Link Error Statistics:");
1030 		(void *) fprintf(stderr, "%s\n",
1031 		    gettext("            Failed to retrieve Link "
1032 		    "Error Statistics!"));
1033 		return (1);
1034 	}
1035 	printHBAPortPhyStatistics(phystat.SASPhyStatistics);
1036 	return (0);
1037 }
1038 
1039 /*
1040  * Check whether the pWWN exist in the WWNs list which specified by user.
1041  *
1042  * Arguments:
1043  *	input - contains all the input parameters.
1044  *	pWWN - pointer to the hba port sas address.
1045  *
1046  *  Return Value:
1047  *	    1		true, the pWWN exist in the sas address list specified.
1048  *	    0		false.
1049  */
1050 static int
1051 isPortWWNInArgv(inputArg_t *input, PHBA_WWN pWWN)
1052 {
1053 	int 		port_wwn_counter = 0;
1054 	int		portfound = 0;
1055 	uint64_t	hbaWWN;
1056 
1057 	/* list only ports given in wwn_argv */
1058 	for (port_wwn_counter = 0;
1059 	    port_wwn_counter < input->wwnCount;
1060 	    port_wwn_counter++) {
1061 		hbaWWN = strtoull(input->wwn_argv[port_wwn_counter], NULL,
1062 		    16);
1063 		if (hbaWWN == 0 && errno != 0)
1064 			continue;
1065 		if (wwnConversion(pWWN->wwn) == hbaWWN) {
1066 			if (input->wwn_flag) {
1067 				input->wwn_flag[port_wwn_counter]++;
1068 			}
1069 			portfound = 1;
1070 		}
1071 	}
1072 	return (portfound);
1073 }
1074 
1075 /*
1076  * Check whether the string value exists in the input list,
1077  * which specified by user.
1078  *
1079  * Arguments:
1080  *	input - contains all the input parameters.
1081  *	stringName - could be hba adapter name
1082  *	                      hba-port name.
1083  *
1084  *  Return Value:
1085  *	    1		true, the HBA exists in the list specified.
1086  *	    0		false.
1087  */
1088 static int
1089 isStringInArgv(inputArg_t *input, const char *stringName)
1090 {
1091 	int 		counter = 0;
1092 	int		found = 0;
1093 
1094 	/* list only hba(s) given in wwn_argv */
1095 	for (counter = 0;
1096 	    counter < input->wwnCount;
1097 	    counter++) {
1098 		if (strcmp(input->wwn_argv[counter],
1099 		    stringName) == 0) {
1100 			if (input->wwn_flag)
1101 				input->wwn_flag[counter]++;
1102 			found = 1;
1103 		}
1104 	}
1105 	return (found);
1106 }
1107 
1108 /*
1109  * Callback function for hba subcommand.
1110  *
1111  * Arguments:
1112  *      attrs - pointer to adapter attributes currently being processed.
1113  *	input - contains all the input parameters.
1114  *	numberOfPorts - number of ports of this HBA.
1115  *
1116  *  Return Value:
1117  *  	matching number
1118  */
1119 static int handleHBA(SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input,
1120     int numberOfPorts, const char *adapterName)
1121 {
1122 	int matchingHBA = 1;
1123 
1124 	if (input->wwnCount == 0) {
1125 		printHBAInfo(attrs, input->pflag, numberOfPorts, adapterName);
1126 	} else {
1127 		if (isStringInArgv(input, adapterName)) {
1128 			printHBAInfo(attrs,
1129 			    input->pflag, numberOfPorts, adapterName);
1130 		} else {
1131 			matchingHBA = 0;
1132 		}
1133 	}
1134 
1135 	return (matchingHBA);
1136 }
1137 
1138 /*
1139  * Callback function for hba-port subcommand.
1140  *
1141  * Arguments:
1142  *      handle - handle to hba port.
1143  *      portIndex - the index of hba port currently being processed.
1144  *      port - pointer to hba port attributes.
1145  *      attrs - pointer to adapter attributes currently being processed.
1146  *	input - contains all the input parameters.
1147  *
1148  *  Return Value:
1149  *	    0		sucessfully processed handle
1150  *	    >0		error has occured
1151  */
1152 /*ARGSUSED*/
1153 static int handleHBAPort(HBA_HANDLE handle, char *adapterName,
1154     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
1155     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
1156 {
1157 	int ret = 0;
1158 	printHBAPortInfo(port, attrs, input->pflag);
1159 	ret = processHBAPortPhyInfo(handle, portIndex, port, input->pflag);
1160 	return (ret);
1161 }
1162 
1163 /*
1164  * Callback function for expander subcommand.
1165  *
1166  * Arguments:
1167  *      handle - handle to hba port.
1168  *      portIndex - the index of hba port currently being processed.
1169  *      port - pointer to hba port attributes.
1170  *      attrs - pointer to adapter attributes currently being processed.
1171  *	input - contains all the input parameters.
1172  *
1173  *  Return Value:
1174  *	    0		sucessfully processed handle
1175  *	    >0		error has occured
1176  */
1177 /*ARGSUSED*/
1178 static int handleExpander(HBA_HANDLE handle, char *adapterName,
1179     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
1180     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
1181 {
1182 	SMHBA_PORTATTRIBUTES	attr;
1183 	SMHBA_SAS_PORT		sasport;
1184 	HBA_STATUS		status;
1185 	int			ret = 0;
1186 	int			i, numberOfRP;
1187 	rp_tree_t		*rpnode;
1188 	rp_tree_t		*rproot = NULL;
1189 	rp_tree_t		*unsolved_head = NULL;
1190 	rp_tree_t		*unsolved_tail = NULL;
1191 	rp_tree_t		*unsolved_sentinel = NULL;
1192 	int			printPort = 0;
1193 	int			numberOfEXP = 0;
1194 	int			unsolved_inserted = 0;
1195 	int			unsolved_left = 0;
1196 	int			disco_port_fail = 0;
1197 	boolean_t		firstPrinted = B_FALSE;
1198 
1199 	(void *) memset(&attr, 0, sizeof (attr));
1200 	(void *) memset(&sasport, 0, sizeof (sasport));
1201 	attr.PortSpecificAttribute.SASPort = &sasport;
1202 
1203 	/*
1204 	 * Retrive all expander device from this hba port first.
1205 	 */
1206 	if ((numberOfRP = port->PortSpecificAttribute.SASPort->
1207 	    NumberofDiscoveredPorts) == 0) {
1208 		/* no remote port. just return 0. */
1209 		return (ret);
1210 	}
1211 
1212 	for (i = 0; i < numberOfRP; i++) {
1213 		rpnode = calloc(1, sizeof (rp_tree_t));
1214 		rpnode->portattr.PortSpecificAttribute.SASPort =
1215 		    &rpnode->sasattr;
1216 		status = SMHBA_GetDiscoveredPortAttributes(handle,
1217 		    portIndex, i, &rpnode->portattr);
1218 		if (status != HBA_STATUS_OK) {
1219 			disco_port_fail++;
1220 			free(rpnode);
1221 			ret++;
1222 			continue;
1223 		}
1224 
1225 		if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
1226 			numberOfEXP++;
1227 		}
1228 		/*
1229 		 * We will try to insert this expander device and target
1230 		 * ports into the topology tree. If we failed, we can chain
1231 		 * them together and try again when we have all the
1232 		 * discovered port information in hands.
1233 		 */
1234 		if (rproot == NULL && memcmp(port->
1235 		    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
1236 		    rpnode->sasattr.AttachedSASAddress.wwn,
1237 		    sizeof (HBA_WWN)) == 0) {
1238 			/*
1239 			 * The root node of tree should
1240 			 * be set up first.
1241 			 */
1242 			rproot = rpnode;
1243 		} else {
1244 			/*
1245 			 * If we can not set up the root node of
1246 			 * the tree or we failed to insert
1247 			 * the disocvered port node, queue it up then.
1248 			 */
1249 			if (rproot == NULL ||
1250 			    sas_rp_tree_insert(&rproot, rpnode) != 0) {
1251 				if (unsolved_head == NULL) {
1252 					unsolved_head = rpnode;
1253 					unsolved_tail = rpnode;
1254 				} else {
1255 					rpnode->sibling = unsolved_head;
1256 					unsolved_head = rpnode;
1257 				}
1258 			}
1259 		}
1260 	}
1261 
1262 	if (disco_port_fail) {
1263 		(void *) fprintf(stderr, "%s %d %s %s\n",
1264 		    gettext("Error: Failed to get attributes for"),
1265 		    disco_port_fail,
1266 		    gettext("connected ports of HBA port"),
1267 		    port->OSDeviceName);
1268 	}
1269 
1270 	/* no expander found.  No need further processing. */
1271 	if (numberOfEXP == 0) {
1272 		while (unsolved_head) {
1273 			unsolved_tail =
1274 			    unsolved_head->sibling;
1275 			free(unsolved_head);
1276 			unsolved_head = unsolved_tail;
1277 		}
1278 		if (rproot) sas_rp_tree_free(rproot);
1279 		return (ret);
1280 	}
1281 
1282 	/*
1283 	 * When we're here, we should already have all information,
1284 	 * now we try again to insert them into the topology tree.
1285 	 * unsolved_head is the pointer which point to the head of
1286 	 * unsolved rpnode linked list.
1287 	 * unsolved_tail is the pointer which point to the tail of
1288 	 * unsolved rpnode linked list.
1289 	 * unsolved_sentinel is for insertion failure detection.
1290 	 * When we're trying to insert the rpnodes from unsolved
1291 	 * linked list, it may happen that some of the rpnodes can
1292 	 * not be inserted no matter how many times we loop through
1293 	 * this linked list. So we use unsolved_sentinel to identify
1294 	 * the tail of last round of scanning, and unsolved_inserted
1295 	 * which is a counter will be used to count how many rpnodes
1296 	 * have been inserted from last round, if it is zero, which
1297 	 * means that we can not insert rpnodes into rptree any more,
1298 	 * and we should stop and deallocate the memory they occupied.
1299 	 */
1300 	unsolved_sentinel = unsolved_tail;
1301 	while (unsolved_head) {
1302 		rpnode = unsolved_head;
1303 		unsolved_head = unsolved_head->sibling;
1304 		if (unsolved_head == NULL)
1305 			unsolved_tail = NULL;
1306 		rpnode->sibling = NULL;
1307 		if (sas_rp_tree_insert(&rproot, rpnode) != 0) {
1308 			unsolved_tail->sibling = rpnode;
1309 			unsolved_tail = rpnode;
1310 			if (rpnode == unsolved_sentinel) {
1311 				/*
1312 				 * We just scanned one round for the
1313 				 * unsolved list. Check to see whether we
1314 				 * have nodes inserted, if none, we should
1315 				 * break in case of an indefinite loop.
1316 				 */
1317 				if (unsolved_inserted == 0) {
1318 					/*
1319 					 * Indicate there is unhandled node.
1320 					 * Chain free the whole unsolved
1321 					 * list here.
1322 					 */
1323 					unsolved_left++;
1324 					break;
1325 				} else {
1326 					unsolved_inserted = 0;
1327 					unsolved_sentinel = unsolved_tail;
1328 				}
1329 			}
1330 		} else {
1331 			/*
1332 			 * We just inserted one rpnode, increment the
1333 			 * unsolved_inserted counter. We will utilize this
1334 			 * counter to detect an indefinite insertion loop.
1335 			 */
1336 			unsolved_inserted++;
1337 		}
1338 	}
1339 
1340 	/* check if there is left out discovered ports. */
1341 	if (unsolved_left) {
1342 		ret++;
1343 		(void *) fprintf(stderr, "%s %s\n",
1344 		    gettext("Error: Failed to establish expander topology on"),
1345 		    port->OSDeviceName);
1346 		(void *) fprintf(stderr, "%s\n",
1347 		    gettext("       Folowing port(s) are unresolved."));
1348 		while (unsolved_head) {
1349 			unsolved_tail =
1350 			    unsolved_head->sibling;
1351 			(void *) fprintf(stderr, "%s%016llx ",
1352 			    firstPrinted ? "" : "\t",
1353 			    wwnConversion(unsolved_head->sasattr.
1354 			    LocalSASAddress.wwn));
1355 			if (firstPrinted == B_FALSE) firstPrinted = B_TRUE;
1356 			free(unsolved_head);
1357 			unsolved_head = unsolved_tail;
1358 		}
1359 		(void *) fprintf(stderr, "\n");
1360 		/* still print what we have */
1361 		ret += sas_rp_tree_print(handle, adapterName, portIndex,
1362 		    port, rproot, input, 2 * TABLEN, &printPort);
1363 	} else {
1364 		ret += sas_rp_tree_print(handle, adapterName, portIndex,
1365 		    port, rproot, input, 2 * TABLEN, &printPort);
1366 	}
1367 
1368 	if (rproot) sas_rp_tree_free(rproot);
1369 
1370 	return (ret);
1371 }
1372 
1373 /*
1374  * Callback function for target-port subcommand.
1375  *
1376  * Arguments:
1377  *      handle - handle to hba port.
1378  *      portIndex - the index of hba port currently being processed.
1379  *      port - pointer to hba port attributes.
1380  *      attrs - pointer to adapter attributes currently being processed.
1381  *	input - contains all the input parameters.
1382  *
1383  *  Return Value:
1384  *	    0		sucessfully processed handle
1385  *	    >0		error has occured
1386  */
1387 /*ARGSUSED*/
1388 static int handleTargetPort(HBA_HANDLE handle, char *adapterName,
1389     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
1390     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
1391 {
1392 	HBA_STATUS		status;
1393 	SMHBA_PORTATTRIBUTES	targetattr;
1394 	SMHBA_SAS_PORT		sasattr;
1395 	int			i;
1396 	int			ret = 0;
1397 	int			disco_port_fail = 0;
1398 
1399 	targetattr.PortSpecificAttribute.SASPort = &sasattr;
1400 
1401 	for (i = 0; i < port->PortSpecificAttribute.SASPort->
1402 	    NumberofDiscoveredPorts; i++) {
1403 		status = SMHBA_GetDiscoveredPortAttributes(handle,
1404 		    portIndex, i, &targetattr);
1405 		if (status != HBA_STATUS_OK) {
1406 			disco_port_fail++;
1407 		} else {
1408 			/* skip expander device */
1409 			if (targetattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
1410 				ret += searchTargetPort(handle, portIndex, port,
1411 				    &targetattr, &sasattr, input->pflag);
1412 			}
1413 		}
1414 	}
1415 
1416 	if (disco_port_fail) {
1417 		ret++;
1418 		(void *) fprintf(stderr, "%s %d %s %s\n",
1419 		    gettext("Error: Failed to get attributes for"),
1420 		    disco_port_fail,
1421 		    gettext("connected ports of HBA port"),
1422 		    port->OSDeviceName);
1423 	}
1424 	return (ret);
1425 }
1426 
1427 /*
1428  * ****************************************************************************
1429  *
1430  * compareLUName -
1431  * 	compare names directly and also check if disk namees match with
1432  *	different slice number or /devices path are speicified and matches.
1433  *
1434  * cmdArg	- first string to compare
1435  * osName	- os name from attributes
1436  *
1437  * returns 	B_TRUE if the strings match either directly or via devid
1438  *		B_FALSE otherwise
1439  *
1440  * ****************************************************************************
1441  */
1442 static boolean_t
1443 compareLUName(char *cmdArg, char *osName)
1444 {
1445 
1446 	boolean_t	isSame = B_FALSE;
1447 	char		dev1[MAXPATHLEN], dev2[MAXPATHLEN];
1448 	char		*ch1, *ch2;
1449 
1450 	if (strcmp(cmdArg, osName) == 0) {
1451 		isSame = B_TRUE;
1452 	} else {
1453 		/* user input didn't match, try to  match the core of args. */
1454 		(void) strlcpy(dev1, cmdArg, MAXPATHLEN);
1455 		(void) strlcpy(dev2, osName, MAXPATHLEN);
1456 		/* is this /devices path */
1457 		if (((ch1 = strrchr(dev1, ',')) != NULL) &&
1458 		    ((ch2 = strrchr(dev2, ',')) != NULL)) {
1459 			*ch1 = *ch2 = '\0';
1460 			if (strcmp(dev1, dev2) == 0) {
1461 				isSame = B_TRUE;
1462 			}
1463 		/* is this a /dev link */
1464 		} else if ((strncmp(dev1, "/dev/", 5) == 0) &&
1465 		    (strncmp(dev2, "/dev/", 5) == 0)) {
1466 			if ((strstr(dev1, "dsk") != NULL) &&
1467 			    ((strstr(dev2, "dsk") != NULL))) {
1468 				/* if it is disk link */
1469 				if (((ch1 = strrchr(dev1, 's')) != NULL) &&
1470 				    ((ch2 = strrchr(dev2, 's')) != NULL)) {
1471 					*ch1 = *ch2 = '\0';
1472 					if (strcmp(dev1, dev2) == 0) {
1473 						isSame = B_TRUE;
1474 					}
1475 				}
1476 			} else {
1477 				/* other dev links */
1478 				if (strcmp(dev1, dev2) == 0) {
1479 					isSame = B_TRUE;
1480 				}
1481 			}
1482 		}
1483 	} /* compare */
1484 
1485 	return (isSame);
1486 }
1487 
1488 /*
1489  * Process logical-unit(lu) subcommand.
1490  *
1491  * Arguments:
1492  *      luCount - number of OS device name(s) specified by user.
1493  *      luArgv - array of OS device name(s) specified by user.
1494  *      options - all the options specified by user.
1495  *
1496  *  Return Value:
1497  *	    0		sucessfully processed handle
1498  *	    >0		error has occured
1499  */
1500 int
1501 sas_util_list_logicalunit(int luCount, char **luArgv, cmdOptions_t *options)
1502 {
1503 	HBA_STATUS		status;
1504 	int			processHBA_flags = 0;
1505 	int			lu;
1506 	boolean_t		pathFound;
1507 	boolean_t		verbose;
1508 	inputArg_t		input;
1509 	discoveredDevice	*LUListWalk = NULL;
1510 	int			err_cnt = 0;
1511 
1512 	for (; options->optval; options++) {
1513 		if (options->optval == 'v') {
1514 			processHBA_flags |= PRINT_VERBOSE;
1515 		}
1516 	}
1517 
1518 	/* HBA_LoadLibrary() */
1519 	if ((status = HBA_LoadLibrary()) != HBA_STATUS_OK) {
1520 		(void *) fprintf(stderr, "%s %s\n",
1521 		    gettext("Failed to load SM-HBA libraries."
1522 		    "Reason:"), getHBAStatus(status));
1523 		err_cnt++;
1524 		return (err_cnt);
1525 	}
1526 
1527 	(void *) memset(&input, 0, sizeof (input));
1528 	input.pflag = processHBA_flags;
1529 	input.wwnCount = luCount;
1530 	input.wwn_argv = luArgv;
1531 
1532 	err_cnt += processHBA(&input, handleLogicalUnit);
1533 	verbose = (input.pflag & PRINT_VERBOSE) ? B_TRUE : B_FALSE;
1534 
1535 	if (luCount == 0) {
1536 		/* list all paths */
1537 		for (LUListWalk = LUList; LUListWalk != NULL;
1538 		    LUListWalk = LUListWalk->next) {
1539 			err_cnt += printOSDeviceNameInfo(LUListWalk, verbose);
1540 		}
1541 	} else {
1542 		/*
1543 		 * When operands provided, we should set the error code
1544 		 * only if there are issues related with the operands.
1545 		 */
1546 		err_cnt = 0;
1547 		/*
1548 		 * list any paths not found first
1549 		 * this gives the user cleaner output
1550 		 */
1551 		for (lu = 0; lu < luCount; lu++) {
1552 			for (LUListWalk = LUList, pathFound = B_FALSE;
1553 			    LUListWalk != NULL;
1554 			    LUListWalk = LUListWalk->next) {
1555 				if (compareLUName(luArgv[lu],
1556 				    LUListWalk->OSDeviceName)) {
1557 					pathFound = B_TRUE;
1558 					break;
1559 				}
1560 			}
1561 			if (pathFound == B_FALSE) {
1562 				(void *) fprintf(stderr,
1563 				    "Error: Logical Unit %s Not Found \n",
1564 				    luArgv[lu]);
1565 				err_cnt++;
1566 			}
1567 		}
1568 		/* list all paths requested in order requested */
1569 		for (lu = 0; lu < luCount; lu++) {
1570 			for (LUListWalk = LUList; LUListWalk != NULL;
1571 			    LUListWalk = LUListWalk->next) {
1572 				if (compareLUName(luArgv[lu],
1573 				    LUListWalk->OSDeviceName)) {
1574 					err_cnt += printOSDeviceNameInfo(
1575 					    LUListWalk,
1576 					    verbose);
1577 				}
1578 			}
1579 		}
1580 	}
1581 	(void) HBA_FreeLibrary();
1582 	return (err_cnt);
1583 }
1584 
1585 /*
1586  * Callback function for logical-unit(lu) subcommand.
1587  *
1588  * Arguments:
1589  *      handle - handle to hba port.
1590  *      portIndex - the index of hba port currently being processed.
1591  *      port - pointer to hba port attributes.
1592  *      attrs - pointer to adapter attributes currently being processed.
1593  *	input - contains all the input parameters.
1594  *
1595  *  Return Value:
1596  *	    0		sucessfully processed handle
1597  *	    >0		error has occured
1598  */
1599 /*ARGSUSED*/
1600 static int handleLogicalUnit(HBA_HANDLE handle, char *adapterName,
1601     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
1602     SMHBA_ADAPTERATTRIBUTES *attrs, inputArg_t *input)
1603 {
1604 	HBA_STATUS		status;
1605 	SMHBA_TARGETMAPPING	*map;
1606 	HBA_WWN			hbaPortWWN, domainPortWWN;
1607 	char			*portName = NULL;
1608 	int			numentries;
1609 	int			count = 0;
1610 	int			ret = 0;
1611 
1612 	hbaPortWWN = port->PortSpecificAttribute.SASPort->LocalSASAddress;
1613 	portName = port->OSDeviceName;
1614 
1615 	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
1616 	switch (status) {
1617 		case HBA_STATUS_OK:
1618 			break;
1619 		case HBA_STATUS_ERROR_NOT_SUPPORTED:
1620 			/* don't increase error flag for no phy configuration */
1621 			return (ret);
1622 		case HBA_STATUS_ERROR:
1623 		default:
1624 			return (++ret);
1625 	}
1626 
1627 	if ((map = calloc(1, sizeof (*map))) == NULL) {
1628 		(void *) fprintf(stderr, "%s\n",
1629 		    gettext("No enough memory on heap."));
1630 		return (++ret);
1631 	}
1632 	map->NumberOfEntries = 1;
1633 
1634 	/*
1635 	 * First, we need to get the target mapping data from this hba
1636 	 * port.
1637 	 */
1638 	status = SMHBA_GetTargetMapping(handle,
1639 	    hbaPortWWN, domainPortWWN, map);
1640 
1641 	if (status == HBA_STATUS_ERROR_MORE_DATA) {
1642 		numentries = map->NumberOfEntries;
1643 		free(map);
1644 		map = calloc(1, sizeof (HBA_UINT32) +
1645 		    (numentries * sizeof (SMHBA_SCSIENTRY)));
1646 		if (map == NULL) {
1647 			(void *) fprintf(stderr, "%s\n",
1648 			    gettext("No enough memory on heap."));
1649 			return (++ret);
1650 		}
1651 		map->NumberOfEntries = numentries;
1652 		status = SMHBA_GetTargetMapping(handle,
1653 		    hbaPortWWN, domainPortWWN, map);
1654 	}
1655 
1656 	if (status != HBA_STATUS_OK) {
1657 		(void *) fprintf(stderr, "%s %016llx %s %s\n",
1658 		    gettext("Error: Failed to get SCSI mapping data for "
1659 		    "the HBA port"), wwnConversion(hbaPortWWN.wwn),
1660 		    gettext("Reason:"),
1661 		    getHBAStatus(status));
1662 		free(map);
1663 		return (++ret);
1664 	}
1665 
1666 	/*
1667 	 * By iterating each entry of the targetmapping data, we will
1668 	 * construct a global list of logical unit.
1669 	 */
1670 	for (count = 0; count < map->NumberOfEntries; count++) {
1671 		ret += searchDevice(
1672 		    &(map->entry[count]), handle, hbaPortWWN, domainPortWWN,
1673 		    portName, input->pflag);
1674 	}
1675 	free(map);
1676 	return (ret);
1677 }
1678 
1679 /*
1680  * Search the matching targetmapping data for given target port and SAM LUN
1681  *	and return target mapping data if found.
1682  *
1683  * Arguments:
1684  *      handle - handle to hba port.
1685  *      portIndex - hba port index
1686  *      port - hba port attributes.
1687  *      targetportWWN - target port SAS address.
1688  *      domainportWWN - domain port SAS address.
1689  *      domainportttr - target port SAS attributes.
1690  *      samLUN - samLUN from report LUNs data.
1691  *      data - matching target mapping data.
1692  *
1693  *  Return Value:
1694  *	    0		sucessfully processed handle
1695  *	    >0		error has occured
1696  */
1697 static int
1698 searchTargetPortMappingData(HBA_HANDLE handle, HBA_UINT32 portIndex,
1699     SMHBA_PORTATTRIBUTES *port, SMHBA_SAS_PORT *sasattr,
1700     struct targetPortConfig *configData)
1701 {
1702 	int			ret = 0;
1703 	HBA_STATUS		status;
1704 	SMHBA_TARGETMAPPING	*map = NULL;
1705 	HBA_WWN			hbaPortWWN, domainPortWWN;
1706 	int			numentries, count;
1707 	targetPortMappingData_t	*TPMapData;
1708 	struct scsi_inquiry	inq;
1709 	struct scsi_extended_sense  sense;
1710 	HBA_UINT32		responseSize, senseSize = 0;
1711 	uchar_t			rawLUNs[DEFAULT_LUN_LENGTH], *lun_string;
1712 	HBA_UINT8		scsiStatus;
1713 	rep_luns_rsp_t		*lun_resp;
1714 	int			lunNum, numberOfLun, lunCount;
1715 	uint32_t		lunlength, tmp_lunlength;
1716 	uint64_t		sasLUN;
1717 	SMHBA_SCSILUN		smhbaLUN;
1718 
1719 	hbaPortWWN = port->PortSpecificAttribute.SASPort->
1720 	    LocalSASAddress;
1721 
1722 	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
1723 	if (status == HBA_STATUS_OK) {
1724 		if ((map = calloc(1, sizeof (*map))) == NULL) {
1725 			(void *) fprintf(stderr, "%s\n",
1726 			gettext("No enough memory on heap."));
1727 			return (++ret);
1728 		}
1729 		map->NumberOfEntries = 1;
1730 
1731 		status = SMHBA_GetTargetMapping(handle, hbaPortWWN,
1732 		    domainPortWWN, map);
1733 
1734 		if (status == HBA_STATUS_ERROR_MORE_DATA) {
1735 			numentries = map->NumberOfEntries;
1736 			free(map);
1737 			map = calloc(1, sizeof (HBA_UINT32) +
1738 			    (numentries * sizeof (SMHBA_SCSIENTRY)));
1739 			if (map == NULL) {
1740 				(void *) fprintf(stderr, "%s\n",
1741 				    gettext("No enough memory on heap."));
1742 				return (++ret);
1743 			}
1744 			map->NumberOfEntries = numentries;
1745 			status = SMHBA_GetTargetMapping(handle,
1746 			    hbaPortWWN, domainPortWWN, map);
1747 		}
1748 
1749 		if (status != HBA_STATUS_OK) {
1750 			/* continue to build mapping data based SCSI info */
1751 			ret++;
1752 			free(map);
1753 			map = NULL;
1754 		}
1755 	}
1756 
1757 	/*
1758 	 * Get report lun data.
1759 	 */
1760 	responseSize = DEFAULT_LUN_LENGTH;
1761 	senseSize = sizeof (struct scsi_extended_sense);
1762 	(void) memset(&sense, 0, sizeof (sense));
1763 	status = SMHBA_ScsiReportLUNs(
1764 	    handle,
1765 	    hbaPortWWN,
1766 	    sasattr->LocalSASAddress,
1767 	    domainPortWWN,
1768 	    (void *)rawLUNs,
1769 	    &responseSize,
1770 	    &scsiStatus,
1771 	    (void *) &sense, &senseSize);
1772 
1773 	/*
1774 	 * if HBA_STATUS_ERROR_NOT_A_TARGET is return, we can assume this is
1775 	 * a remote HBA and move on
1776 	 */
1777 	if (status != HBA_STATUS_OK) {
1778 		configData->reportLUNsFailed = B_TRUE;
1779 		if (map != NULL) {
1780 			/*
1781 			 * Let's search mapping data and indicate that Report
1782 			 * LUNs failed.
1783 			 */
1784 			for (count = 0; count < map->NumberOfEntries; count++) {
1785 				if (memcmp(map->entry[count].PortLun.
1786 				    PortWWN.wwn, sasattr->LocalSASAddress.wwn,
1787 				    sizeof (HBA_WWN)) == 0) {
1788 					/* allocate mapping data for each LUN */
1789 					TPMapData = calloc(1,
1790 					    sizeof (targetPortMappingData_t));
1791 					if (TPMapData == NULL) {
1792 						(void *) fprintf(stderr, "%s\n",
1793 						    gettext("No enough "
1794 						    "memory."));
1795 						free(map);
1796 						return (++ret);
1797 					}
1798 					TPMapData->mappingExist = B_TRUE;
1799 					TPMapData->osLUN =
1800 					    map->entry[count].ScsiId.ScsiOSLun;
1801 					(void) strlcpy(TPMapData->osDeviceName,
1802 					    map->entry[count].ScsiId.
1803 					    OSDeviceName,
1804 					    sizeof (TPMapData->osDeviceName));
1805 					TPMapData->inq_vid[0] = '\0';
1806 					TPMapData->inq_pid[0] = '\0';
1807 					TPMapData->inq_dtype = DTYPE_UNKNOWN;
1808 					if (configData->map == NULL) {
1809 						configData->map = TPMapData;
1810 					} else {
1811 						TPMapData->next =
1812 						    configData->map->next;
1813 						configData->map = TPMapData;
1814 					}
1815 				}
1816 			}
1817 		}
1818 		(void) free(map);
1819 		return (++ret);
1820 	}
1821 	lun_resp = (rep_luns_rsp_t *)((void *)rawLUNs);
1822 	(void) memcpy(&tmp_lunlength, &(lun_resp->length),
1823 	    sizeof (tmp_lunlength));
1824 	lunlength = ntohl(tmp_lunlength);
1825 	(void) memcpy(&numberOfLun, &lunlength, sizeof (numberOfLun));
1826 	for (lunCount = 0; lunCount < (numberOfLun / 8); lunCount++) {
1827 		/* allocate mapping data for each LUN */
1828 		TPMapData = calloc(1,
1829 		    sizeof (targetPortMappingData_t));
1830 		if (TPMapData == NULL) {
1831 			(void *) fprintf(stderr, "%s\n",
1832 			    gettext("No enough memory."));
1833 			free(map);
1834 			return (++ret);
1835 		}
1836 
1837 		(void) memcpy(&TPMapData->reportLUN, lun_resp->
1838 		    lun[lunCount].val, sizeof (SMHBA_SCSILUN));
1839 
1840 		/*
1841 		 * now issue standard inquiry to get Vendor
1842 		 * and product information
1843 		 */
1844 		responseSize = sizeof (struct scsi_inquiry);
1845 		senseSize = sizeof (struct scsi_extended_sense);
1846 		(void) memset(&inq, 0, sizeof (struct scsi_inquiry));
1847 		(void) memset(&sense, 0, sizeof (sense));
1848 		sasLUN = ntohll(wwnConversion(lun_resp->lun[lunCount].val));
1849 		(void) memcpy(&smhbaLUN, &sasLUN, sizeof (SMHBA_SCSILUN));
1850 		status = SMHBA_ScsiInquiry(
1851 		    handle,
1852 		    hbaPortWWN,
1853 		    sasattr->LocalSASAddress,
1854 		    domainPortWWN,
1855 		    smhbaLUN,
1856 		    0,
1857 		    0,
1858 		    (void *) &inq, &responseSize,
1859 		    &scsiStatus,
1860 		    (void *) &sense, &senseSize);
1861 		if (status != HBA_STATUS_OK) {
1862 			TPMapData->inq_vid[0] = '\0';
1863 			TPMapData->inq_pid[0] = '\0';
1864 			TPMapData->inq_dtype = DTYPE_UNKNOWN;
1865 			/* indicate that inquiry for this lun is failed */
1866 			TPMapData->inquiryFailed = B_TRUE;
1867 		} else {
1868 			(void *) memcpy(TPMapData->inq_vid, inq.inq_vid,
1869 			    sizeof (TPMapData->inq_vid));
1870 			(void *) memcpy(TPMapData->inq_pid, inq.inq_pid,
1871 			    sizeof (TPMapData->inq_pid));
1872 			TPMapData->inq_dtype = inq.inq_dtype;
1873 		}
1874 
1875 		if (map != NULL) {
1876 			for (count = 0; count < map->NumberOfEntries; count++) {
1877 				if ((memcmp(map->entry[count].PortLun.
1878 				    PortWWN.wwn, sasattr->LocalSASAddress.wwn,
1879 				    sizeof (HBA_WWN)) == 0) &&
1880 				    (memcmp(&(map->entry[count].PortLun.
1881 				    TargetLun), &smhbaLUN,
1882 				    sizeof (SMHBA_SCSILUN))
1883 				    == 0)) {
1884 					TPMapData->mappingExist = B_TRUE;
1885 					TPMapData->osLUN =
1886 					    map->entry[count].ScsiId.ScsiOSLun;
1887 					(void) strlcpy(TPMapData->osDeviceName,
1888 					    map->entry[count].ScsiId.
1889 					    OSDeviceName,
1890 					    sizeof (TPMapData->osDeviceName));
1891 					break;
1892 				}
1893 			}
1894 			if (count == map->NumberOfEntries) {
1895 				TPMapData->osDeviceName[0] = '\0';
1896 				lun_string = lun_resp->lun[lunCount].val;
1897 				lunNum = ((lun_string[0] & 0x3F) << 8) |
1898 				    lun_string[1];
1899 				TPMapData->osLUN = lunNum;
1900 			}
1901 		} else {
1902 		/* Not able to get any target mapping information */
1903 			TPMapData->osDeviceName[0] = '\0';
1904 			lun_string = lun_resp->lun[lunCount].val;
1905 			lunNum = ((lun_string[0] & 0x3F) << 8) |
1906 			    lun_string[1];
1907 			TPMapData->osLUN = lunNum;
1908 		}
1909 
1910 		if (configData->map == NULL) {
1911 			configData->map = TPMapData;
1912 		} else {
1913 			TPMapData->next = configData->map->next;
1914 			configData->map = TPMapData;
1915 		}
1916 	}
1917 	free(map);
1918 	return (ret);
1919 }
1920 
1921 /*
1922  * Search the discovered LUs and construct the global LU list.
1923  *
1924  * Arguments:
1925  *      handle - handle to hba port.
1926  *      portIndex - hba port index
1927  *      port - hba port attributes.
1928  *      targetattr - target port attributes.
1929  *      sasattr - target port SAS attributes.
1930  *      pflag - options the user specified.
1931  *
1932  *  Return Value:
1933  *	    0		sucessfully processed handle
1934  *	    >0		error has occured
1935  */
1936 static int
1937 searchTargetPort(HBA_HANDLE handle, HBA_UINT32 portIndex,
1938     SMHBA_PORTATTRIBUTES *port, SMHBA_PORTATTRIBUTES *targetattr,
1939     SMHBA_SAS_PORT *sasattr, int pflag)
1940 {
1941 	int			ret = 0;
1942 	HBA_WWN			expander;
1943 	HBA_WWN			domainPortWWN;
1944 	targetPortList_t 	*discoveredTP, *newTP;
1945 	targetPortConfig_t	*TPConfig, *newConfig, *prevConfig;
1946 	boolean_t		foundTP = B_FALSE;
1947 	boolean_t		foundConfig = B_FALSE;
1948 	int			status;
1949 	SMHBA_PORTATTRIBUTES	tgtattr;
1950 	SMHBA_SAS_PORT		tgtsasport;
1951 	int			expanderValid = 0;
1952 
1953 	status = get_domainPort(handle, portIndex, port, &domainPortWWN);
1954 	switch (status) {
1955 		case HBA_STATUS_OK:
1956 			break;
1957 		case HBA_STATUS_ERROR_NOT_SUPPORTED:
1958 			/* don't increase error flag for no phy configuration */
1959 			return (ret);
1960 		case HBA_STATUS_ERROR:
1961 		default:
1962 			return (++ret);
1963 	}
1964 
1965 	/*
1966 	 * First, we will iterate the already constructed target port
1967 	 * list to see whether there is a target port already exist with
1968 	 * matching target port SAS address.
1969 	 */
1970 	for (discoveredTP = gTargetPortList; discoveredTP != NULL;
1971 	    discoveredTP = discoveredTP->next) {
1972 		if (memcmp((void *)sasattr->LocalSASAddress.wwn,
1973 		    (void *)discoveredTP->sasattr.LocalSASAddress.wwn,
1974 		    sizeof (HBA_WWN)) == 0) {
1975 			/*
1976 			 * if the target port exist and
1977 			 * verbose is not set, just return
1978 			 */
1979 			if (((pflag & PRINT_VERBOSE) == 0) &&
1980 			    ((pflag & PRINT_TARGET_SCSI) == 0)) {
1981 				return (ret);
1982 			}
1983 			foundTP = B_TRUE;
1984 			break;
1985 		}
1986 	}
1987 
1988 	if (foundTP == B_TRUE) {
1989 		/*
1990 		 * If there is a target port already exist, we should
1991 		 * add more information on the target port to construct the
1992 		 * whole topology.
1993 		 * Here we will check whether the current hba port name
1994 		 * has already been added.
1995 		 */
1996 		/* first get the expander SAS address compare */
1997 		if (memcmp((void *)port->PortSpecificAttribute.SASPort->
1998 		    LocalSASAddress.wwn, (void *)sasattr->
1999 		    AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
2000 			/* NO expander */
2001 			(void) memset((void *)expander.wwn, 0,
2002 			    sizeof (HBA_WWN));
2003 			expanderValid = 1;
2004 		} else {
2005 			if (wwnConversion(sasattr->AttachedSASAddress.wwn)
2006 			    != 0) {
2007 				/* expander exist.  We should verify it.  */
2008 				(void) memcpy((void *)expander.wwn,
2009 				    (void *)sasattr->AttachedSASAddress.wwn,
2010 				    sizeof (HBA_WWN));
2011 
2012 				(void *) memset(&tgtattr, 0, sizeof (tgtattr));
2013 				(void *) memset(&tgtsasport, 0,
2014 				    sizeof (tgtsasport));
2015 				tgtattr.PortSpecificAttribute.SASPort
2016 				    = &tgtsasport;
2017 				status = SMHBA_GetPortAttributesByWWN(handle,
2018 				    sasattr->AttachedSASAddress, domainPortWWN,
2019 				    &tgtattr);
2020 				if (status == HBA_STATUS_OK && tgtattr.PortType
2021 				    == HBA_PORTTYPE_SASEXPANDER) {
2022 					expanderValid = 1;
2023 				}
2024 			}
2025 		}
2026 
2027 		for (TPConfig = discoveredTP->configEntry,
2028 		    foundConfig = B_FALSE; TPConfig != NULL;
2029 		    TPConfig = TPConfig->next) {
2030 			if ((strcmp(TPConfig->hbaPortName,
2031 			    port->OSDeviceName) == 0) &&
2032 			    (memcmp((void *)expander.wwn, (void *)TPConfig->
2033 			    expanderSASAddr.wwn,
2034 			    sizeof (HBA_WWN)) == 0)) {
2035 				foundConfig = B_TRUE;
2036 				break;
2037 			}
2038 		}
2039 
2040 		/*
2041 		 * If we get here, it means that it is a new hba port/exapnder
2042 		 * sas address for this discovered target port.
2043 		 */
2044 		if (foundConfig == B_FALSE) {
2045 			newConfig = (targetPortConfig_t *)calloc(1,
2046 			    sizeof (targetPortConfig_t));
2047 			if (newConfig == NULL) {
2048 				(void *) fprintf(stderr,
2049 				    "%s\n", strerror(errno));
2050 				return (++ret);
2051 			}
2052 
2053 			(void) strlcpy(newConfig->hbaPortName, port->
2054 			    OSDeviceName, sizeof (newConfig->hbaPortName));
2055 			(void) memcpy((void *)newConfig->expanderSASAddr.wwn,
2056 			    (void *)expander.wwn, sizeof (HBA_WWN));
2057 			newConfig->expanderValid = expanderValid;
2058 			if (discoveredTP->configEntry == NULL) {
2059 				discoveredTP->configEntry = newConfig;
2060 			} else {
2061 				TPConfig = discoveredTP->configEntry;
2062 				prevConfig = TPConfig;
2063 				while (TPConfig != NULL &&
2064 				    sas_name_comp(newConfig->hbaPortName,
2065 				    TPConfig->hbaPortName) > 0) {
2066 					prevConfig = TPConfig;
2067 					TPConfig = TPConfig->next;
2068 				}
2069 				if (TPConfig == prevConfig) {
2070 					/* Should be inserted in the head. */
2071 					newConfig->next = TPConfig;
2072 					discoveredTP->configEntry = newConfig;
2073 				} else {
2074 					newConfig->next = TPConfig;
2075 					prevConfig->next = newConfig;
2076 				}
2077 			}
2078 			/* if scsi option is not set return */
2079 			if ((pflag & PRINT_TARGET_SCSI) == 0) {
2080 				return (0);
2081 			} else {
2082 				return (searchTargetPortMappingData(
2083 				    handle, portIndex, port,
2084 				    sasattr, newConfig));
2085 			}
2086 		}
2087 	} else {
2088 		/*
2089 		 * Here we got a new target port which has not ever exist
2090 		 * in our global target port list. So add it to the list.
2091 		 * list.
2092 		 */
2093 		newTP = (targetPortList_t *)calloc(1,
2094 		    sizeof (targetPortList_t));
2095 
2096 		if (newTP == NULL) {
2097 			(void *) fprintf(stderr, "%s\n", strerror(errno));
2098 			return (++ret);
2099 		}
2100 
2101 		(void) memcpy((void *)&newTP->targetattr, (void *)targetattr,
2102 		    sizeof (SMHBA_PORTATTRIBUTES));
2103 		(void) memcpy((void *)&newTP->sasattr, (void *)sasattr,
2104 		    sizeof (SMHBA_SAS_PORT));
2105 
2106 		newConfig = (targetPortConfig_t *)calloc(1,
2107 		    sizeof (targetPortConfig_t));
2108 
2109 		if (newConfig == NULL) {
2110 			(void *) fprintf(stderr, "%s\n", strerror(errno));
2111 			free(newTP);
2112 			return (++ret);
2113 		}
2114 
2115 		(void) strlcpy(newConfig->hbaPortName, port->OSDeviceName,
2116 		    sizeof (newConfig->hbaPortName));
2117 		if (memcmp((void *)port->PortSpecificAttribute.SASPort->
2118 		    LocalSASAddress.wwn, (void *)sasattr->
2119 		    AttachedSASAddress.wwn, sizeof (HBA_WWN)) == 0) {
2120 			/* NO expander */
2121 			(void) memset((void *)newConfig->expanderSASAddr.wwn,
2122 			    0, sizeof (HBA_WWN));
2123 		} else {
2124 			/* expander exist.  We should verify it. */
2125 			(void) memcpy((void *)newConfig->expanderSASAddr.wwn,
2126 			    (void *)sasattr->AttachedSASAddress.wwn,
2127 			    sizeof (HBA_WWN));
2128 
2129 			(void *) memset(&tgtattr, 0, sizeof (tgtattr));
2130 			(void *) memset(&tgtsasport, 0, sizeof (tgtsasport));
2131 			tgtattr.PortSpecificAttribute.SASPort = &tgtsasport;
2132 			status = SMHBA_GetPortAttributesByWWN(handle,
2133 			    sasattr->AttachedSASAddress, domainPortWWN,
2134 			    &tgtattr);
2135 			if (status == HBA_STATUS_OK && tgtattr.PortType ==
2136 			    HBA_PORTTYPE_SASEXPANDER) {
2137 				expanderValid = 1;
2138 			}
2139 			newConfig->expanderValid = expanderValid;
2140 		}
2141 
2142 		newTP->configEntry = newConfig;
2143 
2144 		newTP->next = gTargetPortList; /* insert at head */
2145 		gTargetPortList = newTP; /* set new head */
2146 
2147 		/* if scsi option is not set return */
2148 		if ((pflag & PRINT_TARGET_SCSI) == 0) {
2149 			return (0);
2150 		} else {
2151 			return (searchTargetPortMappingData(
2152 			    handle, portIndex, port, sasattr, newConfig));
2153 		}
2154 	}
2155 	return (ret);
2156 }
2157 
2158 /*
2159  * Search the discovered LUs and construct the global LU list.
2160  *
2161  * Arguments:
2162  *      entryP - one of the target mapping data.
2163  *      handle - handle to hba port.
2164  *      hbaPortWWN - hba port sas address.
2165  *      domainPortWWN - domain port WWN for this sas domain.
2166  *      portName - HBA port OS Device Name.
2167  *      pflag - options the user specified.
2168  *
2169  *  Return Value:
2170  *	    0		sucessfully processed handle
2171  *	    >0		error has occured
2172  */
2173 static int
2174 searchDevice(PSMHBA_SCSIENTRY entryP,
2175     HBA_HANDLE handle, HBA_WWN hbaPortWWN, HBA_WWN domainPortWWN,
2176     char *portName, int pflag)
2177 {
2178 	HBA_STATUS		status;
2179 	int			ret = 0;
2180 	discoveredDevice 	*discoveredLU, *newDevice;
2181 	portList		*portElem, *newPort, *prevElem;
2182 	tgtPortWWNList 		*newTgtWWN, *TgtWWNList;
2183 	boolean_t		foundDevice = B_FALSE;
2184 	boolean_t		foundPort = B_FALSE;
2185 	struct scsi_inquiry	inq;
2186 	HBA_UINT32		responseSize, senseSize = 0;
2187 	HBA_UINT8		inq_status;
2188 	SMHBA_SCSILUN		smhbaLUN;
2189 	struct scsi_extended_sense sense;
2190 
2191 	/* if OSDeviceName is not set, we don't need to search */
2192 	if (entryP->ScsiId.OSDeviceName[0] == '\0') {
2193 		return (ret);
2194 	}
2195 
2196 	/*
2197 	 * First, we will iterate the already constructed discovered LU
2198 	 * list to see whether there is a LU already exist with the same OS
2199 	 * device name as current target mapping data entry.
2200 	 */
2201 	for (discoveredLU = LUList; discoveredLU != NULL;
2202 	    discoveredLU = discoveredLU->next) {
2203 		if (strcmp(entryP->ScsiId.OSDeviceName,
2204 		    discoveredLU->OSDeviceName) == 0) {
2205 			/*
2206 			 * if there is existing OS Device Name and
2207 			 * verbose is not set, just return
2208 			 */
2209 			if ((pflag & PRINT_VERBOSE) == 0) {
2210 				return (ret);
2211 			}
2212 			foundDevice = B_TRUE;
2213 			break;
2214 		}
2215 	}
2216 
2217 	if (foundDevice == B_TRUE) {
2218 		/*
2219 		 * If there is a discovered LU already exist, we should
2220 		 * add more information on this LU to construct the whole
2221 		 * topology.
2222 		 * Here we will check whether the current hba port has
2223 		 * already been added.
2224 		 */
2225 		for (portElem = discoveredLU->HBAPortList,
2226 		    foundPort = B_FALSE;  portElem != NULL;
2227 		    portElem = portElem->next) {
2228 			if (strcmp(portElem->portName,
2229 			    portName) == 0) {
2230 				foundPort = B_TRUE;
2231 				break;
2232 			}
2233 		}
2234 
2235 		/*
2236 		 * If we get here, it means that it is a new hba port name
2237 		 * for this discovered LU.
2238 		 */
2239 		if (foundPort == B_FALSE) {
2240 			newPort = (portList *)calloc(1, sizeof (portList));
2241 			if (newPort == NULL) {
2242 				(void *) fprintf(stderr,
2243 				    "%s\n", strerror(errno));
2244 				return (++ret);
2245 			}
2246 			(void) strlcpy(newPort->portName, portName,
2247 			    sizeof (newPort->portName));
2248 
2249 			portElem = discoveredLU->HBAPortList;
2250 			prevElem = portElem;
2251 			while (portElem != NULL &&
2252 			    sas_name_comp(newPort->portName, portElem->portName)
2253 			    > 0) {
2254 				prevElem = portElem;
2255 				portElem = portElem->next;
2256 			}
2257 			if (portElem == prevElem) {
2258 				/* Insert in the head of list. */
2259 				newPort->next = portElem;
2260 				discoveredLU->HBAPortList = newPort;
2261 			} else {
2262 				newPort->next = portElem;
2263 				prevElem->next = newPort;
2264 			}
2265 			/* add Target Port */
2266 			newPort->tgtPortWWN = (tgtPortWWNList *)calloc(1,
2267 			    sizeof (tgtPortWWNList));
2268 			if (newPort->tgtPortWWN == NULL) {
2269 				(void *) fprintf(stderr,
2270 				    "%s\n", strerror(errno));
2271 				return (++ret);
2272 			}
2273 			(void *) memcpy((void *)&(newPort->tgtPortWWN->portWWN),
2274 			    (void *)&(entryP->PortLun.PortWWN),
2275 			    sizeof (HBA_WWN));
2276 			/* Set LUN data */
2277 			newPort->tgtPortWWN->scsiOSLun =
2278 			    entryP->ScsiId.ScsiOSLun;
2279 		} else {
2280 			/*
2281 			 * Otherwise, we just need to add the target port
2282 			 * sas address information.
2283 			 */
2284 			for (TgtWWNList = portElem->tgtPortWWN;
2285 			    TgtWWNList != NULL;
2286 			    TgtWWNList = TgtWWNList->next) {
2287 				if (memcmp(&TgtWWNList->portWWN,
2288 				    &entryP->PortLun.PortWWN,
2289 				    sizeof (HBA_WWN)) == 0)
2290 					return (0);
2291 			}
2292 			/* add it to existing */
2293 			newTgtWWN = (tgtPortWWNList *)calloc(1,
2294 			    sizeof (tgtPortWWNList));
2295 			if (newTgtWWN == NULL) {
2296 				(void *) fprintf(stderr,
2297 				    "%s\n", strerror(errno));
2298 				return (++ret);
2299 			}
2300 			/* insert at head */
2301 			newTgtWWN->next = portElem->tgtPortWWN;
2302 			portElem->tgtPortWWN = newTgtWWN;
2303 			(void *) memcpy((void *)&(newTgtWWN->portWWN),
2304 			    (void *)&(entryP->PortLun.PortWWN),
2305 			    sizeof (HBA_WWN));
2306 			/* Set LUN data */
2307 			newTgtWWN->scsiOSLun =
2308 			    entryP->ScsiId.ScsiOSLun;
2309 		}
2310 	} else {
2311 		/*
2312 		 * Here we got a new discovered LU which has not ever exist
2313 		 * in our global LU list. So add it into our global LU
2314 		 * list.
2315 		 */
2316 		newDevice = (discoveredDevice *)calloc(1,
2317 		    sizeof (discoveredDevice));
2318 
2319 		if (newDevice == NULL) {
2320 			(void *) fprintf(stderr, "%s\n", strerror(errno));
2321 			return (++ret);
2322 		}
2323 		newDevice->next = LUList; /* insert at head */
2324 		LUList = newDevice; /* set new head */
2325 
2326 		/* copy device name */
2327 		(void *) strlcpy(newDevice->OSDeviceName,
2328 		    entryP->ScsiId.OSDeviceName,
2329 		    sizeof (newDevice->OSDeviceName));
2330 
2331 		/* if verbose is not set return */
2332 		if ((pflag & PRINT_VERBOSE) == 0) {
2333 			return (0);
2334 		}
2335 
2336 		/* copy WWN data */
2337 		newDevice->HBAPortList = (portList *)calloc(1,
2338 		    sizeof (portList));
2339 		if (newDevice->HBAPortList == NULL) {
2340 			(void *) fprintf(stderr, "%s\n", strerror(errno));
2341 			return (++ret);
2342 		}
2343 		(void) strlcpy(newDevice->HBAPortList->portName,
2344 		    portName, sizeof (newDevice->HBAPortList->portName));
2345 
2346 		newDevice->HBAPortList->tgtPortWWN =
2347 		    (tgtPortWWNList *)calloc(1, sizeof (tgtPortWWNList));
2348 		if (newDevice->HBAPortList->tgtPortWWN == NULL) {
2349 			(void *) fprintf(stderr, "%s\n", strerror(errno));
2350 			return (++ret);
2351 		}
2352 
2353 		(void *) memcpy((void *)&(newDevice->HBAPortList->\
2354 		    tgtPortWWN->portWWN),
2355 		    (void *)&(entryP->PortLun.PortWWN),
2356 		    sizeof (HBA_WWN));
2357 		newDevice->HBAPortList->tgtPortWWN->scsiOSLun =
2358 		    entryP->ScsiId.ScsiOSLun;
2359 
2360 		responseSize = sizeof (struct scsi_inquiry);
2361 		senseSize = sizeof (struct scsi_extended_sense);
2362 		(void *) memset(&inq, 0, sizeof (struct scsi_inquiry));
2363 		(void *) memset(&sense, 0, sizeof (sense));
2364 		(void *) memcpy(&smhbaLUN, &entryP->PortLun.TargetLun,
2365 		    sizeof (smhbaLUN));
2366 
2367 		/*
2368 		 * Retrieve the VPD data for the newly found discovered LU.
2369 		 */
2370 		status = SMHBA_ScsiInquiry(
2371 		    handle,
2372 		    hbaPortWWN,
2373 		    entryP->PortLun.PortWWN,
2374 		    domainPortWWN,
2375 		    smhbaLUN,
2376 		    0,
2377 		    0,
2378 		    (void *) &inq, &responseSize,
2379 		    &inq_status,
2380 		    (void *) &sense, &senseSize);
2381 
2382 		if (status != HBA_STATUS_OK) {
2383 			/* init VID/PID/dType as '\0' */
2384 			newDevice->VID[0] = '\0';
2385 			newDevice->PID[0] = '\0';
2386 			newDevice->dType = DTYPE_UNKNOWN;
2387 			/* initialize inq status */
2388 			newDevice->inquiryFailed = B_TRUE;
2389 			ret++;
2390 		} else {
2391 			(void *) memcpy(newDevice->VID, inq.inq_vid,
2392 			    sizeof (newDevice->VID));
2393 			(void *) memcpy(newDevice->PID, inq.inq_pid,
2394 			    sizeof (newDevice->PID));
2395 			newDevice->dType = inq.inq_dtype;
2396 			/* initialize inq status */
2397 			newDevice->inquiryFailed = B_FALSE;
2398 		}
2399 	}
2400 	return (ret);
2401 }
2402 
2403 /*
2404  * Function we use to insert a newly discovered port.
2405  * Return:
2406  * 	0 - success
2407  * 	>0 - failed
2408  */
2409 static int
2410 sas_rp_tree_insert(rp_tree_t **rproot,
2411     rp_tree_t *rpnode)
2412 {
2413 	HBA_UINT8 *wwn1, *wwn2, *wwn3;
2414 	rp_tree_t *node_ptr;
2415 	int ret = 0;
2416 
2417 	if (rproot == NULL) {
2418 		(void *) fprintf(stderr, "%s\n",
2419 		    gettext("Error: NULL rproot"));
2420 		return (1);
2421 	}
2422 
2423 	if (rpnode == NULL) {
2424 		(void *) fprintf(stderr, "%s\n",
2425 		    gettext("Error: NULL rpnode"));
2426 		return (1);
2427 	}
2428 
2429 	if (*rproot == NULL) {
2430 		*rproot = rpnode;
2431 		return (0);
2432 	}
2433 
2434 	wwn1 = (*rproot)->sasattr.LocalSASAddress.wwn;
2435 	wwn2 = (*rproot)->sasattr.AttachedSASAddress.wwn;
2436 	wwn3 = rpnode->sasattr.AttachedSASAddress.wwn;
2437 
2438 	/*
2439 	 * If the attched sas address is equal to the local sas address,
2440 	 * then this should be a child node of current root node.
2441 	 */
2442 	if (memcmp(wwn1, wwn3, sizeof (HBA_WWN)) == 0) {
2443 		(void) sas_rp_tree_insert(&(*rproot)->child, rpnode);
2444 		rpnode->parent = *rproot;
2445 	} else if (memcmp(wwn2, wwn3, sizeof (HBA_WWN)) == 0) {
2446 		/*
2447 		 * If the attached sas address is equal to the attached sas
2448 		 * address of current root node, then this should be a
2449 		 * sibling node.
2450 		 * Insert the SAS/SATA Device at the head of sibling list.
2451 		 */
2452 		if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
2453 			rpnode->sibling = *rproot;
2454 			*rproot = rpnode;
2455 		} else {
2456 			/*
2457 			 * Insert the SAS Expander at the tail of sibling
2458 			 * list.
2459 			 */
2460 			node_ptr = *rproot;
2461 			while (node_ptr->sibling != NULL)
2462 				node_ptr = node_ptr->sibling;
2463 			node_ptr->sibling = rpnode;
2464 		}
2465 		rpnode->parent = (*rproot)->parent;
2466 	} else {
2467 		/*
2468 		 * If enter here, we should first try to insert the discovered
2469 		 * port node into the child sub-tree, then try to insert to the
2470 		 * sibling sub-trees. If we failed to insert the discovered
2471 		 * port node, return 1. The caller will queue this node
2472 		 * up and retry insertion later.
2473 		 */
2474 		if ((*rproot)->child) {
2475 			ret = sas_rp_tree_insert(&(*rproot)->child, rpnode);
2476 		}
2477 		if ((*rproot)->child == NULL || ret != 0) {
2478 			if ((*rproot)->sibling) {
2479 				ret = sas_rp_tree_insert(&(*rproot)->sibling,
2480 				    rpnode);
2481 			} else
2482 				ret = 1;
2483 		}
2484 		return (ret);
2485 	}
2486 	return (0);
2487 }
2488 
2489 /*
2490  * Function which will print out the whole disocvered port topology.
2491  * Here we use the Preorder Traversal algorithm.
2492  * The indentation rules are:
2493  * 	1 * TABLEN - for attributes
2494  * 	2 * TABLEN - for next tier target port/expander
2495  */
2496 static int
2497 sas_rp_tree_print(HBA_HANDLE handle, char *adapterName,
2498     HBA_UINT32 portIndex, SMHBA_PORTATTRIBUTES *port,
2499     rp_tree_t *rpnode, inputArg_t *input,
2500     int gident, int *printPort)
2501 {
2502 	int ret = 0, lident;
2503 
2504 	if (rpnode == NULL)
2505 		return (ret);
2506 	lident = gident;
2507 
2508 	/*
2509 	 * We assume that all the nodes are disocvered ports(sas device or
2510 	 * expander).
2511 	 */
2512 	if (input->wwnCount > 0) {
2513 		/* Adjust local indentation if a discovered port specified. */
2514 		lident = 2 * TABLEN;
2515 		/*
2516 		 * Check whether current node match one of the specified
2517 		 * SAS addresses.
2518 		 */
2519 		if ((rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) ||
2520 		    !isPortWWNInArgv(input,
2521 		    &rpnode->sasattr.LocalSASAddress)) {
2522 			/*
2523 			 * Step down to child tree first.
2524 			 */
2525 			ret += sas_rp_tree_print(handle, adapterName,
2526 			    portIndex, port, rpnode->child, input,
2527 			    gident + 2 * TABLEN, printPort);
2528 			/*
2529 			 * Then check the sibling tree.
2530 			 */
2531 			ret += sas_rp_tree_print(handle, adapterName,
2532 			    portIndex, port, rpnode->sibling, input,
2533 			    gident, printPort);
2534 			return (ret);
2535 		}
2536 	}
2537 
2538 	if ((rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) ||
2539 	    (input->pflag & PRINT_TARGET_PORT)) {
2540 		/*
2541 		 * We should print the header(HBA Name + HBA Port Name)
2542 		 * on-demand. It means that, if we have expander device
2543 		 * address specified on the command line, we should print
2544 		 * the header once we find a matching one. Or we will
2545 		 * print the header from the beginning of the output.
2546 		 */
2547 		if (g_printHBA == 0) {
2548 			(void *) fprintf(stdout, "%s %s\n",
2549 			    "HBA Name:", adapterName);
2550 			g_printHBA = 1;
2551 		}
2552 
2553 		if (*printPort == 0) {
2554 			(void *) fprintf(stdout, "%s%s %s\n",
2555 			    getIndentSpaces(TABLEN),
2556 			    "HBA Port Name:", port->OSDeviceName);
2557 			*printPort = 1;
2558 		}
2559 		ret += sas_print_rpnode(input, rpnode, lident, gident);
2560 	}
2561 
2562 	/*
2563 	 * If operands provided with "-t" option specified, we will print
2564 	 * the immediate child nodes information under the expander.
2565 	 */
2566 	if (input->pflag & PRINT_TARGET_PORT) {
2567 		/* no operand. ignore the option. */
2568 		if (input->wwnCount > 0) {
2569 			if (rpnode->portattr.PortType ==
2570 			    HBA_PORTTYPE_SASEXPANDER) {
2571 				ret += sas_rp_tree_print_desc(handle,
2572 				    portIndex, port, rpnode->child,
2573 				    input,
2574 				    lident + 2 * TABLEN,
2575 				    gident + 2 * TABLEN);
2576 			}
2577 		}
2578 	}
2579 
2580 	/*
2581 	 * Here we use DFS(Depth First Search) algorithm to traverse the
2582 	 * whole tree.
2583 	 */
2584 	ret += sas_rp_tree_print(handle, adapterName,
2585 	    portIndex, port, rpnode->child, input,
2586 	    gident + 2 * TABLEN, printPort);
2587 	ret += sas_rp_tree_print(handle, adapterName,
2588 	    portIndex, port, rpnode->sibling, input,
2589 	    gident, printPort);
2590 	return (ret);
2591 }
2592 
2593 /*
2594  * Function which will destroy the whole discovered port tree.
2595  * Here we use the Postorder Traversal algorithm.
2596  */
2597 static void sas_rp_tree_free(rp_tree_t *rproot)
2598 {
2599 	tgt_mapping *cur, *next;
2600 
2601 	if (rproot == NULL)
2602 		return;
2603 
2604 	/*
2605 	 * Free child tree first.
2606 	 */
2607 	if (rproot->child) {
2608 		sas_rp_tree_free(rproot->child);
2609 	}
2610 
2611 	/*
2612 	 * Free sibling trees then.
2613 	 */
2614 	if (rproot->sibling) {
2615 		sas_rp_tree_free(rproot->sibling);
2616 	}
2617 
2618 	/*
2619 	 * Free root node at last.
2620 	 */
2621 	cur = rproot->first_entry;
2622 	while (cur != NULL) {
2623 		next = cur->next;
2624 		free(cur);
2625 		cur = next;
2626 	}
2627 	free(rproot);
2628 }
2629 
2630 /*
2631  * Function used to print out all the descendant nodes.
2632  * handle - handle to HBA.
2633  * port - port attributes of current HBA port.
2634  * desc - the root node of a subtree which will be processed.
2635  * input - input argument.
2636  * lident - local indentation for shifting indentation.
2637  * gident - global indentation, can also be used to obtain Tier number.
2638  */
2639 /*ARGSUSED*/
2640 static int
2641 sas_rp_tree_print_desc(HBA_HANDLE handle, HBA_UINT32 portIndex,
2642     SMHBA_PORTATTRIBUTES *port, rp_tree_t *desc,
2643     inputArg_t *input, int lident, int gident)
2644 {
2645 	int ret = 0;
2646 	rp_tree_t   *rp_node;
2647 
2648 	if (desc == NULL)
2649 		return (ret);
2650 	/*
2651 	 * Walk through the subtree of desc by Pre-Order Traversal Algo.
2652 	 */
2653 	for (rp_node = desc; rp_node !=	NULL; rp_node = rp_node->sibling) {
2654 		ret += sas_print_rpnode(input, rp_node, lident, gident);
2655 	}
2656 
2657 	return (ret);
2658 }
2659 
2660 /*
2661  * Function used to print the information of specified SAS address.
2662  * handle - handle to a HBA.
2663  * port - port attributes of a HBA port.
2664  * rpnode - discovered port which will be processed.
2665  * lident - local indentation used for shifting indentation.
2666  * gident - global indentation used for calculating "Tier" number.
2667  */
2668 static int
2669 sas_print_rpnode(inputArg_t *input,
2670     rp_tree_t *rpnode, int lident, int gident)
2671 {
2672 	int ret = 0;
2673 
2674 	if (rpnode->portattr.PortType == HBA_PORTTYPE_SASEXPANDER) {
2675 		(void *) fprintf(stdout, "%s%s(Tier %d): %016llx\n",
2676 		    getIndentSpaces(lident),
2677 		    "Expander SAS Address",
2678 		    gident / (2 * TABLEN),
2679 		    wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
2680 	} else {
2681 		(void *) fprintf(stdout, "%s%s %016llx\n",
2682 		    getIndentSpaces(lident),
2683 		    "Target Port SAS Address:",
2684 		    wwnConversion(rpnode->sasattr.LocalSASAddress.wwn));
2685 	}
2686 	if (input->pflag & PRINT_VERBOSE) {
2687 		if (rpnode->portattr.PortType != HBA_PORTTYPE_SASEXPANDER) {
2688 			(void *) fprintf(stdout, "%s%s %s\n",
2689 			    getIndentSpaces(TABLEN + lident),
2690 			    "Type:",
2691 			    getStateString(rpnode->portattr.PortType,
2692 			    porttype_string));
2693 		} else {
2694 			(void *) fprintf(stdout, "%s%s %s\n",
2695 			    getIndentSpaces(TABLEN + lident),
2696 			    "OS Device Name:",
2697 			    rpnode->portattr.OSDeviceName);
2698 			(void *) fprintf(stdout, "%s%s %s\n",
2699 			    getIndentSpaces(TABLEN + lident),
2700 			    "State: ",
2701 			    getStateString(rpnode->portattr.PortState,
2702 			    portstate_string));
2703 		}
2704 	}
2705 	rpnode->printed = 1;
2706 	return (ret);
2707 }
2708 
2709 /*
2710  * Function used to get the correct domainPortWWN as needed by some of the
2711  * SMHBA APIs.
2712  * handle - handle to a HBA.
2713  * portIndex - index to locate the port.
2714  * port - pointer to the structure holding port attributes.
2715  * pdomainPort - pointer to the buffer holding domainPortWWN.
2716  */
2717 HBA_STATUS
2718 get_domainPort(HBA_HANDLE handle,
2719     int portIndex, PSMHBA_PORTATTRIBUTES port,
2720     HBA_WWN *pdomainPort)
2721 {
2722 	HBA_STATUS status;
2723 	PSMHBA_SAS_PORT sasport;
2724 	SMHBA_SAS_PHY phyattr;
2725 
2726 	sasport = port->PortSpecificAttribute.SASPort;
2727 	(void *) memset(pdomainPort, 0, sizeof (HBA_WWN));
2728 	/*
2729 	 * Since iport can exist without any phys,
2730 	 * sasinfo hba-port -v has indicated numberOfPhys;
2731 	 * if there is no phys within the hba, just return OK.
2732 	 */
2733 	if (sasport->NumberofPhys > 0) {
2734 		status = SMHBA_GetSASPhyAttributes(handle, portIndex,
2735 		    0, &phyattr);
2736 		if (status != HBA_STATUS_OK)
2737 			return (status);
2738 		(void *) memcpy(pdomainPort, &phyattr.domainPortWWN,
2739 		    sizeof (HBA_WWN));
2740 	} else {
2741 		/* return not supported for no phy configured */
2742 		return (HBA_STATUS_ERROR_NOT_SUPPORTED);
2743 	}
2744 	return (HBA_STATUS_OK);
2745 }
2746 
2747 /*
2748  * Comparison function for comparing names possibly ending with digits.
2749  * Return:
2750  * 	<0 - name1 is less than name2.
2751  * 	0 - name1 is equal with name2.
2752  * 	>0 - name1 is more than name2.
2753  */
2754 static int
2755 sas_name_comp(const char *name1, const char *name2)
2756 {
2757 	int i = 0;
2758 
2759 	if (name1 == name2)
2760 		return (0);
2761 
2762 	while ((name1[i] == name2[i]) && (name1[i] != '\0'))
2763 		i++;
2764 
2765 	/* If neither of name1[i] and name2[i] is '\0'. */
2766 	if (isdigit(name1[i]) && isdigit(name2[i]))
2767 		return (atoi(&name1[i]) - atoi(&name2[i]));
2768 
2769 	/* One of name1[i] and name2[i] is not digit. */
2770 	return (name1[i] - name2[i]);
2771 }
2772 /*
2773  * Comparison function for sorting HBA/HBA Port.
2774  * arg1 - first argument of type sas_elem_t.
2775  * arg2 - second argument of type sas_elem_t.
2776  * Return:
2777  * 	<0 - arg1 is less than arg2.
2778  * 	0 - arg1 is equal with arg2.
2779  * 	>0 - arg1 is more than arg2.
2780  */
2781 static int
2782 sas_elem_compare(const void *arg1, const void *arg2)
2783 {
2784 	sas_elem_t *p1, *p2;
2785 	p1 = (sas_elem_t *)arg1;
2786 	p2 = (sas_elem_t *)arg2;
2787 	return (sas_name_comp(p1->name, p2->name));
2788 }
2789 
2790 /*
2791  * Sorting function for HBA/HBA Port output.
2792  * array - elements array of type sas_elem_t.
2793  * nelem - number of elements in array of type sas_elem_t.
2794  */
2795 static void
2796 sas_elem_sort(sas_elem_t *array, int nelem)
2797 {
2798 	qsort((void *)array, nelem, sizeof (sas_elem_t), sas_elem_compare);
2799 }
2800