/* * Copyright (c) 2009-2011, Broadcom Corporation * Copyright (c) 2014, QLogic Corporation * * Written by: Benjamin Li (benli@broadcom.com) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Adam Dunkels. * 4. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * nic_id.c - Using sysfs to determine the PCI vendor, device, subvendor and * subdevice ID's * */ #include #include #include #include #include #include #include "logger.h" #include "nic.h" #define PFX "nic_id " /******************************************************************************* * Sysfs constant strings used to get PCI vendor, and device ID's ******************************************************************************/ const char uio_vendor_id_template[] = "/sys/class/uio/uio%d/device/vendor"; const char uio_subvendor_id_template[] = "/sys/class/uio/uio%d/device/subsystem_vendor"; const char uio_device_id_template[] = "/sys/class/uio/uio%d/device/device"; const char uio_subdevice_id_template[] = "/sys/class/uio/uio%d/device/subsystem_device"; const char uio_device_symlink_template[] = "/sys/class/uio/uio%d/device"; /** * get_id() - Utility function to read hex values from sysfs * @param nic - NIC device to use * @param sysfs_template - sysfs path template to use * @param sysfs_template_size - sysfs path template size in bytes * @parm id - this is the value returned from the sysfs entry * @return 0 on success <0 on failure */ static int get_id(nic_t *nic, const char *sysfs_template, const size_t sysfs_template_size, uint32_t *id) { int rc = 0; FILE *fp; size_t chars_read; char buf[7]; char *path; size_t path_size; path_size = sysfs_template_size + 4; path = malloc(path_size); if (path == NULL) { LOG_ERR("Could not allocate memory for %s", sysfs_template); return -ENOMEM; } snprintf(path, path_size, sysfs_template, nic->uio_minor); fp = fopen(path, "r"); if (fp == NULL) { LOG_ERR(PFX "%s: Could not open path: %s [%s]", nic->log_name, path, strerror(errno)); rc = -EIO; goto error_fopen; } chars_read = fread(buf, sizeof(buf), 1, fp); if (chars_read != 1) { LOG_ERR(PFX "%s: Could not read from: %s [%s]", nic->log_name, path, strerror(ferror(fp))); rc = -EIO; goto error; } chars_read = sscanf(buf, "%x", id); if (chars_read != 1) { LOG_ERR(PFX "%s: Could interpret value: %s from: %s [%s]", nic->log_name, buf, path, strerror(errno)); rc = -EIO; goto error; } error: fclose(fp); error_fopen: free(path); return rc; } static int get_vendor(nic_t *nic, uint32_t *id) { return get_id(nic, uio_vendor_id_template, sizeof(uio_vendor_id_template), id); } static int get_subvendor(nic_t *nic, uint32_t *id) { return get_id(nic, uio_subvendor_id_template, sizeof(uio_subvendor_id_template), id); } static int get_device(nic_t *nic, uint32_t *id) { return get_id(nic, uio_device_id_template, sizeof(uio_device_id_template), id); } static int get_subdevice(nic_t *nic, uint32_t *id) { return get_id(nic, uio_subdevice_id_template, sizeof(uio_subdevice_id_template), id); } int get_bus_slot_func_num(nic_t *nic, uint32_t *bus, uint32_t *slot, uint32_t *func) { size_t size; char *path, *tok, *tok2; int path_tokens, i; size_t path_size; char *read_pci_bus_slot_func_str; char pci_bus_slot_func_str[32]; int rc; char *saveptr; path_size = sizeof(uio_device_symlink_template) + 4; path = malloc(path_size); if (path == NULL) { LOG_ERR(PFX "%s: Could not allocate path memory for %s", nic->log_name, uio_device_symlink_template); rc = -ENOMEM; goto error_alloc_path; } read_pci_bus_slot_func_str = malloc(128); if (read_pci_bus_slot_func_str == NULL) { LOG_ERR(PFX "%s: Could not allocate read pci bus memory for %s", nic->log_name, uio_device_symlink_template); rc = -ENOMEM; goto error_alloc_read_pci_bus; } snprintf(path, path_size, uio_device_symlink_template, nic->uio_minor); size = readlink(path, read_pci_bus_slot_func_str, 128); if (size == -1) { LOG_ERR(PFX "%s: Error with %s: %s", nic->log_name, path, strerror(errno)); rc = errno; goto error; } if (size > ((128) - 1)) { read_pci_bus_slot_func_str[128 - 1] = '\0'; LOG_ERR(PFX "%s: not enough space (%d) for reading PCI " "slot:bus.func %s: %s", nic->log_name, size, path, strerror(errno)); rc = -EIO; goto error; } /* readlink() doesn't NULL terminate the string */ read_pci_bus_slot_func_str[size] = '\0'; path_tokens = 0; tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); while (tok != NULL) { path_tokens++; tok = strtok_r(NULL, "/", &saveptr); } size = readlink(path, read_pci_bus_slot_func_str, 128); if (size == -1) { LOG_ERR(PFX "%s: Error with %s: %s", nic->log_name, path, strerror(errno)); rc = errno; goto error; } if (size > ((128) - 1)) { read_pci_bus_slot_func_str[128 - 1] = '\0'; LOG_ERR(PFX "%s: not enough space for reading PCI " "slot:bus.func %s: %s", nic->log_name, path, strerror(errno)); rc = -EIO; goto error; } /* readlink() doesn't NULL terminate the string */ read_pci_bus_slot_func_str[size] = '\0'; tok = strtok_r(read_pci_bus_slot_func_str, "/", &saveptr); for (i = 0; i < path_tokens - 1; i++) tok = strtok_r(NULL, "/", &saveptr); strcpy(pci_bus_slot_func_str, tok); tok = strtok_r(pci_bus_slot_func_str, ":", &saveptr); if (tok == NULL) { LOG_ERR(PFX "%s: Error with slot string: %s", nic->log_name, pci_bus_slot_func_str); rc = -EIO; goto error; } tok = strtok_r(NULL, ":", &saveptr); if (tok == NULL) { LOG_ERR(PFX "%s: Error parsing slot: %s", nic->log_name, pci_bus_slot_func_str); rc = -EIO; goto error; } sscanf(tok, "%x", bus); /* Need to extract the next token "xx.x" */ tok = strtok_r(NULL, ":", &saveptr); if (tok == NULL) { LOG_ERR(PFX "%s: Error extracing bus.func: %s", nic->log_name, pci_bus_slot_func_str); rc = -EIO; goto error; } tok2 = strtok_r(tok, ".", &saveptr); if (tok2 == NULL) { LOG_ERR(PFX "%s: Error parsing bus: %s", nic->log_name, pci_bus_slot_func_str); rc = -EIO; goto error; } sscanf(tok2, "%x", slot); tok2 = strtok_r(NULL, ".", &saveptr); if (tok2 == NULL) { LOG_ERR(PFX "%s: Error parsing func: %s", nic->log_name, pci_bus_slot_func_str); rc = -EIO; goto error; } sscanf(tok2, "%x", func); LOG_INFO(PFX "%s: is found at %02x:%02x.%02x", nic->log_name, *bus, *slot, *func); rc = 0; error: free(read_pci_bus_slot_func_str); error_alloc_read_pci_bus: free(path); error_alloc_path: return rc; } /** * find_set_nic_lib() - Match the NIC library to the NIC * @param nic - NIC device to determine which NIC library to use * @return 0 on success <0 on failure */ int find_set_nic_lib(nic_t *nic) { uint32_t vendor; uint32_t subvendor; uint32_t device; uint32_t subdevice; uint32_t pci_bus; uint32_t pci_slot; uint32_t pci_func; int rc = 0; nic_lib_handle_t *handle; struct pci_device_id *pci_entry; size_t name_size; rc = get_vendor(nic, &vendor); if (rc != 0) { LOG_ERR(PFX "%s: Could not get vendor id [0x%x]", nic->log_name, rc); return rc; } rc = get_subvendor(nic, &subvendor); if (rc != 0) { LOG_ERR(PFX "%s: Could not get subvendor id [0x%x]", nic->log_name, rc); return rc; } rc = get_device(nic, &device); if (rc != 0) { LOG_ERR(PFX "%s: Could not get device id [0x%x]", nic->log_name, rc); return rc; } rc = get_subdevice(nic, &subdevice); if (rc != 0) { LOG_ERR(PFX "%s: Could not get subdevice id [0x%x]", nic->log_name, rc); return rc; } get_bus_slot_func_num(nic, &pci_bus, &pci_slot, &pci_func); LOG_DEBUG(PFX "%s: Looking for device vendor: " "0x%x subvendor: 0x%x device: 0x%x subdevice: 0x%x", nic->log_name, vendor, subvendor, device, subdevice); rc = find_nic_lib_using_pci_id(vendor, device, subvendor, subdevice, &handle, &pci_entry); if (rc != 0) { LOG_WARN(PFX "%s: Couldn't find proper NIC library", nic->log_name); return rc; } nic->nic_library = handle; nic->pci_id = pci_entry; /* Prepare the NIC library op table */ nic->ops = handle->ops; (*nic->ops->lib_ops.get_library_name) (&nic->library_name, &name_size); return 0; }