/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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 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 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(&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_; }