/* * 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 "adapt_impl.h" #include #include #include static void get_device_serial_number(struct pci_device *dev, struct hba_info *hba_info) { pciaddr_t offset; u_int32_t pcie_cap_header; u_int16_t pcie_cap_id; u_int16_t status; u_int8_t cap_ptr; u_int32_t dword_low = 0; u_int32_t dword_high = 0; int rc; /* Default */ snprintf(hba_info->SerialNumber, sizeof(hba_info->SerialNumber), "Unknown"); /* * Read the Status Register in the PCIe configuration * header space to see if the PCI Capability List is * supported by this device. */ rc = pci_device_cfg_read_u16(dev, &status, PCI_STATUS); if (rc) { fprintf(stderr, "Failed reading PCI Status Register\n"); return; } if (!(status & PCI_STATUS_CAP_LIST)) { printf("PCI capabilities are not supported\n"); return; } /* * Read the offset (cap_ptr) of first entry in the capability list in * the PCI configuration space. */ rc = pci_device_cfg_read_u8(dev, &cap_ptr, PCI_CAPABILITY_LIST); if (rc) { fprintf(stderr, "Failed reading PCI Capability List Register\n"); return; } offset = cap_ptr; /* Search for the PCIe capability */ while (offset) { u_int8_t cap_id; u_int8_t next_cap; rc = pci_device_cfg_read_u8(dev, &cap_id, offset + PCI_CAP_LIST_ID); if (rc) { #if defined(__x86_64__) fprintf(stderr, "Failed reading capability ID at 0x%lx\n", offset + PCI_CAP_LIST_ID); #elif defined(__i386__) fprintf(stderr, "Failed reading capability ID at 0x%llx\n", offset + PCI_CAP_LIST_ID); #endif return; } if (cap_id != PCI_CAP_ID_EXP) { rc = pci_device_cfg_read_u8(dev, &next_cap, offset + PCI_CAP_LIST_NEXT); if (rc) { #if defined(__x86_64__) fprintf(stderr, "Failed reading next capability " "offset at 0x%lx\n", offset + PCI_CAP_LIST_NEXT); #elif defined(__i386__) fprintf(stderr, "Failed reading next capability " "offset at 0x%llx\n", offset + PCI_CAP_LIST_NEXT); #endif return; } offset = (pciaddr_t)next_cap; continue; } /* * PCIe Capability Structure exists! */ /* * The first PCIe extended capability is located at * offset 0x100 in the device configuration space. */ offset = 0x100; do { rc = pci_device_cfg_read_u32(dev, &pcie_cap_header, offset); if (rc) { fprintf(stderr, "Failed reading PCIe config header\n"); return; } /* Get the PCIe Extended Capability ID */ pcie_cap_id = pcie_cap_header & 0xffff; if (pcie_cap_id != PCI_EXT_CAP_ID_DSN) { /* Get the offset of the next capability */ offset = (pciaddr_t)pcie_cap_header >> 20; continue; } /* * Found the serial number register! */ rc = pci_device_cfg_read_u32(dev, &dword_low, offset + 4); rc = pci_device_cfg_read_u32(dev, &dword_high, offset + 8); snprintf(hba_info->SerialNumber, sizeof(hba_info->SerialNumber), "%02X%02X%02X%02X%02X%02X\n", dword_high >> 24, (dword_high >> 16) & 0xff, (dword_high >> 8) & 0xff, (dword_low >> 16) & 0xff, (dword_low >> 8) & 0xff, dword_low & 0xff); break; } while (offset); break; } } static void get_pci_device_info(struct pci_device *dev, struct hba_info *hba_info) { const char *name; u_int8_t revision; char *unknown = "Unknown"; name = pci_device_get_vendor_name(dev); if (!name) name = unknown; sa_strncpy_safe(hba_info->Manufacturer, sizeof(hba_info->Manufacturer), name, sizeof(hba_info->Manufacturer)); name = pci_device_get_device_name(dev); if (!name) name = unknown; sa_strncpy_safe(hba_info->ModelDescription, sizeof(hba_info->ModelDescription), name, sizeof(hba_info->ModelDescription)); /* * Reading hardware revision from PCIe * configuration header space. */ pci_device_cfg_read_u8(dev, &revision, PCI_REVISION_ID); snprintf(hba_info->HardwareVersion, sizeof(hba_info->HardwareVersion), "%02x", revision); hba_info->NumberOfPorts = 1; /* * Searching for serial number in PCIe extended * capabilities space */ get_device_serial_number(dev, hba_info); } HBA_STATUS find_pci_device(struct hba_info *hba_info) { struct pci_device_iterator *iterator; struct pci_device *dev; struct pci_slot_match match; int rc; rc = pci_system_init(); if (rc) { fprintf(stderr, "pci_system_init failed\n"); return HBA_STATUS_ERROR; } match.domain = hba_info->domain; match.bus = hba_info->bus; match.dev = hba_info->dev; match.func = hba_info->func; iterator = pci_slot_match_iterator_create(&match); for (;;) { dev = pci_device_next(iterator); if (!dev) break; get_pci_device_info(dev, hba_info); } pci_system_cleanup(); return HBA_STATUS_OK; }