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