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