171 lines
6.0 KiB
C++
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;
|
|
}
|