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 USBDB {
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 }
40 auto const& device = vendor.value()->devices.get(device_id);
41 if (!device.has_value())
42 return ""sv;
43 return device.value()->name;
44}
45
46const StringView Database::get_interface(u16 vendor_id, u16 device_id, u16 interface_id) const
47{
48 auto const& vendor = m_vendors.get(vendor_id);
49 if (!vendor.has_value())
50 return ""sv;
51 auto const& device = vendor.value()->devices.get(device_id);
52 if (!device.has_value())
53 return ""sv;
54 auto const& interface = device.value()->interfaces.get(interface_id);
55 if (!interface.has_value())
56 return ""sv;
57 return interface.value()->name;
58}
59
60const StringView Database::get_class(u8 class_id) const
61{
62 auto const& xclass = m_classes.get(class_id);
63 if (!xclass.has_value())
64 return ""sv;
65 return xclass.value()->name;
66}
67
68const StringView Database::get_subclass(u8 class_id, u8 subclass_id) const
69{
70 auto const& xclass = m_classes.get(class_id);
71 if (!xclass.has_value())
72 return ""sv;
73 auto const& subclass = xclass.value()->subclasses.get(subclass_id);
74 if (!subclass.has_value())
75 return ""sv;
76 return subclass.value()->name;
77}
78
79const StringView Database::get_protocol(u8 class_id, u8 subclass_id, u8 protocol_id) const
80{
81 auto const& xclass = m_classes.get(class_id);
82 if (!xclass.has_value())
83 return ""sv;
84 auto const& subclass = xclass.value()->subclasses.get(subclass_id);
85 if (!subclass.has_value())
86 return ""sv;
87 auto const& protocol = subclass.value()->protocols.get(protocol_id);
88 if (!protocol.has_value())
89 return ""sv;
90 return protocol.value()->name;
91}
92
93int Database::init()
94{
95 if (m_ready)
96 return 0;
97
98 m_view = StringView { m_file->bytes() };
99
100 ParseMode mode = ParseMode::UnknownMode;
101
102 OwnPtr<Vendor> current_vendor {};
103 OwnPtr<Device> current_device {};
104 OwnPtr<Class> current_class {};
105 OwnPtr<Subclass> current_subclass {};
106
107 auto commit_device = [&]() {
108 if (current_device && current_vendor) {
109 auto id = current_device->id;
110 current_vendor->devices.set(id, current_device.release_nonnull());
111 }
112 };
113
114 auto commit_vendor = [&]() {
115 commit_device();
116 if (current_vendor) {
117 auto id = current_vendor->id;
118 m_vendors.set(id, current_vendor.release_nonnull());
119 }
120 };
121
122 auto commit_subclass = [&]() {
123 if (current_subclass && current_class) {
124 auto id = current_subclass->id;
125 current_class->subclasses.set(id, current_subclass.release_nonnull());
126 }
127 };
128
129 auto commit_class = [&]() {
130 commit_subclass();
131 if (current_class) {
132 auto id = current_class->id;
133 m_classes.set(id, current_class.release_nonnull());
134 }
135 };
136
137 auto commit_all = [&]() {
138 commit_vendor();
139 commit_class();
140 };
141
142 auto lines = m_view.split_view('\n');
143
144 for (auto& line : lines) {
145 if (line.length() < 2 || line[0] == '#')
146 continue;
147
148 if (line[0] == 'C') {
149 mode = ParseMode::ClassMode;
150 commit_all();
151 } else if ((line[0] >= '0' && line[0] <= '9') || (line[0] >= 'a' && line[0] <= 'f')) {
152 mode = ParseMode::VendorMode;
153 commit_all();
154 } else if (line[0] != '\t') {
155 mode = ParseMode::UnknownMode;
156 continue;
157 }
158
159 switch (mode) {
160 case ParseMode::VendorMode:
161 if (line[0] != '\t') {
162 commit_vendor();
163 current_vendor = make<Vendor>();
164 current_vendor->id = AK::StringUtils::convert_to_uint_from_hex<u16>(line.substring_view(0, 4)).value_or(0);
165 current_vendor->name = line.substring_view(6, line.length() - 6);
166 } else if (line[0] == '\t' && line[1] != '\t') {
167 commit_device();
168 current_device = make<Device>();
169 current_device->id = AK::StringUtils::convert_to_uint_from_hex<u16>((line.substring_view(1, 4))).value_or(0);
170 current_device->name = line.substring_view(7, line.length() - 7);
171 } else if (line[0] == '\t' && line[1] == '\t') {
172 auto interface = make<Interface>();
173 interface->interface = AK::StringUtils::convert_to_uint_from_hex<u16>((line.substring_view(2, 4))).value_or(0);
174 interface->name = line.substring_view(7, line.length() - 7);
175 current_device->interfaces.set(interface->interface, move(interface));
176 }
177 break;
178 case ParseMode::ClassMode:
179 if (line[0] != '\t') {
180 commit_class();
181 current_class = make<Class>();
182 current_class->id = AK::StringUtils::convert_to_uint_from_hex<u16>((line.substring_view(2, 2))).value_or(0);
183 current_class->name = line.substring_view(6, line.length() - 6);
184 } else if (line[0] == '\t' && line[1] != '\t') {
185 commit_subclass();
186 current_subclass = make<Subclass>();
187 current_subclass->id = AK::StringUtils::convert_to_uint_from_hex<u16>((line.substring_view(1, 2))).value_or(0);
188 current_subclass->name = line.substring_view(5, line.length() - 5);
189 } else if (line[0] == '\t' && line[1] == '\t') {
190 auto protocol = make<Protocol>();
191 protocol->id = AK::StringUtils::convert_to_uint_from_hex<u16>((line.substring_view(2, 2))).value_or(0);
192 protocol->name = line.substring_view(6, line.length() - 6);
193 current_subclass->protocols.set(protocol->id, move(protocol));
194 }
195 break;
196 default:
197 break;
198 }
199 }
200
201 commit_all();
202
203 m_ready = true;
204
205 return 0;
206}
207
208}