xref: /illumos-gate/usr/src/lib/sun_sas/common/devtree_device_disco.c (revision 5328fc53d11d7151861fa272e4fb0248b8f0e145)
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 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
26  * Copyright 2019 Joyent, Inc.
27  */
28 
29 #include	<sun_sas.h>
30 #include	<sys/types.h>
31 #include	<netinet/in.h>
32 #include	<inttypes.h>
33 #include	<ctype.h>
34 #include	<sys/scsi/scsi_address.h>
35 #include	<libdevid.h>
36 
37 /*
38  * Get the preferred minor node for the given path.
39  * ":n" for tapes, ":c,raw" for disks,
40  * and ":0" for enclosures.
41  */
42 static void
43 get_minor(char *devpath, char *minor)
44 {
45 	const char	ROUTINE[] = "get_minor";
46 	char	fullpath[MAXPATHLEN];
47 	int	fd;
48 
49 	if ((strstr(devpath, "/st@")) || (strstr(devpath, "/tape@"))) {
50 		(void) strcpy(minor, ":n");
51 	} else if (strstr(devpath, "/smp@")) {
52 		(void) strcpy(minor, ":smp");
53 	} else if ((strstr(devpath, "/ssd@")) || (strstr(devpath, "/sd@")) ||
54 	    (strstr(devpath, "/disk@"))) {
55 		(void) strcpy(minor, ":c,raw");
56 	} else if ((strstr(devpath, "/ses@")) || (strstr(devpath,
57 	    "/enclosure@"))) {
58 		(void) snprintf(fullpath, MAXPATHLEN, "%s%s%s", DEVICES_DIR,
59 		    devpath, ":0");
60 		/* reset errno to 0 */
61 		errno = 0;
62 		if ((fd = open(fullpath, O_RDONLY)) == -1) {
63 			/*
64 			 * :0 minor doesn't exist. assume bound to sgen driver
65 			 * and :ses minor exist.
66 			 */
67 			if (errno == ENOENT) {
68 				(void) strcpy(minor, ":ses");
69 			}
70 		} else {
71 			(void) strcpy(minor, ":0");
72 			(void) close(fd);
73 		}
74 	} else {
75 		log(LOG_DEBUG, ROUTINE, "Unrecognized target (%s)",
76 		    devpath);
77 		minor[0] = '\0';
78 	}
79 
80 }
81 
82 /*
83  * Free the attached port allocation.
84  */
85 static void
86 free_attached_port(struct sun_sas_port *port_ptr)
87 {
88 	struct sun_sas_port *tgt_port, *last_tgt_port;
89 	struct ScsiEntryList *scsi_info = NULL, *last_scsi_info = NULL;
90 
91 	tgt_port = port_ptr->first_attached_port;
92 	while (tgt_port != NULL) {
93 		/* Free target mapping data list first. */
94 		scsi_info = tgt_port->scsiInfo;
95 		while (scsi_info != NULL) {
96 			last_scsi_info = scsi_info;
97 			scsi_info = scsi_info->next;
98 			free(last_scsi_info);
99 		}
100 		last_tgt_port = tgt_port;
101 		tgt_port = tgt_port->next;
102 		free(last_tgt_port->port_attributes.\
103 		    PortSpecificAttribute.SASPort);
104 		free(last_tgt_port);
105 	}
106 
107 	port_ptr->first_attached_port = NULL;
108 	port_ptr->port_attributes.PortSpecificAttribute.\
109 	    SASPort->NumberofDiscoveredPorts = 0;
110 }
111 
112 /*
113  * Fill domainPortWWN.
114  * should be called after completing discovered port discovery.
115  */
116 void
117 fillDomainPortWWN(struct sun_sas_port *port_ptr)
118 {
119 	const char    ROUTINE[] = "fillDomainPortWWN";
120 	struct sun_sas_port *disco_port_ptr;
121 	struct phy_info *phy_ptr;
122 	uint64_t    domainPort = 0;
123 	struct ScsiEntryList	    *mapping_ptr;
124 
125 	for (disco_port_ptr = port_ptr->first_attached_port;
126 	    disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
127 		if (disco_port_ptr->port_attributes.PortType ==
128 		    HBA_PORTTYPE_SASEXPANDER &&
129 		    wwnConversion(disco_port_ptr->port_attributes.
130 		    PortSpecificAttribute.SASPort->
131 		    AttachedSASAddress.wwn) ==
132 		    wwnConversion(port_ptr->port_attributes.
133 		    PortSpecificAttribute.SASPort->
134 		    LocalSASAddress.wwn)) {
135 			(void) memcpy(&domainPort,
136 			    disco_port_ptr->port_attributes.
137 			    PortSpecificAttribute.
138 			    SASPort->LocalSASAddress.wwn, 8);
139 			break;
140 		}
141 	}
142 
143 	if (domainPort == 0) {
144 		if (port_ptr->first_attached_port) {
145 			/*
146 			 * there is no expander device attached on an HBA port
147 			 * domainPortWWN should not stay to 0 since multiple
148 			 * hba ports can have the same LocalSASAddres within
149 			 * the same HBA.
150 			 * Set the SAS address of direct attached target.
151 			 */
152 			if (wwnConversion(port_ptr->port_attributes.
153 			    PortSpecificAttribute.SASPort->
154 			    LocalSASAddress.wwn) ==
155 			    wwnConversion(port_ptr->first_attached_port->
156 			    port_attributes.PortSpecificAttribute.
157 			    SASPort->AttachedSASAddress.wwn)) {
158 				(void) memcpy(&domainPort,
159 				    port_ptr->first_attached_port->
160 				    port_attributes.PortSpecificAttribute.
161 				    SASPort->LocalSASAddress.wwn, 8);
162 			} else {
163 				/*
164 				 * SAS address is not upstream connected.
165 				 * domainPortWWN stays as 0.
166 				 */
167 				log(LOG_DEBUG, ROUTINE,
168 				    "DomainPortWWN is not set. "
169 				    "Device(s) are visible on the HBA port "
170 				    "but there is no expander or directly "
171 				    "attached port with matching upsteam "
172 				    "attached SAS address for "
173 				    "HBA port (Local SAS Address: %016llx).",
174 				    wwnConversion(port_ptr->port_attributes.
175 				    PortSpecificAttribute.
176 				    SASPort->LocalSASAddress.wwn));
177 				return;
178 			}
179 		} else {
180 			/*
181 			 * There existss an iport without properly configured
182 			 * child smp ndoes or  child node or pathinfo.
183 			 * domainPortWWN stays as 0.
184 			 */
185 			log(LOG_DEBUG, ROUTINE,
186 			    "DomainPortWWN is not set.  No properly "
187 			    "configured smp or directly attached port "
188 			    "found on HBA port(Local SAS Address: %016llx).",
189 			    wwnConversion(port_ptr->port_attributes.
190 			    PortSpecificAttribute.
191 			    SASPort->LocalSASAddress.wwn));
192 			return;
193 		}
194 	}
195 
196 	/* fill up phy info */
197 	for (phy_ptr = port_ptr->first_phy; phy_ptr != NULL;
198 	    phy_ptr = phy_ptr->next) {
199 		(void) memcpy(phy_ptr->phy.domainPortWWN.wwn, &domainPort, 8);
200 	}
201 
202 	/* fill up target mapping */
203 	for (disco_port_ptr = port_ptr->first_attached_port;
204 	    disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
205 		for (mapping_ptr = disco_port_ptr->scsiInfo;
206 		    mapping_ptr != NULL;
207 		    mapping_ptr = mapping_ptr->next) {
208 			(void) memcpy(mapping_ptr->entry.PortLun.
209 			    domainPortWWN.wwn, &domainPort, 8);
210 		}
211 	}
212 }
213 
214 /*
215  * Finds attached device(target) from devinfo node.
216  */
217 static HBA_STATUS
218 get_attached_devices_info(di_node_t node, struct sun_sas_port *port_ptr)
219 {
220 	const char		    ROUTINE[] = "get_attached_devices_info";
221 	char			    *propStringData = NULL;
222 	int			    *propIntData = NULL;
223 	int64_t			    *propInt64Data = NULL;
224 	scsi_lun_t		    samLun;
225 	ddi_devid_t		    devid;
226 	char			    *guidStr;
227 	char			    *unit_address;
228 	char			    *charptr;
229 	char			    *devpath, link[MAXNAMELEN];
230 	char			    fullpath[MAXPATHLEN+1];
231 	char			    minorname[MAXNAMELEN+1];
232 	SMHBA_PORTATTRIBUTES	    *portattrs;
233 	struct ScsiEntryList	    *mapping_ptr;
234 	HBA_WWN			    SASAddress, AttachedSASAddress;
235 	struct sun_sas_port	    *disco_port_ptr;
236 	uint_t			    state = 0;
237 	int			    portfound, rval, size;
238 	int			    port_state = HBA_PORTSTATE_ONLINE;
239 	uint64_t		    tmpAddr;
240 
241 	if (port_ptr == NULL) {
242 		log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
243 		return (HBA_STATUS_ERROR);
244 	}
245 
246 	if ((devpath = di_devfs_path(node)) == NULL) {
247 		log(LOG_DEBUG, ROUTINE,
248 		    "Device in device tree has no path. Skipping.");
249 		return (HBA_STATUS_ERROR);
250 	}
251 
252 	if ((di_instance(node) == -1) || di_retired(node)) {
253 		log(LOG_DEBUG, ROUTINE,
254 		    "dev node (%s) returned instance of -1 or is retired. "
255 		    " Skipping.", devpath);
256 		di_devfs_path_free(devpath);
257 		return (HBA_STATUS_OK);
258 	}
259 	state = di_state(node);
260 	/* when node is not attached and online, set the state to offline. */
261 	if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
262 	    ((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
263 		log(LOG_DEBUG, ROUTINE,
264 		    "dev node (%s) is either OFFLINE or DETACHED",
265 		    devpath);
266 		port_state = HBA_PORTSTATE_OFFLINE;
267 	}
268 
269 	/* add the "/devices" in the begining at the end */
270 	(void) snprintf(fullpath, sizeof (fullpath), "%s%s",
271 	    DEVICES_DIR, devpath);
272 
273 	(void) memset(&SASAddress, 0, sizeof (SASAddress));
274 	if ((unit_address = di_bus_addr(node)) != NULL) {
275 		if ((charptr = strchr(unit_address, ',')) != NULL) {
276 			*charptr = '\0';
277 		}
278 		for (charptr = unit_address; *charptr != '\0'; charptr++) {
279 			if (isxdigit(*charptr)) {
280 				break;
281 			}
282 		}
283 		if (*charptr != '\0') {
284 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
285 			(void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
286 		} else {
287 			log(LOG_DEBUG, ROUTINE,
288 			    "No proper target port info on unit address of %s",
289 			    fullpath);
290 			di_devfs_path_free(devpath);
291 			return (HBA_STATUS_ERROR);
292 		}
293 	} else {
294 		log(LOG_DEBUG, ROUTINE,
295 		    "Fail to get unit address of %s.",
296 		    fullpath);
297 		di_devfs_path_free(devpath);
298 		return (HBA_STATUS_ERROR);
299 	}
300 
301 	(void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
302 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "attached-port",
303 	    &propStringData) != -1) {
304 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
305 			if (isxdigit(*charptr)) {
306 				break;
307 			}
308 		}
309 		if (*charptr != '\0') {
310 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
311 			(void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
312 			/* check the attached address of hba port. */
313 			if (memcmp(port_ptr->port_attributes.
314 			    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
315 			    &tmpAddr, 8) == 0) {
316 				/*
317 				 * When attached-port is set from iport
318 				 * attached-port prop, we do the cross check
319 				 * with device's own SAS address.
320 				 *
321 				 * If not set, we store device's own SAS
322 				 * address to iport attached SAS address.
323 				 */
324 				if (wwnConversion(port_ptr->port_attributes.
325 				    PortSpecificAttribute.SASPort->
326 				    AttachedSASAddress.wwn)) {
327 					/* verify the Attaached SAS Addr. */
328 					if (memcmp(port_ptr->port_attributes.
329 					    PortSpecificAttribute.SASPort->
330 					    AttachedSASAddress.wwn,
331 					    SASAddress.wwn, 8) != 0) {
332 				/* indentation move begin. */
333 				log(LOG_DEBUG, ROUTINE,
334 				    "iport attached-port(%016llx) do not"
335 				    " match with level 1 Local"
336 				    " SAS address(%016llx).",
337 				    wwnConversion(port_ptr->port_attributes.
338 				    PortSpecificAttribute.
339 				    SASPort->AttachedSASAddress.wwn),
340 				    wwnConversion(SASAddress.wwn));
341 				di_devfs_path_free(devpath);
342 				free_attached_port(port_ptr);
343 				return (HBA_STATUS_ERROR);
344 				/* indentation move ends. */
345 					}
346 				} else {
347 					(void) memcpy(port_ptr->port_attributes.
348 					    PortSpecificAttribute.
349 					    SASPort->AttachedSASAddress.wwn,
350 					    &SASAddress.wwn[0], 8);
351 				}
352 			}
353 		} else {
354 			log(LOG_DEBUG, ROUTINE,
355 			    "No proper attached SAS address value on device %s",
356 			    fullpath);
357 			di_devfs_path_free(devpath);
358 			free_attached_port(port_ptr);
359 			return (HBA_STATUS_ERROR);
360 		}
361 	} else {
362 		log(LOG_DEBUG, ROUTINE,
363 		    "Property AttachedSASAddress not found for device \"%s\"",
364 		    fullpath);
365 		di_devfs_path_free(devpath);
366 		free_attached_port(port_ptr);
367 		return (HBA_STATUS_ERROR);
368 	}
369 
370 	/*
371 	 * walk the disco list to make sure that there isn't a matching
372 	 * port and node wwn or a matching device path
373 	 */
374 	portfound = 0;
375 	for (disco_port_ptr = port_ptr->first_attached_port;
376 	    disco_port_ptr != NULL;
377 	    disco_port_ptr = disco_port_ptr->next) {
378 		if ((disco_port_ptr->port_attributes.PortState !=
379 		    HBA_PORTSTATE_ERROR) && (memcmp(disco_port_ptr->
380 		    port_attributes.PortSpecificAttribute.
381 		    SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8) == 0)) {
382 			/*
383 			 * found matching disco_port
384 			 * look for matching device path
385 			 */
386 			portfound = 1;
387 			for (mapping_ptr = disco_port_ptr->scsiInfo;
388 			    mapping_ptr != NULL;
389 			    mapping_ptr = mapping_ptr->next) {
390 				if (strstr(mapping_ptr-> entry.ScsiId.
391 				    OSDeviceName, devpath) != 0) {
392 					log(LOG_DEBUG, ROUTINE,
393 					    "Found an already discovered "
394 					    "device %s.", fullpath);
395 					di_devfs_path_free(devpath);
396 					return (HBA_STATUS_OK);
397 				}
398 			}
399 			if (portfound == 1) {
400 				break;
401 			}
402 		}
403 	}
404 
405 	if (portfound == 0) {
406 		/*
407 		 * there are no matching SAS address.
408 		 * this must be a new device
409 		 */
410 		if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
411 		    sizeof (struct sun_sas_port))) == NULL)  {
412 			OUT_OF_MEMORY(ROUTINE);
413 			di_devfs_path_free(devpath);
414 			free_attached_port(port_ptr);
415 			return (HBA_STATUS_ERROR);
416 		}
417 
418 		if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
419 		    SASPort = (struct SMHBA_SAS_Port *)calloc(1,
420 		    sizeof (struct SMHBA_SAS_Port))) == NULL) {
421 			OUT_OF_MEMORY("add_hba_port_info");
422 			di_devfs_path_free(devpath);
423 			free_attached_port(port_ptr);
424 			return (HBA_STATUS_ERROR);
425 		}
426 
427 		(void) memcpy(disco_port_ptr->port_attributes.
428 		    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
429 		    SASAddress.wwn, 8);
430 		(void) memcpy(disco_port_ptr->port_attributes.
431 		    PortSpecificAttribute.SASPort->AttachedSASAddress.wwn,
432 		    AttachedSASAddress.wwn, 8);
433 
434 		/* Default to unknown until we figure out otherwise */
435 		rval = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
436 		    "variant", &propStringData);
437 		if (rval < 0) {
438 			/* check if it is SMP target */
439 			charptr = di_driver_name(node);
440 			if (charptr != NULL && (strncmp(charptr, "smp",
441 			    strlen(charptr)) == 0)) {
442 				disco_port_ptr->port_attributes.PortType =
443 				    HBA_PORTTYPE_SASEXPANDER;
444 				disco_port_ptr->port_attributes.
445 				    PortSpecificAttribute.
446 				    SASPort->PortProtocol =
447 				    HBA_SASPORTPROTOCOL_SMP;
448 				if (lookupSMPLink(devpath, (char *)link) ==
449 				    HBA_STATUS_OK) {
450 		/* indentation changed here. */
451 		(void) strlcpy(disco_port_ptr->port_attributes.
452 		    OSDeviceName, link,
453 		    sizeof (disco_port_ptr->port_attributes.OSDeviceName));
454 		/* indentation change ends here. */
455 				} else {
456 		/* indentation changed here. */
457 		get_minor(devpath, minorname);
458 		(void) snprintf(fullpath, sizeof (fullpath), "%s%s%s",
459 		    DEVICES_DIR, devpath, minorname);
460 		(void) strlcpy(disco_port_ptr->port_attributes.
461 		    OSDeviceName, fullpath,
462 		    sizeof (disco_port_ptr->port_attributes.OSDeviceName));
463 		/* indentation change ends here. */
464 				}
465 			} else {
466 				disco_port_ptr->port_attributes.PortType =
467 				    HBA_PORTTYPE_SASDEVICE;
468 				disco_port_ptr->port_attributes.\
469 				    PortSpecificAttribute.\
470 				    SASPort->PortProtocol =
471 				    HBA_SASPORTPROTOCOL_SSP;
472 			}
473 		} else {
474 			if ((strcmp(propStringData, "sata") == 0) ||
475 			    (strcmp(propStringData, "atapi") == 0)) {
476 				disco_port_ptr->port_attributes.PortType =
477 				    HBA_PORTTYPE_SATADEVICE;
478 				disco_port_ptr->port_attributes.\
479 				    PortSpecificAttribute.SASPort->PortProtocol
480 				    = HBA_SASPORTPROTOCOL_SATA;
481 			} else {
482 				log(LOG_DEBUG, ROUTINE,
483 				    "Unexpected variant prop value %s found on",
484 				    " device %s", propStringData, fullpath);
485 				/*
486 				 * Port type will be 0
487 				 * which is not valid type.
488 				 */
489 			}
490 		}
491 
492 		/* SMP device was handled already */
493 		portattrs = &disco_port_ptr->port_attributes;
494 		if (portattrs->OSDeviceName[0] == '\0') {
495 			size = sizeof (portattrs->OSDeviceName);
496 			(void) strlcpy(portattrs->OSDeviceName,
497 			    fullpath, size);
498 		}
499 
500 		/* add new discovered port into the list */
501 
502 		if (port_ptr->first_attached_port == NULL) {
503 			port_ptr->first_attached_port = disco_port_ptr;
504 			disco_port_ptr->index = 0;
505 			port_ptr->port_attributes.PortSpecificAttribute.\
506 			    SASPort->NumberofDiscoveredPorts = 1;
507 		} else {
508 			disco_port_ptr->next = port_ptr->first_attached_port;
509 			port_ptr->first_attached_port = disco_port_ptr;
510 			disco_port_ptr->index = port_ptr->port_attributes.\
511 			    PortSpecificAttribute.\
512 			    SASPort->NumberofDiscoveredPorts;
513 			port_ptr->port_attributes.PortSpecificAttribute.\
514 			    SASPort->NumberofDiscoveredPorts++;
515 		}
516 		disco_port_ptr->port_attributes.PortState = port_state;
517 	}
518 
519 	if (disco_port_ptr->port_attributes.PortType ==
520 	    HBA_PORTTYPE_SASEXPANDER) {
521 	    /* No mapping data for expander device.  return ok here. */
522 		di_devfs_path_free(devpath);
523 		return (HBA_STATUS_OK);
524 	}
525 
526 	if ((mapping_ptr = (struct ScsiEntryList *)calloc
527 		    (1, sizeof (struct ScsiEntryList))) == NULL) {
528 		OUT_OF_MEMORY(ROUTINE);
529 		di_devfs_path_free(devpath);
530 		free_attached_port(port_ptr);
531 		return (HBA_STATUS_ERROR);
532 	}
533 
534 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun",
535 	    &propIntData) != -1) {
536 		mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
537 	} else {
538 		if ((charptr = strchr(unit_address, ',')) != NULL) {
539 			charptr++;
540 			mapping_ptr->entry.ScsiId.ScsiOSLun =
541 			    strtoull(charptr, NULL, 10);
542 		} else {
543 			log(LOG_DEBUG, ROUTINE,
544 			    "Failed to get LUN from the unit address of device "
545 			    " %s.", fullpath);
546 			di_devfs_path_free(devpath);
547 			free_attached_port(port_ptr);
548 			return (HBA_STATUS_ERROR);
549 		}
550 	}
551 
552 	/* get TargetLun(SAM-LUN). */
553 	if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "lun64",
554 	    &propInt64Data) != -1) {
555 		samLun = scsi_lun64_to_lun(*propInt64Data);
556 		(void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
557 		    &samLun, 8);
558 	} else {
559 		log(LOG_DEBUG, "get_attached_devices_info",
560 		    "No lun64 prop found on device %s.", fullpath);
561 		di_devfs_path_free(devpath);
562 		free_attached_port(port_ptr);
563 		return (HBA_STATUS_ERROR);
564 	}
565 
566 	if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
567 	    "target", &propIntData) != -1) {
568 		mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
569 	} else {
570 		mapping_ptr->entry.ScsiId.ScsiTargetNumber = di_instance(node);
571 	}
572 
573 	/* get ScsiBusNumber */
574 	mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
575 
576 	(void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
577 	    SASAddress.wwn, 8);
578 
579 	/* Store the devices path for now.  We'll convert to /dev later */
580 	get_minor(devpath, minorname);
581 	(void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
582 	    sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
583 	    "%s%s%s", DEVICES_DIR, devpath, minorname);
584 
585 	/* reset errno to 0 */
586 	errno = 0;
587 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "devid",
588 	    &propStringData) != -1) {
589 		if (devid_str_decode(propStringData, &devid, NULL) != -1) {
590 			guidStr = devid_to_guid(devid);
591 			if (guidStr != NULL) {
592 				(void) strlcpy(mapping_ptr->entry.LUID.buffer,
593 				    guidStr,
594 				    sizeof (mapping_ptr->entry.LUID.buffer));
595 				devid_free_guid(guidStr);
596 			} else {
597 				/*
598 				 * Note:
599 				 * if logical unit associated page 83 id
600 				 * descriptor is not avaialble for the device
601 				 * devid_to_guid returns NULL with errno 0.
602 				 */
603 				log(LOG_DEBUG, ROUTINE,
604 				    "failed to get devid guid on (%s) : %s",
605 				    devpath, strerror(errno));
606 			}
607 
608 			devid_free(devid);
609 		} else {
610 			/*
611 			 * device may not support proper page 83 id descriptor.
612 			 * leave LUID attribute to NULL and continue.
613 			 */
614 			log(LOG_DEBUG, ROUTINE,
615 			    "failed to decode devid prop on (%s) : %s",
616 			    devpath, strerror(errno));
617 		}
618 	} else {
619 		/* leave LUID attribute to NULL and continue. */
620 		log(LOG_DEBUG, ROUTINE,
621 		    "failed to get devid prop on (%s) : %s",
622 		    devpath, strerror(errno));
623 	}
624 
625 	if (disco_port_ptr->scsiInfo == NULL) {
626 		disco_port_ptr->scsiInfo = mapping_ptr;
627 	} else {
628 		mapping_ptr->next = disco_port_ptr->scsiInfo;
629 		disco_port_ptr->scsiInfo = mapping_ptr;
630 	}
631 
632 	di_devfs_path_free(devpath);
633 
634 	return (HBA_STATUS_OK);
635 }
636 
637 /*
638  * Finds attached device(target) from pathinfo node.
639  */
640 static HBA_STATUS
641 get_attached_paths_info(di_path_t path, struct sun_sas_port *port_ptr)
642 {
643 	char			    ROUTINE[] = "get_attached_paths_info";
644 	char			    *propStringData = NULL;
645 	int			    *propIntData = NULL;
646 	int64_t			    *propInt64Data = NULL;
647 	scsi_lun_t		    samLun;
648 	ddi_devid_t		    devid;
649 	char			    *guidStr;
650 	char			    *unit_address;
651 	char			    *charptr;
652 	char			    *clientdevpath = NULL;
653 	char			    *pathdevpath = NULL;
654 	char			    fullpath[MAXPATHLEN+1];
655 	char			    minorname[MAXNAMELEN+1];
656 	SMHBA_PORTATTRIBUTES	    *portattrs;
657 	struct ScsiEntryList	    *mapping_ptr;
658 	HBA_WWN			    SASAddress, AttachedSASAddress;
659 	struct sun_sas_port	    *disco_port_ptr;
660 	di_path_state_t		    state = 0;
661 	di_node_t		    clientnode;
662 	int			    portfound, size;
663 	int			    port_state = HBA_PORTSTATE_ONLINE;
664 	uint64_t		    tmpAddr;
665 
666 	if (port_ptr == NULL) {
667 		log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
668 		return (HBA_STATUS_ERROR);
669 	}
670 
671 	/* if not null, free before return. */
672 	pathdevpath = di_path_devfs_path(path);
673 
674 	state = di_path_state(path);
675 	/* when node is not attached and online, set the state to offline. */
676 	if ((state == DI_PATH_STATE_OFFLINE) ||
677 	    (state == DI_PATH_STATE_FAULT)) {
678 		log(LOG_DEBUG, ROUTINE,
679 		    "path node (%s) is either OFFLINE or FAULT state",
680 		    pathdevpath ?  pathdevpath : "(missing device path)");
681 		port_state = HBA_PORTSTATE_OFFLINE;
682 	}
683 
684 	if ((clientnode = di_path_client_node(path)) != DI_NODE_NIL) {
685 		if (di_retired(clientnode)) {
686 			log(LOG_DEBUG, ROUTINE,
687 			    "client node of path (%s) is retired. Skipping.",
688 			    pathdevpath ?  pathdevpath :
689 			    "(missing device path)");
690 			if (pathdevpath) di_devfs_path_free(pathdevpath);
691 			return (HBA_STATUS_OK);
692 		}
693 		if ((clientdevpath = di_devfs_path(clientnode)) == NULL) {
694 			log(LOG_DEBUG, ROUTINE,
695 			    "Client device of path (%s) has no path. Skipping.",
696 			    pathdevpath ?  pathdevpath :
697 			    "(missing device path)");
698 			if (pathdevpath) di_devfs_path_free(pathdevpath);
699 			return (HBA_STATUS_ERROR);
700 		}
701 	} else {
702 		log(LOG_DEBUG, ROUTINE,
703 		    "Failed to get client device from a path (%s).",
704 		    pathdevpath ?  pathdevpath :
705 		    "(missing device path)");
706 		if (pathdevpath) di_devfs_path_free(pathdevpath);
707 		return (HBA_STATUS_ERROR);
708 	}
709 
710 	/* add the "/devices" in the begining and the :devctl at the end */
711 	(void) snprintf(fullpath, sizeof (fullpath), "%s%s", DEVICES_DIR,
712 	    clientdevpath);
713 
714 	(void) memset(&SASAddress, 0, sizeof (SASAddress));
715 	if ((unit_address = di_path_bus_addr(path)) != NULL) {
716 		if ((charptr = strchr(unit_address, ',')) != NULL) {
717 			*charptr = '\0';
718 		}
719 		for (charptr = unit_address; *charptr != '\0'; charptr++) {
720 			if (isxdigit(*charptr)) {
721 				break;
722 			}
723 		}
724 		if (*charptr != '\0') {
725 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
726 			(void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
727 		} else {
728 			log(LOG_DEBUG, ROUTINE,
729 			    "No proper target port info on unit address of "
730 			    "path (%s).", pathdevpath ?  pathdevpath :
731 			    "(missing device path)");
732 			if (pathdevpath) di_devfs_path_free(pathdevpath);
733 			di_devfs_path_free(clientdevpath);
734 			return (HBA_STATUS_ERROR);
735 		}
736 	} else {
737 		log(LOG_DEBUG, ROUTINE, "Fail to get unit address of path(%s).",
738 		    "path (%s).", pathdevpath ?  pathdevpath :
739 		    "(missing device path)");
740 		if (pathdevpath) di_devfs_path_free(pathdevpath);
741 		di_devfs_path_free(clientdevpath);
742 		return (HBA_STATUS_ERROR);
743 	}
744 
745 	(void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
746 	if (di_path_prop_lookup_strings(path, "attached-port",
747 	    &propStringData) != -1) {
748 		for (charptr = propStringData; *charptr != '\0'; charptr++) {
749 			if (isxdigit(*charptr)) {
750 				break;
751 			}
752 		}
753 		if (*charptr != '\0') {
754 			tmpAddr = htonll(strtoll(charptr, NULL, 16));
755 			(void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
756 			/*  check the attached address of hba port. */
757 			if (memcmp(port_ptr->port_attributes.
758 			    PortSpecificAttribute.SASPort->
759 			    LocalSASAddress.wwn, &tmpAddr, 8) == 0) {
760 				if (wwnConversion(port_ptr->port_attributes.
761 				    PortSpecificAttribute.SASPort->
762 				    AttachedSASAddress.wwn)) {
763 					/* verify the attaached SAS Addr. */
764 					if (memcmp(port_ptr->port_attributes.
765 					    PortSpecificAttribute.SASPort->
766 					    AttachedSASAddress.wwn,
767 					    SASAddress.wwn, 8) != 0) {
768 				/* indentation move begin. */
769 				log(LOG_DEBUG, ROUTINE,
770 				    "iport attached-port(%016llx) do not"
771 				    " match with level 1 Local"
772 				    " SAS address(%016llx).",
773 				    wwnConversion(port_ptr->port_attributes.
774 				    PortSpecificAttribute.
775 				    SASPort->AttachedSASAddress.wwn),
776 				    wwnConversion(SASAddress.wwn));
777 				if (pathdevpath)
778 					di_devfs_path_free(pathdevpath);
779 				di_devfs_path_free(clientdevpath);
780 				free_attached_port(port_ptr);
781 				return (HBA_STATUS_ERROR);
782 				/* indentation move ends. */
783 					}
784 				} else {
785 					/* store the Attaached SAS Addr. */
786 					(void) memcpy(port_ptr->port_attributes.
787 					    PortSpecificAttribute.
788 					    SASPort->AttachedSASAddress.wwn,
789 					    &SASAddress.wwn[0], 8);
790 				}
791 			}
792 		} else {
793 			log(LOG_DEBUG, ROUTINE,
794 			    "No proper attached SAS address value of path (%s)",
795 			    pathdevpath ?  pathdevpath :
796 			    "(missing device path)");
797 			if (pathdevpath) di_devfs_path_free(pathdevpath);
798 			di_devfs_path_free(clientdevpath);
799 			free_attached_port(port_ptr);
800 			return (HBA_STATUS_ERROR);
801 		}
802 	} else {
803 		log(LOG_DEBUG, ROUTINE,
804 		    "Property attached-port not found for path (%s)",
805 		    pathdevpath ?  pathdevpath :
806 		    "(missing device path)");
807 		if (pathdevpath) di_devfs_path_free(pathdevpath);
808 		di_devfs_path_free(clientdevpath);
809 		free_attached_port(port_ptr);
810 		return (HBA_STATUS_ERROR);
811 	}
812 
813 	/*
814 	 * walk the disco list to make sure that there isn't a matching
815 	 * port and node wwn or a matching device path
816 	 */
817 	portfound = 0;
818 	for (disco_port_ptr = port_ptr->first_attached_port;
819 	    disco_port_ptr != NULL;
820 	    disco_port_ptr = disco_port_ptr->next) {
821 		if ((disco_port_ptr->port_attributes.PortState !=
822 		    HBA_PORTSTATE_ERROR) &&
823 		    (memcmp(disco_port_ptr->port_attributes.
824 		    PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
825 		    SASAddress.wwn, 8) == 0)) {
826 			/*
827 			 * found matching disco_port
828 			 * look for matching device path
829 			 */
830 			portfound = 1;
831 			for (mapping_ptr = disco_port_ptr->scsiInfo;
832 			    mapping_ptr != NULL;
833 			    mapping_ptr = mapping_ptr->next) {
834 				if (strstr(mapping_ptr-> entry.ScsiId.
835 				    OSDeviceName, clientdevpath) != 0) {
836 					log(LOG_DEBUG, ROUTINE,
837 					    "Found an already discovered "
838 					    "device %s.", clientdevpath);
839 					if (pathdevpath)
840 						di_devfs_path_free(pathdevpath);
841 					di_devfs_path_free(clientdevpath);
842 					return (HBA_STATUS_OK);
843 				}
844 			}
845 			if (portfound == 1) {
846 				break;
847 			}
848 		}
849 	}
850 
851 	if (portfound == 0) {
852 		/*
853 		 * there are no matching SAS address.
854 		 * this must be a new device
855 		 */
856 		if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
857 				    sizeof (struct sun_sas_port))) == NULL)  {
858 			OUT_OF_MEMORY(ROUTINE);
859 			if (pathdevpath) di_devfs_path_free(pathdevpath);
860 			di_devfs_path_free(clientdevpath);
861 			free_attached_port(port_ptr);
862 			return (HBA_STATUS_ERROR);
863 		}
864 
865 		if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
866 		    SASPort = (struct SMHBA_SAS_Port *)calloc(1,
867 		    sizeof (struct SMHBA_SAS_Port))) == NULL) {
868 			OUT_OF_MEMORY("add_hba_port_info");
869 			if (pathdevpath) di_devfs_path_free(pathdevpath);
870 			di_devfs_path_free(clientdevpath);
871 			free_attached_port(port_ptr);
872 			return (HBA_STATUS_ERROR);
873 		}
874 
875 		(void) memcpy(disco_port_ptr->port_attributes.
876 		    PortSpecificAttribute.
877 		    SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8);
878 		(void) memcpy(disco_port_ptr->port_attributes.
879 		    PortSpecificAttribute.
880 		    SASPort->AttachedSASAddress.wwn, AttachedSASAddress.wwn, 8);
881 
882 		/* Default to unknown until we figure out otherwise */
883 		if (di_path_prop_lookup_strings(path, "variant",
884 		    &propStringData) != -1) {
885 			if ((strcmp(propStringData, "sata") == 0) ||
886 			    (strcmp(propStringData, "atapi") == 0)) {
887 				disco_port_ptr->port_attributes.PortType =
888 				    HBA_PORTTYPE_SATADEVICE;
889 				disco_port_ptr->port_attributes.\
890 				    PortSpecificAttribute.SASPort->PortProtocol
891 				    = HBA_SASPORTPROTOCOL_SATA;
892 			} else {
893 				log(LOG_DEBUG, ROUTINE,
894 				    "Unexpected variant prop value %s found on",
895 				    " path (%s)", propStringData,
896 				    pathdevpath ?  pathdevpath :
897 				    "(missing device path)");
898 				/*
899 				 * Port type will be 0
900 				 * which is not valid type.
901 				 */
902 			}
903 		} else {
904 			disco_port_ptr->port_attributes.PortType =
905 			    HBA_PORTTYPE_SASDEVICE;
906 			disco_port_ptr->port_attributes.PortSpecificAttribute.\
907 			    SASPort->PortProtocol = HBA_SASPORTPROTOCOL_SSP;
908 		}
909 
910 		portattrs = &disco_port_ptr->port_attributes;
911 		if (portattrs->OSDeviceName[0] == '\0') {
912 			size = sizeof (portattrs->OSDeviceName);
913 			if (pathdevpath != NULL) {
914 				(void) strlcpy(portattrs->OSDeviceName,
915 				    pathdevpath, size);
916 			}
917 		}
918 
919 		/* add new discovered port into the list */
920 		if (port_ptr->first_attached_port == NULL) {
921 			port_ptr->first_attached_port = disco_port_ptr;
922 			disco_port_ptr->index = 0;
923 			port_ptr->port_attributes.PortSpecificAttribute.\
924 			    SASPort->NumberofDiscoveredPorts = 1;
925 		} else {
926 			disco_port_ptr->next = port_ptr->first_attached_port;
927 			port_ptr->first_attached_port = disco_port_ptr;
928 			disco_port_ptr->index = port_ptr->port_attributes.\
929 			    PortSpecificAttribute.\
930 			    SASPort->NumberofDiscoveredPorts;
931 			port_ptr->port_attributes.PortSpecificAttribute.\
932 			    SASPort->NumberofDiscoveredPorts++;
933 		}
934 		disco_port_ptr->port_attributes.PortState = port_state;
935 	}
936 
937 	if ((mapping_ptr = (struct ScsiEntryList *)calloc
938 		    (1, sizeof (struct ScsiEntryList))) == NULL) {
939 		OUT_OF_MEMORY(ROUTINE);
940 		if (pathdevpath) di_devfs_path_free(pathdevpath);
941 		di_devfs_path_free(clientdevpath);
942 		free_attached_port(port_ptr);
943 		return (HBA_STATUS_ERROR);
944 	}
945 
946 	if (di_path_prop_lookup_ints(path, "lun", &propIntData) != -1) {
947 		mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
948 	} else {
949 		if ((charptr = strchr(unit_address, ',')) != NULL) {
950 			charptr++;
951 			mapping_ptr->entry.ScsiId.ScsiOSLun =
952 			    strtoull(charptr, NULL, 10);
953 		} else {
954 			log(LOG_DEBUG, ROUTINE,
955 			    "Failed to get LUN from unit address of path(%s).",
956 			    pathdevpath ?  pathdevpath :
957 			    "(missing device path)");
958 			if (pathdevpath) di_devfs_path_free(pathdevpath);
959 			di_devfs_path_free(clientdevpath);
960 			free_attached_port(port_ptr);
961 			return (HBA_STATUS_ERROR);
962 		}
963 	}
964 
965 	/* Get TargetLun(SAM LUN). */
966 	if (di_path_prop_lookup_int64s(path, "lun64", &propInt64Data) != -1) {
967 		samLun = scsi_lun64_to_lun(*propInt64Data);
968 		(void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
969 		    &samLun, 8);
970 	} else {
971 		log(LOG_DEBUG, ROUTINE, "No lun64 prop found on path (%s)",
972 		    pathdevpath ?  pathdevpath :
973 		    "(missing device path)");
974 		if (pathdevpath) di_devfs_path_free(pathdevpath);
975 		di_devfs_path_free(clientdevpath);
976 		free_attached_port(port_ptr);
977 		return (HBA_STATUS_ERROR);
978 	}
979 
980 	if (di_path_prop_lookup_ints(path, "target", &propIntData) != -1) {
981 		mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
982 	} else {
983 		mapping_ptr->entry.ScsiId.ScsiTargetNumber =
984 		    di_path_instance(path);
985 	}
986 
987 	/* get ScsiBusNumber */
988 	mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
989 
990 	(void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
991 	    SASAddress.wwn, 8);
992 
993 	/* Store the devices path for now.  We'll convert to /dev later */
994 	get_minor(clientdevpath, minorname);
995 	(void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
996 	    sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
997 	    "%s%s%s", DEVICES_DIR, clientdevpath, minorname);
998 
999 	/* get luid. */
1000 	errno = 0; /* reset errno to 0 */
1001 	if (di_prop_lookup_strings(DDI_DEV_T_ANY, clientnode, "devid",
1002 	    &propStringData) != -1) {
1003 		if (devid_str_decode(propStringData, &devid, NULL) != -1) {
1004 			guidStr = devid_to_guid(devid);
1005 			if (guidStr != NULL) {
1006 				(void) strlcpy(mapping_ptr->entry.LUID.buffer,
1007 				    guidStr,
1008 				    sizeof (mapping_ptr->entry.LUID.buffer));
1009 				devid_free_guid(guidStr);
1010 			} else {
1011 				/*
1012 				 * Note:
1013 				 * if logical unit associated page 83 id
1014 				 * descriptor is not avaialble for the device
1015 				 * devid_to_guid returns NULL with errno 0.
1016 				 */
1017 				log(LOG_DEBUG, ROUTINE,
1018 				    "failed to get devid guid on (%s)",
1019 				    " associated with path(%s) : %s",
1020 				    clientdevpath,
1021 				    pathdevpath ?  pathdevpath :
1022 				    "(missing device path)",
1023 				    strerror(errno));
1024 			}
1025 
1026 			devid_free(devid);
1027 		} else {
1028 			/*
1029 			 * device may not support proper page 83 id descriptor.
1030 			 * leave LUID attribute to NULL and continue.
1031 			 */
1032 			log(LOG_DEBUG, ROUTINE,
1033 			    "failed to decode devid prop on (%s)",
1034 			    " associated with path(%s) : %s",
1035 			    clientdevpath,
1036 			    pathdevpath ?  pathdevpath :
1037 			    "(missing device path)",
1038 			    strerror(errno));
1039 		}
1040 	} else {
1041 		/* leave LUID attribute to NULL and continue. */
1042 		log(LOG_DEBUG, ROUTINE, "Failed to get devid on %s"
1043 		    " associated with path(%s) : %s", clientdevpath,
1044 		    pathdevpath ?  pathdevpath : "(missing device path)",
1045 		    strerror(errno));
1046 	}
1047 
1048 	if (disco_port_ptr->scsiInfo == NULL) {
1049 		disco_port_ptr->scsiInfo = mapping_ptr;
1050 	} else {
1051 		mapping_ptr->next = disco_port_ptr->scsiInfo;
1052 		disco_port_ptr->scsiInfo = mapping_ptr;
1053 	}
1054 
1055 	if (pathdevpath) di_devfs_path_free(pathdevpath);
1056 	di_devfs_path_free(clientdevpath);
1057 
1058 	return (HBA_STATUS_OK);
1059 }
1060 
1061 /*
1062  * walks the devinfo tree retrieving all hba information
1063  */
1064 extern HBA_STATUS
1065 devtree_attached_devices(di_node_t node, struct sun_sas_port *port_ptr)
1066 {
1067 	const char		ROUTINE[] = "devtree_attached_devices";
1068 	di_node_t		nodechild = DI_NODE_NIL;
1069 	di_path_t		path = DI_PATH_NIL;
1070 
1071 	/* child should be device */
1072 	if ((nodechild = di_child_node(node)) == DI_NODE_NIL) {
1073 		log(LOG_DEBUG, ROUTINE,
1074 		    "No devinfo child on the HBA port node.");
1075 	}
1076 
1077 	if ((path = di_path_phci_next_path(node, path)) ==
1078 	    DI_PATH_NIL) {
1079 		log(LOG_DEBUG, ROUTINE,
1080 		    "No pathinfo node on the HBA port node.");
1081 	}
1082 
1083 	if ((nodechild == DI_NODE_NIL) && (path == DI_PATH_NIL)) {
1084 		return (HBA_STATUS_OK);
1085 	}
1086 
1087 	while (nodechild != DI_NODE_NIL) {
1088 		if (get_attached_devices_info(nodechild, port_ptr)
1089 		    != HBA_STATUS_OK) {
1090 			break;
1091 		}
1092 		nodechild = di_sibling_node(nodechild);
1093 	}
1094 
1095 
1096 	while (path != DI_PATH_NIL) {
1097 		if (get_attached_paths_info(path, port_ptr)
1098 		    != HBA_STATUS_OK) {
1099 			break;
1100 		}
1101 		path = di_path_phci_next_path(node, path);
1102 	}
1103 
1104 	return (HBA_STATUS_OK);
1105 }
1106