Index: extensions/browser/api/usb/usb_api.cc |
diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc |
index 16710ddabbba41f9b003f5c6fed7d5b4cd442720..303523964fe927b17fff7499a5e2ef08532c86a5 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)); |
} |
} |