devtree_device_disco.c revision 9e86db79b7d1bbc5f2f04e99954cbd5eae0e22bb
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sun_sas.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <ctype.h>
#include <sys/scsi/scsi_address.h>
#include <libdevid.h>
/*
* Get the preferred minor node for the given path.
* ":n" for tapes, ":c,raw" for disks,
* and ":0" for enclosures.
*/
static void
get_minor(char *devpath, char *minor)
{
const char ROUTINE[] = "get_minor";
char fullpath[MAXPATHLEN];
int fd;
if ((strstr(devpath, "/st@")) || (strstr(devpath, "/tape@"))) {
(void) strcpy(minor, ":n");
} else if (strstr(devpath, "/smp@")) {
(void) strcpy(minor, ":smp");
} else if ((strstr(devpath, "/ssd@")) || (strstr(devpath, "/sd@")) ||
(strstr(devpath, "/disk@"))) {
(void) strcpy(minor, ":c,raw");
} else if ((strstr(devpath, "/ses@")) || (strstr(devpath,
"/enclosure@"))) {
(void) snprintf(fullpath, MAXPATHLEN, "%s%s%s", DEVICES_DIR,
devpath, ":0");
/* reset errno to 0 */
errno = 0;
if ((fd = open(fullpath, O_RDONLY)) == -1) {
/*
* :0 minor doesn't exist. assume bound to sgen driver
* and :ses minor exist.
*/
if (errno == ENOENT) {
(void) strcpy(minor, ":ses");
}
} else {
(void) strcpy(minor, ":0");
(void) close(fd);
}
} else {
log(LOG_DEBUG, ROUTINE, "Unrecognized target (%s)",
devpath);
minor[0] = '\0';
}
}
/*
* Get the LUID through libdevid.
*
* devpath: /devices path for the devices.
* luidi: devid string.
*/
static void
get_luid(char *devpath, char *luid)
{
const char ROUTINE[] = "get_luid";
int fd;
ddi_devid_t devid = NULL;
char *devidstr;
/* reset errno. */
errno = 0;
if ((fd = open(devpath, O_RDONLY|O_NDELAY)) >= 0) {
if (devid_get(fd, &devid) == 0) {
if ((devidstr = devid_to_guid(devid)) != NULL) {
(void) strlcpy(luid, devidstr, 256);
devid_free_guid(devidstr);
} else {
log(LOG_DEBUG, ROUTINE,
"failed to get devid guid on (%s) : %s",
devpath, strerror(errno));
}
devid_free(devid);
} else {
log(LOG_DEBUG, ROUTINE,
"failed to get devid on (%s)", devpath);
}
(void) close(fd);
} else {
log(LOG_DEBUG, ROUTINE, "open failed on (%s) error: %s",
devpath, strerror(errno));
}
}
/*
* Free the attached port allocation.
*/
static void
free_attached_port(struct sun_sas_port *port_ptr)
{
struct sun_sas_port *tgt_port, *last_tgt_port;
struct ScsiEntryList *scsi_info = NULL, *last_scsi_info = NULL;
tgt_port = port_ptr->first_attached_port;
while (tgt_port != NULL) {
/* Free target mapping data list first. */
scsi_info = tgt_port->scsiInfo;
while (scsi_info != NULL) {
last_scsi_info = scsi_info;
scsi_info = scsi_info->next;
free(last_scsi_info);
}
last_tgt_port = tgt_port;
tgt_port = tgt_port->next;
free(last_tgt_port->port_attributes.\
PortSpecificAttribute.SASPort);
free(last_tgt_port);
}
port_ptr->first_attached_port = NULL;
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts = 0;
}
/*
* Fill domainPortWWN.
* should be called after completing discovered port discovery.
*/
void
fillDomainPortWWN(struct sun_sas_port *port_ptr)
{
const char ROUTINE[] = "fillDomainPortWWN";
struct sun_sas_port *disco_port_ptr;
struct phy_info *phy_ptr;
uint64_t domainPort = 0;
struct ScsiEntryList *mapping_ptr;
for (disco_port_ptr = port_ptr->first_attached_port;
disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
if (disco_port_ptr->port_attributes.PortType ==
HBA_PORTTYPE_SASEXPANDER &&
wwnConversion(disco_port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn) ==
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
LocalSASAddress.wwn)) {
(void) memcpy(&domainPort,
disco_port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->LocalSASAddress.wwn, 8);
break;
}
}
if (domainPort == 0) {
if (port_ptr->first_attached_port) {
/*
* there is no expander device attached on an HBA port
* domainPortWWN should not stay to 0 since multiple
* hba ports can have the same LocalSASAddres within
* the same HBA.
* Set the SAS address of direct attached target.
*/
if (wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
LocalSASAddress.wwn) ==
wwnConversion(port_ptr->first_attached_port->
port_attributes.PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn)) {
(void) memcpy(&domainPort,
port_ptr->first_attached_port->
port_attributes.PortSpecificAttribute.
SASPort->LocalSASAddress.wwn, 8);
} else {
/*
* SAS address is not upstream connected.
* domainPortWWN stays as 0.
*/
log(LOG_DEBUG, ROUTINE,
"DomainPortWWN is not set. "
"Device(s) are visible on the HBA port "
"but there is no expander or directly "
"attached port with matching upsteam "
"attached SAS address for "
"HBA port (Local SAS Address: %016llx).",
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->LocalSASAddress.wwn));
return;
}
} else {
/*
* There existss an iport without properly configured
* child smp ndoes or child node or pathinfo.
* domainPortWWN stays as 0.
*/
log(LOG_DEBUG, ROUTINE,
"DomainPortWWN is not set. No properly "
"configured smp or directly attached port "
"found on HBA port(Local SAS Address: %016llx).",
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->LocalSASAddress.wwn));
return;
}
}
/* fill up phy info */
for (phy_ptr = port_ptr->first_phy; phy_ptr != NULL;
phy_ptr = phy_ptr->next) {
(void) memcpy(phy_ptr->phy.domainPortWWN.wwn, &domainPort, 8);
}
/* fill up target mapping */
for (disco_port_ptr = port_ptr->first_attached_port;
disco_port_ptr != NULL; disco_port_ptr = disco_port_ptr->next) {
for (mapping_ptr = disco_port_ptr->scsiInfo;
mapping_ptr != NULL;
mapping_ptr = mapping_ptr->next) {
(void) memcpy(mapping_ptr->entry.PortLun.
domainPortWWN.wwn, &domainPort, 8);
}
}
}
/*
* Finds attached device(target) from devinfo node.
*/
static HBA_STATUS
get_attached_devices_info(di_node_t node, struct sun_sas_port *port_ptr)
{
const char ROUTINE[] = "get_attached_devices_info";
char *propStringData = NULL;
int *propIntData = NULL;
int64_t *propInt64Data = NULL;
uchar_t *propByteData = NULL;
scsi_lun_t samLun;
char *unit_address;
char *charptr;
char *devpath, link[MAXNAMELEN];
char fullpath[MAXPATHLEN+1];
char minorname[MAXNAMELEN+1];
struct ScsiEntryList *mapping_ptr;
HBA_WWN SASAddress, AttachedSASAddress;
struct sun_sas_port *disco_port_ptr;
uint_t state = 0;
int portfound, rval, size, count, i;
int port_state = HBA_PORTSTATE_ONLINE;
uint64_t tmpAddr;
if (port_ptr == NULL) {
log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
return (HBA_STATUS_ERROR);
}
if ((devpath = di_devfs_path(node)) == NULL) {
log(LOG_DEBUG, ROUTINE,
"Device in device tree has no path. Skipping.");
return (HBA_STATUS_ERROR);
}
if ((di_instance(node) == -1) || di_retired(node)) {
log(LOG_DEBUG, ROUTINE,
"dev node (%s) returned instance of -1 or is retired. "
" Skipping.", devpath);
di_devfs_path_free(devpath);
return (HBA_STATUS_OK);
}
state = di_state(node);
/* when node is not attached and online, set the state to offline. */
if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
log(LOG_DEBUG, ROUTINE,
"dev node (%s) is either OFFLINE or DETACHED",
devpath);
port_state = HBA_PORTSTATE_OFFLINE;
}
/* add the "/devices" in the begining at the end */
(void) snprintf(fullpath, sizeof (fullpath), "%s%s",
DEVICES_DIR, devpath);
(void) memset(&SASAddress, 0, sizeof (SASAddress));
if ((unit_address = di_bus_addr(node)) != NULL) {
if ((charptr = strchr(unit_address, ',')) != NULL) {
*charptr = '\0';
}
for (charptr = unit_address; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (*charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
} else {
log(LOG_DEBUG, ROUTINE,
"No proper target port info on unit address of %s",
fullpath);
di_devfs_path_free(devpath);
return (HBA_STATUS_ERROR);
}
} else {
log(LOG_DEBUG, ROUTINE,
"Fail to get unit address of %s.",
fullpath);
di_devfs_path_free(devpath);
return (HBA_STATUS_ERROR);
}
(void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "attached-port",
&propStringData) != -1) {
for (charptr = propStringData; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (*charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
/* check the attached address of hba port. */
if (memcmp(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
&tmpAddr, 8) == 0) {
/*
* When attached-port is set from iport
* attached-port prop, we do the cross check
* with device's own SAS address.
*
* If not set, we store device's own SAS
* address to iport attached SAS address.
*/
if (wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn)) {
/* verify the Attaached SAS Addr. */
if (memcmp(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn,
SASAddress.wwn, 8) != 0) {
/* indentation move begin. */
log(LOG_DEBUG, ROUTINE,
"iport attached-port(%016llx) do not"
" match with level 1 Local"
" SAS address(%016llx).",
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn),
wwnConversion(SASAddress.wwn));
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
/* indentation move ends. */
}
} else {
(void) memcpy(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn,
&SASAddress.wwn[0], 8);
}
}
} else {
log(LOG_DEBUG, ROUTINE,
"No proper attached SAS address value on device %s",
fullpath);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
} else {
log(LOG_DEBUG, ROUTINE,
"Property AttachedSASAddress not found for device \"%s\"",
fullpath);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
/*
* walk the disco list to make sure that there isn't a matching
* port and node wwn or a matching device path
*/
portfound = 0;
for (disco_port_ptr = port_ptr->first_attached_port;
disco_port_ptr != NULL;
disco_port_ptr = disco_port_ptr->next) {
if ((disco_port_ptr->port_attributes.PortState !=
HBA_PORTSTATE_ERROR) && (memcmp(disco_port_ptr->
port_attributes.PortSpecificAttribute.
SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8) == 0)) {
/*
* found matching disco_port
* look for matching device path
*/
portfound = 1;
for (mapping_ptr = disco_port_ptr->scsiInfo;
mapping_ptr != NULL;
mapping_ptr = mapping_ptr->next) {
if (strstr(mapping_ptr-> entry.ScsiId.
OSDeviceName, devpath) != 0) {
log(LOG_DEBUG, ROUTINE,
"Found an already discovered "
"device %s.", fullpath);
di_devfs_path_free(devpath);
return (HBA_STATUS_OK);
}
}
if (portfound == 1) {
break;
}
}
}
if (portfound == 0) {
/*
* there are no matching SAS address.
* this must be a new device
*/
if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
sizeof (struct sun_sas_port))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
SASPort = (struct SMHBA_SAS_Port *)calloc(1,
sizeof (struct SMHBA_SAS_Port))) == NULL) {
OUT_OF_MEMORY("add_hba_port_info");
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
(void) memcpy(disco_port_ptr->port_attributes.
PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
SASAddress.wwn, 8);
(void) memcpy(disco_port_ptr->port_attributes.
PortSpecificAttribute.SASPort->AttachedSASAddress.wwn,
AttachedSASAddress.wwn, 8);
/* Default to unknown until we figure out otherwise */
rval = di_prop_lookup_strings(DDI_DEV_T_ANY, node,
"variant", &propStringData);
if (rval < 0) {
/* check if it is SMP target */
charptr = di_driver_name(node);
if (charptr != NULL && (strncmp(charptr, "smp",
strlen(charptr)) == 0)) {
disco_port_ptr->port_attributes.PortType =
HBA_PORTTYPE_SASEXPANDER;
disco_port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->PortProtocol =
HBA_SASPORTPROTOCOL_SMP;
if (lookupSMPLink(devpath, (char *)link) ==
HBA_STATUS_OK) {
/* indentation changed here. */
(void) strlcpy(disco_port_ptr->port_attributes.
OSDeviceName, link,
sizeof (disco_port_ptr->port_attributes.OSDeviceName));
/* indentation change ends here. */
} else {
/* indentation changed here. */
get_minor(devpath, minorname);
(void) snprintf(fullpath, sizeof (fullpath), "%s%s%s",
DEVICES_DIR, devpath, minorname);
(void) strlcpy(disco_port_ptr->port_attributes.
OSDeviceName, fullpath,
sizeof (disco_port_ptr->port_attributes.OSDeviceName));
/* indentation change ends here. */
}
} else {
disco_port_ptr->port_attributes.PortType =
HBA_PORTTYPE_SASDEVICE;
disco_port_ptr->port_attributes.\
PortSpecificAttribute.\
SASPort->PortProtocol =
HBA_SASPORTPROTOCOL_SSP;
}
} else {
if ((strcmp(propStringData, "sata") == 0) ||
(strcmp(propStringData, "atapi") == 0)) {
disco_port_ptr->port_attributes.PortType =
HBA_PORTTYPE_SATADEVICE;
disco_port_ptr->port_attributes.\
PortSpecificAttribute.SASPort->PortProtocol
= HBA_SASPORTPROTOCOL_SATA;
} else {
log(LOG_DEBUG, ROUTINE,
"Unexpected variant prop value %s found on",
" device %s", propStringData, fullpath);
/*
* Port type will be 0
* which is not valid type.
*/
}
}
/* SMP device was handled already */
if (disco_port_ptr->port_attributes.OSDeviceName[0] == '\0') {
/* indentation change due to ctysle check on sizeof. */
size = sizeof (disco_port_ptr->port_attributes.OSDeviceName);
(void) strlcpy(disco_port_ptr->port_attributes.
OSDeviceName, fullpath, size);
}
/* add new discovered port into the list */
if (port_ptr->first_attached_port == NULL) {
port_ptr->first_attached_port = disco_port_ptr;
disco_port_ptr->index = 0;
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts = 1;
} else {
disco_port_ptr->next = port_ptr->first_attached_port;
port_ptr->first_attached_port = disco_port_ptr;
disco_port_ptr->index = port_ptr->port_attributes.\
PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts;
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts++;
}
disco_port_ptr->port_attributes.PortState = port_state;
}
if (disco_port_ptr->port_attributes.PortType ==
HBA_PORTTYPE_SASEXPANDER) {
/* No mapping data for expander device. return ok here. */
di_devfs_path_free(devpath);
return (HBA_STATUS_OK);
}
if ((mapping_ptr = (struct ScsiEntryList *)calloc
(1, sizeof (struct ScsiEntryList))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "lun",
&propIntData) != -1) {
mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
} else {
if ((charptr = strchr(unit_address, ',')) != NULL) {
charptr++;
mapping_ptr->entry.ScsiId.ScsiOSLun =
strtoull(charptr, NULL, 10);
} else {
log(LOG_DEBUG, ROUTINE,
"Failed to get LUN from the unit address of device "
" %s.", fullpath);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
}
/* get TargetLun(SAM-LUN). */
if (di_prop_lookup_int64(DDI_DEV_T_ANY, node, "lun64",
&propInt64Data) != -1) {
samLun = scsi_lun64_to_lun(*propInt64Data);
(void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
&samLun, 8);
} else {
log(LOG_DEBUG, "get_attached_devices_info",
"No lun64 prop found on device %s.", fullpath);
di_devfs_path_free(devpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if (di_prop_lookup_ints(DDI_DEV_T_ANY, node,
"target", &propIntData) != -1) {
mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
} else {
mapping_ptr->entry.ScsiId.ScsiTargetNumber = di_instance(node);
}
/* get ScsiBusNumber */
mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
(void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
SASAddress.wwn, 8);
/* Store the devices path for now. We'll convert to /dev later */
get_minor(devpath, minorname);
(void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
"%s%s%s", DEVICES_DIR, devpath, minorname);
count = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, "inquiry-page-83",
(uchar_t **)&propByteData);
if (count < 0) {
get_luid(mapping_ptr->entry.ScsiId.OSDeviceName,
mapping_ptr->entry.LUID.buffer);
} else {
for (i = 0, charptr = mapping_ptr->entry.LUID.buffer; i < count;
i++, charptr += 2) {
(void) sprintf(charptr, "%02x", propByteData[i]);
}
*charptr = '\0';
}
if (disco_port_ptr->scsiInfo == NULL) {
disco_port_ptr->scsiInfo = mapping_ptr;
} else {
mapping_ptr->next = disco_port_ptr->scsiInfo;
disco_port_ptr->scsiInfo = mapping_ptr;
}
di_devfs_path_free(devpath);
return (HBA_STATUS_OK);
}
/*
* Finds attached device(target) from pathinfo node.
*/
static HBA_STATUS
get_attached_paths_info(di_path_t path, struct sun_sas_port *port_ptr)
{
char ROUTINE[] = "get_attached_paths_info";
char *propStringData = NULL;
int *propIntData = NULL;
int64_t *propInt64Data = NULL;
scsi_lun_t samLun;
char *unit_address;
char *charptr;
char *clientdevpath = NULL;
char *pathdevpath = NULL;
char fullpath[MAXPATHLEN+1];
char minorname[MAXNAMELEN+1];
struct ScsiEntryList *mapping_ptr;
HBA_WWN SASAddress, AttachedSASAddress;
struct sun_sas_port *disco_port_ptr;
di_path_state_t state = 0;
di_node_t clientnode;
int portfound, size;
int port_state = HBA_PORTSTATE_ONLINE;
uint64_t tmpAddr;
if (port_ptr == NULL) {
log(LOG_DEBUG, ROUTINE, "NULL port_ptr argument");
return (HBA_STATUS_ERROR);
}
/* if not null, free before return. */
pathdevpath = di_path_devfs_path(path);
state = di_path_state(path);
/* when node is not attached and online, set the state to offline. */
if ((state == DI_PATH_STATE_OFFLINE) ||
(state == DI_PATH_STATE_FAULT)) {
log(LOG_DEBUG, ROUTINE,
"path node (%s) is either OFFLINE or FAULT state",
pathdevpath ? pathdevpath : "(missing device path)");
port_state = HBA_PORTSTATE_OFFLINE;
}
if (clientnode = di_path_client_node(path)) {
if (di_retired(clientnode)) {
log(LOG_DEBUG, ROUTINE,
"client node of path (%s) is retired. Skipping.",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
return (HBA_STATUS_OK);
}
if ((clientdevpath = di_devfs_path(clientnode)) == NULL) {
log(LOG_DEBUG, ROUTINE,
"Client device of path (%s) has no path. Skipping.",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
return (HBA_STATUS_ERROR);
}
} else {
log(LOG_DEBUG, ROUTINE,
"Failed to get client device from a path (%s).",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
return (HBA_STATUS_ERROR);
}
/* add the "/devices" in the begining and the :devctl at the end */
(void) snprintf(fullpath, sizeof (fullpath), "%s%s", DEVICES_DIR,
clientdevpath);
(void) memset(&SASAddress, 0, sizeof (SASAddress));
if ((unit_address = di_path_bus_addr(path)) != NULL) {
if ((charptr = strchr(unit_address, ',')) != NULL) {
*charptr = '\0';
}
for (charptr = unit_address; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(&SASAddress.wwn[0], &tmpAddr, 8);
} else {
log(LOG_DEBUG, ROUTINE,
"No proper target port info on unit address of "
"path (%s).", pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
return (HBA_STATUS_ERROR);
}
} else {
log(LOG_DEBUG, ROUTINE, "Fail to get unit address of path(%s).",
"path (%s).", pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
return (HBA_STATUS_ERROR);
}
(void) memset(&AttachedSASAddress, 0, sizeof (AttachedSASAddress));
if (di_path_prop_lookup_strings(path, "attached-port",
&propStringData) != -1) {
for (charptr = propStringData; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (*charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(AttachedSASAddress.wwn, &tmpAddr, 8);
/* check the attached address of hba port. */
if (memcmp(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
LocalSASAddress.wwn, &tmpAddr, 8) == 0) {
if (wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn)) {
/* verify the attaached SAS Addr. */
if (memcmp(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn,
SASAddress.wwn, 8) != 0) {
/* indentation move begin. */
log(LOG_DEBUG, ROUTINE,
"iport attached-port(%016llx) do not"
" match with level 1 Local"
" SAS address(%016llx).",
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn),
wwnConversion(SASAddress.wwn));
if (pathdevpath)
di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
/* indentation move ends. */
}
} else {
/* store the Attaached SAS Addr. */
(void) memcpy(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn,
&SASAddress.wwn[0], 8);
}
}
} else {
log(LOG_DEBUG, ROUTINE,
"No proper attached SAS address value of path (%s)",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
} else {
log(LOG_DEBUG, ROUTINE,
"Property attached-port not found for path (%s)",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
/*
* walk the disco list to make sure that there isn't a matching
* port and node wwn or a matching device path
*/
portfound = 0;
for (disco_port_ptr = port_ptr->first_attached_port;
disco_port_ptr != NULL;
disco_port_ptr = disco_port_ptr->next) {
if ((disco_port_ptr->port_attributes.PortState !=
HBA_PORTSTATE_ERROR) &&
(memcmp(disco_port_ptr->port_attributes.
PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
SASAddress.wwn, 8) == 0)) {
/*
* found matching disco_port
* look for matching device path
*/
portfound = 1;
for (mapping_ptr = disco_port_ptr->scsiInfo;
mapping_ptr != NULL;
mapping_ptr = mapping_ptr->next) {
if (strstr(mapping_ptr-> entry.ScsiId.
OSDeviceName, clientdevpath) != 0) {
log(LOG_DEBUG, ROUTINE,
"Found an already discovered "
"device %s.", clientdevpath);
if (pathdevpath)
di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
return (HBA_STATUS_OK);
}
}
if (portfound == 1) {
break;
}
}
}
if (portfound == 0) {
/*
* there are no matching SAS address.
* this must be a new device
*/
if ((disco_port_ptr = (struct sun_sas_port *)calloc(1,
sizeof (struct sun_sas_port))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if ((disco_port_ptr->port_attributes.PortSpecificAttribute.\
SASPort = (struct SMHBA_SAS_Port *)calloc(1,
sizeof (struct SMHBA_SAS_Port))) == NULL) {
OUT_OF_MEMORY("add_hba_port_info");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
(void) memcpy(disco_port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->LocalSASAddress.wwn, SASAddress.wwn, 8);
(void) memcpy(disco_port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->AttachedSASAddress.wwn, AttachedSASAddress.wwn, 8);
/* Default to unknown until we figure out otherwise */
if (di_path_prop_lookup_strings(path, "variant",
&propStringData) != -1) {
if ((strcmp(propStringData, "sata") == 0) ||
(strcmp(propStringData, "atapi") == 0)) {
disco_port_ptr->port_attributes.PortType =
HBA_PORTTYPE_SATADEVICE;
disco_port_ptr->port_attributes.\
PortSpecificAttribute.SASPort->PortProtocol
= HBA_SASPORTPROTOCOL_SATA;
} else {
log(LOG_DEBUG, ROUTINE,
"Unexpected variant prop value %s found on",
" path (%s)", propStringData,
pathdevpath ? pathdevpath :
"(missing device path)");
/*
* Port type will be 0
* which is not valid type.
*/
}
} else {
disco_port_ptr->port_attributes.PortType =
HBA_PORTTYPE_SASDEVICE;
disco_port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->PortProtocol = HBA_SASPORTPROTOCOL_SSP;
}
if (disco_port_ptr->port_attributes.OSDeviceName[0] == '\0') {
/* indentation change due to ctysle check on sizeof. */
size = sizeof (disco_port_ptr->port_attributes.OSDeviceName);
if (pathdevpath != NULL) {
(void) strlcpy(disco_port_ptr->port_attributes.
OSDeviceName, pathdevpath, size);
}
}
/* add new discovered port into the list */
if (port_ptr->first_attached_port == NULL) {
port_ptr->first_attached_port = disco_port_ptr;
disco_port_ptr->index = 0;
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts = 1;
} else {
disco_port_ptr->next = port_ptr->first_attached_port;
port_ptr->first_attached_port = disco_port_ptr;
disco_port_ptr->index = port_ptr->port_attributes.\
PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts;
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofDiscoveredPorts++;
}
disco_port_ptr->port_attributes.PortState = port_state;
}
if ((mapping_ptr = (struct ScsiEntryList *)calloc
(1, sizeof (struct ScsiEntryList))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if (di_path_prop_lookup_ints(path, "lun", &propIntData) != -1) {
mapping_ptr->entry.ScsiId.ScsiOSLun = *propIntData;
} else {
if ((charptr = strchr(unit_address, ',')) != NULL) {
charptr++;
mapping_ptr->entry.ScsiId.ScsiOSLun =
strtoull(charptr, NULL, 10);
} else {
log(LOG_DEBUG, ROUTINE,
"Failed to get LUN from unit address of path(%s).",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
}
/* Get TargetLun(SAM LUN). */
if (di_path_prop_lookup_int64s(path, "lun64", &propInt64Data) != -1) {
samLun = scsi_lun64_to_lun(*propInt64Data);
(void) memcpy(&mapping_ptr->entry.PortLun.TargetLun,
&samLun, 8);
} else {
log(LOG_DEBUG, ROUTINE, "No lun64 prop found on path (%s)",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if (di_path_prop_lookup_ints(path, "target", &propIntData) != -1) {
mapping_ptr->entry.ScsiId.ScsiTargetNumber = *propIntData;
} else {
mapping_ptr->entry.ScsiId.ScsiTargetNumber =
di_path_instance(path);
}
/* get ScsiBusNumber */
mapping_ptr->entry.ScsiId.ScsiBusNumber = port_ptr->cntlNumber;
(void) memcpy(mapping_ptr->entry.PortLun.PortWWN.wwn,
SASAddress.wwn, 8);
/* Store the devices path for now. We'll convert to /dev later */
get_minor(clientdevpath, minorname);
(void) snprintf(mapping_ptr->entry.ScsiId.OSDeviceName,
sizeof (mapping_ptr->entry.ScsiId.OSDeviceName),
"%s%s%s", DEVICES_DIR, clientdevpath, minorname);
/* get luid. */
if (di_prop_lookup_strings(DDI_DEV_T_ANY, clientnode,
"client-guid", &propStringData) != -1) {
(void) strlcpy(mapping_ptr->entry.LUID.buffer, propStringData,
sizeof (mapping_ptr->entry.LUID.buffer));
} else {
log(LOG_DEBUG, ROUTINE, "No client-guid prop found on path(%s)",
pathdevpath ? pathdevpath :
"(missing device path)");
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
free_attached_port(port_ptr);
return (HBA_STATUS_ERROR);
}
if (disco_port_ptr->scsiInfo == NULL) {
disco_port_ptr->scsiInfo = mapping_ptr;
} else {
mapping_ptr->next = disco_port_ptr->scsiInfo;
disco_port_ptr->scsiInfo = mapping_ptr;
}
if (pathdevpath) di_devfs_path_free(pathdevpath);
di_devfs_path_free(clientdevpath);
return (HBA_STATUS_OK);
}
/*
* walks the devinfo tree retrieving all hba information
*/
extern HBA_STATUS
devtree_attached_devices(di_node_t node, struct sun_sas_port *port_ptr)
{
const char ROUTINE[] = "devtree_attached_devices";
di_node_t nodechild = DI_NODE_NIL;
di_path_t path = DI_PATH_NIL;
/* child should be device */
if ((nodechild = di_child_node(node)) == DI_NODE_NIL) {
log(LOG_DEBUG, ROUTINE,
"No devinfo child on the HBA port node.");
}
if ((path = di_path_phci_next_path(node, path)) ==
DI_PATH_NIL) {
log(LOG_DEBUG, ROUTINE,
"No pathinfo node on the HBA port node.");
}
if ((nodechild == DI_NODE_NIL) && (path == DI_PATH_NIL)) {
return (HBA_STATUS_OK);
}
while (nodechild != DI_NODE_NIL) {
if (get_attached_devices_info(nodechild, port_ptr)
!= HBA_STATUS_OK) {
break;
}
nodechild = di_sibling_node(nodechild);
}
while (path != DI_PATH_NIL) {
if (get_attached_paths_info(path, port_ptr)
!= HBA_STATUS_OK) {
break;
}
path = di_path_phci_next_path(node, path);
}
return (HBA_STATUS_OK);
}