summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--device/usb/mojo/type_converters.cc2
-rw-r--r--third_party/WebKit/LayoutTests/usb/usbDevice.html94
-rw-r--r--third_party/WebKit/Source/modules/webusb/USBDevice.cpp142
-rw-r--r--third_party/WebKit/Source/modules/webusb/USBDevice.h4
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, &parameters);
- if (error)
- resolver->reject(error);
- else
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, &parameters, 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, &parameters);
- if (error)
- resolver->reject(error);
- else
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, &parameters, 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, &parameters);
- if (error) {
- resolver->reject(error);
- } else {
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, &parameters, 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