diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh index 3db26c42fe..07fc9d97c9 100755 --- a/Kernel/makeall.sh +++ b/Kernel/makeall.sh @@ -21,6 +21,7 @@ build_targets="$build_targets ../Libraries/LibM" build_targets="$build_targets ../Libraries/LibCore" build_targets="$build_targets ../Libraries/LibIPC" build_targets="$build_targets ../Libraries/LibDraw" +build_targets="$build_targets ../Libraries/LibPCIDB" build_targets="$build_targets ../Servers/SystemServer" build_targets="$build_targets ../Servers/LookupServer" build_targets="$build_targets ../Servers/WindowServer" diff --git a/Libraries/LibPCIDB/Database.cpp b/Libraries/LibPCIDB/Database.cpp new file mode 100644 index 0000000000..12aa8ea067 --- /dev/null +++ b/Libraries/LibPCIDB/Database.cpp @@ -0,0 +1,224 @@ +#include +#include +#include + +#include "Database.h" + +namespace PCIDB { + +RefPtr Database::open(const StringView& file_name) +{ + auto res = adopt(*new Database(file_name)); + if (res->init() != 0) + return nullptr; + return res; +} + +const StringView Database::get_vendor(u16 vendor_id) const +{ + const auto& vendor = m_vendors.get(vendor_id); + if (!vendor.has_value()) + return ""; + return vendor.value()->name; +} + +const StringView Database::get_device(u16 vendor_id, u16 device_id) const +{ + const auto& vendor = m_vendors.get(vendor_id); + if (!vendor.has_value()) + return ""; + const auto& device = vendor.value()->devices.get(device_id); + if (!device.has_value()) + return ""; + return device.value()->name; +} + +const StringView Database::get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const +{ + const auto& vendor = m_vendors.get(vendor_id); + if (!vendor.has_value()) + return ""; + const auto& device = vendor.value()->devices.get(device_id); + if (!device.has_value()) + return ""; + const auto& subsystem = device.value()->subsystems.get((subvendor_id << 16) + subdevice_id); + if (!subsystem.has_value()) + return ""; + return subsystem.value()->name; +} + +const StringView Database::get_class(u8 class_id) const +{ + const auto& xclass = m_classes.get(class_id); + if (!xclass.has_value()) + return ""; + return xclass.value()->name; +} + +const StringView Database::get_subclass(u8 class_id, u8 subclass_id) const +{ + const auto& xclass = m_classes.get(class_id); + if (!xclass.has_value()) + return ""; + const auto& subclass = xclass.value()->subclasses.get(subclass_id); + if (!subclass.has_value()) + return ""; + return subclass.value()->name; +} + +const StringView Database::get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const +{ + const auto& xclass = m_classes.get(class_id); + if (!xclass.has_value()) + return ""; + const auto& subclass = xclass.value()->subclasses.get(subclass_id); + if (!subclass.has_value()) + return ""; + const auto& programming_interface = subclass.value()->programming_interfaces.get(programming_interface_id); + if (!programming_interface.has_value()) + return ""; + return programming_interface.value()->name; +} + +u8 parse_hex_digit(char digit) +{ + if (digit >= '0' && digit <= '9') + return digit - '0'; + ASSERT(digit >= 'a' && digit <= 'f'); + return 10 + (digit - 'a'); +} + +template +T parse_hex(StringView str, int count) +{ + ASSERT(str.length() >= count); + + T res = 0; + for (int i = 0; i < count; i++) + res = (res << 4) + parse_hex_digit(str[i]); + + return res; +} + +int Database::init() +{ + if (m_ready) + return 0; + + if (!m_file.is_valid()) + return -1; + + m_view = StringView((const char*)m_file.pointer(), m_file.size()); + + ParseMode mode = ParseMode::UnknownMode; + + OwnPtr current_vendor = nullptr; + OwnPtr current_device = nullptr; + OwnPtr current_class = nullptr; + OwnPtr current_subclass = nullptr; + + auto commit_device = [&]() { + if (current_device && current_vendor) { + auto id = current_device->id; + current_vendor->devices.set(id, current_device.release_nonnull()); + } + }; + + auto commit_vendor = [&]() { + commit_device(); + if (current_vendor) { + auto id = current_vendor->id; + m_vendors.set(id, current_vendor.release_nonnull()); + } + }; + + auto commit_subclass = [&]() { + if (current_subclass && current_class) { + auto id = current_subclass->id; + current_class->subclasses.set(id, current_subclass.release_nonnull()); + } + }; + + auto commit_class = [&]() { + commit_subclass(); + if (current_class) { + auto id = current_class->id; + m_classes.set(id, current_class.release_nonnull()); + } + }; + + auto commit_all = [&]() { + commit_vendor(); + commit_class(); + }; + + auto lines = m_view.split_view('\n'); + + + for (auto& line : lines) { + if (line.length() < 2 || line[0] == '#') + continue; + + if (line[0] == 'C') { + mode = ParseMode::ClassMode; + commit_all(); + } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) { + mode = ParseMode::VendorMode; + commit_all(); + } else if (line[0] != '\t') { + mode = ParseMode::UnknownMode; + continue; + } + + switch (mode) { + case ParseMode::VendorMode: + if (line[0] != '\t') { + commit_vendor(); + current_vendor = make(); + current_vendor->id = parse_hex(line, 4); + current_vendor->name = line.substring_view(6, line.length() - 6); + } else if (line[0] == '\t' && line[1] != '\t') { + commit_device(); + current_device = make(); + current_device->id = parse_hex(line.substring_view(1, line.length() - 1), 4); + current_device->name = line.substring_view(7, line.length() - 7); + } else if (line[0] == '\t' && line[1] == '\t') { + auto subsystem = make(); + subsystem->vendor_id = parse_hex(line.substring_view(2, 4), 4); + subsystem->device_id = parse_hex(line.substring_view(7, 4), 4); + subsystem->name = line.substring_view(13, line.length() - 13); + current_device->subsystems.set((subsystem->vendor_id << 8) + subsystem->device_id, move(subsystem)); + } + break; + case ParseMode::ClassMode: + if (line[0] != '\t') { + commit_class(); + current_class = make(); + current_class->id = parse_hex(line.substring_view(2, 2), 2); + current_class->name = line.substring_view(6, line.length() - 6); + } else if (line[0] == '\t' && line[1] != '\t') { + commit_subclass(); + current_subclass = make(); + current_subclass->id = parse_hex(line.substring_view(1, 2), 2); + current_subclass->name = line.substring_view(5, line.length() - 5); + } else if (line[0] == '\t' && line[1] == '\t') { + auto programming_interface = make(); + programming_interface->id = parse_hex(line.substring_view(2, 2), 2); + programming_interface->name = line.substring_view(6, line.length() - 6); + current_subclass->programming_interfaces.set(programming_interface->id, move(programming_interface)); + } + break; + default: + break; + } + } + + commit_all(); + + m_ready = true; + + + return 0; +} + +} diff --git a/Libraries/LibPCIDB/Database.h b/Libraries/LibPCIDB/Database.h new file mode 100644 index 0000000000..98935da81f --- /dev/null +++ b/Libraries/LibPCIDB/Database.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace PCIDB { + +struct Subsystem { + u16 vendor_id; + u16 device_id; + StringView name; +}; + +struct Device { + u16 id; + StringView name; + HashMap> subsystems; +}; + +struct Vendor { + u16 id; + StringView name; + HashMap> devices; +}; + +struct ProgrammingInterface { + u8 id { 0 }; + StringView name {}; +}; + +struct Subclass { + u8 id { 0 }; + StringView name {}; + HashMap> programming_interfaces; +}; + +struct Class { + u8 id { 0 }; + StringView name {}; + HashMap> subclasses; +}; + +class Database : public RefCounted { +public: + static RefPtr open(const StringView& file_name); + static RefPtr open() { return open("/res/pci.ids"); }; + + const StringView get_vendor(u16 vendor_id) const; + const StringView get_device(u16 vendor_id, u16 device_id) const; + const StringView get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const; + const StringView get_class(u8 class_id) const; + const StringView get_subclass(u8 class_id, u8 subclass_id) const; + const StringView get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const; + +private: + Database(const StringView& file_name) + : m_file(file_name) {}; + + int init(); + + enum ParseMode { + UnknownMode, + VendorMode, + ClassMode, + }; + + MappedFile m_file {}; + StringView m_view {}; + HashMap> m_vendors; + HashMap> m_classes; + bool m_ready { false }; +}; + +} diff --git a/Libraries/LibPCIDB/Makefile b/Libraries/LibPCIDB/Makefile new file mode 100644 index 0000000000..990099fa01 --- /dev/null +++ b/Libraries/LibPCIDB/Makefile @@ -0,0 +1,20 @@ +include ../../Makefile.common + +OBJS = \ + Database.o + +LIBRARY = libpcidb.a +DEFINES += -DUSERLAND + +all: $(LIBRARY) + +$(LIBRARY): $(OBJS) + @echo "LIB $@"; $(AR) rcs $@ $(OBJS) $(LIBS) + +.cpp.o: + @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< + +-include $(OBJS:%.o=%.d) + +clean: + @echo "CLEAN"; rm -f $(LIBRARY) $(OBJS) *.d diff --git a/Libraries/LibPCIDB/install.sh b/Libraries/LibPCIDB/install.sh new file mode 100755 index 0000000000..16019bc702 --- /dev/null +++ b/Libraries/LibPCIDB/install.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +set -e +SERENITY_ROOT=../../ + +mkdir -p $SERENITY_ROOT/Root/usr/include/LibPCIDB/ +cp *.h $SERENITY_ROOT/Root/usr/include/LibPCIDB/ +cp libpcidb.a $SERENITY_ROOT/Root/usr/lib/ diff --git a/Userland/Makefile b/Userland/Makefile index 5da8e05a89..955eb07470 100644 --- a/Userland/Makefile +++ b/Userland/Makefile @@ -19,7 +19,7 @@ clean: $(APPS) : % : %.o $(OBJS) @echo "LD $@" - @$(LD) -o $@ $(LDFLAGS) $< -lc -lgui -ldraw -laudio -lipc -lcore + @$(LD) -o $@ $(LDFLAGS) $< -lc -lgui -ldraw -laudio -lipc -lcore -lpcidb %.o: %.cpp @echo "CXX $<"