blob: 2bd71ab52c87f16437203959c22d0f1ff9492fd3 [file] [log] [blame] [edit]
/*
* Copyright (C) 2025 The Android Open Source Project
*
* 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_libusb_device.h"
#include <stdint.h>
#include <stdlib.h>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <format>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <vector>
#include <libusb/libusb.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
#include "adb.h"
#include "adb_trace.h"
#include "adb_utils.h"
#include "fdevent/fdevent.h"
#include "transport.h"
#include "usb.h"
using namespace std::chrono_literals;
using android::base::ScopedLockAssertion;
using android::base::StringPrintf;
static bool endpoint_is_output(uint8_t endpoint) {
return (endpoint & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_OUT;
}
LibUsbDevice::LibUsbDevice(libusb_device* device)
: device_(device), device_address_(GetDeviceAddress()) {
libusb_ref_device(device);
Init();
}
LibUsbDevice::~LibUsbDevice() {
ReleaseInterface();
CloseDeviceHandle();
CloseDevice();
}
bool LibUsbDevice::IsInitialized() const {
return initialized_;
}
void LibUsbDevice::Init() {
initialized_ = OpenDeviceHandle();
session_ = GenerateSessionId(device_);
}
void LibUsbDevice::ReleaseInterface() {
if (interface_claimed_) {
libusb_release_interface(device_handle_, interface_num_);
interface_claimed_ = false;
}
}
void LibUsbDevice::CloseDeviceHandle() {
if (device_handle_ != nullptr) {
libusb_close(device_handle_);
device_handle_ = nullptr;
}
}
void LibUsbDevice::CloseDevice() {
if (device_ != nullptr) {
libusb_unref_device(device_);
device_ = nullptr;
}
}
bool LibUsbDevice::Write(apacket* packet) {
VLOG(USB) << "Write " << command_to_string(packet->msg.command)
<< " payload=" << packet->msg.data_length;
int transferred;
int data_size = sizeof(packet->msg);
auto r = libusb_bulk_transfer(device_handle_, write_endpoint_, (unsigned char*)&packet->msg,
data_size, &transferred, 0);
if ((r != 0) || (transferred != data_size)) {
VLOG(USB) << "LibUsbDevice::Write failed at header " << libusb_error_name(r);
return false;
}
data_size = packet->payload.size();
if (data_size == 0) {
return true;
}
r = libusb_bulk_transfer(device_handle_, write_endpoint_,
(unsigned char*)packet->payload.data(), data_size, &transferred, 0);
if ((r != 0) || (transferred != data_size)) {
VLOG(USB) << "LibUsbDevice::Write failed at payload " << libusb_error_name(r);
return false;
}
if ((data_size & zlp_mask_) == 0) {
VLOG(USB) << "Sending zlp (payload_size=" << data_size
<< ", endpoint_size=" << out_endpoint_size_
<< ", modulo=" << data_size % out_endpoint_size_ << ")";
libusb_bulk_transfer(device_handle_, write_endpoint_,
(unsigned char*)packet->payload.data(), 0, &transferred, 0);
}
return true;
}
bool LibUsbDevice::Read(apacket* packet) {
VLOG(USB) << "LibUsbDevice Read()";
int transferred;
int data_size = sizeof(packet->msg);
auto r = libusb_bulk_transfer(device_handle_, read_endpoint_, (unsigned char*)&packet->msg,
data_size, &transferred, 0);
if ((r != 0) || (transferred != data_size)) {
VLOG(USB) << "LibUsbDevice::READ failed at header " << libusb_error_name(r);
return false;
}
VLOG(USB) << "Read " << command_to_string(packet->msg.command)
<< " header, now expecting=" << packet->msg.data_length;
if (packet->msg.data_length == 0) {
packet->payload.resize(0);
return true;
}
packet->payload.resize(packet->msg.data_length);
data_size = packet->msg.data_length;
r = libusb_bulk_transfer(device_handle_, read_endpoint_, (unsigned char*)packet->payload.data(),
data_size, &transferred, 0);
if ((r != 0) || (transferred != data_size)) {
VLOG(USB) << "LibUsbDevice::READ failed at payload << " << libusb_error_name(r);
return false;
}
VLOG(USB) << "Read " << command_to_string(packet->msg.command) << " got =" << transferred;
return true;
}
void LibUsbDevice::Reset() {
if (device_handle_ == nullptr) {
return;
}
int rc = libusb_reset_device(device_handle_);
if (rc != 0) {
LOG(ERROR) << "libusb_reset_device failed: " << libusb_error_name(rc);
}
}
std::string LibUsbDevice::GetDeviceAddress() {
uint8_t ports[7];
int port_count = libusb_get_port_numbers(device_, ports, 7);
if (port_count < 0) return "";
std::string address =
android::base::StringPrintf("%d-%d", libusb_get_bus_number(device_), ports[0]);
for (int port = 1; port < port_count; ++port) {
address += android::base::StringPrintf(".%d", ports[port]);
}
return address;
}
std::optional<libusb_device_descriptor> LibUsbDevice::GetDeviceDescriptor() {
libusb_device_descriptor device_desc;
int rc = libusb_get_device_descriptor(device_, &device_desc);
if (rc != 0) {
LOG(WARNING) << "failed to get device descriptor for device :" << libusb_error_name(rc);
return {};
}
return device_desc;
}
std::string LibUsbDevice::GetSerial() {
return serial_;
}
bool LibUsbDevice::FindAdbInterface() {
std::optional<libusb_device_descriptor> device_desc = GetDeviceDescriptor();
if (!device_desc.has_value()) {
return false;
}
if (device_desc->bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
// Assume that all Android devices have the device class set to per interface.
// TODO: Is this assumption valid?
VLOG(USB) << "skipping device with incorrect class at " << device_address_;
return false;
}
libusb_config_descriptor* config;
int rc = libusb_get_active_config_descriptor(device_, &config);
if (rc != 0) {
LOG(WARNING) << "failed to get active config descriptor for device at " << device_address_
<< ": " << libusb_error_name(rc);
return false;
}
// Use size_t for interface_num so <iostream>s don't mangle it.
size_t interface_num;
uint8_t bulk_in = 0, bulk_out = 0;
size_t packet_size = 0;
bool found_adb = false;
for (interface_num = 0; interface_num < config->bNumInterfaces; ++interface_num) {
const libusb_interface& interface = config->interface[interface_num];
if (interface.num_altsetting == 0) {
continue;
}
const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
if (!is_adb_interface(interface_desc.bInterfaceClass, interface_desc.bInterfaceSubClass,
interface_desc.bInterfaceProtocol)) {
VLOG(USB) << "skipping non-adb interface at " << device_address_ << " (interface "
<< interface_num << ")";
continue;
}
VLOG(USB) << "found potential adb interface at " << device_address_ << " (interface "
<< interface_num << ")";
bool found_in = false;
bool found_out = false;
for (size_t endpoint_num = 0; endpoint_num < interface_desc.bNumEndpoints; ++endpoint_num) {
const auto& endpoint_desc = interface_desc.endpoint[endpoint_num];
const uint8_t endpoint_addr = endpoint_desc.bEndpointAddress;
const uint8_t endpoint_attr = endpoint_desc.bmAttributes;
VLOG(USB) << "Scanning endpoint=" << endpoint_num
<< ", addr=" << std::format("{:#02x}", endpoint_addr)
<< ", attr=" << std::format("{:#02x}", endpoint_attr);
const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
continue;
}
if (endpoint_is_output(endpoint_addr) && !found_out) {
found_out = true;
out_endpoint_size_ = endpoint_desc.wMaxPacketSize;
VLOG(USB) << "Device " << GetSerial()
<< " uses wMaxPacketSize=" << out_endpoint_size_;
zlp_mask_ = out_endpoint_size_ - 1;
bulk_out = endpoint_addr;
} else if (!endpoint_is_output(endpoint_addr) && !found_in) {
found_in = true;
bulk_in = endpoint_addr;
}
size_t endpoint_packet_size = endpoint_desc.wMaxPacketSize;
CHECK(endpoint_packet_size != 0);
if (packet_size == 0) {
packet_size = endpoint_packet_size;
} else {
CHECK(packet_size == endpoint_packet_size);
}
}
if (found_in && found_out) {
found_adb = true;
break;
} else {
VLOG(USB) << "rejecting potential adb interface at " << device_address_ << "(interface "
<< interface_num << "): missing bulk endpoints "
<< "(found_in = " << found_in << ", found_out = " << found_out << ")";
}
}
libusb_free_config_descriptor(config);
if (!found_adb) {
VLOG(USB) << "ADB interface missing endpoints: bulk_out=" << bulk_out
<< " and bulk_in=" << bulk_in;
return false;
}
interface_num_ = interface_num;
write_endpoint_ = bulk_out;
read_endpoint_ = bulk_in;
VLOG(USB) << "Found ADB interface=" << interface_num_
<< " bulk_in=" << std::format("{:#02x}", bulk_in)
<< " bulk_out=" << std::format("{:#02x}", bulk_out);
return true;
}
std::string LibUsbDevice::GetAddress() const {
return std::string("usb:") + device_address_;
}
bool LibUsbDevice::RetrieveSerial() {
auto device_desc = GetDeviceDescriptor();
serial_.resize(512);
int rc = libusb_get_string_descriptor_ascii(device_handle_, device_desc->iSerialNumber,
reinterpret_cast<unsigned char*>(&serial_[0]),
serial_.length());
if (rc == 0) {
LOG(WARNING) << "received empty serial from device at " << device_address_;
return false;
} else if (rc < 0) {
VLOG(USB) << "failed to get serial from device " << device_address_ << " :"
<< libusb_error_name(rc);
return false;
}
serial_.resize(rc);
return true;
}
// libusb gives us an int which is a value from 'enum libusb_speed'
static uint64_t ToConnectionSpeed(int speed) {
switch (speed) {
case LIBUSB_SPEED_LOW:
return 1;
case LIBUSB_SPEED_FULL:
return 12;
case LIBUSB_SPEED_HIGH:
return 480;
case LIBUSB_SPEED_SUPER:
return 5000;
case LIBUSB_SPEED_SUPER_PLUS:
return 10000;
case LIBUSB_SPEED_SUPER_PLUS_X2:
return 20000;
case LIBUSB_SPEED_UNKNOWN:
default:
return 0;
}
}
// libusb gives us a bitfield made of 'enum libusb_supported_speed' values
static uint64_t ExtractMaxSuperSpeed(uint16_t wSpeedSupported) {
if (wSpeedSupported == 0) {
return 0;
}
int msb = 0;
while (wSpeedSupported >>= 1) {
msb++;
}
switch (1 << msb) {
case LIBUSB_LOW_SPEED_OPERATION:
return 1;
case LIBUSB_FULL_SPEED_OPERATION:
return 12;
case LIBUSB_HIGH_SPEED_OPERATION:
return 480;
case LIBUSB_SUPER_SPEED_OPERATION:
return 5000;
default:
return 0;
}
}
static uint64_t ExtractMaxSuperSpeedPlus(libusb_ssplus_usb_device_capability_descriptor* cap) {
// The exponents is one of {bytes, kB, MB, or GB}. We express speed in MB so we use a 0
// multiplier for value which would result in 0MB anyway.
static uint64_t exponent[] = {0, 0, 1, 1000};
uint64_t max_speed = 0;
for (uint8_t i = 0; i < cap->numSublinkSpeedAttributes; i++) {
libusb_ssplus_sublink_attribute* attr = &cap->sublinkSpeedAttributes[i];
uint64_t speed = attr->mantissa * exponent[attr->exponent];
max_speed = std::max(max_speed, speed);
}
return max_speed;
}
void LibUsbDevice::RetrieveSpeeds() {
negotiated_speed_ = ToConnectionSpeed(libusb_get_device_speed(device_));
// To discover the maximum speed supported by an USB device, we walk its capability
// descriptors.
struct libusb_bos_descriptor* bos = nullptr;
if (libusb_get_bos_descriptor(device_handle_, &bos)) {
return;
}
for (int i = 0; i < bos->bNumDeviceCaps; i++) {
switch (bos->dev_capability[i]->bDevCapabilityType) {
case LIBUSB_BT_SS_USB_DEVICE_CAPABILITY: {
libusb_ss_usb_device_capability_descriptor* cap = nullptr;
if (!libusb_get_ss_usb_device_capability_descriptor(nullptr, bos->dev_capability[i],
&cap)) {
max_speed_ = std::max(max_speed_, ExtractMaxSuperSpeed(cap->wSpeedSupported));
libusb_free_ss_usb_device_capability_descriptor(cap);
}
} break;
case LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY: {
libusb_ssplus_usb_device_capability_descriptor* cap = nullptr;
if (!libusb_get_ssplus_usb_device_capability_descriptor(
nullptr, bos->dev_capability[i], &cap)) {
max_speed_ = std::max(max_speed_, ExtractMaxSuperSpeedPlus(cap));
libusb_free_ssplus_usb_device_capability_descriptor(cap);
}
} break;
default:
break;
}
}
libusb_free_bos_descriptor(bos);
}
bool LibUsbDevice::OpenDeviceHandle() {
if (device_handle_) {
VLOG(USB) << "device already open";
return true;
}
int rc = libusb_open(device_, &device_handle_);
if (rc != 0) {
VLOG(USB) << "Unable to open device: " << GetSerial() << " :" << libusb_strerror(rc);
return false;
}
if (!RetrieveSerial()) {
return false;
}
if (!FindAdbInterface()) {
return false;
}
RetrieveSpeeds();
return true;
}
bool LibUsbDevice::ClaimInterface() {
VLOG(USB) << "ClaimInterface for " << GetSerial();
if (interface_claimed_) {
VLOG(USB) << "Interface already open";
return true;
}
if (!FindAdbInterface()) {
VLOG(USB) << "Unable to open interface for " << GetSerial();
return false;
}
int rc = libusb_claim_interface(device_handle_, interface_num_);
if (rc != 0) {
VLOG(USB) << "failed to claim adb interface for device " << serial_.c_str() << ":"
<< libusb_error_name(rc);
return false;
}
for (uint8_t endpoint : {read_endpoint_, write_endpoint_}) {
rc = libusb_clear_halt(device_handle_, endpoint);
if (rc != 0) {
VLOG(USB) << "failed to clear halt on device " << serial_ << " endpoint" << endpoint
<< ": " << libusb_error_name(rc);
libusb_release_interface(device_handle_, interface_num_);
return false;
}
}
VLOG(USB) << "Claimed interface for " << GetSerial() << ", "
<< StringPrintf("bulk_in = %#x, bulk_out = %#x", read_endpoint_, write_endpoint_);
interface_claimed_ = true;
return true;
}
bool LibUsbDevice::Open() {
if (!OpenDeviceHandle()) {
VLOG(USB) << "Unable to attach, cannot open device";
return false;
}
if (!ClaimInterface()) {
VLOG(USB) << "failed to claim interface " << GetSerial();
return false;
}
VLOG(USB) << "Attached device " << GetSerial();
return true;
}
bool LibUsbDevice::Close() {
ReleaseInterface();
CloseDeviceHandle();
return true;
}
uint64_t LibUsbDevice::MaxSpeedMbps() {
return max_speed_;
}
uint64_t LibUsbDevice::NegotiatedSpeedMbps() {
return negotiated_speed_;
}
USBSessionID LibUsbDevice::GenerateSessionId(libusb_device* dev) {
libusb_device_descriptor desc{};
auto result = libusb_get_device_descriptor(dev, &desc);
if (result != LIBUSB_SUCCESS) {
LOG(WARNING) << "Unable to retrieve device descriptor: " << libusb_error_name(result);
return USBSessionID{};
}
USBSessionID session{};
session.fields.vendor = desc.idVendor;
session.fields.product = desc.idProduct;
session.fields.port = libusb_get_port_number(dev);
session.fields.address = libusb_get_device_address(dev);
return session;
}
USBSessionID LibUsbDevice::GetSessionId() const {
return session_;
}