Serenity Operating System
at master 228 lines 7.3 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/OwnPtr.h> 8#include <AK/RefPtr.h> 9#include <AK/StringView.h> 10 11#include "Database.h" 12 13namespace PCIDB { 14 15RefPtr<Database> Database::open(DeprecatedString const& filename) 16{ 17 auto file_or_error = Core::MappedFile::map(filename); 18 if (file_or_error.is_error()) 19 return nullptr; 20 auto res = adopt_ref(*new Database(file_or_error.release_value())); 21 if (res->init() != 0) 22 return nullptr; 23 return res; 24} 25 26const StringView Database::get_vendor(u16 vendor_id) const 27{ 28 auto const& vendor = m_vendors.get(vendor_id); 29 if (!vendor.has_value()) 30 return ""sv; 31 return vendor.value()->name; 32} 33 34const StringView Database::get_device(u16 vendor_id, u16 device_id) const 35{ 36 auto const& vendor = m_vendors.get(vendor_id); 37 if (!vendor.has_value()) 38 return ""sv; 39 auto const& device = vendor.value()->devices.get(device_id); 40 if (!device.has_value()) 41 return ""sv; 42 return device.value()->name; 43} 44 45const StringView Database::get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const 46{ 47 auto const& vendor = m_vendors.get(vendor_id); 48 if (!vendor.has_value()) 49 return ""sv; 50 auto const& device = vendor.value()->devices.get(device_id); 51 if (!device.has_value()) 52 return ""sv; 53 auto const& subsystem = device.value()->subsystems.get((subvendor_id << 16) + subdevice_id); 54 if (!subsystem.has_value()) 55 return ""sv; 56 return subsystem.value()->name; 57} 58 59const StringView Database::get_class(u8 class_id) const 60{ 61 auto const& xclass = m_classes.get(class_id); 62 if (!xclass.has_value()) 63 return ""sv; 64 return xclass.value()->name; 65} 66 67const StringView Database::get_subclass(u8 class_id, u8 subclass_id) const 68{ 69 auto const& xclass = m_classes.get(class_id); 70 if (!xclass.has_value()) 71 return ""sv; 72 auto const& subclass = xclass.value()->subclasses.get(subclass_id); 73 if (!subclass.has_value()) 74 return ""sv; 75 return subclass.value()->name; 76} 77 78const StringView Database::get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const 79{ 80 auto const& xclass = m_classes.get(class_id); 81 if (!xclass.has_value()) 82 return ""sv; 83 auto const& subclass = xclass.value()->subclasses.get(subclass_id); 84 if (!subclass.has_value()) 85 return ""sv; 86 auto const& programming_interface = subclass.value()->programming_interfaces.get(programming_interface_id); 87 if (!programming_interface.has_value()) 88 return ""sv; 89 return programming_interface.value()->name; 90} 91 92static u8 parse_hex_digit(char digit) 93{ 94 if (digit >= '0' && digit <= '9') 95 return digit - '0'; 96 VERIFY(digit >= 'a' && digit <= 'f'); 97 return 10 + (digit - 'a'); 98} 99 100template<typename T> 101static T parse_hex(StringView str, size_t count) 102{ 103 VERIFY(str.length() >= count); 104 105 T res = 0; 106 for (size_t i = 0; i < count; i++) 107 res = (res << 4) + parse_hex_digit(str[i]); 108 109 return res; 110} 111 112int Database::init() 113{ 114 if (m_ready) 115 return 0; 116 117 m_view = StringView { m_file->bytes() }; 118 119 ParseMode mode = ParseMode::UnknownMode; 120 121 OwnPtr<Vendor> current_vendor {}; 122 OwnPtr<Device> current_device {}; 123 OwnPtr<Class> current_class {}; 124 OwnPtr<Subclass> current_subclass {}; 125 126 auto commit_device = [&]() { 127 if (current_device && current_vendor) { 128 auto id = current_device->id; 129 current_vendor->devices.set(id, current_device.release_nonnull()); 130 } 131 }; 132 133 auto commit_vendor = [&]() { 134 commit_device(); 135 if (current_vendor) { 136 auto id = current_vendor->id; 137 m_vendors.set(id, current_vendor.release_nonnull()); 138 } 139 }; 140 141 auto commit_subclass = [&]() { 142 if (current_subclass && current_class) { 143 auto id = current_subclass->id; 144 current_class->subclasses.set(id, current_subclass.release_nonnull()); 145 } 146 }; 147 148 auto commit_class = [&]() { 149 commit_subclass(); 150 if (current_class) { 151 auto id = current_class->id; 152 m_classes.set(id, current_class.release_nonnull()); 153 } 154 }; 155 156 auto commit_all = [&]() { 157 commit_vendor(); 158 commit_class(); 159 }; 160 161 auto lines = m_view.split_view('\n'); 162 163 for (auto& line : lines) { 164 if (line.length() < 2 || line[0] == '#') 165 continue; 166 167 if (line[0] == 'C') { 168 mode = ParseMode::ClassMode; 169 commit_all(); 170 } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) { 171 mode = ParseMode::VendorMode; 172 commit_all(); 173 } else if (line[0] != '\t') { 174 mode = ParseMode::UnknownMode; 175 continue; 176 } 177 178 switch (mode) { 179 case ParseMode::VendorMode: 180 if (line[0] != '\t') { 181 commit_vendor(); 182 current_vendor = make<Vendor>(); 183 current_vendor->id = parse_hex<u16>(line, 4); 184 current_vendor->name = line.substring_view(6, line.length() - 6); 185 } else if (line[0] == '\t' && line[1] != '\t') { 186 commit_device(); 187 current_device = make<Device>(); 188 current_device->id = parse_hex<u16>(line.substring_view(1, line.length() - 1), 4); 189 current_device->name = line.substring_view(7, line.length() - 7); 190 } else if (line[0] == '\t' && line[1] == '\t') { 191 auto subsystem = make<Subsystem>(); 192 subsystem->vendor_id = parse_hex<u16>(line.substring_view(2, 4), 4); 193 subsystem->device_id = parse_hex<u16>(line.substring_view(7, 4), 4); 194 subsystem->name = line.substring_view(13, line.length() - 13); 195 current_device->subsystems.set((subsystem->vendor_id << 8) + subsystem->device_id, move(subsystem)); 196 } 197 break; 198 case ParseMode::ClassMode: 199 if (line[0] != '\t') { 200 commit_class(); 201 current_class = make<Class>(); 202 current_class->id = parse_hex<u8>(line.substring_view(2, 2), 2); 203 current_class->name = line.substring_view(6, line.length() - 6); 204 } else if (line[0] == '\t' && line[1] != '\t') { 205 commit_subclass(); 206 current_subclass = make<Subclass>(); 207 current_subclass->id = parse_hex<u8>(line.substring_view(1, 2), 2); 208 current_subclass->name = line.substring_view(5, line.length() - 5); 209 } else if (line[0] == '\t' && line[1] == '\t') { 210 auto programming_interface = make<ProgrammingInterface>(); 211 programming_interface->id = parse_hex<u8>(line.substring_view(2, 2), 2); 212 programming_interface->name = line.substring_view(6, line.length() - 6); 213 current_subclass->programming_interfaces.set(programming_interface->id, move(programming_interface)); 214 } 215 break; 216 default: 217 break; 218 } 219 } 220 221 commit_all(); 222 223 m_ready = true; 224 225 return 0; 226} 227 228}