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 |