| // 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 "libusb.hpp" |
| |
| #include <stdplus/util/cexec.hpp> |
| |
| #include <system_error> |
| #include <utility> |
| |
| namespace google |
| { |
| namespace hoth |
| { |
| namespace libusb |
| { |
| |
| int toErrno(int error) |
| { |
| switch (error) |
| { |
| case LIBUSB_SUCCESS: |
| return 0; |
| case LIBUSB_ERROR_IO: |
| return EIO; |
| case LIBUSB_ERROR_INVALID_PARAM: |
| return EINVAL; |
| case LIBUSB_ERROR_ACCESS: |
| return EACCES; |
| case LIBUSB_ERROR_NO_DEVICE: |
| return ENODEV; |
| case LIBUSB_ERROR_NOT_FOUND: |
| return ENOENT; |
| case LIBUSB_ERROR_BUSY: |
| return EBUSY; |
| case LIBUSB_ERROR_TIMEOUT: |
| return ETIMEDOUT; |
| case LIBUSB_ERROR_OVERFLOW: |
| return EOVERFLOW; |
| case LIBUSB_ERROR_PIPE: |
| return EPIPE; |
| case LIBUSB_ERROR_INTERRUPTED: |
| return EINTR; |
| case LIBUSB_ERROR_NO_MEM: |
| return ENOMEM; |
| case LIBUSB_ERROR_NOT_SUPPORTED: |
| return ENOTSUP; |
| default: |
| return ENOSYS; |
| } |
| } |
| |
| inline auto makeError(int error, const char* msg) |
| { |
| return std::system_error(toErrno(-error), std::generic_category(), msg); |
| } |
| |
| template <typename... Args> |
| inline auto callCheck(const char* msg, Args&&... args) |
| { |
| return stdplus::util::callCheckRet<makeError, Args...>( |
| msg, std::forward<Args>(args)...); |
| } |
| |
| InterfaceClaim::InterfaceClaim(LibusbIntf* libusb, libusb_device_handle* handle, |
| int interface) : |
| libusb(libusb), |
| handle(init(handle, interface), interface, libusb) |
| {} |
| |
| void InterfaceClaim::drop(libusb_device_handle*&& handle, int& interface, |
| LibusbIntf*& libusb) |
| { |
| libusb->release_interface(handle, interface); |
| } |
| |
| libusb_device_handle* InterfaceClaim::init(libusb_device_handle* handle, |
| int interface) |
| { |
| callCheck("claim_interface", &LibusbIntf::claim_interface, libusb, handle, |
| interface); |
| return handle; |
| } |
| |
| InterfaceClaim DeviceHandle::claim_interface(int interface) |
| { |
| return InterfaceClaim(libusb, *handle, interface); |
| } |
| |
| unsigned DeviceHandle::bulk_transfer(const libusb_endpoint_descriptor& ep, |
| uint8_t* data, size_t size, |
| std::chrono::milliseconds timeout) |
| { |
| int ret; |
| callCheck("bulk_transfer", &LibusbIntf::bulk_transfer, libusb, *handle, |
| ep.bEndpointAddress, data, size, &ret, timeout.count()); |
| return ret; |
| } |
| |
| void DeviceHandle::drop(libusb_device_handle*&& handle, LibusbIntf*& libusb) |
| { |
| libusb->close(handle); |
| } |
| |
| libusb_config_descriptor& Device::get_active_config_descriptor() |
| { |
| libusb_config_descriptor* ret; |
| callCheck("get_active_config_descriptor", |
| &LibusbIntf::get_active_config_descriptor, libusb, *dev, &ret); |
| return *ret; |
| } |
| |
| uint8_t Device::get_bus_number() |
| { |
| return libusb->get_bus_number(*dev); |
| } |
| |
| std::vector<uint8_t> Device::get_port_numbers() |
| { |
| // 7 is the maximum depth per the usb spec |
| std::vector<uint8_t> ret(7); |
| int r = callCheck("get_port_numbers", &LibusbIntf::get_port_numbers, libusb, |
| *dev, ret.data(), ret.size()); |
| ret.resize(r); |
| return ret; |
| } |
| |
| DeviceHandle Device::open() |
| { |
| libusb_device_handle* handle; |
| callCheck("open", &LibusbIntf::open, libusb, *dev, &handle); |
| return DeviceHandle(libusb, handle); |
| } |
| |
| void Device::drop(libusb_device*&& dev, LibusbIntf*& libusb) |
| { |
| libusb->unref_device(dev); |
| } |
| |
| std::vector<Device> Context::get_device_list() |
| { |
| libusb_device** devs; |
| callCheck("get_device_list", &LibusbIntf::get_device_list, libusb, *ctx, |
| &devs); |
| std::vector<Device> ret; |
| for (size_t i = 0; devs[i] != nullptr; ++i) |
| { |
| ret.push_back(Device(libusb, devs[i])); |
| } |
| libusb->free_device_list(devs, 0); |
| return ret; |
| } |
| |
| void Context::drop(libusb_context*&& ctx, LibusbIntf*& libusb) |
| { |
| libusb->exit(ctx); |
| } |
| |
| libusb_context* Context::init() |
| { |
| libusb_context* ctx = nullptr; |
| callCheck("init", &LibusbIntf::init, libusb, &ctx); |
| return ctx; |
| } |
| |
| } // namespace libusb |
| } // namespace hoth |
| } // namespace google |