Database.cpp 8.3 KB

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