/* * 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" static int sys_read_port_state(const char *, const char *, u_int32_t *); static int sys_read_classes(const char *, const char *, u_int32_t *); static struct sa_table rports_table; /* table of discovered ports */ /* * Handle a single remote port from the /sys directory entry. * The return value is 0 unless an error is detected which should stop the * directory read. */ static int sysfs_get_rport(struct dirent *dp, void *arg) { struct port_info *rp; HBA_PORTATTRIBUTES *rpa; int rc; u_int32_t hba; u_int32_t port; u_int32_t rp_index; char *rport_dir; char buf[256]; /* * Parse name into bus number, channel number, and remote port number. */ hba = ~0; port = ~0; rp_index = ~0; rc = sscanf(dp->d_name, SYSFS_RPORT_DIR, &hba, &port, &rp_index); if (rc != 3) { fprintf(stderr, "%s: remote port %s didn't parse." " rc %d h 0x%x p 0x%x rp 0x%x\n", __func__, dp->d_name, rc, hba, port, rp_index); return 0; } /* * Allocate a remote port. */ rp = malloc(sizeof(*rp)); if (rp == NULL) { fprintf(stderr, "%s: malloc for remote port %s failed," " errno=0x%x\n", __func__, dp->d_name, errno); return ENOMEM; } memset(rp, 0, sizeof(*rp)); rp->ap_kern_hba = hba; rp->ap_index = port; rp->ap_disc_index = rp_index; rpa = &rp->ap_attr; snprintf(rpa->OSDeviceName, sizeof(rpa->OSDeviceName), "%s/%s", SYSFS_RPORT_ROOT, dp->d_name); rport_dir = rpa->OSDeviceName; rc = 0; rc |= sys_read_wwn(rport_dir, "node_name", &rpa->NodeWWN); rc |= sys_read_wwn(rport_dir, "port_name", &rpa->PortWWN); rc |= sa_sys_read_u32(rport_dir, "port_id", &rpa->PortFcId); rc |= sa_sys_read_u32(rport_dir, "scsi_target_id", &rp->ap_scsi_target); sa_sys_read_line(rport_dir, "maxframe_size", buf, sizeof(buf)); sscanf(buf, "%d", &rpa->PortMaxFrameSize); rc |= sys_read_port_state(rport_dir, "port_state", &rpa->PortState); rc |= sys_read_classes(rport_dir, "supported_classes", &rpa->PortSupportedClassofService); if (rc != 0 || sa_table_append(&rports_table, rp) < 0) { if (rc != 0) fprintf(stderr, "%s: errors (%x) from /sys reads in %s\n", __func__, rc, dp->d_name); else fprintf(stderr, "%s: sa_table_append error on rport %s\n", __func__, dp->d_name); free(rp); } return 0; } /* * Get remote port information from /sys. */ static void sysfs_find_rports(void) { sa_dir_read(SYSFS_RPORT_ROOT, sysfs_get_rport, NULL); } /* * Read port state as formatted by scsi_transport_fc.c in the linux kernel. */ static int sys_read_port_state(const char *dir, const char *file, u_int32_t *statep) { char buf[256]; int rc; rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); if (rc == 0) { rc = sa_enum_encode(port_states_table, buf, statep); if (rc != 0) fprintf(stderr, "%s: parse error. file %s/%s line '%s'\n", __func__, dir, file, buf); } return rc; } /* * Read class list as formatted by scsi_transport_fc.c in the linux kernel. * Format is expected to be "Class 3[, Class 4]..." * Actually accepts "[Class ]3[,[ ][Class ]4]..." (i.e., "Class" and spaces * are optional). */ static int sys_read_classes(const char *dir, const char *file, u_int32_t *classp) { char buf[256]; int rc; u_int32_t val; char *cp; char *ep; *classp = 0; rc = sa_sys_read_line(dir, file, buf, sizeof(buf)); if (rc == 0 && strstr(buf, "unspecified") == NULL) { for (cp = buf; *cp != '\0'; cp = ep) { if (strncmp(cp, "Class ", 6) == 0) cp += 6; val = strtoul(cp, &ep, 10); *classp |= 1 << val; if (*ep == '\0') break; if (*ep == ',') { ep++; if (*ep == ' ') ep++; } else { fprintf(stderr, "%s: parse error. file %s/%s " "line '%s' ep '%c'\n", __func__, dir, file, buf, *ep); rc = -1; break; } } } return rc; } /* * Get all discovered ports for a particular port using /sys. */ void get_rport_info(struct port_info *pp) { struct port_info *rp; int rp_count = 0; int ri; if (rports_table.st_size == 0) sysfs_find_rports(); for (ri = 0; ri < rports_table.st_size; ri++) { rp = sa_table_lookup(&rports_table, ri); if (rp != NULL) { if (rp->ap_kern_hba == pp->ap_kern_hba && rp->ap_index == pp->ap_index && rp->ap_adapt == NULL) { rp->ap_adapt = pp->ap_adapt; if (sa_table_lookup(&pp->ap_rports, rp->ap_disc_index)) { fprintf(stderr, "%s: discovered port exists. " "hba %x port %x rport %x\n", __func__, pp->ap_kern_hba, pp->ap_index, rp->ap_disc_index); } sa_table_insert(&pp->ap_rports, rp->ap_disc_index, rp); } rp_count++; } } }