| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "usb_util.hpp" |
| |
| #include <algorithm> |
| #include <charconv> |
| #include <span> |
| #include <stdexcept> |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| template <typename Int> |
| Int extract_separated_int(std::string_view& str, char separator) |
| { |
| if (str.empty()) |
| { |
| throw std::invalid_argument("Int str is empty"); |
| } |
| |
| Int ret; |
| auto term = str.substr(0, str.find(separator)); |
| const auto *end = term.data() + term.size(); |
| auto res = std::from_chars(term.data(), end, ret); |
| if (static_cast<bool>(res.ec) || res.ptr != end) |
| { |
| throw std::invalid_argument("Failed to parse int"); |
| } |
| str.remove_prefix(term.size() + (term.size() == str.size() ? 0 : 1)); |
| return ret; |
| } |
| |
| libusb::Device find_dev(libusb::Context& ctx, std::string_view usb_id) |
| { |
| auto bus_id = extract_separated_int<uint8_t>(usb_id, '-'); |
| std::vector<uint8_t> port_ids; |
| do |
| { |
| port_ids.push_back(extract_separated_int<uint8_t>(usb_id, '.')); |
| } while (!usb_id.empty()); |
| |
| for (auto& dev : ctx.get_device_list()) |
| { |
| if (bus_id == dev.get_bus_number() && |
| dev.get_port_numbers() == port_ids) |
| { |
| return std::move(dev); |
| } |
| } |
| |
| throw std::runtime_error("Failed to find usb device"); |
| } |
| |
| const libusb_interface_descriptor& |
| find_intf(libusb::Device& dev, uint8_t usbClass, |
| std::span<const uint8_t> usbSubclasses, uint8_t usbProtocol) |
| { |
| const auto& config = dev.get_active_config_descriptor(); |
| for (uint8_t i = 0; i < config.bNumInterfaces; ++i) |
| { |
| const auto& interface = config.interface[i]; |
| for (int j = 0; j < interface.num_altsetting; ++j) |
| { |
| const auto& setting = interface.altsetting[j]; |
| if (setting.bInterfaceClass == usbClass && |
| (std::count(usbSubclasses.begin(), usbSubclasses.end(), |
| setting.bInterfaceSubClass) > 0) && |
| setting.bInterfaceProtocol == usbProtocol) |
| { |
| return setting; |
| } |
| } |
| } |
| |
| throw std::runtime_error("Failed to find hoth mailbox interface"); |
| } |
| |
| const libusb_endpoint_descriptor& |
| find_ep(const libusb_interface_descriptor& interface, uint8_t dir) |
| { |
| for (uint8_t i = 0; i < interface.bNumEndpoints; ++i) |
| { |
| const auto& endpoint = interface.endpoint[i]; |
| if ((endpoint.bmAttributes & libusb::kTransferTypeBits) == |
| LIBUSB_TRANSFER_TYPE_BULK && |
| (endpoint.bEndpointAddress & libusb::kAddressDirectionBits) == dir) |
| { |
| return endpoint; |
| } |
| } |
| |
| throw std::runtime_error("Failed to find hoth transfer endpoint"); |
| } |
| |
| } // namespace hoth |
| |
| } // namespace google |