| Index: device/usb/usb_service_impl.cc
|
| diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc
|
| index 2261ad6930bdd1b246daf7e3abc957a9994131a9..0a3e3f232820a83478ec9fb8bb65967412b8c130 100644
|
| --- a/device/usb/usb_service_impl.cc
|
| +++ b/device/usb/usb_service_impl.cc
|
| @@ -20,6 +20,7 @@
|
| #include "components/device_event_log/device_event_log.h"
|
| #include "device/usb/usb_device_handle.h"
|
| #include "device/usb/usb_error.h"
|
| +#include "device/usb/webusb_descriptors.h"
|
| #include "third_party/libusb/src/libusb/libusb.h"
|
|
|
| #if defined(OS_WIN)
|
| @@ -33,10 +34,25 @@
|
| #include "device/udev_linux/scoped_udev.h"
|
| #endif // USE_UDEV
|
|
|
| +using net::IOBufferWithSize;
|
| +
|
| namespace device {
|
|
|
| namespace {
|
|
|
| +// Standard USB requests and descriptor types:
|
| +const uint16_t kUsbVersion2_1 = 0x0210;
|
| +const uint8_t kGetDescriptorRequest = 0x06;
|
| +const uint8_t kStringDescriptorType = 0x03;
|
| +const uint8_t kBosDescriptorType = 0x0F;
|
| +
|
| +// WebUSB requests:
|
| +const uint8_t kGetAllowedOriginsRequest = 0x01;
|
| +const uint8_t kGetLandingPageRequest = 0x02;
|
| +const uint8_t kUrlDescriptorType = 0x03;
|
| +
|
| +const int kControlTransferTimeout = 60000; // 1 minute
|
| +
|
| #if defined(OS_WIN)
|
|
|
| // Wrapper around a HDEVINFO that automatically destroys it.
|
| @@ -165,71 +181,6 @@ void GetDeviceListOnBlockingThread(
|
| base::Bind(callback, platform_devices, device_count));
|
| }
|
|
|
| -#if defined(USE_UDEV)
|
| -
|
| -void EnumerateUdevDevice(scoped_refptr<UsbDeviceImpl> device,
|
| - scoped_refptr<base::SequencedTaskRunner> task_runner,
|
| - const base::Closure& success_closure,
|
| - const base::Closure& failure_closure) {
|
| - ScopedUdevPtr udev(udev_new());
|
| - ScopedUdevEnumeratePtr udev_enumerate(udev_enumerate_new(udev.get()));
|
| -
|
| - udev_enumerate_add_match_subsystem(udev_enumerate.get(), "usb");
|
| - if (udev_enumerate_scan_devices(udev_enumerate.get()) != 0) {
|
| - task_runner->PostTask(FROM_HERE, failure_closure);
|
| - return;
|
| - }
|
| -
|
| - std::string bus_number =
|
| - base::IntToString(libusb_get_bus_number(device->platform_device()));
|
| - std::string device_address =
|
| - base::IntToString(libusb_get_device_address(device->platform_device()));
|
| - udev_list_entry* devices =
|
| - udev_enumerate_get_list_entry(udev_enumerate.get());
|
| - for (udev_list_entry* i = devices; i != NULL;
|
| - i = udev_list_entry_get_next(i)) {
|
| - ScopedUdevDevicePtr udev_device(
|
| - udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i)));
|
| - if (udev_device) {
|
| - const char* value =
|
| - udev_device_get_sysattr_value(udev_device.get(), "busnum");
|
| - if (!value || bus_number != value) {
|
| - continue;
|
| - }
|
| - value = udev_device_get_sysattr_value(udev_device.get(), "devnum");
|
| - if (!value || device_address != value) {
|
| - continue;
|
| - }
|
| -
|
| - value = udev_device_get_sysattr_value(udev_device.get(), "manufacturer");
|
| - if (value) {
|
| - device->set_manufacturer_string(base::UTF8ToUTF16(value));
|
| - }
|
| - value = udev_device_get_sysattr_value(udev_device.get(), "product");
|
| - if (value) {
|
| - device->set_product_string(base::UTF8ToUTF16(value));
|
| - }
|
| - value = udev_device_get_sysattr_value(udev_device.get(), "serial");
|
| - if (value) {
|
| - device->set_serial_number(base::UTF8ToUTF16(value));
|
| - }
|
| -
|
| - value = udev_device_get_devnode(udev_device.get());
|
| - if (value) {
|
| - device->set_device_path(value);
|
| - task_runner->PostTask(FROM_HERE, success_closure);
|
| - return;
|
| - }
|
| -
|
| - break;
|
| - }
|
| - }
|
| -
|
| - task_runner->PostTask(FROM_HERE, failure_closure);
|
| -}
|
| -
|
| -#else
|
| -
|
| void OnReadStringDescriptor(
|
| const base::Callback<void(const base::string16&)>& callback,
|
| UsbTransferStatus status,
|
| @@ -254,11 +205,157 @@ void ReadStringDescriptor(
|
| uint8 index,
|
| uint16 language_id,
|
| const base::Callback<void(const base::string16&)>& callback) {
|
| - scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(256);
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(256);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
|
| + kGetDescriptorRequest, kStringDescriptorType << 8 | index, language_id,
|
| + buffer, buffer->size(), kControlTransferTimeout,
|
| + base::Bind(&OnReadStringDescriptor, callback));
|
| +}
|
| +
|
| +void OnReadWebUsbLandingPage(scoped_refptr<UsbDevice> device,
|
| + const base::Closure& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED || length < 2) {
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + uint8_t string_length = buffer->data()[0];
|
| + if (string_length < 2 || string_length > length ||
|
| + buffer->data()[1] != kUrlDescriptorType) {
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + GURL url(std::string(&buffer->data()[2], string_length - 2));
|
| + if (url.is_valid()) {
|
| + UsbDeviceImpl* device_impl = static_cast<UsbDeviceImpl*>(device.get());
|
| + device_impl->set_webusb_landing_page(url);
|
| + }
|
| + callback.Run();
|
| +}
|
| +
|
| +void ReadWebUsbLandingPage(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback,
|
| + uint8 vendor_code) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(256);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, 0, kGetLandingPageRequest, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadWebUsbLandingPage, device_handle->GetDevice(),
|
| + callback));
|
| +}
|
| +
|
| +void OnReadWebUsbAllowedOrigins(scoped_refptr<UsbDevice> device,
|
| + const base::Closure& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED) {
|
| + USB_LOG(EVENT) << "Failed to read WebUSB allowed origins.";
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + scoped_ptr<WebUsbDescriptorSet> descriptors(new WebUsbDescriptorSet());
|
| + if (descriptors->Parse(
|
| + std::vector<uint8>(buffer->data(), buffer->data() + length))) {
|
| + UsbDeviceImpl* device_impl = static_cast<UsbDeviceImpl*>(device.get());
|
| + device_impl->set_webusb_allowed_origins(descriptors.Pass());
|
| + }
|
| + callback.Run();
|
| +}
|
| +
|
| +void OnReadWebUsbAllowedOriginsHeader(
|
| + scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback,
|
| + uint8 vendor_code,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED || length != 4) {
|
| + USB_LOG(EVENT) << "Failed to read WebUSB allowed origins header.";
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + uint16 new_length = buffer->data()[2] | (buffer->data()[3] << 8);
|
| + scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, 0, kGetAllowedOriginsRequest, new_buffer, new_buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadWebUsbAllowedOrigins, device_handle->GetDevice(),
|
| + callback));
|
| +}
|
| +
|
| +void ReadWebUsbAllowedOrigins(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback,
|
| + uint8 vendor_code) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(4);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, 0, kGetAllowedOriginsRequest, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback,
|
| + vendor_code));
|
| +}
|
| +
|
| +void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED) {
|
| + USB_LOG(EVENT) << "Failed to read BOS descriptor.";
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + WebUsbPlatformCapabilityDescriptor descriptor;
|
| + if (!descriptor.ParseFromBosDescriptor(
|
| + std::vector<uint8>(buffer->data(), buffer->data() + length))) {
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + base::Closure barrier = base::BarrierClosure(2, callback);
|
| + ReadWebUsbLandingPage(device_handle, barrier, descriptor.vendor_code);
|
| + ReadWebUsbAllowedOrigins(device_handle, barrier, descriptor.vendor_code);
|
| +}
|
| +
|
| +void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED || length != 5) {
|
| + USB_LOG(EVENT) << "Failed to read BOS descriptor header.";
|
| + callback.Run();
|
| + return;
|
| + }
|
| +
|
| + uint16 new_length = buffer->data()[2] | (buffer->data()[3] << 8);
|
| + scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
|
| + kGetDescriptorRequest, kBosDescriptorType << 8, 0, new_buffer,
|
| + new_buffer->size(), kControlTransferTimeout,
|
| + base::Bind(&OnReadBosDescriptor, device_handle, callback));
|
| +}
|
| +
|
| +void ReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const base::Closure& callback) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(5);
|
| device_handle->ControlTransfer(
|
| USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
|
| - 6 /* GET_DESCRIPTOR */, 3 /* STRING */ << 8 | index, language_id, buffer,
|
| - 256, 60, base::Bind(&OnReadStringDescriptor, callback));
|
| + kGetDescriptorRequest, kBosDescriptorType << 8, 0, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadBosDescriptorHeader, device_handle, callback));
|
| }
|
|
|
| void CloseHandleAndRunContinuation(scoped_refptr<UsbDeviceHandle> device_handle,
|
| @@ -277,11 +374,12 @@ void SaveStringAndRunContinuation(
|
| continuation.Run();
|
| }
|
|
|
| +// This function runs |barrier| once for every string it tries to read.
|
| void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle,
|
| uint8 manufacturer,
|
| uint8 product,
|
| uint8 serial_number,
|
| - const base::Closure& success_closure,
|
| + const base::Closure& barrier,
|
| const base::string16& languages) {
|
| // Default to English unless the device provides a language and then just pick
|
| // the first one.
|
| @@ -292,57 +390,144 @@ void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle,
|
|
|
| scoped_refptr<UsbDeviceImpl> device =
|
| static_cast<UsbDeviceImpl*>(device_handle->GetDevice().get());
|
| - base::Closure continuation =
|
| - base::BarrierClosure(3, base::Bind(&CloseHandleAndRunContinuation,
|
| - device_handle, success_closure));
|
|
|
| - if (manufacturer == 0) {
|
| - continuation.Run();
|
| - } else {
|
| + if (manufacturer != 0) {
|
| ReadStringDescriptor(
|
| device_handle, manufacturer, language_id,
|
| base::Bind(&SaveStringAndRunContinuation,
|
| base::Bind(&UsbDeviceImpl::set_manufacturer_string, device),
|
| - continuation));
|
| + barrier));
|
| }
|
|
|
| - if (product == 0) {
|
| - continuation.Run();
|
| - } else {
|
| + if (product != 0) {
|
| ReadStringDescriptor(
|
| device_handle, product, language_id,
|
| base::Bind(&SaveStringAndRunContinuation,
|
| base::Bind(&UsbDeviceImpl::set_product_string, device),
|
| - continuation));
|
| + barrier));
|
| }
|
|
|
| - if (serial_number == 0) {
|
| - continuation.Run();
|
| - } else {
|
| + if (serial_number != 0) {
|
| ReadStringDescriptor(
|
| device_handle, serial_number, language_id,
|
| base::Bind(&SaveStringAndRunContinuation,
|
| base::Bind(&UsbDeviceImpl::set_serial_number, device),
|
| - continuation));
|
| + barrier));
|
| }
|
| }
|
|
|
| -void ReadDeviceLanguage(uint8 manufacturer,
|
| - uint8 product,
|
| - uint8 serial_number,
|
| - const base::Closure& success_closure,
|
| - const base::Closure& failure_closure,
|
| - scoped_refptr<UsbDeviceHandle> device_handle) {
|
| +void OnDeviceOpenedReadDescriptors(
|
| + uint8 manufacturer,
|
| + uint8 product,
|
| + uint8 serial_number,
|
| + bool read_bos_descriptors,
|
| + const base::Closure& success_closure,
|
| + const base::Closure& failure_closure,
|
| + scoped_refptr<UsbDeviceHandle> device_handle) {
|
| if (device_handle) {
|
| - ReadStringDescriptor(
|
| - device_handle, 0, 0,
|
| - base::Bind(&OnReadLanguageIds, device_handle, manufacturer, product,
|
| - serial_number, success_closure));
|
| + int count = 0;
|
| + if (manufacturer != 0)
|
| + count++;
|
| + if (product != 0)
|
| + count++;
|
| + if (serial_number != 0)
|
| + count++;
|
| + if (read_bos_descriptors)
|
| + count++;
|
| + DCHECK_GT(count, 0);
|
| +
|
| + base::Closure barrier =
|
| + base::BarrierClosure(count, base::Bind(&CloseHandleAndRunContinuation,
|
| + device_handle, success_closure));
|
| +
|
| + if (manufacturer != 0 || product != 0 || serial_number != 0) {
|
| + ReadStringDescriptor(
|
| + device_handle, 0, 0,
|
| + base::Bind(&OnReadLanguageIds, device_handle, manufacturer, product,
|
| + serial_number, barrier));
|
| + }
|
| +
|
| + if (read_bos_descriptors) {
|
| + ReadBosDescriptor(device_handle, barrier);
|
| + }
|
| } else {
|
| failure_closure.Run();
|
| }
|
| }
|
|
|
| +#if defined(USE_UDEV)
|
| +
|
| +void EnumerateUdevDevice(scoped_refptr<UsbDeviceImpl> device,
|
| + bool read_bos_descriptors,
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner,
|
| + const base::Closure& success_closure,
|
| + const base::Closure& failure_closure) {
|
| + ScopedUdevPtr udev(udev_new());
|
| + ScopedUdevEnumeratePtr udev_enumerate(udev_enumerate_new(udev.get()));
|
| +
|
| + udev_enumerate_add_match_subsystem(udev_enumerate.get(), "usb");
|
| + if (udev_enumerate_scan_devices(udev_enumerate.get()) != 0) {
|
| + task_runner->PostTask(FROM_HERE, failure_closure);
|
| + return;
|
| + }
|
| +
|
| + std::string bus_number =
|
| + base::IntToString(libusb_get_bus_number(device->platform_device()));
|
| + std::string device_address =
|
| + base::IntToString(libusb_get_device_address(device->platform_device()));
|
| + udev_list_entry* devices =
|
| + udev_enumerate_get_list_entry(udev_enumerate.get());
|
| + for (udev_list_entry* i = devices; i != NULL;
|
| + i = udev_list_entry_get_next(i)) {
|
| + ScopedUdevDevicePtr udev_device(
|
| + udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i)));
|
| + if (udev_device) {
|
| + const char* value =
|
| + udev_device_get_sysattr_value(udev_device.get(), "busnum");
|
| + if (!value || bus_number != value) {
|
| + continue;
|
| + }
|
| + value = udev_device_get_sysattr_value(udev_device.get(), "devnum");
|
| + if (!value || device_address != value) {
|
| + continue;
|
| + }
|
| +
|
| + value = udev_device_get_sysattr_value(udev_device.get(), "manufacturer");
|
| + if (value) {
|
| + device->set_manufacturer_string(base::UTF8ToUTF16(value));
|
| + }
|
| + value = udev_device_get_sysattr_value(udev_device.get(), "product");
|
| + if (value) {
|
| + device->set_product_string(base::UTF8ToUTF16(value));
|
| + }
|
| + value = udev_device_get_sysattr_value(udev_device.get(), "serial");
|
| + if (value) {
|
| + device->set_serial_number(base::UTF8ToUTF16(value));
|
| + }
|
| +
|
| + value = udev_device_get_devnode(udev_device.get());
|
| + if (value) {
|
| + device->set_device_path(value);
|
| +
|
| + if (read_bos_descriptors) {
|
| + task_runner->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&UsbDevice::Open, device,
|
| + base::Bind(&OnDeviceOpenedReadDescriptors, 0, 0, 0,
|
| + true, success_closure, failure_closure)));
|
| + } else {
|
| + task_runner->PostTask(FROM_HERE, success_closure);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + break;
|
| + }
|
| + }
|
| +
|
| + task_runner->PostTask(FROM_HERE, failure_closure);
|
| +}
|
| +
|
| #endif // USE_UDEV
|
|
|
| } // namespace
|
| @@ -573,20 +758,22 @@ void UsbServiceImpl::EnumerateDevice(PlatformUsbDevice platform_device,
|
| base::Closure add_device =
|
| base::Bind(&UsbServiceImpl::AddDevice, weak_factory_.GetWeakPtr(),
|
| refresh_complete, device);
|
| + bool read_bos_descriptors = descriptor.bcdUSB >= kUsbVersion2_1;
|
|
|
| #if defined(USE_UDEV)
|
| blocking_task_runner_->PostTask(
|
| - FROM_HERE, base::Bind(&EnumerateUdevDevice, device, task_runner_,
|
| - add_device, refresh_complete));
|
| + FROM_HERE,
|
| + base::Bind(&EnumerateUdevDevice, device, read_bos_descriptors,
|
| + task_runner_, add_device, refresh_complete));
|
| #else
|
| if (descriptor.iManufacturer == 0 && descriptor.iProduct == 0 &&
|
| - descriptor.iSerialNumber == 0) {
|
| - // Don't bother disturbing the device if it has no string descriptors to
|
| - // offer.
|
| + descriptor.iSerialNumber == 0 && !read_bos_descriptors) {
|
| + // Don't bother disturbing the device if it has no descriptors to offer.
|
| add_device.Run();
|
| } else {
|
| - device->Open(base::Bind(&ReadDeviceLanguage, descriptor.iManufacturer,
|
| - descriptor.iProduct, descriptor.iSerialNumber,
|
| + device->Open(base::Bind(&OnDeviceOpenedReadDescriptors,
|
| + descriptor.iManufacturer, descriptor.iProduct,
|
| + descriptor.iSerialNumber, read_bos_descriptors,
|
| add_device, refresh_complete));
|
| }
|
| #endif
|
|
|