mystuff/devel/adb/files/fastboot_usb_libusb.cpp
2026-03-21 23:43:54 +01:00

171 lines
6.0 KiB
C++

/*
* USB transport for fastboot using libusb-1.0.
* Used on OpenBSD (and other platforms without Linux usbfs).
*
* 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
*/
#include <libusb-1.0/libusb.h>
#include <stdio.h>
#include <string.h>
#include <memory>
#include <android-base/macros.h>
#include "usb.h"
static libusb_context* g_ctx = nullptr;
class LibUsbTransport : public UsbTransport {
public:
LibUsbTransport(libusb_device_handle* h, uint8_t ep_in, uint8_t ep_out,
int ifc_num, uint32_t ms_timeout)
: handle_(h), ep_in_(ep_in), ep_out_(ep_out),
ifc_num_(ifc_num), ms_timeout_(ms_timeout) {}
~LibUsbTransport() override { Close(); }
ssize_t Read(void* data, size_t len) override {
int transferred = 0;
int r = libusb_bulk_transfer(handle_, ep_in_,
reinterpret_cast<unsigned char*>(data),
static_cast<int>(len), &transferred,
ms_timeout_);
if (r == 0 || r == LIBUSB_ERROR_TIMEOUT)
return transferred;
return -1;
}
ssize_t Write(const void* data, size_t len) override {
int transferred = 0;
int r = libusb_bulk_transfer(handle_, ep_out_,
const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(data)),
static_cast<int>(len), &transferred,
ms_timeout_);
if (r == 0)
return transferred;
return -1;
}
int Close() override {
if (handle_) {
libusb_release_interface(handle_, ifc_num_);
libusb_close(handle_);
handle_ = nullptr;
}
return 0;
}
int Reset() override {
if (handle_)
return libusb_reset_device(handle_) ? -1 : 0;
return -1;
}
private:
libusb_device_handle* handle_;
uint8_t ep_in_, ep_out_;
int ifc_num_;
uint32_t ms_timeout_;
DISALLOW_COPY_AND_ASSIGN(LibUsbTransport);
};
std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback,
uint32_t timeout_ms) {
if (!g_ctx) {
if (libusb_init(&g_ctx) != 0)
return nullptr;
}
libusb_device** devs;
ssize_t cnt = libusb_get_device_list(g_ctx, &devs);
if (cnt < 0)
return nullptr;
std::unique_ptr<UsbTransport> result;
for (ssize_t i = 0; i < cnt && !result; i++) {
libusb_device* dev = devs[i];
struct libusb_device_descriptor desc;
if (libusb_get_device_descriptor(dev, &desc) != 0)
continue;
struct libusb_config_descriptor* config;
if (libusb_get_active_config_descriptor(dev, &config) != 0)
continue;
for (int j = 0; j < (int)config->bNumInterfaces && !result; j++) {
const struct libusb_interface* ifc = &config->interface[j];
for (int k = 0; k < ifc->num_altsetting && !result; k++) {
const struct libusb_interface_descriptor* id = &ifc->altsetting[k];
usb_ifc_info info = {};
info.dev_vendor = desc.idVendor;
info.dev_product = desc.idProduct;
info.dev_class = desc.bDeviceClass;
info.dev_subclass = desc.bDeviceSubClass;
info.dev_protocol = desc.bDeviceProtocol;
info.ifc_class = id->bInterfaceClass;
info.ifc_subclass = id->bInterfaceSubClass;
info.ifc_protocol = id->bInterfaceProtocol;
uint8_t ep_in = 0, ep_out = 0;
for (int e = 0; e < (int)id->bNumEndpoints; e++) {
const struct libusb_endpoint_descriptor* ep = &id->endpoint[e];
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) !=
LIBUSB_TRANSFER_TYPE_BULK)
continue;
if (ep->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
ep_in = ep->bEndpointAddress;
info.has_bulk_in = 1;
} else {
ep_out = ep->bEndpointAddress;
info.has_bulk_out = 1;
}
}
libusb_device_handle* h;
if (libusb_open(dev, &h) != 0)
continue;
info.writable = 1;
if (desc.iSerialNumber) {
libusb_get_string_descriptor_ascii(
h, desc.iSerialNumber,
reinterpret_cast<unsigned char*>(info.serial_number),
sizeof(info.serial_number));
}
if (id->iInterface) {
libusb_get_string_descriptor_ascii(
h, id->iInterface,
reinterpret_cast<unsigned char*>(info.interface),
sizeof(info.interface));
}
snprintf(info.device_path, sizeof(info.device_path),
"usb:%d:%d", libusb_get_bus_number(dev),
libusb_get_device_address(dev));
if (callback(&info) == 0 && ep_in && ep_out) {
if (libusb_claim_interface(h, id->bInterfaceNumber) == 0) {
result = std::make_unique<LibUsbTransport>(
h, ep_in, ep_out, id->bInterfaceNumber,
timeout_ms);
} else {
libusb_close(h);
}
} else {
libusb_close(h);
}
}
}
libusb_free_config_descriptor(config);
}
libusb_free_device_list(devs, 1);
return result;
}