#include <sun_sas.h>
#include <sys/modctl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <ctype.h>
struct sun_sas_hba *global_hba_head;
static void
free_hba_port(struct sun_sas_hba *hba_ptr)
{
struct sun_sas_port *hba_port = NULL;
struct sun_sas_port *last_hba_port = NULL;
struct sun_sas_port *tgt_port = NULL;
struct sun_sas_port *last_tgt_port = NULL;
struct ScsiEntryList *scsi_info = NULL;
struct ScsiEntryList *last_scsi_info = NULL;
struct phy_info *phy_ptr = NULL;
struct phy_info *last_phy = NULL;
hba_port = hba_ptr->first_port;
while (hba_port != NULL) {
tgt_port = hba_port->first_attached_port;
while (tgt_port != NULL) {
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);
}
hba_port->first_attached_port = NULL;
phy_ptr = hba_port->first_phy;
while (phy_ptr != NULL) {
last_phy = phy_ptr;
phy_ptr = phy_ptr->next;
free(last_phy);
}
hba_port->first_phy = NULL;
last_hba_port = hba_port;
hba_port = hba_port->next;
free(last_hba_port->port_attributes.\
PortSpecificAttribute.SASPort);
free(last_hba_port);
}
hba_ptr->first_port = NULL;
}
static HBA_STATUS
add_hba_port_info(di_node_t portNode, struct sun_sas_hba *hba_ptr, int protocol)
{
const char ROUTINE[] = "add_hba_port_info";
struct sun_sas_port *port_ptr;
char *portDevpath;
int *propIntData;
char *propStringData;
uint64_t tmpAddr;
char *charptr, cntlLink[MAXPATHLEN] = {'\0'};
int rval;
di_node_t branchNode;
uint_t state = HBA_PORTSTATE_UNKNOWN;
if (hba_ptr == NULL) {
log(LOG_DEBUG, ROUTINE,
"Sun_sas handle ptr set to NULL.");
return (HBA_STATUS_ERROR_ARG);
}
if ((port_ptr = (struct sun_sas_port *)calloc(1,
sizeof (struct sun_sas_port))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
return (HBA_STATUS_ERROR);
}
if ((port_ptr->port_attributes.PortSpecificAttribute.SASPort =
(struct SMHBA_SAS_Port *)calloc(1, sizeof (struct SMHBA_SAS_Port)))
== NULL) {
OUT_OF_MEMORY(ROUTINE);
return (HBA_STATUS_ERROR);
}
if ((portDevpath = di_devfs_path(portNode)) == NULL) {
log(LOG_DEBUG, ROUTINE,
"Unable to get device path from HBA Port Node.");
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
}
branchNode = di_init(portDevpath, DINFOPROP);
if (branchNode == DI_NODE_NIL) {
di_fini(branchNode);
log(LOG_DEBUG, ROUTINE,
"Unable to take devinfoi branch snapshot on HBA port \"%s\""
" due to %s", portDevpath, strerror(errno));
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
}
state = di_state(portNode);
if (((state & DI_DRIVER_DETACHED) == DI_DRIVER_DETACHED) ||
((state & DI_DEVICE_OFFLINE) == DI_DEVICE_OFFLINE)) {
log(LOG_DEBUG, ROUTINE,
"HBA port node %s is either OFFLINE or DETACHED",
portDevpath);
port_ptr->port_attributes.PortState = HBA_PORTSTATE_OFFLINE;
} else {
port_ptr->port_attributes.PortState = HBA_PORTSTATE_ONLINE;
}
port_ptr->port_attributes.PortType = HBA_PORTTYPE_SASDEVICE;
(void) strlcpy(port_ptr->device_path, portDevpath, MAXPATHLEN + 1);
if (lookupControllerLink(portDevpath, (char *)cntlLink) ==
HBA_STATUS_OK) {
(void) strlcpy(port_ptr->port_attributes.OSDeviceName, cntlLink,
sizeof (port_ptr->port_attributes.OSDeviceName));
if ((charptr = strrchr(cntlLink, '/')) != NULL) {
charptr++;
}
if (charptr[0] == 'c') {
port_ptr->cntlNumber = atoi(++charptr);
} else {
port_ptr->cntlNumber = -1;
}
} else {
(void) snprintf(port_ptr->port_attributes.OSDeviceName,
sizeof (port_ptr->port_attributes.OSDeviceName),
"%s%s%s", DEVICES_DIR, portDevpath, SCSI_SUFFIX);
}
di_devfs_path_free(portDevpath);
port_ptr->port_attributes.PortSpecificAttribute.
SASPort->PortProtocol = protocol;
rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode,
"initiator-port", &propStringData);
if (rval < 0) {
log(LOG_DEBUG, ROUTINE,
"Unable to get initiator-port from HBA port node %s.",
port_ptr->port_attributes.OSDeviceName);
di_fini(branchNode);
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
} else {
for (charptr = propStringData; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (*charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->LocalSASAddress.wwn,
&tmpAddr, 8);
} else {
log(LOG_DEBUG, ROUTINE,
"No proper intiator-port prop value on HBA port %s",
port_ptr->port_attributes.OSDeviceName);
}
}
rval = di_prop_lookup_strings(DDI_DEV_T_ANY, branchNode,
"attached-port", &propStringData);
if (rval < 0) {
log(LOG_DEBUG, ROUTINE,
"Unable to get attached-port from HBA port node %s.",
port_ptr->port_attributes.OSDeviceName);
di_fini(branchNode);
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
} else {
for (charptr = propStringData; *charptr != '\0'; charptr++) {
if (isxdigit(*charptr)) {
break;
}
}
if (*charptr != '\0') {
tmpAddr = htonll(strtoll(charptr, NULL, 16));
(void) memcpy(port_ptr->port_attributes.
PortSpecificAttribute.SASPort->
AttachedSASAddress.wwn, &tmpAddr, 8);
} else {
log(LOG_DEBUG, ROUTINE,
"No proper attached-port prop value: "
"HBA port Local SAS Address(%016llx)",
wwnConversion(port_ptr->port_attributes.
PortSpecificAttribute.
SASPort->LocalSASAddress.wwn));
}
}
rval = di_prop_lookup_ints(DDI_DEV_T_ANY, branchNode,
"num-phys", &propIntData);
if (rval < 0) {
log(LOG_DEBUG, ROUTINE,
"Unable to get NumberofPhys from HBA port %s.",
port_ptr->port_attributes.OSDeviceName);
di_fini(branchNode);
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
} else {
port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofPhys = *propIntData;
}
if (port_ptr->port_attributes.PortSpecificAttribute.\
SASPort->NumberofPhys > 0) {
if (get_phy_info(branchNode, port_ptr) != HBA_STATUS_OK) {
log(LOG_DEBUG, ROUTINE,
"Failed to get phy info on HBA port %s.",
port_ptr->port_attributes.OSDeviceName);
di_fini(branchNode);
S_FREE(port_ptr->port_attributes.
PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
}
}
di_fini(branchNode);
if (devtree_attached_devices(portNode, port_ptr) != HBA_STATUS_OK) {
log(LOG_DEBUG, ROUTINE,
"Failed to get attached device info HBA port %s.",
port_ptr->port_attributes.OSDeviceName);
S_FREE(port_ptr->port_attributes.PortSpecificAttribute.SASPort);
S_FREE(port_ptr);
return (HBA_STATUS_ERROR);
}
fillDomainPortWWN(port_ptr);
if (hba_ptr->first_port == NULL) {
port_ptr->index = 0;
hba_ptr->first_port = port_ptr;
} else {
port_ptr->index = hba_ptr->first_port->index + 1;
port_ptr->next = hba_ptr->first_port;
hba_ptr->first_port = port_ptr;
}
return (HBA_STATUS_OK);
}
HBA_STATUS
refresh_hba(di_node_t hbaNode, struct sun_sas_hba *hba_ptr)
{
const char ROUTINE[] = "refresh_hba";
di_node_t portNode;
int protocol = 0;
int *propIntData;
free_hba_port(hba_ptr);
if ((portNode = di_child_node(hbaNode)) == NULL) {
log(LOG_DEBUG, ROUTINE,
"HBA node doesn't have iport child.");
return (HBA_STATUS_ERROR);
}
if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
"supported-protocol", &propIntData)) == -1) {
log(LOG_DEBUG, ROUTINE,
"Unable to get supported-protocol from HBA node.");
} else {
protocol = *propIntData;
}
while (portNode != DI_NODE_NIL) {
if (di_prop_lookup_ints(DDI_DEV_T_ANY, portNode,
"virtual-port", &propIntData) >= 0) {
if (*propIntData) {
portNode = di_sibling_node(portNode);
continue;
}
}
if (add_hba_port_info(portNode, hba_ptr, protocol)
== HBA_STATUS_ERROR) {
S_FREE(hba_ptr->first_port);
S_FREE(hba_ptr);
return (HBA_STATUS_ERROR);
}
portNode = di_sibling_node(portNode);
}
return (HBA_STATUS_OK);
}
HBA_STATUS
devtree_get_one_hba(di_node_t hbaNode)
{
const char ROUTINE[] = "devtree_get_one_hba";
char *propdata = NULL;
int *propIntData = NULL;
struct sun_sas_hba *new_hba, *hba_ptr;
char *hbaDevpath, *hba_driver;
int protocol = 0;
di_node_t portNode;
int hba_instance = -1;
hba_instance = di_instance(hbaNode);
if (hba_instance == -1) {
log(LOG_DEBUG, ROUTINE,
"portNode has instance of -1");
return (DI_WALK_CONTINUE);
}
if ((hbaDevpath = di_devfs_path(hbaNode)) == NULL) {
log(LOG_DEBUG, ROUTINE, "Unable to get "
"device path from hbaNode");
return (HBA_STATUS_ERROR);
}
if (global_hba_head) {
for (hba_ptr = global_hba_head;
hba_ptr != NULL;
hba_ptr = hba_ptr->next) {
if ((strncmp(hba_ptr->device_path, hbaDevpath,
strlen(hbaDevpath))) == 0) {
if (refresh_hba(hbaNode, hba_ptr) !=
HBA_STATUS_OK) {
log(LOG_DEBUG, ROUTINE, "Refresh failed"
" on hbaNode %s", hbaDevpath);
}
di_devfs_path_free(hbaDevpath);
return (HBA_STATUS_OK);
}
}
}
if ((new_hba = (struct sun_sas_hba *)calloc(1,
sizeof (struct sun_sas_hba))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
di_devfs_path_free(hbaDevpath);
return (HBA_STATUS_ERROR);
}
(void) strlcpy(new_hba->device_path, hbaDevpath,
sizeof (new_hba->device_path));
di_devfs_path_free(hbaDevpath);
(void) snprintf(new_hba->adapter_attributes.HBASymbolicName,
sizeof (new_hba->adapter_attributes.HBASymbolicName),
"%s%s", DEVICES_DIR, new_hba->device_path);
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"Manufacturer", (char **)&propdata)) == -1) {
(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
SUN_MICROSYSTEMS,
sizeof (new_hba->adapter_attributes.Manufacturer));
} else {
(void) strlcpy(new_hba->adapter_attributes.Manufacturer,
propdata,
sizeof (new_hba->adapter_attributes.Manufacturer));
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"SerialNumber", (char **)&propdata)) == -1) {
new_hba->adapter_attributes.SerialNumber[0] = '\0';
} else {
(void) strlcpy(new_hba->adapter_attributes.SerialNumber,
propdata,
sizeof (new_hba->adapter_attributes.SerialNumber));
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"ModelName", (char **)&propdata)) == -1) {
new_hba->adapter_attributes.Model[0] = '\0';
} else {
(void) strlcpy(new_hba->adapter_attributes.Model,
propdata,
sizeof (new_hba->adapter_attributes.Model));
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"firmware-version", (char **)&propdata)) == -1) {
log(LOG_DEBUG, ROUTINE,
"Property \"%s\" not found for device \"%s\"",
"firmware-version", new_hba->device_path);
} else {
(void) strlcpy(new_hba->adapter_attributes.FirmwareVersion,
propdata,
sizeof (new_hba->adapter_attributes.FirmwareVersion));
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"hardware-version", (char **)&propdata)) == -1) {
log(LOG_DEBUG, ROUTINE,
"Property \"%s\" not found for device \"%s\"",
"hardware-version", new_hba->device_path);
} else {
(void) strlcpy(new_hba->adapter_attributes.HardwareVersion,
propdata,
sizeof (new_hba->adapter_attributes.HardwareVersion));
}
if ((di_prop_lookup_strings(DDI_DEV_T_ANY, hbaNode,
"driver-version", (char **)&propdata)) == -1) {
log(LOG_DEBUG, ROUTINE,
"Property \"%s\" not found for device \"%s\"",
"driver-version", new_hba->device_path);
} else {
(void) strlcpy(new_hba->adapter_attributes.DriverVersion,
propdata,
sizeof (new_hba->adapter_attributes.DriverVersion));
}
if ((di_prop_lookup_ints(DDI_DEV_T_ANY, hbaNode,
"supported-protocol", &propIntData)) == -1) {
log(LOG_DEBUG, ROUTINE,
"Unable to get supported-protocol from HBA node.");
} else {
protocol = *propIntData;
}
new_hba->adapter_attributes.OptionROMVersion[0] = '\0';
new_hba->adapter_attributes.RedundantOptionROMVersion[0] = '\0';
new_hba->adapter_attributes.RedundantFirmwareVersion[0] = '\0';
new_hba->adapter_attributes.VendorSpecificID = 0;
if ((hba_driver = di_driver_name(hbaNode)) != NULL) {
(void) strlcpy(new_hba->adapter_attributes.DriverName,
hba_driver,
sizeof (new_hba->adapter_attributes.DriverName));
} else {
log(LOG_DEBUG, ROUTINE,
"HBA driver name not found for device \"%s\"",
new_hba->device_path);
}
(void) snprintf(new_hba->handle_name, HANDLE_NAME_LENGTH, "%s-%s-%d",
"SUNW", new_hba->adapter_attributes.DriverName, hba_instance);
if ((portNode = di_child_node(hbaNode)) == NULL) {
log(LOG_DEBUG, ROUTINE,
"HBA driver doesn't have iport child. \"%s\"",
new_hba->device_path);
new_hba->index = hba_count++;
if (global_hba_head != NULL) {
if (global_hba_head->open_handles) {
new_hba->open_handles =
global_hba_head->open_handles;
global_hba_head->open_handles = NULL;
}
new_hba->next = global_hba_head;
global_hba_head = new_hba;
} else {
global_hba_head = new_hba;
}
return (HBA_STATUS_OK);
}
while (portNode != DI_NODE_NIL) {
if (di_prop_lookup_ints(DDI_DEV_T_ANY, portNode,
"virtual-port", &propIntData) >= 0) {
if (*propIntData) {
portNode = di_sibling_node(portNode);
continue;
}
}
if (add_hba_port_info(portNode, new_hba, protocol)
== HBA_STATUS_ERROR) {
S_FREE(new_hba->first_port);
S_FREE(new_hba);
return (HBA_STATUS_ERROR);
}
portNode = di_sibling_node(portNode);
}
new_hba->index = hba_count++;
if (global_hba_head != NULL) {
if (global_hba_head->open_handles) {
new_hba->open_handles = global_hba_head->open_handles;
global_hba_head->open_handles = NULL;
}
new_hba->next = global_hba_head;
global_hba_head = new_hba;
} else {
global_hba_head = new_hba;
}
return (HBA_STATUS_OK);
}
static int
lookup_smhba_sas_hba(di_node_t node, void *arg)
{
const char ROUTINE[] = "lookup_smhba_sas_hba";
int *propData, rval;
walkarg_t *wa = (walkarg_t *)arg;
if (IS_STUB_NODE(node)) {
log(LOG_DEBUG, ROUTINE, "Walk continue");
return (DI_WALK_CONTINUE);
}
rval = di_prop_lookup_ints(DDI_DEV_T_ANY, node,
"sm-hba-supported", &propData);
if (rval >= 0) {
if (*propData) {
if (devtree_get_one_hba(node) != HBA_STATUS_OK) {
*(wa->flag) = B_TRUE;
}
log(LOG_DEBUG, ROUTINE, "Walk prunechild");
return (DI_WALK_PRUNECHILD);
}
}
return (DI_WALK_CONTINUE);
}
HBA_STATUS
devtree_get_all_hbas(di_node_t root)
{
const char ROUTINE[] = "devtree_get_all_hbas";
int rv, ret = HBA_STATUS_ERROR;
walkarg_t wa;
wa.devpath = NULL;
if ((wa.flag = (boolean_t *)calloc(1,
sizeof (boolean_t))) == NULL) {
OUT_OF_MEMORY(ROUTINE);
return (HBA_STATUS_ERROR);
}
*wa.flag = B_FALSE;
rv = di_walk_node(root, DI_WALK_SIBFIRST, &wa, lookup_smhba_sas_hba);
if (rv == 0) {
if (global_hba_head) {
ret = HBA_STATUS_OK;
} else if (*(wa.flag)) {
ret = HBA_STATUS_ERROR;
} else {
ret = HBA_STATUS_OK;
}
}
S_FREE(wa.flag);
if (ret == HBA_STATUS_OK)
(void) registerSysevent();
return (ret);
}