From 96d885e93ad8ca966c7d02a932cd49f31f7dd6fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 25 Sep 2024 16:41:55 +0800 Subject: [PATCH] GPU: embed pci.ids into fastfetch --- CMakeLists.txt | 18 ++++++- scripts/gen-pciids.py | 89 +++++++++++++++++++++++++++++++++++ src/common/init.c | 3 ++ src/detection/gpu/gpu.h | 2 +- src/detection/gpu/gpu_bsd.c | 2 +- src/detection/gpu/gpu_linux.c | 25 +--------- src/detection/gpu/gpu_pci.c | 88 ++++++++++++++++++++++++++++++---- src/detection/gpu/gpu_sunos.c | 2 +- 8 files changed, 191 insertions(+), 38 deletions(-) create mode 100644 scripts/gen-pciids.py diff --git a/CMakeLists.txt b/CMakeLists.txt index ea2c93f3..655e4792 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ option(BUILD_TESTS "Build tests" OFF) # Also create test executables option(SET_TWEAK "Add tweak to project version" ON) # This is set to off by github actions for release builds option(IS_MUSL "Build with musl libc" OFF) # Used by Github Actions option(INSTALL_LICENSE "Install license into /usr/share/licenses" ON) +option(ENABLE_EMBEDDED_PCIIDS "Embed pci.ids into fastfetch, requires `python`" OFF) set(BINARY_LINK_TYPE_OPTIONS dlopen dynamic static) set(BINARY_LINK_TYPE dlopen CACHE STRING "How to link fastfetch") @@ -254,6 +255,16 @@ else() file(READ "src/data/help.json" DATATEXT_JSON_HELP) endif() +if(ENABLE_EMBEDDED_PCIIDS AND NOT EXISTS "fastfetch_pciids.c.inc") + if(Python_FOUND) + execute_process(COMMAND ${Python_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/scripts/gen-pciids.py" + OUTPUT_FILE "fastfetch_pciids.c.inc") + else() + message(STATUS "Python3 is not found, 'fastfetch_pciids.c.inc' will not be generated") + set(ENABLE_EMBEDDED_PCIIDS OFF) + endif() +endif() + fastfetch_encode_c_string("${DATATEXT_JSON_HELP}" DATATEXT_JSON_HELP) fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE) fastfetch_load_text(src/data/help_footer.txt DATATEXT_HELP_FOOTER) @@ -1130,14 +1141,17 @@ if(ENABLE_LIBZFS) endif() endif() - if(ENABLE_THREADS) - target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_THREADS=1) if(CMAKE_USE_PTHREADS_INIT) #Threads::Threads is not set for WIN32 target_link_libraries(libfastfetch PRIVATE Threads::Threads) endif() endif() +if(ENABLE_EMBEDDED_PCIIDS) + target_compile_definitions(libfastfetch PRIVATE FF_HAVE_EMBEDDED_PCIIDS=1) +endif() + if(LINUX) target_link_libraries(libfastfetch PRIVATE "m" diff --git a/scripts/gen-pciids.py b/scripts/gen-pciids.py new file mode 100644 index 00000000..bc98fec5 --- /dev/null +++ b/scripts/gen-pciids.py @@ -0,0 +1,89 @@ +from requests import get as http_get + +class PciDeviceModel: + def __init__(self, id: int, name: str): + self.id = id + self.name = name + +class PciVendorModel: + def __init__(self, id: int, name: str): + self.id = id + self.name = name + self.devices: list[PciDeviceModel] = [] + +def main(keep_vendor_list: set[int]): + vendors: list[PciVendorModel] = [] + try: + with open('pci.ids', 'r') as f: + full_text = f.read() + except FileNotFoundError: + response = http_get('https://pci-ids.ucw.cz/v2.2/pci.ids') + full_text = response.text + + dev_list_text = full_text[:full_text.rfind('\n\n\n')] # remove known classes + for line in dev_list_text.split('\n'): + if not line or line[0] == '#': + continue + if line[0] != '\t': + id, name = line.split(' ', maxsplit=1) + vendors.append(PciVendorModel(int(id, 16), name)) + elif line[1] != '\t': + id, name = line[1:].split(' ', maxsplit=1) + vendors[-1].devices.append(PciDeviceModel(int(id, 16), name)) + + code = """\ +#include +#include + +typedef struct FFPciDevice +{ + const uint32_t id; + const char* name; +} FFPciDevice; + +typedef struct FFPciVendor +{ + const uint32_t id; + const char* name; + const FFPciDevice* devices; + const uint32_t nDevices; +} FFPciVendor; +""" + + if keep_vendor_list: + vendors = [vendor for vendor in vendors if vendor.id in keep_vendor_list] + + for vendor in vendors: + if vendor.devices: + code += f""" +// {vendor.name} +static const FFPciDevice pciDevices_{vendor.id:04X}[] = {{ + {',\n '.join(f'{{ 0x{device.id:04X}, "{device.name.replace('"', '\\"')}" }}' for device in vendor.devices)}, + {{}}, +}}; +""" + + code += f""" +const FFPciVendor ffPciVendors[] = {{ + {',\n '.join(f'{{ 0x{vendor.id:04X}, "{vendor.name.replace('"', '\\"')}", {vendor.devices and f"pciDevices_{vendor.id:04X}" or "NULL"}, {len(vendor.devices)} }}' for vendor in vendors)}, + {{}}, +}};""" + + print(code) + +if __name__ == '__main__': + # From + main({ + 0x106b, # Apple + 0x1002, 0x1022, # AMD + 0x8086, 0x8087, 0x03e7, # Intel + 0x0955, 0x10de, 0x12d2, # Nvidia + 0x1ed5, # MThreads + 0x5143, # Qualcomm + 0x14c3, # MTK + 0x15ad, # VMware + 0x1af4, # RedHat + 0x1ab8, # Parallel + 0x1414, # Microsoft + 0x108e, # Oracle + }) diff --git a/src/common/init.c b/src/common/init.c index 8bf6f60e..9845ff52 100644 --- a/src/common/init.c +++ b/src/common/init.c @@ -260,6 +260,9 @@ void ffListFeatures(void) #if FF_HAVE_LINUX_WIRELESS "linux/wireless\n" #endif + #if FF_HAVE_EMBEDDED_PCIIDS + "Embedded pciids\n" + #endif "" , stdout); } diff --git a/src/detection/gpu/gpu.h b/src/detection/gpu/gpu.h index 700caf77..3edd78fc 100644 --- a/src/detection/gpu/gpu.h +++ b/src/detection/gpu/gpu.h @@ -49,5 +49,5 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus); const char* ffGetGPUVendorString(unsigned vendorId); #if defined(__linux__) || defined(__FreeBSD__) || defined(__sun) -void ffGPUParsePciIds(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu); +void ffGPUFillVendorAndName(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu); #endif diff --git a/src/detection/gpu/gpu_bsd.c b/src/detection/gpu/gpu_bsd.c index 330c4dcc..2f1d9cc5 100644 --- a/src/detection/gpu/gpu_bsd.c +++ b/src/detection/gpu/gpu_bsd.c @@ -58,7 +58,7 @@ const char* ffDetectGPUImpl(const FFGPUOptions* options, FFlist* gpus) if (gpu->name.length == 0) { - ffGPUParsePciIds(pc->pc_subclass, pc->pc_vendor, pc->pc_device, gpu); + ffGPUFillVendorAndName(pc->pc_subclass, pc->pc_vendor, pc->pc_device, gpu); } if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA && (options->temp || options->driverSpecific)) diff --git a/src/detection/gpu/gpu_linux.c b/src/detection/gpu/gpu_linux.c index 585fcb42..0db1373a 100644 --- a/src/detection/gpu/gpu_linux.c +++ b/src/detection/gpu/gpu_linux.c @@ -228,29 +228,6 @@ static void pciDetectIntelSpecific(FFGPUResult* gpu, FFstrbuf* pciDir, FFstrbuf* gpu->frequency = (uint32_t) ffStrbufToUInt(buffer, 0); } -static bool loadPciIds(FFstrbuf* pciids) -{ - #ifdef FF_CUSTOM_PCI_IDS_PATH - - ffReadFileBuffer(FF_STR(FF_CUSTOM_PCI_IDS_PATH), pciids); - if (pciids->length > 0) return true; - - #else - - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/hwdata/pci.ids", pciids); - if (pciids->length > 0) return true; - - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/misc/pci.ids", pciids); // debian? - if (pciids->length > 0) return true; - - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/local/share/hwdata/pci.ids", pciids); - if (pciids->length > 0) return true; - - #endif - - return false; -} - static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey) { const uint32_t drmDirPathLength = deviceDir->length; @@ -303,7 +280,7 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf if (gpu->name.length == 0) { - ffGPUParsePciIds(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu); } pciDetectDriver(&gpu->driver, deviceDir, buffer, drmKey); diff --git a/src/detection/gpu/gpu_pci.c b/src/detection/gpu/gpu_pci.c index 7176120d..5aa86a7b 100644 --- a/src/detection/gpu/gpu_pci.c +++ b/src/detection/gpu/gpu_pci.c @@ -1,15 +1,21 @@ #include "gpu.h" +#include "common/io/io.h" +#include #ifdef __FreeBSD__ #include #endif +#if FF_HAVE_EMBEDDED_PCIIDS +#include "fastfetch_pciids.c.inc" +#endif + static const FFstrbuf* loadPciIds() { static FFstrbuf pciids; if (pciids.chars) return &pciids; - ffStrbufinit(&pciids); + ffStrbufInit(&pciids); #ifdef FF_CUSTOM_PCI_IDS_PATH @@ -18,16 +24,18 @@ static const FFstrbuf* loadPciIds() #else // FF_CUSTOM_PCI_IDS_PATH #if __linux__ - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/hwdata/pci.ids", pciids); + ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/hwdata/pci.ids", &pciids); if (pciids.length == 0) - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/misc/pci.ids", pciids); // debian? - if (pciids.length == 0) - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/local/share/hwdata/pci.ids", pciids); + { + ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/misc/pci.ids", &pciids); // debian? + if (pciids.length == 0) + ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/local/share/hwdata/pci.ids", &pciids); + } #elif __FreeBSD__ // https://github.com/freebsd/freebsd-src/blob/main/usr.sbin/pciconf/pathnames.h - ffReadFileBuffer(_PATH_LOCALBASE "/share/pciids/pci.ids", pciids); + ffReadFileBuffer(_PATH_LOCALBASE "/share/pciids/pci.ids", &pciids); if (pciids.length == 0) - ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/pciids/pci.ids", pciids); + ffReadFileBuffer(FASTFETCH_TARGET_DIR_USR "/share/pciids/pci.ids", &pciids); #elif __sun ffReadFileBuffer(FASTFETCH_TARGET_DIR_ROOT "/usr/share/hwdata/pci.ids", &pciids); #endif @@ -37,9 +45,8 @@ static const FFstrbuf* loadPciIds() return &pciids; } -void ffGPUParsePciIds(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu) +static void parsePciIdsFile(const FFstrbuf* content, uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu) { - const FFstrbuf* content = loadPciIds(); if (content->length) { char buffer[32]; @@ -112,3 +119,66 @@ void ffGPUParsePciIds(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUR ffStrbufSetF(&gpu->name, "%s Device %04X%s", gpu->vendor.length ? gpu->vendor.chars : "Unknown", device, subclassStr); } } + +#if FF_HAVE_EMBEDDED_PCIIDS +static inline int pciDeviceCmp(const FFPciDevice* a, const FFPciDevice* b) +{ + return (int) a->id - (int) b->id; +} + +static bool loadPciidsInc(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu) +{ + for (const FFPciVendor* pvendor = ffPciVendors; pvendor->name; pvendor++) + { + if (pvendor->id != vendor) continue; + + if (!gpu->vendor.length) + ffStrbufSetS(&gpu->vendor, pvendor->name); + + const FFPciDevice* pdevice = (const FFPciDevice*) bsearch(&device, pvendor->devices, pvendor->nDevices, sizeof(FFPciDevice), (void*) pciDeviceCmp); + + if (pdevice) + { + uint32_t nameLen = (uint32_t) strlen(pdevice->name); + const char* closingBracket = pdevice->name + nameLen - 1; + if (*closingBracket == ']') + { + const char* openingBracket = memrchr(pdevice->name, '[', nameLen - 1); + if (openingBracket) + { + openingBracket++; + ffStrbufSetNS(&gpu->name, (uint32_t) (closingBracket - openingBracket), openingBracket); + } + } + if (!gpu->name.length) + ffStrbufSetNS(&gpu->name, nameLen, pdevice->name); + return true; + } + + if (!gpu->name.length) + { + const char* subclassStr; + switch (subclass) + { + case 0 /*PCI_CLASS_DISPLAY_VGA*/: subclassStr = " (VGA compatible)"; break; + case 1 /*PCI_CLASS_DISPLAY_XGA*/: subclassStr = " (XGA compatible)"; break; + case 2 /*PCI_CLASS_DISPLAY_3D*/: subclassStr = " (3D)"; break; + default: subclassStr = ""; break; + } + + ffStrbufSetF(&gpu->name, "%s Device %04X%s", gpu->vendor.length ? gpu->vendor.chars : "Unknown", device, subclassStr); + } + return true; + } + return false; +} +#endif + +void ffGPUFillVendorAndName(uint8_t subclass, uint16_t vendor, uint16_t device, FFGPUResult* gpu) +{ + #if FF_HAVE_EMBEDDED_PCIIDS + bool ok = loadPciidsInc(subclass, vendor, device, gpu); + if (ok) return; + #endif + return parsePciIdsFile(loadPciIds(), subclass, vendor, device, gpu); +} diff --git a/src/detection/gpu/gpu_sunos.c b/src/detection/gpu/gpu_sunos.c index 0a8f27a3..6e8d1946 100644 --- a/src/detection/gpu/gpu_sunos.c +++ b/src/detection/gpu/gpu_sunos.c @@ -73,7 +73,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* if (gpu->name.length == 0) { - ffGPUParsePciIds((uint8_t) subclass, (uint16_t) vendorId, (uint16_t) deviceId, gpu); + ffGPUFillVendorAndName((uint8_t) subclass, (uint16_t) vendorId, (uint16_t) deviceId, gpu); } }