Serenity Operating System
at portability 250 lines 8.5 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/OwnPtr.h> 28#include <AK/RefPtr.h> 29#include <AK/StringView.h> 30 31#include "Database.h" 32 33namespace PCIDB { 34 35RefPtr<Database> Database::open(const StringView& file_name) 36{ 37 auto res = adopt(*new Database(file_name)); 38 if (res->init() != 0) 39 return nullptr; 40 return res; 41} 42 43const StringView Database::get_vendor(u16 vendor_id) const 44{ 45 const auto& vendor = m_vendors.get(vendor_id); 46 if (!vendor.has_value()) 47 return ""; 48 return vendor.value()->name; 49} 50 51const StringView Database::get_device(u16 vendor_id, u16 device_id) const 52{ 53 const auto& vendor = m_vendors.get(vendor_id); 54 if (!vendor.has_value()) 55 return ""; 56 const auto& device = vendor.value()->devices.get(device_id); 57 if (!device.has_value()) 58 return ""; 59 return device.value()->name; 60} 61 62const StringView Database::get_subsystem(u16 vendor_id, u16 device_id, u16 subvendor_id, u16 subdevice_id) const 63{ 64 const auto& vendor = m_vendors.get(vendor_id); 65 if (!vendor.has_value()) 66 return ""; 67 const auto& device = vendor.value()->devices.get(device_id); 68 if (!device.has_value()) 69 return ""; 70 const auto& subsystem = device.value()->subsystems.get((subvendor_id << 16) + subdevice_id); 71 if (!subsystem.has_value()) 72 return ""; 73 return subsystem.value()->name; 74} 75 76const StringView Database::get_class(u8 class_id) const 77{ 78 const auto& xclass = m_classes.get(class_id); 79 if (!xclass.has_value()) 80 return ""; 81 return xclass.value()->name; 82} 83 84const StringView Database::get_subclass(u8 class_id, u8 subclass_id) const 85{ 86 const auto& xclass = m_classes.get(class_id); 87 if (!xclass.has_value()) 88 return ""; 89 const auto& subclass = xclass.value()->subclasses.get(subclass_id); 90 if (!subclass.has_value()) 91 return ""; 92 return subclass.value()->name; 93} 94 95const StringView Database::get_programming_interface(u8 class_id, u8 subclass_id, u8 programming_interface_id) const 96{ 97 const auto& xclass = m_classes.get(class_id); 98 if (!xclass.has_value()) 99 return ""; 100 const auto& subclass = xclass.value()->subclasses.get(subclass_id); 101 if (!subclass.has_value()) 102 return ""; 103 const auto& programming_interface = subclass.value()->programming_interfaces.get(programming_interface_id); 104 if (!programming_interface.has_value()) 105 return ""; 106 return programming_interface.value()->name; 107} 108 109u8 parse_hex_digit(char digit) 110{ 111 if (digit >= '0' && digit <= '9') 112 return digit - '0'; 113 ASSERT(digit >= 'a' && digit <= 'f'); 114 return 10 + (digit - 'a'); 115} 116 117template<typename T> 118T parse_hex(StringView str, size_t count) 119{ 120 ASSERT(str.length() >= count); 121 122 T res = 0; 123 for (size_t i = 0; i < count; i++) 124 res = (res << 4) + parse_hex_digit(str[i]); 125 126 return res; 127} 128 129int Database::init() 130{ 131 if (m_ready) 132 return 0; 133 134 if (!m_file.is_valid()) 135 return -1; 136 137 m_view = StringView((const char*)m_file.data(), m_file.size()); 138 139 ParseMode mode = ParseMode::UnknownMode; 140 141 OwnPtr<Vendor> current_vendor = nullptr; 142 OwnPtr<Device> current_device = nullptr; 143 OwnPtr<Class> current_class = nullptr; 144 OwnPtr<Subclass> current_subclass = nullptr; 145 146 auto commit_device = [&]() { 147 if (current_device && current_vendor) { 148 auto id = current_device->id; 149 current_vendor->devices.set(id, current_device.release_nonnull()); 150 } 151 }; 152 153 auto commit_vendor = [&]() { 154 commit_device(); 155 if (current_vendor) { 156 auto id = current_vendor->id; 157 m_vendors.set(id, current_vendor.release_nonnull()); 158 } 159 }; 160 161 auto commit_subclass = [&]() { 162 if (current_subclass && current_class) { 163 auto id = current_subclass->id; 164 current_class->subclasses.set(id, current_subclass.release_nonnull()); 165 } 166 }; 167 168 auto commit_class = [&]() { 169 commit_subclass(); 170 if (current_class) { 171 auto id = current_class->id; 172 m_classes.set(id, current_class.release_nonnull()); 173 } 174 }; 175 176 auto commit_all = [&]() { 177 commit_vendor(); 178 commit_class(); 179 }; 180 181 auto lines = m_view.split_view('\n'); 182 183 184 for (auto& line : lines) { 185 if (line.length() < 2 || line[0] == '#') 186 continue; 187 188 if (line[0] == 'C') { 189 mode = ParseMode::ClassMode; 190 commit_all(); 191 } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) { 192 mode = ParseMode::VendorMode; 193 commit_all(); 194 } else if (line[0] != '\t') { 195 mode = ParseMode::UnknownMode; 196 continue; 197 } 198 199 switch (mode) { 200 case ParseMode::VendorMode: 201 if (line[0] != '\t') { 202 commit_vendor(); 203 current_vendor = make<Vendor>(); 204 current_vendor->id = parse_hex<u16>(line, 4); 205 current_vendor->name = line.substring_view(6, line.length() - 6); 206 } else if (line[0] == '\t' && line[1] != '\t') { 207 commit_device(); 208 current_device = make<Device>(); 209 current_device->id = parse_hex<u16>(line.substring_view(1, line.length() - 1), 4); 210 current_device->name = line.substring_view(7, line.length() - 7); 211 } else if (line[0] == '\t' && line[1] == '\t') { 212 auto subsystem = make<Subsystem>(); 213 subsystem->vendor_id = parse_hex<u16>(line.substring_view(2, 4), 4); 214 subsystem->device_id = parse_hex<u16>(line.substring_view(7, 4), 4); 215 subsystem->name = line.substring_view(13, line.length() - 13); 216 current_device->subsystems.set((subsystem->vendor_id << 8) + subsystem->device_id, move(subsystem)); 217 } 218 break; 219 case ParseMode::ClassMode: 220 if (line[0] != '\t') { 221 commit_class(); 222 current_class = make<Class>(); 223 current_class->id = parse_hex<u8>(line.substring_view(2, 2), 2); 224 current_class->name = line.substring_view(6, line.length() - 6); 225 } else if (line[0] == '\t' && line[1] != '\t') { 226 commit_subclass(); 227 current_subclass = make<Subclass>(); 228 current_subclass->id = parse_hex<u8>(line.substring_view(1, 2), 2); 229 current_subclass->name = line.substring_view(5, line.length() - 5); 230 } else if (line[0] == '\t' && line[1] == '\t') { 231 auto programming_interface = make<ProgrammingInterface>(); 232 programming_interface->id = parse_hex<u8>(line.substring_view(2, 2), 2); 233 programming_interface->name = line.substring_view(6, line.length() - 6); 234 current_subclass->programming_interfaces.set(programming_interface->id, move(programming_interface)); 235 } 236 break; 237 default: 238 break; 239 } 240 } 241 242 commit_all(); 243 244 m_ready = true; 245 246 247 return 0; 248} 249 250}