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