summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/devtools/device/usb/android_usb_browsertest.cc20
-rw-r--r--device/devices_app/usb/device_impl.cc96
-rw-r--r--device/devices_app/usb/device_impl.h12
-rw-r--r--device/devices_app/usb/device_impl_unittest.cc195
-rw-r--r--device/devices_app/usb/public/interfaces/device.mojom37
-rw-r--r--device/devices_app/usb/type_converters.cc13
-rw-r--r--device/devices_app/usb/type_converters.h12
-rw-r--r--device/usb/mock_usb_device_handle.h23
-rw-r--r--device/usb/usb_device_handle.h32
-rw-r--r--device/usb/usb_device_handle_impl.cc240
-rw-r--r--device/usb/usb_device_handle_impl.h35
-rw-r--r--extensions/browser/api/usb/usb_api.cc95
-rw-r--r--extensions/browser/api/usb/usb_api.h8
-rw-r--r--extensions/browser/api/usb/usb_apitest.cc51
-rw-r--r--extensions/test/data/api_test/usb/transfer_event/test.js4
-rw-r--r--extensions/test/data/api_test/usb/transfer_failure/test.js32
16 files changed, 625 insertions, 280 deletions
diff --git a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
index 3029fd6..caa9950 100644
--- a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc
@@ -352,14 +352,18 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
query.buffer, query.size));
}
- void IsochronousTransfer(UsbEndpointDirection direction,
- uint8_t endpoint,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
- unsigned int timeout,
- const TransferCallback& callback) override {}
+ void IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override {}
+
+ void IsochronousTransferOut(
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override {}
protected:
virtual ~MockUsbDeviceHandle() {}
diff --git a/device/devices_app/usb/device_impl.cc b/device/devices_app/usb/device_impl.cc
index fd2f0ff..ae8ad40 100644
--- a/device/devices_app/usb/device_impl.cc
+++ b/device/devices_app/usb/device_impl.cc
@@ -5,7 +5,11 @@
#include "device/devices_app/usb/device_impl.h"
#include <stddef.h>
+
+#include <algorithm>
+#include <numeric>
#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/callback.h"
@@ -128,32 +132,46 @@ void OnControlTransferOutPermissionCheckComplete(
}
}
+mojo::Array<IsochronousPacketPtr> BuildIsochronousPacketArray(
+ mojo::Array<uint32_t> packet_lengths,
+ TransferStatus status) {
+ mojo::Array<IsochronousPacketPtr> packets(packet_lengths.size());
+ for (size_t i = 0; i < packet_lengths.size(); ++i) {
+ packets[i] = IsochronousPacket::New();
+ packets[i]->length = packet_lengths[i];
+ packets[i]->status = status;
+ }
+ return packets;
+}
+
void OnIsochronousTransferIn(
scoped_ptr<Device::IsochronousTransferInCallback> callback,
- uint32_t packet_size,
- UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
- size_t buffer_size) {
- size_t num_packets = buffer_size / packet_size;
- mojo::Array<mojo::Array<uint8_t>> packets(num_packets);
+ const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) {
+ mojo::Array<uint8_t> data;
if (buffer) {
- for (size_t i = 0; i < num_packets; ++i) {
- size_t packet_index = i * packet_size;
- std::vector<uint8_t> bytes(packet_size);
- std::copy(buffer->data() + packet_index,
- buffer->data() + packet_index + packet_size, bytes.begin());
- packets[i].Swap(&bytes);
- }
+ // TODO(rockot/reillyg): We should change UsbDeviceHandle to use a
+ // std::vector<uint8_t> instead of net::IOBuffer. Then we could move
+ // instead of copy.
+ uint32_t buffer_size =
+ std::accumulate(packets.begin(), packets.end(), 0u,
+ [](const uint32_t& a,
+ const UsbDeviceHandle::IsochronousPacket& packet) {
+ return a + packet.length;
+ });
+ std::vector<uint8_t> bytes(buffer_size);
+ std::copy(buffer->data(), buffer->data() + buffer_size, bytes.begin());
+ data.Swap(&bytes);
}
- callback->Run(mojo::ConvertTo<TransferStatus>(status), std::move(packets));
+ callback->Run(std::move(data),
+ mojo::Array<IsochronousPacketPtr>::From(packets));
}
void OnIsochronousTransferOut(
scoped_ptr<Device::IsochronousTransferOutCallback> callback,
- UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
- size_t buffer_size) {
- callback->Run(mojo::ConvertTo<TransferStatus>(status));
+ const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) {
+ callback->Run(mojo::Array<IsochronousPacketPtr>::From(packets));
}
} // namespace
@@ -398,58 +416,46 @@ void DeviceImpl::GenericTransferOut(
void DeviceImpl::IsochronousTransferIn(
uint8_t endpoint_number,
- uint32_t num_packets,
- uint32_t packet_length,
+ mojo::Array<uint32_t> packet_lengths,
uint32_t timeout,
const IsochronousTransferInCallback& callback) {
if (!device_handle_) {
- callback.Run(TransferStatus::TRANSFER_ERROR,
- mojo::Array<mojo::Array<uint8_t>>());
+ callback.Run(mojo::Array<uint8_t>(),
+ BuildIsochronousPacketArray(std::move(packet_lengths),
+ TransferStatus::TRANSFER_ERROR));
return;
}
auto callback_ptr =
make_scoped_ptr(new IsochronousTransferInCallback(callback));
uint8_t endpoint_address = endpoint_number | 0x80;
- size_t transfer_size = static_cast<size_t>(num_packets) * packet_length;
- scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(transfer_size);
- device_handle_->IsochronousTransfer(
- USB_DIRECTION_INBOUND, endpoint_address, buffer, transfer_size,
- num_packets, packet_length, timeout,
- base::Bind(&OnIsochronousTransferIn, base::Passed(&callback_ptr),
- packet_length));
+ device_handle_->IsochronousTransferIn(
+ endpoint_address, packet_lengths.storage(), timeout,
+ base::Bind(&OnIsochronousTransferIn, base::Passed(&callback_ptr)));
}
void DeviceImpl::IsochronousTransferOut(
uint8_t endpoint_number,
- mojo::Array<mojo::Array<uint8_t>> packets,
+ mojo::Array<uint8_t> data,
+ mojo::Array<uint32_t> packet_lengths,
uint32_t timeout,
const IsochronousTransferOutCallback& callback) {
if (!device_handle_) {
- callback.Run(TransferStatus::TRANSFER_ERROR);
+ callback.Run(BuildIsochronousPacketArray(std::move(packet_lengths),
+ TransferStatus::TRANSFER_ERROR));
return;
}
auto callback_ptr =
make_scoped_ptr(new IsochronousTransferOutCallback(callback));
uint8_t endpoint_address = endpoint_number;
- uint32_t packet_size = 0;
- for (size_t i = 0; i < packets.size(); ++i) {
- packet_size =
- std::max(packet_size, static_cast<uint32_t>(packets[i].size()));
- }
- size_t transfer_size = packet_size * packets.size();
- scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(transfer_size);
- memset(buffer->data(), 0, transfer_size);
- for (size_t i = 0; i < packets.size(); ++i) {
- uint8_t* packet =
- reinterpret_cast<uint8_t*>(&buffer->data()[i * packet_size]);
- DCHECK_LE(packets[i].size(), static_cast<size_t>(packet_size));
- memcpy(packet, packets[i].storage().data(), packets[i].size());
+ scoped_refptr<net::IOBuffer> buffer = CreateTransferBuffer(data.size());
+ {
+ const std::vector<uint8_t>& storage = data.storage();
+ std::copy(storage.begin(), storage.end(), buffer->data());
}
- device_handle_->IsochronousTransfer(
- USB_DIRECTION_OUTBOUND, endpoint_address, buffer, transfer_size,
- static_cast<uint32_t>(packets.size()), packet_size, timeout,
+ device_handle_->IsochronousTransferOut(
+ endpoint_address, buffer, packet_lengths.storage(), timeout,
base::Bind(&OnIsochronousTransferOut, base::Passed(&callback_ptr)));
}
diff --git a/device/devices_app/usb/device_impl.h b/device/devices_app/usb/device_impl.h
index 5290fa7..2b438c8 100644
--- a/device/devices_app/usb/device_impl.h
+++ b/device/devices_app/usb/device_impl.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef DEVICE_USB_DEVICE_IMPL_H_
-#define DEVICE_USB_DEVICE_IMPL_H_
+#ifndef DEVICE_DEVICES_APP_USB_DEVICE_IMPL_H_
+#define DEVICE_DEVICES_APP_USB_DEVICE_IMPL_H_
#include <stdint.h>
@@ -84,13 +84,13 @@ class DeviceImpl : public Device {
const GenericTransferOutCallback& callback) override;
void IsochronousTransferIn(
uint8_t endpoint_number,
- uint32_t num_packets,
- uint32_t packet_length,
+ mojo::Array<uint32_t> packet_lengths,
uint32_t timeout,
const IsochronousTransferInCallback& callback) override;
void IsochronousTransferOut(
uint8_t endpoint_number,
- mojo::Array<mojo::Array<uint8_t>> packets,
+ mojo::Array<uint8_t> data,
+ mojo::Array<uint32_t> packet_lengths,
uint32_t timeout,
const IsochronousTransferOutCallback& callback) override;
@@ -110,4 +110,4 @@ class DeviceImpl : public Device {
} // namespace usb
} // namespace device
-#endif // DEVICE_USB_DEVICE_IMPL_H_
+#endif // DEVICE_DEVICES_APP_USB_DEVICE_IMPL_H_
diff --git a/device/devices_app/usb/device_impl_unittest.cc b/device/devices_app/usb/device_impl_unittest.cc
index 87324d2..e5ee893 100644
--- a/device/devices_app/usb/device_impl_unittest.cc
+++ b/device/devices_app/usb/device_impl_unittest.cc
@@ -6,9 +6,12 @@
#include <stddef.h>
#include <stdint.h>
+
#include <map>
+#include <numeric>
#include <queue>
#include <set>
+#include <string>
#include <utility>
#include <vector>
@@ -34,7 +37,7 @@ namespace {
class ConfigBuilder {
public:
- ConfigBuilder(uint8_t value) { config_.configuration_value = value; }
+ explicit ConfigBuilder(uint8_t value) { config_.configuration_value = value; }
ConfigBuilder& AddInterface(uint8_t interface_number,
uint8_t alternate_setting,
@@ -102,21 +105,35 @@ void ExpectTransferInAndThen(TransferStatus expected_status,
continuation.Run();
}
-void ExpectPacketsAndThen(
- TransferStatus expected_status,
- const std::vector<std::vector<uint8_t>>& expected_packets,
- const base::Closure& continuation,
- TransferStatus actual_status,
- mojo::Array<mojo::Array<uint8_t>> actual_packets) {
- EXPECT_EQ(expected_status, actual_status);
+void ExpectPacketsOutAndThen(const std::vector<uint32_t>& expected_packets,
+ const base::Closure& continuation,
+ mojo::Array<IsochronousPacketPtr> actual_packets) {
ASSERT_EQ(expected_packets.size(), actual_packets.size());
for (size_t i = 0; i < expected_packets.size(); ++i) {
- EXPECT_EQ(expected_packets[i].size(), actual_packets[i].size())
- << "Packet sizes differ at index: " << i;
- for (size_t j = 0; j < expected_packets[i].size(); ++j) {
- EXPECT_EQ(expected_packets[i][j], actual_packets[i][j])
- << "Contents of packet " << i << " differ at index " << j;
- }
+ EXPECT_EQ(expected_packets[i], actual_packets[i]->transferred_length)
+ << "Packet lengths differ at index: " << i;
+ EXPECT_EQ(TransferStatus::COMPLETED, actual_packets[i]->status)
+ << "Packet at index " << i << " not completed.";
+ }
+ continuation.Run();
+}
+
+void ExpectPacketsInAndThen(const std::vector<uint8_t>& expected_bytes,
+ const std::vector<uint32_t>& expected_packets,
+ const base::Closure& continuation,
+ mojo::Array<uint8_t> actual_bytes,
+ mojo::Array<IsochronousPacketPtr> actual_packets) {
+ ASSERT_EQ(expected_packets.size(), actual_packets.size());
+ for (size_t i = 0; i < expected_packets.size(); ++i) {
+ EXPECT_EQ(expected_packets[i], actual_packets[i]->transferred_length)
+ << "Packet lengths differ at index: " << i;
+ EXPECT_EQ(TransferStatus::COMPLETED, actual_packets[i]->status)
+ << "Packet at index " << i << " not completed.";
+ }
+ ASSERT_EQ(expected_bytes.size(), actual_bytes.size());
+ for (size_t i = 0; i < expected_bytes.size(); ++i) {
+ EXPECT_EQ(expected_bytes[i], actual_bytes[i])
+ << "Contents differ at index: " << i;
}
continuation.Run();
}
@@ -186,8 +203,11 @@ class USBDeviceImplTest : public testing::Test {
.WillByDefault(Invoke(this, &USBDeviceImplTest::ControlTransfer));
ON_CALL(mock_handle(), GenericTransfer(_, _, _, _, _, _))
.WillByDefault(Invoke(this, &USBDeviceImplTest::GenericTransfer));
- ON_CALL(mock_handle(), IsochronousTransfer(_, _, _, _, _, _, _, _))
- .WillByDefault(Invoke(this, &USBDeviceImplTest::IsochronousTransfer));
+ ON_CALL(mock_handle(), IsochronousTransferIn(_, _, _, _))
+ .WillByDefault(Invoke(this, &USBDeviceImplTest::IsochronousTransferIn));
+ ON_CALL(mock_handle(), IsochronousTransferOut(_, _, _, _, _))
+ .WillByDefault(
+ Invoke(this, &USBDeviceImplTest::IsochronousTransferOut));
return proxy;
}
@@ -206,10 +226,24 @@ class USBDeviceImplTest : public testing::Test {
mock_inbound_data_.push(data);
}
+ void AddMockInboundPackets(
+ const std::vector<uint8_t>& data,
+ const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) {
+ mock_inbound_data_.push(data);
+ mock_inbound_packets_.push(packets);
+ }
+
void AddMockOutboundData(const std::vector<uint8_t>& data) {
mock_outbound_data_.push(data);
}
+ void AddMockOutboundPackets(
+ const std::vector<uint8_t>& data,
+ const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) {
+ mock_outbound_data_.push(data);
+ mock_outbound_packets_.push(packets);
+ }
+
private:
void OpenMockHandle(const UsbDevice::OpenCallback& callback) {
EXPECT_FALSE(is_device_open_);
@@ -336,18 +370,59 @@ class USBDeviceImplTest : public testing::Test {
OutboundTransfer(buffer, length, callback);
}
- void IsochronousTransfer(UsbEndpointDirection direction,
- uint8_t endpoint,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
- unsigned int timeout,
- const UsbDeviceHandle::TransferCallback& callback) {
- if (direction == USB_DIRECTION_INBOUND)
- InboundTransfer(callback);
- else
- OutboundTransfer(buffer, length, callback);
+ void IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const UsbDeviceHandle::IsochronousTransferCallback& callback) {
+ ASSERT_FALSE(mock_inbound_data_.empty());
+ const std::vector<uint8_t>& bytes = mock_inbound_data_.front();
+ size_t length = bytes.size();
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(length);
+ std::copy(bytes.begin(), bytes.end(), buffer->data());
+ mock_inbound_data_.pop();
+
+ ASSERT_FALSE(mock_inbound_packets_.empty());
+ std::vector<UsbDeviceHandle::IsochronousPacket> packets =
+ mock_inbound_packets_.front();
+ ASSERT_EQ(packets.size(), packet_lengths.size());
+ for (size_t i = 0; i < packets.size(); ++i) {
+ EXPECT_EQ(packets[i].length, packet_lengths[i])
+ << "Packet lengths differ at index: " << i;
+ }
+ mock_inbound_packets_.pop();
+
+ callback.Run(buffer, packets);
+ }
+
+ void IsochronousTransferOut(
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const UsbDeviceHandle::IsochronousTransferCallback& callback) {
+ ASSERT_FALSE(mock_outbound_data_.empty());
+ const std::vector<uint8_t>& bytes = mock_outbound_data_.front();
+ size_t length =
+ std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
+ ASSERT_EQ(bytes.size(), length);
+ for (size_t i = 0; i < length; ++i) {
+ EXPECT_EQ(bytes[i], buffer->data()[i]) << "Contents differ at index: "
+ << i;
+ }
+ mock_outbound_data_.pop();
+
+ ASSERT_FALSE(mock_outbound_packets_.empty());
+ std::vector<UsbDeviceHandle::IsochronousPacket> packets =
+ mock_outbound_packets_.front();
+ ASSERT_EQ(packets.size(), packet_lengths.size());
+ for (size_t i = 0; i < packets.size(); ++i) {
+ EXPECT_EQ(packets[i].length, packet_lengths[i])
+ << "Packet lengths differ at index: " << i;
+ }
+ mock_outbound_packets_.pop();
+
+ callback.Run(buffer, packets);
}
scoped_ptr<base::MessageLoop> message_loop_;
@@ -361,6 +436,10 @@ class USBDeviceImplTest : public testing::Test {
std::queue<std::vector<uint8_t>> mock_inbound_data_;
std::queue<std::vector<uint8_t>> mock_outbound_data_;
+ std::queue<std::vector<UsbDeviceHandle::IsochronousPacket>>
+ mock_inbound_packets_;
+ std::queue<std::vector<UsbDeviceHandle::IsochronousPacket>>
+ mock_outbound_packets_;
std::set<uint8_t> claimed_interfaces_;
@@ -758,56 +837,52 @@ TEST_F(USBDeviceImplTest, IsochronousTransfer) {
loop.Run();
}
- std::string outbound_packet_data = "aaaaaaaabbbbbbbbccccccccdddddddd";
- std::vector<uint8_t> fake_outbound_packets(outbound_packet_data.size());
- std::copy(outbound_packet_data.begin(), outbound_packet_data.end(),
- fake_outbound_packets.begin());
+ std::vector<UsbDeviceHandle::IsochronousPacket> fake_packets(4);
+ for (size_t i = 0; i < fake_packets.size(); ++i) {
+ fake_packets[i].length = 8;
+ fake_packets[i].transferred_length = 8;
+ fake_packets[i].status = USB_TRANSFER_COMPLETED;
+ }
+ std::vector<uint32_t> fake_packet_lengths(4, 8);
+
+ std::vector<uint32_t> expected_transferred_lengths(4, 8);
+
+ std::string outbound_data = "aaaaaaaabbbbbbbbccccccccdddddddd";
+ std::vector<uint8_t> fake_outbound_data(outbound_data.size());
+ std::copy(outbound_data.begin(), outbound_data.end(),
+ fake_outbound_data.begin());
- std::string inbound_packet_data = "ddddddddccccccccbbbbbbbbaaaaaaaa";
- std::vector<uint8_t> fake_inbound_packets(inbound_packet_data.size());
- std::copy(inbound_packet_data.begin(), inbound_packet_data.end(),
- fake_inbound_packets.begin());
+ std::string inbound_data = "ddddddddccccccccbbbbbbbbaaaaaaaa";
+ std::vector<uint8_t> fake_inbound_data(inbound_data.size());
+ std::copy(inbound_data.begin(), inbound_data.end(),
+ fake_inbound_data.begin());
AddMockConfig(ConfigBuilder(1).AddInterface(7, 0, 1, 2, 3));
- AddMockOutboundData(fake_outbound_packets);
- AddMockInboundData(fake_inbound_packets);
+ AddMockOutboundPackets(fake_outbound_data, fake_packets);
+ AddMockInboundPackets(fake_inbound_data, fake_packets);
EXPECT_CALL(mock_handle(),
- IsochronousTransfer(USB_DIRECTION_OUTBOUND, 0x01, _,
- fake_outbound_packets.size(), 4, 8, 0, _));
+ IsochronousTransferOut(0x01, _, fake_packet_lengths, 0, _));
{
base::RunLoop loop;
- mojo::Array<mojo::Array<uint8_t>> packets =
- mojo::Array<mojo::Array<uint8_t>>::New(4);
- for (size_t i = 0; i < 4; ++i) {
- std::vector<uint8_t> bytes(8);
- std::copy(outbound_packet_data.begin() + i * 8,
- outbound_packet_data.begin() + i * 8 + 8, bytes.begin());
- packets[i].Swap(&bytes);
- }
device->IsochronousTransferOut(
- 1, std::move(packets), 0,
- base::Bind(&ExpectTransferStatusAndThen, TransferStatus::COMPLETED,
+ 1, mojo::Array<uint8_t>::From(fake_outbound_data),
+ mojo::Array<uint32_t>::From(fake_packet_lengths), 0,
+ base::Bind(&ExpectPacketsOutAndThen, expected_transferred_lengths,
loop.QuitClosure()));
loop.Run();
}
EXPECT_CALL(mock_handle(),
- IsochronousTransfer(USB_DIRECTION_INBOUND, 0x81, _,
- fake_inbound_packets.size(), 4, 8, 0, _));
+ IsochronousTransferIn(0x81, fake_packet_lengths, 0, _));
{
base::RunLoop loop;
- std::vector<std::vector<uint8_t>> packets(4);
- for (size_t i = 0; i < 4; ++i) {
- packets[i].resize(8);
- std::copy(inbound_packet_data.begin() + i * 8,
- inbound_packet_data.begin() + i * 8 + 8, packets[i].begin());
- }
device->IsochronousTransferIn(
- 1, 4, 8, 0, base::Bind(&ExpectPacketsAndThen, TransferStatus::COMPLETED,
- packets, loop.QuitClosure()));
+ 1, mojo::Array<uint32_t>::From(fake_packet_lengths), 0,
+ base::Bind(&ExpectPacketsInAndThen, fake_inbound_data,
+ expected_transferred_lengths, loop.QuitClosure()));
loop.Run();
}
diff --git a/device/devices_app/usb/public/interfaces/device.mojom b/device/devices_app/usb/public/interfaces/device.mojom
index 438aed1..25addb2 100644
--- a/device/devices_app/usb/public/interfaces/device.mojom
+++ b/device/devices_app/usb/public/interfaces/device.mojom
@@ -139,6 +139,12 @@ enum TransferStatus {
SHORT_PACKET,
};
+struct IsochronousPacket {
+ uint32 length;
+ uint32 transferred_length;
+ TransferStatus status;
+};
+
interface Device {
// Retrieve a DeviceInfo struct containing metadata about the device,
// including the set of all available device configurations.
@@ -238,22 +244,22 @@ interface Device {
// transfers can be initiated on the endpoint. The endpoint must be of type
// ISOCHRONOUS.
//
- // |packet_length| specifies the maximum expected number of bytes to receive
- // for each packet in this transfer. |num_packets| specifies the maximum total
- // number of packets to receive.
+ // |packet_lengths| specifies the maximum expected number of bytes to receive
+ // for each packet in this transfer.
//
// |timeout| specifies the request timeout in milliseconds. A timeout of 0
// indicates no timeout: the request will remain pending indefinitely until
// completed or otherwise terminated.
//
- // |packets| contains the set of packets received from the device, in order.
- // No received packet's size will exceed |packet_length|, and will be null
- // if |status| is neither COMPLETED, BABBLE, or SHORT_PACKET.
+ // |data| contains the data received from the device, if any. |packets|
+ // contains the status of each packet received from the device, in order. The
+ // length of the packet indicates its position in |data| while it's
+ // transferred length gives the amount of data actually received from the
+ // device.
IsochronousTransferIn(uint8 endpoint_number,
- uint32 num_packets,
- uint32 packet_length,
+ array<uint32> packet_lengths,
uint32 timeout)
- => (TransferStatus status, array<array<uint8>>? packets);
+ => (array<uint8>? data, array<IsochronousPacket> packets);
// Initiates an outbound isochronous transfer request on a specific endpoint.
// The interface to which |endpoint_number| belongs must be claimed, and the
@@ -261,14 +267,19 @@ interface Device {
// transfers can be initiated on the endpoint. The endpoint must be of type
// ISOCHRONOUS.
//
- // |packets| specifies the series of data packets to send to the device for
- // this transfer.
+ // |data| specifies the bytes to send to the device.
+ //
+ // |packet_lengths| specifies how |data| should be separated into packets when
+ // it is sent to the device.
//
// |timeout| specifies the request timeout in milliseconds. A timeout of 0
// indicates no timeout: the request will remain pending indefinitely until
// completed or otherwise terminated.
+
+ // |packets| contains the status of each packet sent to the device, in order.
IsochronousTransferOut(uint8 endpoint_number,
- array<array<uint8>> packets,
+ array<uint8> data,
+ array<uint32> packet_lengths,
uint32 timeout)
- => (TransferStatus status);
+ => (array<IsochronousPacket> packets);
};
diff --git a/device/devices_app/usb/type_converters.cc b/device/devices_app/usb/type_converters.cc
index e7b2ea6..709b0ca 100644
--- a/device/devices_app/usb/type_converters.cc
+++ b/device/devices_app/usb/type_converters.cc
@@ -267,4 +267,17 @@ TypeConverter<device::usb::DeviceInfoPtr, device::UsbDevice>::Convert(
return info;
}
+// static
+device::usb::IsochronousPacketPtr
+TypeConverter<device::usb::IsochronousPacketPtr,
+ device::UsbDeviceHandle::IsochronousPacket>::
+ Convert(const device::UsbDeviceHandle::IsochronousPacket& packet) {
+ device::usb::IsochronousPacketPtr info =
+ device::usb::IsochronousPacket::New();
+ info->length = packet.length;
+ info->transferred_length = packet.transferred_length;
+ info->status = mojo::ConvertTo<device::usb::TransferStatus>(packet.status);
+ return info;
+}
+
} // namespace mojo
diff --git a/device/devices_app/usb/type_converters.h b/device/devices_app/usb/type_converters.h
index 59c2f4c..15484ea 100644
--- a/device/devices_app/usb/type_converters.h
+++ b/device/devices_app/usb/type_converters.h
@@ -16,8 +16,9 @@
#include "mojo/public/cpp/bindings/array.h"
#include "mojo/public/cpp/bindings/type_converter.h"
-// Type converters to convert objects between internal device/usb data types and
-// public Mojo interface data types.
+// Type converters to translate between internal device/usb data types and
+// public Mojo interface data types. This must be included by any source file
+// that uses these conversions explicitly or implicitly.
namespace device {
class UsbDevice;
@@ -121,6 +122,13 @@ struct TypeConverter<device::usb::DeviceInfoPtr, device::UsbDevice> {
static device::usb::DeviceInfoPtr Convert(const device::UsbDevice& device);
};
+template <>
+struct TypeConverter<device::usb::IsochronousPacketPtr,
+ device::UsbDeviceHandle::IsochronousPacket> {
+ static device::usb::IsochronousPacketPtr Convert(
+ const device::UsbDeviceHandle::IsochronousPacket& packet);
+};
+
} // namespace mojo
#endif // DEVICE_DEVICES_APP_USB_TYPE_CONVERTERS_H_
diff --git a/device/usb/mock_usb_device_handle.h b/device/usb/mock_usb_device_handle.h
index 372068c..26e6190 100644
--- a/device/usb/mock_usb_device_handle.h
+++ b/device/usb/mock_usb_device_handle.h
@@ -5,11 +5,12 @@
#ifndef DEVICE_USB_MOCK_USB_DEVICE_HANDLE_H_
#define DEVICE_USB_MOCK_USB_DEVICE_HANDLE_H_
-#include "device/usb/usb_device_handle.h"
-
#include <stddef.h>
#include <stdint.h>
+#include <vector>
+
+#include "device/usb/usb_device_handle.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -17,7 +18,7 @@ namespace device {
class MockUsbDeviceHandle : public UsbDeviceHandle {
public:
- MockUsbDeviceHandle(UsbDevice* device);
+ explicit MockUsbDeviceHandle(UsbDevice* device);
scoped_refptr<UsbDevice> GetDevice() const override;
MOCK_METHOD0(Close, void());
@@ -44,15 +45,17 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
size_t length,
unsigned int timeout,
const TransferCallback& callback));
- MOCK_METHOD8(IsochronousTransfer,
- void(UsbEndpointDirection direction,
- uint8_t endpoint,
+ MOCK_METHOD4(IsochronousTransferIn,
+ void(uint8_t endpoint,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback));
+ MOCK_METHOD5(IsochronousTransferOut,
+ void(uint8_t endpoint,
scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
- const TransferCallback& callback));
+ const IsochronousTransferCallback& callback));
MOCK_METHOD6(GenericTransfer,
void(UsbEndpointDirection direction,
uint8_t endpoint,
diff --git a/device/usb/usb_device_handle.h b/device/usb/usb_device_handle.h
index a97dddc..b206cf1 100644
--- a/device/usb/usb_device_handle.h
+++ b/device/usb/usb_device_handle.h
@@ -40,9 +40,18 @@ enum UsbTransferStatus {
// UsbDeviceHandle class provides basic I/O related functionalities.
class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
public:
+ struct IsochronousPacket {
+ uint32_t length;
+ uint32_t transferred_length;
+ UsbTransferStatus status;
+ };
+
using ResultCallback = base::Callback<void(bool)>;
using TransferCallback = base::Callback<
void(UsbTransferStatus, scoped_refptr<net::IOBuffer>, size_t)>;
+ using IsochronousTransferCallback =
+ base::Callback<void(scoped_refptr<net::IOBuffer>,
+ const std::vector<IsochronousPacket>& packets)>;
enum TransferRequestType { STANDARD, CLASS, VENDOR, RESERVED };
enum TransferRecipient { DEVICE, INTERFACE, ENDPOINT, OTHER };
@@ -81,17 +90,21 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
unsigned int timeout,
const TransferCallback& callback) = 0;
- virtual void IsochronousTransfer(UsbEndpointDirection direction,
- uint8_t endpoint,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
- unsigned int timeout,
- const TransferCallback& callback) = 0;
+ virtual void IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) = 0;
+
+ virtual void IsochronousTransferOut(
+ uint8_t endpoint_number,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) = 0;
virtual void GenericTransfer(UsbEndpointDirection direction,
- uint8_t endpoint,
+ uint8_t endpoint_number,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
@@ -109,6 +122,7 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
virtual ~UsbDeviceHandle();
+ private:
DISALLOW_COPY_AND_ASSIGN(UsbDeviceHandle);
};
diff --git a/device/usb/usb_device_handle_impl.cc b/device/usb/usb_device_handle_impl.cc
index 338af16..fa4c085 100644
--- a/device/usb/usb_device_handle_impl.cc
+++ b/device/usb/usb_device_handle_impl.cc
@@ -5,6 +5,7 @@
#include "device/usb/usb_device_handle_impl.h"
#include <algorithm>
+#include <numeric>
#include <utility>
#include <vector>
@@ -120,6 +121,26 @@ static void RunTransferCallback(
}
}
+void ReportIsochronousTransferError(
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const UsbDeviceHandle::IsochronousTransferCallback& callback,
+ const std::vector<uint32_t> packet_lengths,
+ UsbTransferStatus status) {
+ std::vector<UsbDeviceHandle::IsochronousPacket> packets(
+ packet_lengths.size());
+ for (size_t i = 0; i < packet_lengths.size(); ++i) {
+ packets[i].length = packet_lengths[i];
+ packets[i].transferred_length = 0;
+ packets[i].status = status;
+ }
+ if (callback_task_runner->RunsTasksOnCurrentThread()) {
+ callback.Run(nullptr, packets);
+ } else {
+ callback_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, nullptr, packets));
+ }
+}
+
} // namespace
class UsbDeviceHandleImpl::InterfaceClaimer
@@ -191,11 +212,10 @@ class UsbDeviceHandleImpl::Transfer {
uint8_t endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
- scoped_refptr<base::TaskRunner> task_runner,
- const TransferCallback& callback);
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const IsochronousTransferCallback& callback);
~Transfer();
@@ -220,9 +240,16 @@ class UsbDeviceHandleImpl::Transfer {
size_t length,
scoped_refptr<base::TaskRunner> callback_task_runner,
const TransferCallback& callback);
+ Transfer(scoped_refptr<UsbDeviceHandleImpl> device_handle,
+ scoped_refptr<InterfaceClaimer> claimed_interface,
+ scoped_refptr<net::IOBuffer> buffer,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const IsochronousTransferCallback& callback);
static void LIBUSB_CALL PlatformCallback(PlatformUsbTransferHandle handle);
+ void IsochronousTransferComplete();
+
UsbTransferType transfer_type_;
scoped_refptr<UsbDeviceHandleImpl> device_handle_;
PlatformUsbTransferHandle platform_transfer_ = nullptr;
@@ -233,6 +260,7 @@ class UsbDeviceHandleImpl::Transfer {
scoped_refptr<base::SequencedTaskRunner> task_runner_;
scoped_refptr<base::TaskRunner> callback_task_runner_;
TransferCallback callback_;
+ IsochronousTransferCallback iso_callback_;
};
// static
@@ -289,11 +317,11 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer(
return nullptr;
}
- libusb_fill_bulk_transfer(
- transfer->platform_transfer_, device_handle->handle_, endpoint,
- reinterpret_cast<uint8_t*>(buffer->data()), static_cast<int>(length),
- &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(),
- timeout);
+ libusb_fill_bulk_transfer(transfer->platform_transfer_,
+ device_handle->handle_, endpoint,
+ reinterpret_cast<uint8_t*>(buffer->data()), length,
+ &UsbDeviceHandleImpl::Transfer::PlatformCallback,
+ transfer.get(), timeout);
return transfer;
}
@@ -320,7 +348,7 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer(
libusb_fill_interrupt_transfer(
transfer->platform_transfer_, device_handle->handle_, endpoint,
- reinterpret_cast<uint8_t*>(buffer->data()), static_cast<int>(length),
+ reinterpret_cast<uint8_t*>(buffer->data()), length,
&UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(),
timeout);
@@ -334,20 +362,16 @@ UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer(
uint8_t endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
scoped_refptr<base::TaskRunner> callback_task_runner,
- const TransferCallback& callback) {
- DCHECK(packets <= length && (packets * packet_length) <= length)
- << "transfer length is too small";
-
+ const IsochronousTransferCallback& callback) {
scoped_ptr<Transfer> transfer(new Transfer(
device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint),
- USB_TRANSFER_ISOCHRONOUS, buffer, length, callback_task_runner,
- callback));
+ buffer, callback_task_runner, callback));
- transfer->platform_transfer_ = libusb_alloc_transfer(packets);
+ int num_packets = static_cast<int>(packet_lengths.size());
+ transfer->platform_transfer_ = libusb_alloc_transfer(num_packets);
if (!transfer->platform_transfer_) {
USB_LOG(ERROR) << "Failed to allocate isochronous transfer.";
return nullptr;
@@ -356,8 +380,10 @@ UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer(
libusb_fill_iso_transfer(
transfer->platform_transfer_, device_handle->handle_, endpoint,
reinterpret_cast<uint8_t*>(buffer->data()), static_cast<int>(length),
- packets, &Transfer::PlatformCallback, transfer.get(), timeout);
- libusb_set_iso_packet_lengths(transfer->platform_transfer_, packet_length);
+ num_packets, &Transfer::PlatformCallback, transfer.get(), timeout);
+
+ for (size_t i = 0; i < packet_lengths.size(); ++i)
+ transfer->platform_transfer_->iso_packet_desc[i].length = packet_lengths[i];
return transfer;
}
@@ -380,6 +406,21 @@ UsbDeviceHandleImpl::Transfer::Transfer(
task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
+UsbDeviceHandleImpl::Transfer::Transfer(
+ scoped_refptr<UsbDeviceHandleImpl> device_handle,
+ scoped_refptr<InterfaceClaimer> claimed_interface,
+ scoped_refptr<net::IOBuffer> buffer,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const IsochronousTransferCallback& callback)
+ : transfer_type_(USB_TRANSFER_ISOCHRONOUS),
+ device_handle_(device_handle),
+ buffer_(buffer),
+ claimed_interface_(claimed_interface),
+ callback_task_runner_(callback_task_runner),
+ iso_callback_(callback) {
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
+}
+
UsbDeviceHandleImpl::Transfer::~Transfer() {
if (platform_transfer_) {
libusb_free_transfer(platform_transfer_);
@@ -431,46 +472,22 @@ void UsbDeviceHandleImpl::Transfer::ProcessCompletion() {
buffer_ = resized_buffer;
}
}
- break;
-
- case USB_TRANSFER_ISOCHRONOUS:
- // Isochronous replies might carry data in the different isoc packets even
- // if the transfer actual_data value is zero. Furthermore, not all of the
- // received packets might contain data, so we need to calculate how many
- // data bytes we are effectively providing and pack the results.
- if (actual_length == 0) {
- size_t packet_buffer_start = 0;
- for (int i = 0; i < platform_transfer_->num_iso_packets; ++i) {
- PlatformUsbIsoPacketDescriptor packet =
- &platform_transfer_->iso_packet_desc[i];
- if (packet->actual_length > 0) {
- // We don't need to copy as long as all packets until now provide
- // all the data the packet can hold.
- if (actual_length < packet_buffer_start) {
- CHECK(packet_buffer_start + packet->actual_length <= length_);
- memmove(buffer_->data() + actual_length,
- buffer_->data() + packet_buffer_start,
- packet->actual_length);
- }
- actual_length += packet->actual_length;
- }
-
- packet_buffer_start += packet->length;
- }
- }
- break;
+ // Fall through!
case USB_TRANSFER_BULK:
case USB_TRANSFER_INTERRUPT:
+ TransferComplete(ConvertTransferStatus(platform_transfer_->status),
+ actual_length);
+ break;
+
+ case USB_TRANSFER_ISOCHRONOUS:
+ IsochronousTransferComplete();
break;
default:
NOTREACHED() << "Invalid usb transfer type";
break;
}
-
- TransferComplete(ConvertTransferStatus(platform_transfer_->status),
- actual_length);
}
/* static */
@@ -484,11 +501,37 @@ void LIBUSB_CALL UsbDeviceHandleImpl::Transfer::PlatformCallback(
void UsbDeviceHandleImpl::Transfer::TransferComplete(UsbTransferStatus status,
size_t bytes_transferred) {
+ base::Closure closure;
+ if (transfer_type_ == USB_TRANSFER_ISOCHRONOUS) {
+ DCHECK_NE(LIBUSB_TRANSFER_COMPLETED, platform_transfer_->status);
+ std::vector<IsochronousPacket> packets(platform_transfer_->num_iso_packets);
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = platform_transfer_->iso_packet_desc[i].length;
+ packets[i].transferred_length = 0;
+ packets[i].status = status;
+ }
+ closure = base::Bind(iso_callback_, buffer_, packets);
+ } else {
+ closure = base::Bind(callback_, status, buffer_, bytes_transferred);
+ }
task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&UsbDeviceHandleImpl::TransferComplete, device_handle_,
- base::Unretained(this),
- base::Bind(callback_, status, buffer_, bytes_transferred)));
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::TransferComplete,
+ device_handle_, base::Unretained(this), closure));
+}
+
+void UsbDeviceHandleImpl::Transfer::IsochronousTransferComplete() {
+ std::vector<IsochronousPacket> packets(platform_transfer_->num_iso_packets);
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = platform_transfer_->iso_packet_desc[i].length;
+ packets[i].transferred_length =
+ platform_transfer_->iso_packet_desc[i].actual_length;
+ packets[i].status =
+ ConvertTransferStatus(platform_transfer_->iso_packet_desc[i].status);
+ }
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&UsbDeviceHandleImpl::TransferComplete,
+ device_handle_, base::Unretained(this),
+ base::Bind(iso_callback_, buffer_, packets)));
}
scoped_refptr<UsbDevice> UsbDeviceHandleImpl::GetDevice() const {
@@ -632,26 +675,42 @@ void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction,
}
}
-void UsbDeviceHandleImpl::IsochronousTransfer(
- UsbEndpointDirection direction,
+void UsbDeviceHandleImpl::IsochronousTransferIn(
+ uint8_t endpoint_number,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) {
+ uint8_t endpoint_address =
+ ConvertTransferDirection(USB_DIRECTION_INBOUND) | endpoint_number;
+ if (task_runner_->BelongsToCurrentThread()) {
+ IsochronousTransferInInternal(endpoint_address, packet_lengths, timeout,
+ task_runner_, callback);
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandleImpl::IsochronousTransferInInternal, this,
+ endpoint_address, packet_lengths, timeout,
+ base::ThreadTaskRunnerHandle::Get(), callback));
+ }
+}
+
+void UsbDeviceHandleImpl::IsochronousTransferOut(
uint8_t endpoint_number,
scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
- const TransferCallback& callback) {
+ const IsochronousTransferCallback& callback) {
uint8_t endpoint_address =
- ConvertTransferDirection(direction) | endpoint_number;
+ ConvertTransferDirection(USB_DIRECTION_OUTBOUND) | endpoint_number;
if (task_runner_->BelongsToCurrentThread()) {
- IsochronousTransferInternal(endpoint_address, buffer, length, packets,
- packet_length, timeout, task_runner_, callback);
+ IsochronousTransferOutInternal(endpoint_address, buffer, packet_lengths,
+ timeout, task_runner_, callback);
} else {
task_runner_->PostTask(
FROM_HERE,
- base::Bind(&UsbDeviceHandleImpl::IsochronousTransferInternal, this,
- endpoint_address, buffer, length, packets, packet_length,
- timeout, base::ThreadTaskRunnerHandle::Get(), callback));
+ base::Bind(&UsbDeviceHandleImpl::IsochronousTransferOutInternal, this,
+ endpoint_address, buffer, packet_lengths, timeout,
+ base::ThreadTaskRunnerHandle::Get(), callback));
}
}
@@ -886,33 +945,50 @@ void UsbDeviceHandleImpl::ControlTransferInternal(
SubmitTransfer(std::move(transfer));
}
-void UsbDeviceHandleImpl::IsochronousTransferInternal(
+void UsbDeviceHandleImpl::IsochronousTransferInInternal(
uint8_t endpoint_address,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
scoped_refptr<base::TaskRunner> callback_task_runner,
- const TransferCallback& callback) {
+ const IsochronousTransferCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
if (!device_) {
- RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_DISCONNECT,
- buffer, 0);
+ ReportIsochronousTransferError(callback_task_runner, callback,
+ packet_lengths, USB_TRANSFER_DISCONNECT);
return;
}
- if (!base::IsValueInRangeForNumericType<int>(length)) {
- USB_LOG(USER) << "Transfer too long.";
- RunTransferCallback(callback_task_runner, callback, USB_TRANSFER_ERROR,
- buffer, 0);
+ size_t length =
+ std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(length));
+ scoped_ptr<Transfer> transfer = Transfer::CreateIsochronousTransfer(
+ this, endpoint_address, buffer, length, packet_lengths, timeout,
+ callback_task_runner, callback);
+
+ SubmitTransfer(std::move(transfer));
+}
+
+void UsbDeviceHandleImpl::IsochronousTransferOutInternal(
+ uint8_t endpoint_address,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const IsochronousTransferCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (!device_) {
+ ReportIsochronousTransferError(callback_task_runner, callback,
+ packet_lengths, USB_TRANSFER_DISCONNECT);
return;
}
+ size_t length =
+ std::accumulate(packet_lengths.begin(), packet_lengths.end(), 0u);
scoped_ptr<Transfer> transfer = Transfer::CreateIsochronousTransfer(
- this, endpoint_address, buffer, static_cast<int>(length), packets,
- packet_length, timeout, callback_task_runner, callback);
+ this, endpoint_address, buffer, length, packet_lengths, timeout,
+ callback_task_runner, callback);
SubmitTransfer(std::move(transfer));
}
diff --git a/device/usb/usb_device_handle_impl.h b/device/usb/usb_device_handle_impl.h
index 7d10e42..ce2daff 100644
--- a/device/usb/usb_device_handle_impl.h
+++ b/device/usb/usb_device_handle_impl.h
@@ -71,14 +71,18 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
unsigned int timeout,
const TransferCallback& callback) override;
- void IsochronousTransfer(UsbEndpointDirection direction,
- uint8_t endpoint_number,
- scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
- unsigned int timeout,
- const TransferCallback& callback) override;
+ void IsochronousTransferIn(
+ uint8_t endpoint,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override;
+
+ void IsochronousTransferOut(
+ uint8_t endpoint,
+ scoped_refptr<net::IOBuffer> buffer,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ const IsochronousTransferCallback& callback) override;
void GenericTransfer(UsbEndpointDirection direction,
uint8_t endpoint_number,
@@ -149,15 +153,20 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
scoped_refptr<base::TaskRunner> callback_task_runner,
const TransferCallback& callback);
- void IsochronousTransferInternal(
+ void IsochronousTransferInInternal(
+ uint8_t endpoint_address,
+ const std::vector<uint32_t>& packet_lengths,
+ unsigned int timeout,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const IsochronousTransferCallback& callback);
+
+ void IsochronousTransferOutInternal(
uint8_t endpoint_address,
scoped_refptr<net::IOBuffer> buffer,
- size_t length,
- unsigned int packets,
- unsigned int packet_length,
+ const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
scoped_refptr<base::TaskRunner> callback_task_runner,
- const TransferCallback& callback);
+ const IsochronousTransferCallback& callback);
void GenericTransferInternal(
uint8_t endpoint_address,
diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc
index 16710ddab..3035239 100644
--- a/extensions/browser/api/usb/usb_api.cc
+++ b/extensions/browser/api/usb/usb_api.cc
@@ -4,6 +4,8 @@
#include "extensions/browser/api/usb/usb_api.h"
+#include <algorithm>
+#include <numeric>
#include <string>
#include <utility>
#include <vector>
@@ -468,7 +470,8 @@ void UsbTransferFunction::OnCompleted(UsbTransferStatus status,
} else {
scoped_ptr<base::ListValue> error_args(new base::ListValue());
error_args->Append(std::move(transfer_info));
- // Returning arguments with an error is wrong but we're stuck with it.
+ // Using ErrorWithArguments is discouraged but required to provide the
+ // detailed transfer info as the transfer may have partially succeeded.
Respond(ErrorWithArguments(std::move(error_args),
ConvertTransferStatusToApi(status)));
}
@@ -1134,48 +1137,89 @@ ExtensionFunction::ResponseAction UsbIsochronousTransferFunction::Run() {
size_t size = 0;
UsbEndpointDirection direction = device::USB_DIRECTION_INBOUND;
- if (!ConvertDirectionFromApi(generic_transfer.direction, &direction)) {
+ if (!ConvertDirectionFromApi(generic_transfer.direction, &direction))
return RespondNow(Error(kErrorConvertDirection));
- }
- if (!GetTransferSize(generic_transfer, &size)) {
+ if (!GetTransferSize(generic_transfer, &size))
return RespondNow(Error(kErrorInvalidTransferLength));
- }
- if (transfer.packets < 0 || transfer.packets >= kMaxPackets) {
+ if (transfer.packets < 0 || transfer.packets >= kMaxPackets)
return RespondNow(Error(kErrorInvalidNumberOfPackets));
- }
+ size_t packets = transfer.packets;
- unsigned int packets = transfer.packets;
if (transfer.packet_length < 0 ||
transfer.packet_length >= kMaxPacketLength) {
return RespondNow(Error(kErrorInvalidPacketLength));
}
- unsigned int packet_length = transfer.packet_length;
- const uint64_t total_length = packets * packet_length;
- if (packets > size || total_length > size) {
+ size_t total_length = packets * transfer.packet_length;
+ if (packets > size || total_length > size)
return RespondNow(Error(kErrorTransferLength));
- }
-
- scoped_refptr<net::IOBuffer> buffer =
- CreateBufferForTransfer(generic_transfer, direction, size);
- if (!buffer.get()) {
- return RespondNow(Error(kErrorMalformedParameters));
- }
+ std::vector<uint32_t> packet_lengths(packets, transfer.packet_length);
int timeout = generic_transfer.timeout ? *generic_transfer.timeout : 0;
- if (timeout < 0) {
+ if (timeout < 0)
return RespondNow(Error(kErrorInvalidTimeout));
- }
- device_handle->IsochronousTransfer(
- direction, generic_transfer.endpoint, buffer.get(), size, packets,
- packet_length, timeout,
- base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this));
+ if (direction == device::USB_DIRECTION_INBOUND) {
+ device_handle->IsochronousTransferIn(
+ generic_transfer.endpoint, packet_lengths, timeout,
+ base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this));
+ } else {
+ scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(
+ generic_transfer, direction, transfer.packets * transfer.packet_length);
+ if (!buffer.get())
+ return RespondNow(Error(kErrorMalformedParameters));
+
+ device_handle->IsochronousTransferOut(
+ generic_transfer.endpoint, buffer.get(), packet_lengths, timeout,
+ base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this));
+ }
return RespondLater();
}
+void UsbIsochronousTransferFunction::OnCompleted(
+ scoped_refptr<net::IOBuffer> data,
+ const std::vector<UsbDeviceHandle::IsochronousPacket>& packets) {
+ size_t length = std::accumulate(
+ packets.begin(), packets.end(), 0,
+ [](const size_t& a, const UsbDeviceHandle::IsochronousPacket& packet) {
+ return a + packet.transferred_length;
+ });
+ scoped_ptr<char[]> buffer(new char[length]);
+
+ UsbTransferStatus status = device::USB_TRANSFER_COMPLETED;
+ size_t buffer_offset = 0;
+ size_t data_offset = 0;
+ for (const auto& packet : packets) {
+ // Capture the error status of the first unsuccessful packet.
+ if (status == device::USB_TRANSFER_COMPLETED &&
+ packet.status != device::USB_TRANSFER_COMPLETED) {
+ status = packet.status;
+ }
+
+ memcpy(&buffer[buffer_offset], data->data() + data_offset,
+ packet.transferred_length);
+ buffer_offset += packet.transferred_length;
+ data_offset += packet.length;
+ }
+
+ scoped_ptr<base::DictionaryValue> transfer_info(new base::DictionaryValue());
+ transfer_info->SetInteger(kResultCodeKey, status);
+ transfer_info->Set(kDataKey,
+ new base::BinaryValue(std::move(buffer), length));
+ if (status == device::USB_TRANSFER_COMPLETED) {
+ Respond(OneArgument(std::move(transfer_info)));
+ } else {
+ scoped_ptr<base::ListValue> error_args(new base::ListValue());
+ error_args->Append(std::move(transfer_info));
+ // Using ErrorWithArguments is discouraged but required to provide the
+ // detailed transfer info as the transfer may have partially succeeded.
+ Respond(ErrorWithArguments(std::move(error_args),
+ ConvertTransferStatusToApi(status)));
+ }
+}
+
UsbResetDeviceFunction::UsbResetDeviceFunction() {
}
@@ -1210,7 +1254,8 @@ void UsbResetDeviceFunction::OnComplete(bool success) {
scoped_ptr<base::ListValue> error_args(new base::ListValue());
error_args->AppendBoolean(false);
- // Returning arguments with an error is wrong but we're stuck with it.
+ // Using ErrorWithArguments is discouraged but required to maintain
+ // compatibility with existing applications.
Respond(ErrorWithArguments(std::move(error_args), kErrorResetDevice));
}
}
diff --git a/extensions/browser/api/usb/usb_api.h b/extensions/browser/api/usb/usb_api.h
index 5bea383..3d1dec1 100644
--- a/extensions/browser/api/usb/usb_api.h
+++ b/extensions/browser/api/usb/usb_api.h
@@ -245,7 +245,7 @@ class UsbClaimInterfaceFunction : public UsbConnectionFunction {
UsbClaimInterfaceFunction();
- protected:
+ private:
~UsbClaimInterfaceFunction() override;
// ExtensionFunction:
@@ -334,7 +334,7 @@ class UsbInterruptTransferFunction : public UsbTransferFunction {
DISALLOW_COPY_AND_ASSIGN(UsbInterruptTransferFunction);
};
-class UsbIsochronousTransferFunction : public UsbTransferFunction {
+class UsbIsochronousTransferFunction : public UsbConnectionFunction {
public:
DECLARE_EXTENSION_FUNCTION("usb.isochronousTransfer", USB_ISOCHRONOUSTRANSFER)
@@ -346,6 +346,10 @@ class UsbIsochronousTransferFunction : public UsbTransferFunction {
// ExtensionFunction:
ResponseAction Run() override;
+ void OnCompleted(
+ scoped_refptr<net::IOBuffer> data,
+ const std::vector<device::UsbDeviceHandle::IsochronousPacket>& packets);
+
DISALLOW_COPY_AND_ASSIGN(UsbIsochronousTransferFunction);
};
diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc
index cc6b47b..7d2e2d9 100644
--- a/extensions/browser/api/usb/usb_apitest.cc
+++ b/extensions/browser/api/usb/usb_apitest.cc
@@ -4,6 +4,8 @@
#include <stddef.h>
+#include <numeric>
+
#include "chrome/browser/extensions/extension_apitest.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
@@ -48,6 +50,43 @@ ACTION_TEMPLATE(InvokeUsbTransferCallback,
::std::tr1::get<k>(args).Run(p1, io_buffer, 1);
}
+ACTION_P2(InvokeUsbIsochronousTransferOutCallback,
+ transferred_length,
+ success_packets) {
+ std::vector<UsbDeviceHandle::IsochronousPacket> packets(arg2.size());
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = arg2[i];
+ if (i < success_packets) {
+ packets[i].transferred_length = transferred_length;
+ packets[i].status = device::USB_TRANSFER_COMPLETED;
+ } else {
+ packets[i].transferred_length = 0;
+ packets[i].status = device::USB_TRANSFER_ERROR;
+ }
+ }
+ arg4.Run(arg1, packets);
+}
+
+ACTION_P2(InvokeUsbIsochronousTransferInCallback,
+ transferred_length,
+ success_packets) {
+ size_t total_length = std::accumulate(arg1.begin(), arg1.end(), 0u);
+ net::IOBuffer* io_buffer = new net::IOBuffer(total_length);
+ std::vector<UsbDeviceHandle::IsochronousPacket> packets(arg1.size());
+ for (size_t i = 0; i < packets.size(); ++i) {
+ packets[i].length = arg1[i];
+ packets[i].transferred_length = transferred_length;
+ if (i < success_packets) {
+ packets[i].transferred_length = transferred_length;
+ packets[i].status = device::USB_TRANSFER_COMPLETED;
+ } else {
+ packets[i].transferred_length = 0;
+ packets[i].status = device::USB_TRANSFER_ERROR;
+ }
+ }
+ arg3.Run(io_buffer, packets);
+}
+
class TestDevicePermissionsPrompt
: public DevicePermissionsPrompt,
public DevicePermissionsPrompt::Prompt::Observer {
@@ -165,10 +204,8 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) {
EXPECT_CALL(*mock_device_handle_.get(),
GenericTransfer(device::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED));
- EXPECT_CALL(
- *mock_device_handle_.get(),
- IsochronousTransfer(device::USB_DIRECTION_OUTBOUND, 3, _, 1, 1, 1, _, _))
- .WillOnce(InvokeUsbTransferCallback<7>(device::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(*mock_device_handle_.get(), IsochronousTransferOut(3, _, _, _, _))
+ .WillOnce(InvokeUsbIsochronousTransferOutCallback(1, 1u));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_event"));
}
@@ -181,10 +218,14 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) {
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) {
- EXPECT_CALL(*mock_device_handle_.get(), GenericTransfer(_, _, _, _, _, _))
+ EXPECT_CALL(*mock_device_handle_.get(),
+ GenericTransfer(device::USB_DIRECTION_OUTBOUND, 1, _, _, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_ERROR))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_TIMEOUT));
+ EXPECT_CALL(*mock_device_handle_.get(), IsochronousTransferIn(2, _, _, _))
+ .WillOnce(InvokeUsbIsochronousTransferInCallback(8, 10u))
+ .WillOnce(InvokeUsbIsochronousTransferInCallback(8, 5u));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_failure"));
}
diff --git a/extensions/test/data/api_test/usb/transfer_event/test.js b/extensions/test/data/api_test/usb/transfer_event/test.js
index 294864a..9d065f1 100644
--- a/extensions/test/data/api_test/usb/transfer_event/test.js
+++ b/extensions/test/data/api_test/usb/transfer_event/test.js
@@ -18,6 +18,7 @@ var tests = [
transfer.data = new ArrayBuffer(1);
usb.controlTransfer(device, transfer, function (result) {
+ chrome.test.assertNoLastError();
chrome.test.succeed();
});
});
@@ -31,6 +32,7 @@ var tests = [
transfer.data = new ArrayBuffer(1);
usb.bulkTransfer(device, transfer, function (result) {
+ chrome.test.assertNoLastError();
chrome.test.succeed();
});
});
@@ -44,6 +46,7 @@ var tests = [
transfer.data = new ArrayBuffer(1);
usb.interruptTransfer(device, transfer, function (result) {
+ chrome.test.assertNoLastError();
chrome.test.succeed();
});
});
@@ -62,6 +65,7 @@ var tests = [
isoTransfer.packetLength = 1;
usb.isochronousTransfer(device, isoTransfer, function (result) {
+ chrome.test.assertNoLastError();
chrome.test.succeed();
});
});
diff --git a/extensions/test/data/api_test/usb/transfer_failure/test.js b/extensions/test/data/api_test/usb/transfer_failure/test.js
index 09c5375..9348c18 100644
--- a/extensions/test/data/api_test/usb/transfer_failure/test.js
+++ b/extensions/test/data/api_test/usb/transfer_failure/test.js
@@ -25,10 +25,42 @@ function createErrorTest(resultCode, errorMessage) {
};
}
+function createIsochronousErrorTest(resultCode, errorMessage) {
+ return function() {
+ usb.findDevices({vendorId: 0, productId: 0}, function(devices) {
+ var device = devices[0];
+ var transfer = {
+ 'transferInfo': {
+ 'direction': "in",
+ 'endpoint': 2,
+ 'length': 160
+ },
+ 'packets': 10,
+ 'packetLength': 16
+ };
+ usb.isochronousTransfer(device, transfer, function (result) {
+ if (errorMessage) {
+ chrome.test.assertLastError(errorMessage);
+ // Device responds with only 8-byte packets and the second half fail.
+ chrome.test.assertTrue(result.data.byteLength == 40);
+ } else {
+ chrome.test.assertNoLastError();
+ // Device responds with a full set of 10 8-byte packets.
+ chrome.test.assertTrue(result.data.byteLength == 80);
+ }
+ chrome.test.assertTrue(resultCode == result.resultCode);
+ chrome.test.succeed();
+ });
+ });
+ };
+}
+
var tests = [
createErrorTest(0, undefined),
createErrorTest(1, "Transfer failed."),
createErrorTest(2, "Transfer timed out."),
+ createIsochronousErrorTest(0, undefined),
+ createIsochronousErrorTest(1, "Transfer failed."),
];
chrome.test.runTests(tests);