/* * Copyright (c) 2008, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "utils.h" #include "api_lib.h" #include "adapt_impl.h" #include #ifndef HBA_STATUS_ERROR_ILLEGAL_FCID #define HBA_STATUS_ERROR_ILLEGAL_FCID 33 /* defined after HBA-API 2.2 */ #endif #define SEND_CT_TIMEOUT (3 * 1000) /* timeout in milliseconds */ /* * The following are temporary settings until we can find a way to * collect these information. */ #define HBA_ROM_VERSION "" #define HBA_FW_VERSION "" #define HBA_VENDOR_SPECIFIC_ID 0 /* * table of /sys port types strings to HBA-API values. */ struct sa_nameval port_types_table[] = { { "Unknown", HBA_PORTTYPE_UNKNOWN }, { "Other", HBA_PORTTYPE_OTHER }, { "Not Present", HBA_PORTTYPE_NOTPRESENT }, { "NPort (fabric via point-to-point)", HBA_PORTTYPE_NPORT }, { "NLPort (fabric via loop)", HBA_PORTTYPE_NLPORT }, { "LPort (private loop)", HBA_PORTTYPE_LPORT }, { "Point-To-Point (direct nport connection)", HBA_PORTTYPE_PTP }, { "NPIV VPORT", HBA_PORTTYPE_NPORT }, { NULL, 0 } }; /* * table of /sys port state strings to HBA-API values. */ struct sa_nameval port_states_table[] = { { "Not Present", HBA_PORTSTATE_UNKNOWN }, { "Online", HBA_PORTSTATE_ONLINE }, { "Offline", HBA_PORTSTATE_OFFLINE }, { "Blocked", HBA_PORTSTATE_UNKNOWN }, { "Bypassed", HBA_PORTSTATE_BYPASSED }, { "Diagnostics", HBA_PORTSTATE_DIAGNOSTICS }, { "Linkdown", HBA_PORTSTATE_LINKDOWN }, { "Error", HBA_PORTSTATE_ERROR }, { "Loopback", HBA_PORTSTATE_LOOPBACK }, { "Deleted", HBA_PORTSTATE_UNKNOWN }, { NULL, 0 } }; /* * table of /sys port speed strings to HBA-API values. */ struct sa_nameval port_speeds_table[] = { { "Unknown", HBA_PORTSPEED_UNKNOWN }, { "1 Gbit", HBA_PORTSPEED_1GBIT }, { "2 Gbit", HBA_PORTSPEED_2GBIT }, { "4 Gbit", HBA_PORTSPEED_4GBIT }, { "10 Gbit", HBA_PORTSPEED_10GBIT }, { "8 Gbit", HBA_PORTSPEED_8GBIT }, { "16 Gbit", HBA_PORTSPEED_16GBIT }, { "32 Gbit", HBA_PORTSPEED_32GBIT }, { "20 Gbit", HBA_PORTSPEED_20GBIT }, { "40 Gbit", HBA_PORTSPEED_40GBIT }, { "Not Negotiated", HBA_PORTSPEED_NOT_NEGOTIATED }, { NULL, 0 } }; /* * parse strings from /sys port speed/support_speeds files * and convert them to bitmasks for the HBA_PORTSPEED supported * Format expected: "1 Gbit[, 10 Gbit]", etc. */ static int sys_read_speed(const char *speedstr, HBA_PORTSPEED *speeds) { int rc = 0; u_int32_t val = 0; int len = 0; const char *cp; struct sa_nameval *tp = port_speeds_table; if (rc == 0 && strstr(speedstr, "Unknown") == NULL) { for (cp = speedstr; *cp != '\0';) { for (; tp->nv_name != NULL; tp++) { len = strlen(tp->nv_name); if (strncasecmp(tp->nv_name, cp, len) == 0) { val |= tp->nv_val; cp += len; break; } } if (*cp == '\0') break; if (*cp == ',') { cp++; if (*cp == ' ') cp++; } else break; /* invalid string */ } } *speeds = val; return rc; } /* * Code for OpenFC-supported adapters. */ static int counting_rports(struct dirent *dp, void *arg) { int *count = (int *)arg; if (!strstr(dp->d_name, "rport-")) return HBA_STATUS_OK; (*count)++; return HBA_STATUS_OK; } static void sysfs_scan_pci(struct udev_device *pci, struct hba_info *hba_info, HBA_ADAPTERATTRIBUTES *atp ) { const char *attr; const char *hba_dir; char buf[256]; char *saveptr; /* for strtok_r */ /* Get vendor_id */ attr = udev_device_get_sysattr_value(pci, "vendor"); hba_info->vendor_id = strtoul(attr, NULL, 0); /* Get device_id */ attr = udev_device_get_sysattr_value(pci, "device"); hba_info->device_id = strtoul(attr, NULL, 0); /* Get subsystem_vendor_id */ attr = udev_device_get_sysattr_value(pci, "subsystem_vendor"); hba_info->subsystem_vendor_id = strtoul(attr, NULL, 0); /* Get subsystem_device_id */ attr = udev_device_get_sysattr_value(pci, "subsystem_device"); hba_info->subsystem_device_id = strtoul(attr, NULL, 0); /* Get device_class */ attr = udev_device_get_sysattr_value(pci, "class"); hba_info->device_class = strtoul(attr, NULL, 0); hba_info->device_class = hba_info->device_class>>8; /* * Get Hardware Information via PCI Library */ sscanf(udev_device_get_sysname(pci), "%x:%x:%x:%x", &hba_info->domain, &hba_info->bus, &hba_info->dev, &hba_info->func); (void) find_pci_device(hba_info); /* Get Number of Ports */ atp->NumberOfPorts = hba_info->NumberOfPorts; /* Get Manufacturer */ sa_strncpy_safe(atp->Manufacturer, sizeof(atp->Manufacturer), hba_info->Manufacturer, sizeof(hba_info->Manufacturer)); /* Get SerialNumber */ sa_strncpy_safe(atp->SerialNumber, sizeof(atp->SerialNumber), hba_info->SerialNumber, sizeof(hba_info->SerialNumber)); /* Get ModelDescription */ sa_strncpy_safe(atp->ModelDescription, sizeof(atp->ModelDescription), hba_info->ModelDescription, sizeof(hba_info->ModelDescription)); if (!strncmp(hba_info->ModelDescription, "Unknown", sizeof(hba_info->ModelDescription))) { snprintf(atp->ModelDescription, sizeof(atp->ModelDescription), "[%04x:%04x]-[%04x:%04x]-(%04x)", hba_info->vendor_id, hba_info->device_id, hba_info->subsystem_vendor_id, hba_info->subsystem_device_id, hba_info->device_class); /* * Get Model * * If the device is a newly developed product, and * the model description is not in pci.ids yet, use * the model description constructed above as the * model string. */ sa_strncpy_safe(atp->Model, sizeof(atp->Model), atp->ModelDescription, sizeof(atp->ModelDescription)); } /* * Get Model * * If the device name has already been added into * the pci.ids file, use the first word of the model * description as the model. If the space after the * first word is not found (new product), use the * model description as the model. */ sa_strncpy_safe(buf, sizeof(buf), atp->ModelDescription, sizeof(atp->ModelDescription)); if (strtok_r(buf, " ", &saveptr)) sa_strncpy_safe(atp->Model, sizeof(atp->Model), buf, strnlen(buf, sizeof(buf))); else sa_strncpy_safe(atp->Model, sizeof(atp->Model), atp->ModelDescription, sizeof(atp->ModelDescription)); /* Get HardwareVersion */ sa_strncpy_safe(atp->HardwareVersion, sizeof(atp->HardwareVersion), hba_info->HardwareVersion, sizeof(hba_info->HardwareVersion)); /* Get OptionROMVersion (TODO) */ sa_strncpy_safe(atp->OptionROMVersion, sizeof(atp->OptionROMVersion), HBA_ROM_VERSION, sizeof(HBA_ROM_VERSION)); /* Get FirmwareVersion (TODO) */ sa_strncpy_safe(atp->FirmwareVersion, sizeof(atp->FirmwareVersion), HBA_FW_VERSION, sizeof(HBA_FW_VERSION)); /* Get VendorSpecificID (TODO) */ atp->VendorSpecificID = HBA_VENDOR_SPECIFIC_ID; /* Get DriverVersion */ hba_dir = udev_device_get_syspath(pci); sa_sys_read_line(hba_dir, SYSFS_MODULE_VER, atp->DriverVersion, sizeof(atp->DriverVersion)); } struct udev_device * find_netdev_by_ifindex(struct udev *udev, const char *ifindex) { struct udev_enumerate *ue; struct udev_list_entry *head; struct udev_device *newnet = NULL; ue = udev_enumerate_new(udev); udev_enumerate_add_match_subsystem(ue, "net"); udev_enumerate_add_match_sysattr(ue, "ifindex", ifindex); udev_enumerate_scan_devices(ue); /* enumerate returns a list, but there should only ever be one device * with a given ifindex */ head = udev_enumerate_get_list_entry(ue); if (head) newnet = udev_device_new_from_syspath(udev, udev_list_entry_get_name(head)); udev_enumerate_unref(ue); return newnet; } struct udev_device * find_phys_if(struct udev_device *net) { const char *ifindex; const char *iflink; struct udev *udev; struct udev_device *lower = NULL; ifindex = udev_device_get_sysattr_value(net, "ifindex"); iflink = udev_device_get_sysattr_value(net, "iflink"); if (strcmp(ifindex, iflink)) { udev = udev_device_get_udev(net); lower = find_netdev_by_ifindex(udev, iflink); } if (lower) { return lower; } else { udev_device_ref(net); return net; } } static int sysfs_scan(struct udev_device *fc_host) { HBA_ADAPTERATTRIBUTES *atp; HBA_PORTATTRIBUTES *pap; uint64_t wwnn; struct hba_info hba_info; struct adapter_info *ap; struct port_info *pp; const char *hba_dir; char drv_dir[80]; char ifname[20], buf[256]; char *driverName; int data[32], rc, i; char *cp; const char *sysname = udev_device_get_sysname(fc_host); const char *syspath = udev_device_get_syspath(fc_host); struct udev_device *pci; struct udev_device *net; const char *ptr = NULL; const char *attr; memset(&hba_info, 0, sizeof(hba_info)); /* * Create a new HBA entry (ap) for the local port * We will create a new HBA entry for each local port. */ ap = malloc(sizeof(*ap)); if (!ap) { fprintf(stderr, "%s: malloc failed, errno=0x%x\n", __func__, errno); return HBA_STATUS_ERROR; } memset(ap, 0, sizeof(*ap)); ap->ad_kern_index = atoi(sysname + sizeof("host") - 1); ap->ad_port_count = 1; /* atp points to the HBA attributes structure */ atp = &ap->ad_attr; /* * Create a new local port entry */ pp = malloc(sizeof(*pp)); if (pp == NULL) { fprintf(stderr, "%s: malloc for local port %d failed," " errno=0x%x\n", __func__, ap->ad_port_count - 1, errno); free(ap); return 0; } memset(pp, 0, sizeof(*pp)); pp->ap_adapt = ap; pp->ap_index = ap->ad_port_count - 1; pp->ap_kern_hba = atoi(sysname + sizeof("host") - 1); /* pap points to the local port attributes structure */ pap = &pp->ap_attr; /* Get PortSymbolicName */ ptr = udev_device_get_sysattr_value(fc_host, "symbolic_name"); sa_strncpy_safe(pap->PortSymbolicName, sizeof(pap->PortSymbolicName), ptr, strlen(ptr)); /* Skip the HBA if it isn't OpenFC */ cp = strstr(pap->PortSymbolicName, " over "); if (!cp) goto skip; pci = udev_device_get_parent_with_subsystem_devtype(fc_host, "pci", NULL); net = udev_device_get_parent_with_subsystem_devtype(fc_host, "net", NULL); if (!pci && net) { /* check for a vlan device, stacked on a real PCI network device */ net = find_phys_if(net); pci = udev_device_get_parent_with_subsystem_devtype(net, "pci", NULL); } /* * Save the host directory and the hba directory * in local port structure */ sa_strncpy_safe(pp->host_dir, sizeof(pp->host_dir), syspath, strlen(syspath)); /* Get NodeWWN */ attr = udev_device_get_sysattr_value(fc_host, "node_name"); wwnn = strtoull(attr, NULL, 16); copy_wwn(&pap->NodeWWN, wwnn); /* Get PortWWN */ attr = udev_device_get_sysattr_value(fc_host, "port_name"); wwnn = strtoull(attr, NULL, 16); copy_wwn(&pap->PortWWN, wwnn); /* Get PortFcId */ attr = udev_device_get_sysattr_value(fc_host, "port_id"); pap->PortFcId = strtoul(attr, NULL, 0); /* Get PortType */ attr = udev_device_get_sysattr_value(fc_host, "port_type"); rc = sa_enum_encode(port_types_table, attr, &pap->PortType); /* Get PortState */ attr = udev_device_get_sysattr_value(fc_host, "port_state"); rc = sa_enum_encode(port_states_table, attr, &pap->PortState); /* Get PortSpeed */ attr = udev_device_get_sysattr_value(fc_host, "speed"); rc = sys_read_speed(attr, &pap->PortSpeed); /* Get PortSupportedSpeed */ attr = udev_device_get_sysattr_value(fc_host, "supported_speeds"); rc = sys_read_speed(attr, &pap->PortSupportedSpeed); /* Get PortMaxFrameSize */ attr = udev_device_get_sysattr_value(fc_host, "maxframe_size"); sscanf(attr, "%d", &pap->PortMaxFrameSize); /* Get PortSupportedFc4Types */ attr = udev_device_get_sysattr_value(fc_host, "supported_fc4s"); sscanf(attr, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7], &data[8], &data[9], &data[10], &data[11], &data[12], &data[13], &data[14], &data[15], &data[16], &data[17], &data[18], &data[19], &data[20], &data[21], &data[22], &data[23], &data[24], &data[25], &data[26], &data[27], &data[28], &data[29], &data[30], &data[31]); for (i = 0; i < 32; i++) pap->PortSupportedFc4Types.bits[i] = data[i]; /* Get PortActiveFc4Types */ attr = udev_device_get_sysattr_value(fc_host, "active_fc4s"); sscanf(attr, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], &data[7], &data[8], &data[9], &data[10], &data[11], &data[12], &data[13], &data[14], &data[15], &data[16], &data[17], &data[18], &data[19], &data[20], &data[21], &data[22], &data[23], &data[24], &data[25], &data[26], &data[27], &data[28], &data[29], &data[30], &data[31]); for (i = 0; i < 32; i++) pap->PortActiveFc4Types.bits[i] = data[i]; /* Get FabricName */ attr = udev_device_get_sysattr_value(fc_host, "fabric_name"); wwnn = strtoull(attr, NULL, 16); copy_wwn(&pap->FabricName, wwnn); /* Get PortSupportedClassofService */ attr = udev_device_get_sysattr_value(fc_host, "supported_classes"); cp = strstr(attr, "Class"); if (cp) pap->PortSupportedClassofService = *(cp + 6) - '0'; /* Get OSDeviceName */ sa_strncpy_safe(pap->OSDeviceName, sizeof(pap->OSDeviceName), sysname, sizeof(sysname)); /* Get NumberofDiscoveredPorts */ snprintf(buf, sizeof(buf), "%s/device", pp->host_dir); sa_dir_read(buf, counting_rports, &pap->NumberofDiscoveredPorts); /* * Add the local port structure into local port table within * the HBA structure. */ if (sa_table_insert(&ap->ad_ports, pp->ap_index, pp) < 0) { fprintf(stderr, "%s: insert of HBA %d port %d failed\n", __func__, ap->ad_kern_index, pp->ap_index); goto skip; } /* Create adapter name */ snprintf(buf, sizeof(buf), "fcoe:%s", ifname); ap->ad_name = strdup(buf); if (pci) sysfs_scan_pci(pci, &hba_info, atp); /* Get NodeSymbolicName */ sa_strncpy_safe(atp->NodeSymbolicName, sizeof(atp->NodeSymbolicName), ap->ad_name, sizeof(atp->NodeSymbolicName)); /* Get NodeWWN - The NodeWWN is the same as * the NodeWWN of the local port. */ memcpy((char *)&atp->NodeWWN, (char *)&pap->NodeWWN, sizeof(pap->NodeWWN)); /* Get DriverName */ hba_dir = udev_device_get_syspath(pci); snprintf(drv_dir, sizeof(drv_dir), "%s" SYSFS_MODULE , hba_dir); i = readlink(drv_dir, buf, sizeof(buf)); if (i < 0) i = 0; buf[i] = '\0'; if (!strstr(buf, "module")) { /* * Does not find "module" in the string. * This should not happen. In this case, set * the driver name to "Unknown". */ driverName = "Unknown"; } else driverName = strstr(buf, "module") + 7; sa_strncpy_safe(atp->DriverName, sizeof(atp->DriverName), driverName, sizeof(atp->DriverName)); /* * Give HBA to library */ rc = adapter_create(ap); if (rc != HBA_STATUS_OK) { fprintf(stderr, "%s: adapter_create failed, status=%d\n", __func__, rc); adapter_destroy(ap); /* free adapter and ports */ } return 0; skip: free(pp); free(ap); return 0; } void copy_wwn(HBA_WWN *dest, fc_wwn_t src) { dest->wwn[0] = (u_char) (src >> 56); dest->wwn[1] = (u_char) (src >> 48); dest->wwn[2] = (u_char) (src >> 40); dest->wwn[3] = (u_char) (src >> 32); dest->wwn[4] = (u_char) (src >> 24); dest->wwn[5] = (u_char) (src >> 16); dest->wwn[6] = (u_char) (src >> 8); dest->wwn[7] = (u_char) src; } /* Test for a non-zero WWN */ int is_wwn_nonzero(HBA_WWN *wwn) { return (wwn->wwn[0] | wwn->wwn[1] | wwn->wwn[2] | wwn->wwn[3] | wwn->wwn[4] | wwn->wwn[5] | wwn->wwn[6] | wwn->wwn[7]) != 0; } int sys_read_wwn(const char *dir, const char *file, HBA_WWN *wwn) { int rc; u_int64_t val; rc = sa_sys_read_u64(dir, file, &val); if (rc == 0) copy_wwn(wwn, val); return rc; } /* Port Statistics */ HBA_STATUS sysfs_get_port_stats(char *dir, HBA_PORTSTATISTICS *sp) { int rc; rc = sa_sys_read_u64(dir, "seconds_since_last_reset", (u_int64_t *)&sp->SecondsSinceLastReset); rc |= sa_sys_read_u64(dir, "tx_frames", (u_int64_t *)&sp->TxFrames); rc |= sa_sys_read_u64(dir, "tx_words", (u_int64_t *)&sp->TxWords); rc |= sa_sys_read_u64(dir, "rx_frames", (u_int64_t *)&sp->RxFrames); rc |= sa_sys_read_u64(dir, "rx_words", (u_int64_t *)&sp->RxWords); rc |= sa_sys_read_u64(dir, "lip_count", (u_int64_t *)&sp->LIPCount); rc |= sa_sys_read_u64(dir, "nos_count", (u_int64_t *)&sp->NOSCount); rc |= sa_sys_read_u64(dir, "error_frames", (u_int64_t *)&sp->ErrorFrames); rc |= sa_sys_read_u64(dir, "dumped_frames", (u_int64_t *)&sp->DumpedFrames); rc |= sa_sys_read_u64(dir, "link_failure_count", (u_int64_t *)&sp->LinkFailureCount); rc |= sa_sys_read_u64(dir, "loss_of_sync_count", (u_int64_t *)&sp->LossOfSyncCount); rc |= sa_sys_read_u64(dir, "loss_of_signal_count", (u_int64_t *)&sp->LossOfSignalCount); rc |= sa_sys_read_u64(dir, "prim_seq_protocol_err_count", (u_int64_t *)&sp->PrimitiveSeqProtocolErrCount); rc |= sa_sys_read_u64(dir, "invalid_tx_word_count", (u_int64_t *)&sp->InvalidTxWordCount); rc |= sa_sys_read_u64(dir, "invalid_crc_count", (u_int64_t *)&sp->InvalidCRCCount); return rc; } /* Port FC-4 Statistics */ HBA_STATUS sysfs_get_port_fc4stats(char *dir, HBA_FC4STATISTICS *fc4sp) { int rc; rc = sa_sys_read_u64(dir, "fcp_input_requests", (u_int64_t *)&fc4sp->InputRequests); rc |= sa_sys_read_u64(dir, "fcp_output_requests", (u_int64_t *)&fc4sp->OutputRequests); rc |= sa_sys_read_u64(dir, "fcp_control_requests", (u_int64_t *)&fc4sp->ControlRequests); rc |= sa_sys_read_u64(dir, "fcp_input_megabytes", (u_int64_t *)&fc4sp->InputMegabytes); rc |= sa_sys_read_u64(dir, "fcp_output_megabytes", (u_int64_t *)&fc4sp->OutputMegabytes); return rc; } /* * Open device and read adapter info if available. */ int adapter_init(void) { struct udev *udev; struct udev_enumerate *ue; struct udev_list_entry *head, *ul; struct udev_device *fc_host; const char *syspath; int re = 0; udev = udev_new(); if (!udev) { return -ENOMEM; } ue = udev_enumerate_new(udev); if (!ue) { re = -ENOMEM; goto err_enum_new; } re = udev_enumerate_add_match_subsystem(ue, "fc_host"); if (re) { goto err_add_match; } re = udev_enumerate_scan_devices(ue); if (re) { goto err_scan_devs; } head = udev_enumerate_get_list_entry(ue); udev_list_entry_foreach(ul, head) { syspath = udev_list_entry_get_name(ul); fc_host = udev_device_new_from_syspath(udev, syspath); if (!fc_host) continue; sysfs_scan(fc_host); udev_device_unref(fc_host); } err_scan_devs: err_add_match: udev_enumerate_unref(ue); err_enum_new: udev_unref(udev); return re; } void adapter_shutdown(void) { } HBA_STATUS get_port_statistics(HBA_HANDLE handle, HBA_UINT32 port, HBA_PORTSTATISTICS *sp) { struct port_info *pp; char dir[80]; int rc; memset(sp, 0xff, sizeof(*sp)); /* unsupported statistics give -1 */ pp = adapter_get_port(handle, port); if (pp == NULL) { fprintf(stderr, "%s: lookup failed. handle 0x%x port 0x%x\n", __func__, handle, port); return HBA_STATUS_ERROR; } snprintf(dir, sizeof(dir), "%s/statistics", pp->host_dir); rc = sysfs_get_port_stats(dir, sp); if (rc != 0) { fprintf(stderr, "%s: sysfs_get_port_stats() failed," " hba index=%d port index=%d, -rc=0x%x\n", __func__, pp->ap_adapt->ad_kern_index, pp->ap_index, -rc); return HBA_STATUS_ERROR; } return HBA_STATUS_OK; } /* * Get FC4 statistics. */ HBA_STATUS get_port_fc4_statistics(HBA_HANDLE handle, HBA_WWN wwn, HBA_UINT8 fc4_type, HBA_FC4STATISTICS *sp) { struct port_info *pp; char dir[80]; int count; int rc; memset(sp, 0xff, sizeof(*sp)); /* unsupported statistics give -1 */ pp = adapter_get_port_by_wwn(handle, wwn, &count); if (count > 1) return HBA_STATUS_ERROR_AMBIGUOUS_WWN; else if (pp == NULL) return HBA_STATUS_ERROR_ILLEGAL_WWN; snprintf(dir, sizeof(dir), "%s/statistics", pp->host_dir); rc = sysfs_get_port_fc4stats(dir, sp); if (rc != 0) { fprintf(stderr, "%s: sysfs_get_port_fc4stats() failed," " hba index=%d port index=%d, -rc=0x%x\n", __func__, pp->ap_adapt->ad_kern_index, pp->ap_index, -rc); return HBA_STATUS_ERROR; } return HBA_STATUS_OK; }