Serenity Operating System
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}