Merge "adb: use the actual wMaxPacketSize for usb endpoints."

This commit is contained in:
Josh Gao 2017-05-04 00:12:22 +00:00 committed by Gerrit Code Review
commit c7988b1f68
8 changed files with 122 additions and 62 deletions

View file

@ -48,3 +48,9 @@ void usb_kick(usb_handle* h) {
should_use_libusb() ? libusb::usb_kick(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_kick(reinterpret_cast<native::usb_handle*>(h));
}
size_t usb_get_max_packet_size(usb_handle* h) {
return should_use_libusb()
? libusb::usb_get_max_packet_size(reinterpret_cast<libusb::usb_handle*>(h))
: native::usb_get_max_packet_size(reinterpret_cast<native::usb_handle*>(h));
}

View file

@ -91,7 +91,7 @@ namespace libusb {
struct usb_handle : public ::usb_handle {
usb_handle(const std::string& device_address, const std::string& serial,
unique_device_handle&& device_handle, uint8_t interface, uint8_t bulk_in,
uint8_t bulk_out, size_t zero_mask)
uint8_t bulk_out, size_t zero_mask, size_t max_packet_size)
: device_address(device_address),
serial(serial),
closing(false),
@ -100,7 +100,8 @@ struct usb_handle : public ::usb_handle {
write("write", zero_mask, true),
interface(interface),
bulk_in(bulk_in),
bulk_out(bulk_out) {}
bulk_out(bulk_out),
max_packet_size(max_packet_size) {}
~usb_handle() {
Close();
@ -143,6 +144,8 @@ struct usb_handle : public ::usb_handle {
uint8_t interface;
uint8_t bulk_in;
uint8_t bulk_out;
size_t max_packet_size;
};
static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
@ -206,6 +209,7 @@ static void poll_for_devices() {
size_t interface_num;
uint16_t zero_mask;
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) {
@ -252,6 +256,14 @@ static void poll_for_devices() {
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) {
@ -280,7 +292,7 @@ static void poll_for_devices() {
}
libusb_device_handle* handle_raw;
rc = libusb_open(list[i], &handle_raw);
rc = libusb_open(device, &handle_raw);
if (rc != 0) {
LOG(WARNING) << "failed to open usb device at " << device_address << ": "
<< libusb_error_name(rc);
@ -324,9 +336,9 @@ static void poll_for_devices() {
}
}
auto result =
std::make_unique<usb_handle>(device_address, device_serial, std::move(handle),
interface_num, bulk_in, bulk_out, zero_mask);
auto result = std::make_unique<usb_handle>(device_address, device_serial,
std::move(handle), interface_num, bulk_in,
bulk_out, zero_mask, packet_size);
usb_handle* usb_handle_raw = result.get();
{
@ -507,4 +519,10 @@ int usb_close(usb_handle* h) {
void usb_kick(usb_handle* h) {
h->Close();
}
size_t usb_get_max_packet_size(usb_handle* h) {
CHECK(h->max_packet_size != 0);
return h->max_packet_size;
}
} // namespace libusb

View file

@ -65,6 +65,7 @@ struct usb_handle : public ::usb_handle {
unsigned char ep_in;
unsigned char ep_out;
size_t max_packet_size;
unsigned zero_mask;
unsigned writeable = 1;
@ -120,9 +121,9 @@ static inline bool contains_non_digit(const char* name) {
}
static void find_usb_device(const std::string& base,
void (*register_device_callback)
(const char*, const char*, unsigned char, unsigned char, int, int, unsigned))
{
void (*register_device_callback)(const char*, const char*,
unsigned char, unsigned char, int, int,
unsigned, size_t)) {
std::unique_ptr<DIR, int(*)(DIR*)> bus_dir(opendir(base.c_str()), closedir);
if (!bus_dir) return;
@ -144,6 +145,7 @@ static void find_usb_device(const std::string& base,
struct usb_interface_descriptor* interface;
struct usb_endpoint_descriptor *ep1, *ep2;
unsigned zero_mask = 0;
size_t max_packet_size = 0;
unsigned vid, pid;
if (contains_non_digit(de->d_name)) continue;
@ -251,7 +253,8 @@ static void find_usb_device(const std::string& base,
continue;
}
/* aproto 01 needs 0 termination */
if(interface->bInterfaceProtocol == 0x01) {
if (interface->bInterfaceProtocol == 0x01) {
max_packet_size = ep1->wMaxPacketSize;
zero_mask = ep1->wMaxPacketSize - 1;
}
@ -281,9 +284,9 @@ static void find_usb_device(const std::string& base,
}
}
register_device_callback(dev_name.c_str(), devpath,
local_ep_in, local_ep_out,
interface->bInterfaceNumber, device->iSerialNumber, zero_mask);
register_device_callback(dev_name.c_str(), devpath, local_ep_in,
local_ep_out, interface->bInterfaceNumber,
device->iSerialNumber, zero_mask, max_packet_size);
break;
}
} else {
@ -497,10 +500,13 @@ int usb_close(usb_handle* h) {
return 0;
}
static void register_device(const char* dev_name, const char* dev_path,
unsigned char ep_in, unsigned char ep_out,
int interface, int serial_index,
unsigned zero_mask) {
size_t usb_get_max_packet_size(usb_handle* h) {
return h->max_packet_size;
}
static void register_device(const char* dev_name, const char* dev_path, unsigned char ep_in,
unsigned char ep_out, int interface, int serial_index,
unsigned zero_mask, size_t max_packet_size) {
// Since Linux will not reassign the device ID (and dev_name) as long as the
// device is open, we can add to the list here once we open it and remove
// from the list when we're finally closed and everything will work out
@ -523,6 +529,7 @@ static void register_device(const char* dev_name, const char* dev_path,
usb->ep_in = ep_in;
usb->ep_out = ep_out;
usb->zero_mask = zero_mask;
usb->max_packet_size = max_packet_size;
// Initialize mark so we don't get garbage collected after the device scan.
usb->mark = true;

View file

@ -51,15 +51,21 @@ struct usb_handle
UInt8 bulkOut;
IOUSBInterfaceInterface190** interface;
unsigned int zero_mask;
size_t max_packet_size;
// For garbage collecting disconnected devices.
bool mark;
std::string devpath;
std::atomic<bool> dead;
usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
zero_mask(0), mark(false), dead(false) {
}
usb_handle()
: bulkIn(0),
bulkOut(0),
interface(nullptr),
zero_mask(0),
max_packet_size(0),
mark(false),
dead(false) {}
};
static std::atomic<bool> usb_inited_flag;
@ -390,6 +396,7 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
}
handle->zero_mask = maxPacketSize - 1;
handle->max_packet_size = maxPacketSize;
}
handle->interface = interface;
@ -558,4 +565,9 @@ void usb_kick(usb_handle *handle) {
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
usb_kick_locked(handle);
}
size_t usb_get_max_packet_size(usb_handle* handle) {
return handle->max_packet_size;
}
} // namespace native

View file

@ -65,6 +65,9 @@ struct usb_handle {
/// Interface name
wchar_t* interface_name;
/// Maximum packet size.
unsigned max_packet_size;
/// Mask for determining when to use zero length packets
unsigned zero_mask;
};
@ -522,6 +525,10 @@ int usb_close(usb_handle* handle) {
return 0;
}
size_t usb_get_max_packet_size(usb_handle* handle) {
return handle->max_packet_size;
}
int recognized_device(usb_handle* handle) {
if (NULL == handle)
return 0;
@ -557,6 +564,7 @@ int recognized_device(usb_handle* handle) {
AdbEndpointInformation endpoint_info;
// assuming zero is a valid bulk endpoint ID
if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
handle->max_packet_size = endpoint_info.max_packet_size;
handle->zero_mask = endpoint_info.max_packet_size - 1;
D("device zero_mask: 0x%x", handle->zero_mask);
} else {

View file

@ -1259,6 +1259,26 @@ class DeviceOfflineTest(DeviceTest):
self.assertEqual(self._get_device_state(serialno), 'device')
def test_packet_size_regression(self):
"""Test for http://b/37783561
Receiving packets of a length divisible by 512 but not 1024 resulted in
the adb client waiting indefinitely for more input.
"""
# The values that trigger things are 507 (512 - 5 bytes from shell protocol) + 1024*n
# Probe some surrounding values as well, for the hell of it.
for length in [506, 507, 508, 1018, 1019, 1020, 1530, 1531, 1532]:
cmd = ['dd', 'if=/dev/zero', 'bs={}'.format(length), 'count=1', '2>/dev/null;'
'echo', 'foo']
rc, stdout, _ = self.device.shell_nocheck(cmd)
self.assertEqual(0, rc)
# Output should be '\0' * length, followed by "foo\n"
self.assertEqual(length, len(stdout) - 4)
self.assertEqual(stdout, "\0" * length + "foo\n")
def main():
random.seed(0)
if len(adb.get_devices()) > 0:

View file

@ -27,57 +27,43 @@
#if ADB_HOST
static constexpr size_t MAX_USB_BULK_PACKET_SIZE = 1024u;
// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
static int UsbReadMessage(usb_handle* h, amessage* msg) {
D("UsbReadMessage");
char buffer[MAX_USB_BULK_PACKET_SIZE];
int n = usb_read(h, buffer, sizeof(buffer));
if (n == sizeof(*msg)) {
memcpy(msg, buffer, sizeof(*msg));
size_t usb_packet_size = usb_get_max_packet_size(h);
CHECK(usb_packet_size >= sizeof(*msg));
CHECK(usb_packet_size < 4096);
char buffer[4096];
int n = usb_read(h, buffer, usb_packet_size);
if (n != sizeof(*msg)) {
D("usb_read returned unexpected length %d (expected %zu)", n, sizeof(*msg));
return -1;
}
memcpy(msg, buffer, sizeof(*msg));
return n;
}
// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
// Call usb_read using a buffer having a multiple of usb_get_max_packet_size() bytes
// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
static int UsbReadPayload(usb_handle* h, apacket* p) {
D("UsbReadPayload");
size_t need_size = p->msg.data_length;
size_t data_pos = 0u;
while (need_size > 0u) {
int n = 0;
if (data_pos + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
// Read directly to p->data.
size_t rem_size = need_size % MAX_USB_BULK_PACKET_SIZE;
size_t direct_read_size = need_size - rem_size;
if (rem_size &&
data_pos + direct_read_size + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
direct_read_size += MAX_USB_BULK_PACKET_SIZE;
}
n = usb_read(h, &p->data[data_pos], direct_read_size);
if (n < 0) {
D("usb_read(size %zu) failed", direct_read_size);
return n;
}
} else {
// Read indirectly using a buffer.
char buffer[MAX_USB_BULK_PACKET_SIZE];
n = usb_read(h, buffer, sizeof(buffer));
if (n < 0) {
D("usb_read(size %zu) failed", sizeof(buffer));
return -1;
}
size_t copy_size = std::min(static_cast<size_t>(n), need_size);
D("usb read %d bytes, need %zu bytes, copy %zu bytes", n, need_size, copy_size);
memcpy(&p->data[data_pos], buffer, copy_size);
}
data_pos += n;
need_size -= std::min(static_cast<size_t>(n), need_size);
D("UsbReadPayload(%d)", p->msg.data_length);
size_t usb_packet_size = usb_get_max_packet_size(h);
CHECK(sizeof(p->data) % usb_packet_size == 0);
// Round the data length up to the nearest packet size boundary.
// The device won't send a zero packet for packet size aligned payloads,
// so don't read any more packets than needed.
size_t len = p->msg.data_length;
size_t rem_size = len % usb_packet_size;
if (rem_size) {
len += usb_packet_size - rem_size;
}
return static_cast<int>(data_pos);
CHECK(len <= sizeof(p->data));
return usb_read(h, &p->data, len);
}
static int remote_read(apacket* p, atransport* t) {

View file

@ -16,6 +16,8 @@
#pragma once
#include <sys/types.h>
// USB host/client interface.
#define ADB_USB_INTERFACE(handle_ref_type) \
@ -23,7 +25,8 @@
int usb_write(handle_ref_type h, const void* data, int len); \
int usb_read(handle_ref_type h, void* data, int len); \
int usb_close(handle_ref_type h); \
void usb_kick(handle_ref_type h)
void usb_kick(handle_ref_type h); \
size_t usb_get_max_packet_size(handle_ref_type)
#if defined(_WIN32) || !ADB_HOST
// Windows and the daemon have a single implementation.