diff options
-rw-r--r-- | device/usb/mojo/type_converters.cc | 2 | ||||
-rw-r--r-- | third_party/WebKit/LayoutTests/usb/usbDevice.html | 94 | ||||
-rw-r--r-- | third_party/WebKit/Source/modules/webusb/USBDevice.cpp | 142 | ||||
-rw-r--r-- | third_party/WebKit/Source/modules/webusb/USBDevice.h | 4 |
4 files changed, 191 insertions, 51 deletions
diff --git a/device/usb/mojo/type_converters.cc b/device/usb/mojo/type_converters.cc index a2daeaa..b7bfd7f 100644 --- a/device/usb/mojo/type_converters.cc +++ b/device/usb/mojo/type_converters.cc @@ -138,7 +138,7 @@ device::usb::EndpointInfoPtr TypeConverter<device::usb::EndpointInfoPtr, device::UsbEndpointDescriptor>:: Convert(const device::UsbEndpointDescriptor& endpoint) { device::usb::EndpointInfoPtr info = device::usb::EndpointInfo::New(); - info->endpoint_number = endpoint.address; + info->endpoint_number = endpoint.address & 0xf; info->direction = ConvertTo<device::usb::TransferDirection>(endpoint.direction); info->type = ConvertTo<device::usb::EndpointType>(endpoint.transfer_type); diff --git a/third_party/WebKit/LayoutTests/usb/usbDevice.html b/third_party/WebKit/LayoutTests/usb/usbDevice.html index e611148..763228a 100644 --- a/third_party/WebKit/LayoutTests/usb/usbDevice.html +++ b/third_party/WebKit/LayoutTests/usb/usbDevice.html @@ -411,6 +411,69 @@ usb_test(usb => { usb_test(usb => { usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); return navigator.usb.getDevices().then(devices => { + assert_equals(1, devices.length); + let device = devices[0]; + let interfaceRequest = { + requestType: 'vendor', + recipient: 'interface', + request: 0x42, + value: 0x1234, + index: 0x5600 // Last byte of index is interface number. + }; + let endpointRequest = { + requestType: 'vendor', + recipient: 'endpoint', + request: 0x42, + value: 0x1234, + index: 0x5681 // Last byte of index is endpoint address. + }; + let data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + return device.open() + .then(() => device.selectConfiguration(1)) + .then(() => Promise.all([ + assertRejectsWithError( + device.controlTransferIn(interfaceRequest, 7), + 'InvalidStateError'), + assertRejectsWithError( + device.controlTransferIn(endpointRequest, 7), + 'NotFoundError'), + assertRejectsWithError( + device.controlTransferOut(interfaceRequest, data), + 'InvalidStateError'), + assertRejectsWithError( + device.controlTransferOut(endpointRequest, data), + 'NotFoundError'), + ])) + .then(() => device.claimInterface(0)) + .then(() => Promise.all([ + device.controlTransferIn(interfaceRequest, 7).then(result => { + assert_true(result instanceof USBInTransferResult); + assert_equals(result.status, 'ok'); + assert_equals(result.data.byteLength, 7); + assert_equals(result.data.getUint16(0), 0x07); + assert_equals(result.data.getUint8(2), 0x42); + assert_equals(result.data.getUint16(3), 0x1234); + assert_equals(result.data.getUint16(5), 0x5600); + }), + device.controlTransferIn(endpointRequest, 7).then(result => { + assert_true(result instanceof USBInTransferResult); + assert_equals(result.status, 'ok'); + assert_equals(result.data.byteLength, 7); + assert_equals(result.data.getUint16(0), 0x07); + assert_equals(result.data.getUint8(2), 0x42); + assert_equals(result.data.getUint16(3), 0x1234); + assert_equals(result.data.getUint16(5), 0x5681); + }), + device.controlTransferOut(interfaceRequest, data), + device.controlTransferOut(endpointRequest, data), + ])) + .then(() => device.close()); + }); +}, 'requests to interfaces and endpoint require an interface claim'); + +usb_test(usb => { + usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); + return navigator.usb.getDevices().then(devices => { assert_equals(devices.length, 1); let device = devices[0]; return device.open() @@ -441,6 +504,37 @@ usb_test(usb => { return navigator.usb.getDevices().then(devices => { assert_equals(devices.length, 1); let device = devices[0]; + let data = new DataView(new ArrayBuffer(1024)); + for (let i = 0; i < 1024; ++i) + data.setUint8(i, i & 0xff); + const notFoundMessage = 'The specified endpoint is not part of a claimed ' + + 'and selected alternate interface.'; + const rangeError = 'The specified endpoint number is out of range.'; + return device.open() + .then(() => device.selectConfiguration(1)) + .then(() => device.claimInterface(0)) + .then(() => Promise.all([ + assertRejectsWithError(device.transferIn(2, 8), + 'NotFoundError', notFoundMessage), // Unclaimed + assertRejectsWithError(device.transferIn(3, 8), 'NotFoundError', + notFoundMessage), // Non-existent + assertRejectsWithError( + device.transferIn(16, 8), 'IndexSizeError', rangeError), + assertRejectsWithError(device.transferOut(2, data), + 'NotFoundError', notFoundMessage), // Unclaimed + assertRejectsWithError(device.transferOut(3, data), 'NotFoundError', + notFoundMessage), // Non-existent + assertRejectsWithError( + device.transferOut(16, data), 'IndexSizeError', rangeError), + ])); + }); +}, 'transfers to unavailable endpoints are rejected'); + +usb_test(usb => { + usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); + return navigator.usb.getDevices().then(devices => { + assert_equals(devices.length, 1); + let device = devices[0]; return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp index 54ec6d9..bbaf4ed6 100644 --- a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp +++ b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp @@ -32,40 +32,7 @@ const char kInterfaceStateChangeInProgress[] = "An operation that changes interf const char kOpenRequired[] = "The device must be opened first."; const char kVisibiltyError[] = "Connection is only allowed while the page is visible. This is a temporary measure until we are able to effectively communicate to the user that the page is connected to a device."; -DOMException* convertControlTransferParameters( - WebUSBDevice::TransferDirection direction, - const USBControlTransferParameters& parameters, - WebUSBDevice::ControlTransferParameters* webParameters) -{ - webParameters->direction = direction; - - if (parameters.requestType() == "standard") - webParameters->type = WebUSBDevice::RequestType::Standard; - else if (parameters.requestType() == "class") - webParameters->type = WebUSBDevice::RequestType::Class; - else if (parameters.requestType() == "vendor") - webParameters->type = WebUSBDevice::RequestType::Vendor; - else - return DOMException::create(TypeMismatchError, "The control transfer requestType parameter is invalid."); - // TODO(reillyg): Check for interface and endpoint availability if that is - // the control transfer target. - if (parameters.recipient() == "device") - webParameters->recipient = WebUSBDevice::RequestRecipient::Device; - else if (parameters.recipient() == "interface") - webParameters->recipient = WebUSBDevice::RequestRecipient::Interface; - else if (parameters.recipient() == "endpoint") - webParameters->recipient = WebUSBDevice::RequestRecipient::Endpoint; - else if (parameters.recipient() == "other") - webParameters->recipient = WebUSBDevice::RequestRecipient::Other; - else - return DOMException::create(TypeMismatchError, "The control transfer recipient parameter is invalid."); - - webParameters->request = parameters.request(); - webParameters->value = parameters.value(); - webParameters->index = parameters.index(); - return nullptr; -} String convertTransferStatus(const WebUSBTransferInfo::Status& status) { @@ -325,6 +292,8 @@ USBDevice::USBDevice(PassOwnPtr<WebUSBDevice> device, ExecutionContext* context) , m_opened(false) , m_deviceStateChangeInProgress(false) , m_configurationIndex(-1) + , m_inEndpoints(15) + , m_outEndpoints(15) { int configurationIndex = findConfigurationIndex(info().activeConfiguration); if (configurationIndex != -1) @@ -348,16 +317,21 @@ void USBDevice::onConfigurationSelected(bool success, size_t configurationIndex) m_interfaceStateChangeInProgress.resize(numInterfaces); m_selectedAlternates.resize(numInterfaces); m_selectedAlternates.fill(0); + m_inEndpoints.clearAll(); + m_outEndpoints.clearAll(); } m_deviceStateChangeInProgress = false; } void USBDevice::onInterfaceClaimedOrUnclaimed(bool claimed, size_t interfaceIndex) { - if (claimed) + if (claimed) { m_claimedInterfaces.set(interfaceIndex); - else + } else { m_claimedInterfaces.clear(interfaceIndex); + m_selectedAlternates[interfaceIndex] = 0; + } + setEndpointsForInterface(interfaceIndex, claimed); m_interfaceStateChangeInProgress.clear(interfaceIndex); } @@ -365,6 +339,7 @@ void USBDevice::onAlternateInterfaceSelected(bool success, size_t interfaceIndex { if (success) m_selectedAlternates[interfaceIndex] = alternateIndex; + setEndpointsForInterface(interfaceIndex, success); m_interfaceStateChangeInProgress.clear(interfaceIndex); } @@ -479,6 +454,9 @@ ScriptPromise USBDevice::releaseInterface(ScriptState* scriptState, uint8_t inte } else if (!m_claimedInterfaces.get(interfaceIndex)) { resolver->resolve(); } else { + // Mark this interface's endpoints unavailable while its state is + // changing. + setEndpointsForInterface(interfaceIndex, false); m_interfaceStateChangeInProgress.set(interfaceIndex); m_device->releaseInterface(interfaceNumber, new ClaimInterfacePromiseAdapter(this, resolver, interfaceIndex, false /* release */)); } @@ -498,6 +476,9 @@ ScriptPromise USBDevice::selectAlternateInterface(ScriptState* scriptState, uint if (alternateIndex == -1) { resolver->reject(DOMException::create(NotFoundError, "The alternate setting provided is not supported by the device in its current configuration.")); } else { + // Mark this old alternate interface's endpoints unavailable while + // the change is in progress. + setEndpointsForInterface(interfaceIndex, false); m_interfaceStateChangeInProgress.set(interfaceIndex); m_device->setInterface(interfaceNumber, alternateSetting, new SelectAlternateInterfacePromiseAdapter(this, resolver, interfaceIndex, alternateIndex)); } @@ -511,10 +492,7 @@ ScriptPromise USBDevice::controlTransferIn(ScriptState* scriptState, const USBCo ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, ¶meters); - if (error) - resolver->reject(error); - else + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, ¶meters, resolver)) m_device->controlTransfer(parameters, nullptr, length, 0, new CallbackPromiseAdapter<InputTransferResult, USBError>(resolver)); } return promise; @@ -526,10 +504,7 @@ ScriptPromise USBDevice::controlTransferOut(ScriptState* scriptState, const USBC ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters); - if (error) - resolver->reject(error); - else + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters, resolver)) m_device->controlTransfer(parameters, nullptr, 0, 0, new CallbackPromiseAdapter<OutputTransferResult, USBError>(resolver)); } return promise; @@ -541,10 +516,7 @@ ScriptPromise USBDevice::controlTransferOut(ScriptState* scriptState, const USBC ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters); - if (error) { - resolver->reject(error); - } else { + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters, resolver)) { BufferSource buffer(data); m_device->controlTransfer(parameters, buffer.data(), buffer.size(), 0, new CallbackPromiseAdapter<OutputTransferResult, USBError>(resolver)); } @@ -723,9 +695,18 @@ bool USBDevice::ensureInterfaceClaimed(uint8_t interfaceNumber, ScriptPromiseRes bool USBDevice::ensureEndpointAvailable(bool inTransfer, uint8_t endpointNumber, ScriptPromiseResolver* resolver) const { - // TODO(reillyg): Check endpoint availability once Blink is tracking which - // alternate interfaces are selected. - return ensureDeviceConfigured(resolver); + if (!ensureDeviceConfigured(resolver)) + return false; + if (endpointNumber == 0 || endpointNumber >= 16) { + resolver->reject(DOMException::create(IndexSizeError, "The specified endpoint number is out of range.")); + return false; + } + auto& bitVector = inTransfer ? m_inEndpoints : m_outEndpoints; + if (!bitVector.get(endpointNumber - 1)) { + resolver->reject(DOMException::create(NotFoundError, "The specified endpoint is not part of a claimed and selected alternate interface.")); + return false; + } + return true; } bool USBDevice::anyInterfaceChangeInProgress() const @@ -737,4 +718,65 @@ bool USBDevice::anyInterfaceChangeInProgress() const return false; } +bool USBDevice::convertControlTransferParameters( + WebUSBDevice::TransferDirection direction, + const USBControlTransferParameters& parameters, + WebUSBDevice::ControlTransferParameters* webParameters, + ScriptPromiseResolver* resolver) const +{ + webParameters->direction = direction; + + if (parameters.requestType() == "standard") { + webParameters->type = WebUSBDevice::RequestType::Standard; + } else if (parameters.requestType() == "class") { + webParameters->type = WebUSBDevice::RequestType::Class; + } else if (parameters.requestType() == "vendor") { + webParameters->type = WebUSBDevice::RequestType::Vendor; + } else { + resolver->reject(DOMException::create(TypeMismatchError, "The control transfer requestType parameter is invalid.")); + return false; + } + + if (parameters.recipient() == "device") { + webParameters->recipient = WebUSBDevice::RequestRecipient::Device; + } else if (parameters.recipient() == "interface") { + size_t interfaceNumber = parameters.index() & 0xff; + if (!ensureInterfaceClaimed(interfaceNumber, resolver)) + return false; + webParameters->recipient = WebUSBDevice::RequestRecipient::Interface; + } else if (parameters.recipient() == "endpoint") { + bool inTransfer = parameters.index() & 0x80; + size_t endpointNumber = parameters.index() & 0x0f; + if (!ensureEndpointAvailable(inTransfer, endpointNumber, resolver)) + return false; + webParameters->recipient = WebUSBDevice::RequestRecipient::Endpoint; + } else if (parameters.recipient() == "other") { + webParameters->recipient = WebUSBDevice::RequestRecipient::Other; + } else { + resolver->reject(DOMException::create(TypeMismatchError, "The control transfer recipient parameter is invalid.")); + return false; + } + + webParameters->request = parameters.request(); + webParameters->value = parameters.value(); + webParameters->index = parameters.index(); + return true; +} + +void USBDevice::setEndpointsForInterface(size_t interfaceIndex, bool set) +{ + const auto& configuration = info().configurations[m_configurationIndex]; + const auto& interface = configuration.interfaces[interfaceIndex]; + const auto& alternate = interface.alternates[m_selectedAlternates[interfaceIndex]]; + for (const auto& endpoint : alternate.endpoints) { + if (endpoint.endpointNumber == 0 || endpoint.endpointNumber >= 16) + continue; // Ignore endpoints with invalid indices. + auto& bitVector = endpoint.direction == WebUSBDevice::TransferDirection::In ? m_inEndpoints : m_outEndpoints; + if (set) + bitVector.set(endpoint.endpointNumber - 1); + else + bitVector.clear(endpoint.endpointNumber - 1); + } +} + } // namespace blink diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.h b/third_party/WebKit/Source/modules/webusb/USBDevice.h index 7f30b53..d28121f 100644 --- a/third_party/WebKit/Source/modules/webusb/USBDevice.h +++ b/third_party/WebKit/Source/modules/webusb/USBDevice.h @@ -106,6 +106,8 @@ private: bool ensureInterfaceClaimed(uint8_t interfaceNumber, ScriptPromiseResolver*) const; bool ensureEndpointAvailable(bool inTransfer, uint8_t endpointNumber, ScriptPromiseResolver*) const; bool anyInterfaceChangeInProgress() const; + bool convertControlTransferParameters(WebUSBDevice::TransferDirection, const USBControlTransferParameters&, WebUSBDevice::ControlTransferParameters*, ScriptPromiseResolver*) const; + void setEndpointsForInterface(size_t interfaceIndex, bool set); OwnPtr<WebUSBDevice> m_device; bool m_opened; @@ -114,6 +116,8 @@ private: WTF::BitVector m_claimedInterfaces; WTF::BitVector m_interfaceStateChangeInProgress; WTF::Vector<size_t> m_selectedAlternates; + WTF::BitVector m_inEndpoints; + WTF::BitVector m_outEndpoints; }; } // namespace blink |