// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/device/usb/usb_device_handle_win.h"
#include <windows.h> // Must be in front of other Windows header files.
#include <usbioctl.h>
#include <usbspec.h>
#include <winioctl.h>
#include <winusb.h>
#include <memory>
#include <numeric>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted_memory.h"
#include "base/not_fatal_until.h"
#include "base/ranges/algorithm.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/object_watcher.h"
#include "components/device_event_log/device_event_log.h"
#include "services/device/public/cpp/usb/usb_utils.h"
#include "services/device/usb/usb_context.h"
#include "services/device/usb/usb_descriptors.h"
#include "services/device/usb/usb_device_win.h"
#include "services/device/usb/usb_service.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
namespace device {
using mojom::UsbControlTransferRecipient;
using mojom::UsbControlTransferType;
using mojom::UsbTransferDirection;
using mojom::UsbTransferStatus;
namespace {
const std::wstring_view kWinUsbDriverName = L"winusb";
uint8_t BuildRequestFlags(UsbTransferDirection direction,
UsbControlTransferType request_type,
UsbControlTransferRecipient recipient) {
uint8_t flags = 0;
switch (direction) {
case UsbTransferDirection::OUTBOUND:
flags |= BMREQUEST_HOST_TO_DEVICE << 7;
break;
case UsbTransferDirection::INBOUND:
flags |= BMREQUEST_DEVICE_TO_HOST << 7;
break;
}
switch (request_type) {
case UsbControlTransferType::STANDARD:
flags |= BMREQUEST_STANDARD << 5;
break;
case UsbControlTransferType::CLASS:
flags |= BMREQUEST_CLASS << 5;
break;
case UsbControlTransferType::VENDOR:
flags |= BMREQUEST_VENDOR << 5;
break;
case UsbControlTransferType::RESERVED:
flags |= 4 << 5; // Not defined by usbspec.h.
break;
}
switch (recipient) {
case UsbControlTransferRecipient::DEVICE:
flags |= BMREQUEST_TO_DEVICE;
break;
case UsbControlTransferRecipient::INTERFACE:
flags |= BMREQUEST_TO_INTERFACE;
break;
case UsbControlTransferRecipient::ENDPOINT:
flags |= BMREQUEST_TO_ENDPOINT;
break;
case UsbControlTransferRecipient::OTHER:
flags |= BMREQUEST_TO_OTHER;
break;
}
return flags;
}
std::pair<DWORD, DWORD> DeviceIoControlBlocking(HANDLE handle,
DWORD control_code,
void* buffer,
DWORD buffer_size) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
DWORD bytes_transferred;
if (!DeviceIoControl(handle, control_code, buffer, buffer_size, buffer,
buffer_size, &bytes_transferred, nullptr)) {
return {GetLastError(), bytes_transferred};
}
return {ERROR_SUCCESS, bytes_transferred};
}
bool ResetPipeBlocking(WINUSB_INTERFACE_HANDLE handle, UCHAR pipeId) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::WILL_BLOCK);
if (!WinUsb_ResetPipe(handle, pipeId)) {
USB_PLOG(DEBUG) << "Failed to reset pipe " << int{pipeId};
return false;
}
return true;
}
bool SetCurrentAlternateSettingBlocking(WINUSB_INTERFACE_HANDLE handle,
UCHAR settingNumber) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::WILL_BLOCK);
if (!WinUsb_SetCurrentAlternateSetting(handle, settingNumber)) {
USB_PLOG(DEBUG) << "Failed to set alternate setting " << int{settingNumber};
return false;
}
return true;
}
} // namespace
// Encapsulates waiting for the completion of an overlapped event.
class UsbDeviceHandleWin::Request : public base::win::ObjectWatcher::Delegate {
public:
Request(WINUSB_INTERFACE_HANDLE handle, int interface_number)
: handle_(handle),
interface_number_(interface_number),
event_(CreateEvent(nullptr, false, false, nullptr)) {
memset(&overlapped_, 0, sizeof(overlapped_));
overlapped_.hEvent = event_.Get();
}
Request(const Request&) = delete;
Request& operator=(const Request&) = delete;
~Request() override = default;
// Starts watching for completion of the overlapped event.
void MaybeStartWatching(
BOOL success,
DWORD last_error,
base::OnceCallback<void(Request*, DWORD, size_t)> callback) {
callback_ = std::move(callback);
if (success) {
OnObjectSignaled(event_.Get());
} else {
if (last_error == ERROR_IO_PENDING)
watcher_.StartWatchingOnce(event_.Get(), this);
else
std::move(callback_).Run(this, last_error, 0);
}
}
void Abort() {
watcher_.StopWatching();
std::move(callback_).Run(this, ERROR_REQUEST_ABORTED, 0);
}
OVERLAPPED* overlapped() { return &overlapped_; }
int interface_number() const { return interface_number_; }
// base::win::ObjectWatcher::Delegate
void OnObjectSignaled(HANDLE object) override {
DCHECK_EQ(object, event_.Get());
DWORD size;
BOOL result =
WinUsb_GetOverlappedResult(handle_, &overlapped_, &size, true);
DWORD last_error = GetLastError();
if (result)
std::move(callback_).Run(this, ERROR_SUCCESS, size);
else
std::move(callback_).Run(this, last_error, 0);
}
private:
WINUSB_INTERFACE_HANDLE handle_;
int interface_number_;
OVERLAPPED overlapped_;
base::win::ScopedHandle event_;
base::win::ObjectWatcher watcher_;
base::OnceCallback<void(Request*, DWORD, size_t)> callback_;
};
UsbDeviceHandleWin::Interface::Interface() = default;
UsbDeviceHandleWin::Interface::~Interface() = default;
scoped_refptr<UsbDevice> UsbDeviceHandleWin::GetDevice() const {
return device_;
}
void UsbDeviceHandleWin::Close() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_)
return;
device_->HandleClosed(this);
if (hub_handle_.IsValid()) {
// Pending I/O operations on |hub_handle_| have been posted to
// |blocking_task_runner_|. Transfer ownership of the handle to a task on
// this runner which will close it on completion. This is guaranteed to run
// after any queued operations have completed.
blocking_task_runner_->PostTask(
FROM_HERE, base::DoNothingWithBoundArgs(std::move(hub_handle_)));
}
for (auto& map_entry : interfaces_) {
Interface* interface = &map_entry.second;
if (interface->function_handle.IsValid())
CancelIo(interface->function_handle.Get());
if (interface->claimed) {
interface->claimed = false;
ReleaseInterfaceReference(interface);
}
}
// Aborting requests may run or destroy callbacks holding the last reference
// to this object so hold a reference for the rest of this method.
scoped_refptr<UsbDeviceHandleWin> self(this);
// Avoid using an iterator here because Abort() will remove the entry from
// |requests_|.
while (!requests_.empty())
requests_.front()->Abort();
device_ = nullptr;
}
void UsbDeviceHandleWin::SetConfiguration(int configuration_value,
ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Setting device configuration is not supported on Windows.
task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(callback), false));
}
void UsbDeviceHandleWin::ClaimInterface(int interface_number,
ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
auto interface_it = interfaces_.find(interface_number);
if (interface_it == interfaces_.end()) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
Interface* interface = &interface_it->second;
OpenInterfaceHandle(
interface,
base::BindOnce(&UsbDeviceHandleWin::OnInterfaceClaimed,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void UsbDeviceHandleWin::ReleaseInterface(int interface_number,
ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
auto interface_it = interfaces_.find(interface_number);
if (interface_it == interfaces_.end()) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
Interface* interface = &interface_it->second;
if (!interface->claimed) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
interface->claimed = false;
ReleaseInterfaceReference(interface);
task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(callback), true));
}
void UsbDeviceHandleWin::SetInterfaceAlternateSetting(int interface_number,
int alternate_setting,
ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
auto interface_it = interfaces_.find(interface_number);
if (interface_it == interfaces_.end()) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
Interface& interface = interface_it->second;
if (!interface.claimed) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
bool found_alternate = false;
if (interface.info) {
for (const auto& alternate : interface.info->alternates) {
if (alternate->alternate_setting == alternate_setting) {
found_alternate = true;
break;
}
}
}
if (!found_alternate) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
// Prevent |interface.handle| from being released while the blocking call
// is in progress.
DCHECK(interface.handle.IsValid());
interface.reference_count++;
// Use a strong reference to |this| rather than a weak pointer to prevent
// |interface.handle| from being freed because |this| was destroyed.
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&SetCurrentAlternateSettingBlocking,
interface.handle.Get(), alternate_setting),
base::BindOnce(&UsbDeviceHandleWin::OnSetAlternateInterfaceSetting, this,
interface_number, alternate_setting, std::move(callback)));
}
void UsbDeviceHandleWin::ResetDevice(ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Resetting the device is not supported on Windows.
task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(callback), false));
}
void UsbDeviceHandleWin::ClearHalt(mojom::UsbTransferDirection direction,
uint8_t endpoint_number,
ResultCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
uint8_t endpoint_address =
ConvertEndpointNumberToAddress(endpoint_number, direction);
auto endpoint_it = endpoints_.find(endpoint_address);
if (endpoint_it == endpoints_.end()) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
auto interface_it =
interfaces_.find(endpoint_it->second.interface->interface_number);
CHECK(interface_it != interfaces_.end(), base::NotFatalUntil::M130);
Interface& interface = interface_it->second;
if (!interface.claimed) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
// Prevent |interface.handle| from being released while the blocking call
// is in progress.
DCHECK(interface.handle.IsValid());
interface.reference_count++;
// Use a strong reference to |this| rather than a weak pointer to prevent
// |interface.handle| from being freed because |this| was destroyed.
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ResetPipeBlocking, interface.handle.Get(),
endpoint_address),
base::BindOnce(&UsbDeviceHandleWin::OnClearHalt, this,
interface.interface_number, std::move(callback)));
}
void UsbDeviceHandleWin::ControlTransfer(
UsbTransferDirection direction,
UsbControlTransferType request_type,
UsbControlTransferRecipient recipient,
uint8_t request,
uint16_t value,
uint16_t index,
scoped_refptr<base::RefCountedBytes> buffer,
unsigned int timeout,
TransferCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!device_) {
task_runner_->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
UsbTransferStatus::DISCONNECT, nullptr, 0));
return;
}
if (hub_handle_.IsValid()) {
if (direction == UsbTransferDirection::INBOUND &&
request_type == UsbControlTransferType::STANDARD &&
recipient == UsbControlTransferRecipient::DEVICE &&
request == USB_REQUEST_GET_DESCRIPTOR) {
if ((value >> 8) == USB_DEVICE_DESCRIPTOR_TYPE) {
auto node_connection_info =
std::make_unique<USB_NODE_CONNECTION_INFORMATION_EX>();
node_connection_info->ConnectionIndex = device_->port_number();
auto task_callback =
base::BindOnce(&DeviceIoControlBlocking, hub_handle_.Get(),
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
node_connection_info.get(),
sizeof(USB_NODE_CONNECTION_INFORMATION_EX));
auto reply_callback =
base::BindOnce(&UsbDeviceHandleWin::GotNodeConnectionInformation,
weak_factory_.GetWeakPtr(), std::move(callback),
std::move(node_connection_info), buffer);
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, std::move(task_callback), std::move(reply_callback));
return;
} else if (((value >> 8) == USB_CONFIGURATION_DESCRIPTOR_TYPE) ||
((value >> 8) == USB_STRING_DESCRIPTOR_TYPE) ||
((value >> 8) == USB_BOS_DESCRIPTOR_TYPE)) {
size_t size = sizeof(USB_DESCRIPTOR_REQUEST) + buffer->size();
auto request_buffer = base::MakeRefCounted<base::RefCountedBytes>(size);
USB_DESCRIPTOR_REQUEST descriptor_request;
descriptor_request.ConnectionIndex = device_->port_number();
descriptor_request.SetupPacket.bmRequest = BMREQUEST_DEVICE_TO_HOST;
descriptor_request.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
descriptor_request.SetupPacket.wValue = value;
descriptor_request.SetupPacket.wIndex = index;
descriptor_request.SetupPacket.wLength = buffer->size();
base::span(request_buffer->as_vector())
.first<sizeof(USB_DESCRIPTOR_REQUEST)>()
.copy_from(base::byte_span_from_ref(descriptor_request));
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&DeviceIoControlBlocking, hub_handle_.Get(),
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
request_buffer->as_vector().data(),
request_buffer->as_vector().size()),
base::BindOnce(&UsbDeviceHandleWin::GotDescriptorFromNodeConnection,
weak_factory_.GetWeakPtr(), std::move(callback),
request_buffer, buffer));
return;
}
}
// Unsupported transfer for hub.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), UsbTransferStatus::TRANSFER_ERROR,
nullptr, 0));
return;
}
// Submit a normal control transfer.
OpenInterfaceForControlTransfer(
recipient, index,
base::BindOnce(&UsbDeviceHandleWin::OnInterfaceOpenedForControlTransfer,
weak_factory_.GetWeakPtr(), direction, request_type,
recipient, request, value, index, std::move(buffer),
timeout, std::move(callback)));
}
void UsbDeviceHandleWin::IsochronousTransferIn(
uint8_t endpoint_number,
const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
IsochronousTransferCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Isochronous is not yet supported on Windows.
ReportIsochronousError(packet_lengths, std::move(callback),
UsbTransferStatus::TRANSFER_ERROR);
}
void UsbDeviceHandleWin::IsochronousTransferOut(
uint8_t endpoint_number,
scoped_refptr<base::RefCountedBytes> buffer,
const std::vector<uint32_t>& packet_lengths,
unsigned int timeout,
IsochronousTransferCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Isochronous is not yet supported on Windows.
ReportIsochronousError(packet_lengths, std::move(callback),
UsbTransferStatus::TRANSFER_ERROR);
}
void UsbDeviceHandleWin::GenericTransfer(
UsbTransferDirection direction,
uint8_t endpoint_number,
scoped_refptr<base::RefCountedBytes> buffer,
unsigned int timeout,
TransferCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
uint8_t endpoint_address =
ConvertEndpointNumberToAddress(endpoint_number, direction);
auto endpoint_it = endpoints_.find(endpoint_address);
if (endpoint_it == endpoints_.end()) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), UsbTransferStatus::TRANSFER_ERROR,
nullptr, 0));
return;
}
auto interface_it =
interfaces_.find(endpoint_it->second.interface->interface_number);
CHECK(interface_it != interfaces_.end(), base::NotFatalUntil::M130);
Interface* interface = &interface_it->second;
if (!interface->claimed) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), UsbTransferStatus::TRANSFER_ERROR,
nullptr, 0));
return;
}
DCHECK(interface->handle.IsValid());
Request* request = MakeRequest(interface);
BOOL result;
if (direction == UsbTransferDirection::INBOUND) {
result = WinUsb_ReadPipe(
interface->handle.Get(), endpoint_address, buffer->as_vector().data(),
buffer->as_vector().size(), nullptr, request->overlapped());
} else {
result = WinUsb_WritePipe(
interface->handle.Get(), endpoint_address, buffer->as_vector().data(),
buffer->as_vector().size(), nullptr, request->overlapped());
}
DWORD last_error = GetLastError();
request->MaybeStartWatching(
result, last_error,
base::BindOnce(&UsbDeviceHandleWin::TransferComplete,
weak_factory_.GetWeakPtr(), std::move(callback),
std::move(buffer)));
}
const mojom::UsbInterfaceInfo* UsbDeviceHandleWin::FindInterfaceByEndpoint(
uint8_t endpoint_address) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto it = endpoints_.find(endpoint_address);
if (it != endpoints_.end())
return it->second.interface;
return nullptr;
}
UsbDeviceHandleWin::UsbDeviceHandleWin(scoped_refptr<UsbDeviceWin> device)
: device_(std::move(device)),
task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
blocking_task_runner_(UsbService::CreateBlockingTaskRunner()) {
if (const auto* config = device_->GetActiveConfiguration()) {
for (const auto& interface : config->interfaces) {
for (const auto& alternate : interface->alternates) {
if (alternate->alternate_setting != 0)
continue;
Interface& interface_info = interfaces_[interface->interface_number];
interface_info.info = interface.get();
interface_info.interface_number = interface->interface_number;
interface_info.first_interface = interface->first_interface;
RegisterEndpoints(interface.get(), *alternate);
if (device_->driver_type() == UsbDeviceWin::DriverType::kComposite) {
if (interface->interface_number == interface->first_interface) {
auto it = device_->functions().find(interface->interface_number);
if (it != device_->functions().end()) {
interface_info.function_driver = it->second.driver;
interface_info.function_path = it->second.path;
}
}
}
}
}
}
if (device_->driver_type() == UsbDeviceWin::DriverType::kWinUSB) {
// If this is not a composite device we can assume UsbServiceWin has
// set up the device with a single function entry no matter how many
// functions the device appears to have based on its descriptors.
DCHECK_EQ(1u, device_->functions().size());
DCHECK(base::Contains(device_->functions(), 0));
const UsbDeviceWin::FunctionInfo& function_info =
device_->functions().find(0)->second;
// This may create a fake interface 0 (for internal bookkeeping purposes) if
// the device doesn't have any interfaces.
Interface& interface_info = interfaces_[0];
interface_info.function_driver = function_info.driver;
interface_info.function_path = function_info.path;
}
}
UsbDeviceHandleWin::UsbDeviceHandleWin(
scoped_refptr<UsbDeviceWin> device,
base::win::ScopedHandle handle,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
: device_(std::move(device)),
hub_handle_(std::move(handle)),
task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
blocking_task_runner_(std::move(blocking_task_runner)) {}
UsbDeviceHandleWin::~UsbDeviceHandleWin() = default;
void UsbDeviceHandleWin::UpdateFunction(int interface_number,
const std::wstring& function_driver,
const std::wstring& function_path) {
auto it = interfaces_.find(interface_number);
if (it == interfaces_.end())
return;
Interface* interface = &it->second;
interface->function_driver = function_driver;
interface->function_path = function_path;
// Move callback lists onto the stack to avoid re-entrancy concerns.
auto callbacks = std::move(interface->ready_callbacks);
if (base::EqualsCaseInsensitiveASCII(function_driver, kWinUsbDriverName) &&
!function_path.empty()) {
// This path can also be used for requests for endpoint 0.
callbacks.insert(callbacks.end(),
std::make_move_iterator(ep0_ready_callbacks_.begin()),
std::make_move_iterator(ep0_ready_callbacks_.end()));
ep0_ready_callbacks_.clear();
} else if (!ep0_ready_callbacks_.empty()) {
// If all functions have been enumerated without finding one with the
// WinUSB driver. Give up waiting and report an error.
if (AllFunctionsEnumerated()) {
callbacks.insert(callbacks.end(),
std::make_move_iterator(ep0_ready_callbacks_.begin()),
std::make_move_iterator(ep0_ready_callbacks_.end()));
ep0_ready_callbacks_.clear();
}
}
for (auto& callback : callbacks)
std::move(callback).Run(interface);
}
void UsbDeviceHandleWin::OpenInterfaceHandle(Interface* interface,
OpenInterfaceCallback callback) {
if (interface->handle.IsValid()) {
std::move(callback).Run(interface);
return;
}
Interface* first_interface = GetFirstInterfaceForFunction(interface);
if (first_interface != interface) {
OpenInterfaceHandle(
first_interface,
base::BindOnce(&UsbDeviceHandleWin::OnFirstInterfaceOpened,
weak_factory_.GetWeakPtr(), interface->interface_number,
std::move(callback)));
return;
}
if (interface->function_driver.empty()) {
interface->ready_callbacks.push_back(
base::BindOnce(&UsbDeviceHandleWin::OnFunctionAvailable,
weak_factory_.GetWeakPtr(), std::move(callback)));
return;
}
OnFunctionAvailable(std::move(callback), interface);
}
UsbDeviceHandleWin::Interface* UsbDeviceHandleWin::GetFirstInterfaceForFunction(
Interface* interface) {
switch (device_->driver_type()) {
case UsbDeviceWin::DriverType::kUnsupported:
NOTREACHED_IN_MIGRATION();
return nullptr;
case UsbDeviceWin::DriverType::kWinUSB:
// If WinUSB has been loaded for a composite device then all of its
// interfaces must be treated as a single function.
DCHECK(base::Contains(interfaces_, 0));
return &interfaces_[0];
case UsbDeviceWin::DriverType::kComposite: {
if (interface->interface_number == interface->first_interface)
return interface;
auto it = interfaces_.find(interface->first_interface);
CHECK(it != interfaces_.end(), base::NotFatalUntil::M130);
return &it->second;
}
}
}
void UsbDeviceHandleWin::OnFunctionAvailable(OpenInterfaceCallback callback,
Interface* interface) {
absl::Cleanup run_callback = [&callback, interface] {
std::move(callback).Run(interface);
};
if (interface->handle.IsValid())
return;
if (!base::EqualsCaseInsensitiveASCII(interface->function_driver,
kWinUsbDriverName)) {
USB_LOG(ERROR) << "Interface " << int{interface->interface_number}
<< " uses driver \"" << interface->function_driver
<< "\" instead of WinUSB.";
return;
}
if (interface->function_path.empty()) {
USB_LOG(ERROR) << "Interface " << int{interface->interface_number}
<< " has no device path.";
return;
}
DCHECK(!interface->function_handle.IsValid());
interface->function_handle.Set(CreateFile(
interface->function_path.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, /*lpSecurityAttributes=*/nullptr,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, /*hTemplateFile=*/nullptr));
if (!interface->function_handle.IsValid()) {
USB_PLOG(ERROR) << "Failed to open " << interface->function_path;
return;
}
WINUSB_INTERFACE_HANDLE handle;
if (!WinUsb_Initialize(interface->function_handle.Get(), &handle)) {
USB_PLOG(ERROR) << "Failed to initialize WinUSB handle";
return;
}
interface->handle.Set(handle);
}
void UsbDeviceHandleWin::OnFirstInterfaceOpened(int interface_number,
OpenInterfaceCallback callback,
Interface* first_interface) {
auto interface_it = interfaces_.find(interface_number);
CHECK(interface_it != interfaces_.end(), base::NotFatalUntil::M130);
Interface* interface = &interface_it->second;
if (device_->driver_type() == UsbDeviceWin::DriverType::kComposite) {
DCHECK_NE(interface->first_interface, interface->interface_number);
DCHECK_EQ(interface->first_interface, first_interface->interface_number);
}
absl::Cleanup run_callback = [&callback, interface] {
std::move(callback).Run(interface);
};
if (!first_interface->handle.IsValid())
return;
first_interface->reference_count++;
int index =
interface->interface_number - first_interface->interface_number - 1;
WINUSB_INTERFACE_HANDLE handle;
if (WinUsb_GetAssociatedInterface(first_interface->handle.Get(), index,
&handle)) {
interface->handle.Set(handle);
} else {
USB_PLOG(ERROR) << "Failed to get associated interface " << index
<< " from interface "
<< int{first_interface->interface_number};
ReleaseInterfaceReference(first_interface);
}
}
void UsbDeviceHandleWin::OnInterfaceClaimed(ResultCallback callback,
Interface* interface) {
if (interface->handle.IsValid()) {
interface->claimed = true;
interface->reference_count++;
}
std::move(callback).Run(interface->claimed);
}
void UsbDeviceHandleWin::OnSetAlternateInterfaceSetting(int interface_number,
int alternate_setting,
ResultCallback callback,
bool result) {
auto it = interfaces_.find(interface_number);
CHECK(it != interfaces_.end(), base::NotFatalUntil::M130);
Interface& interface = it->second;
if (!result) {
ReleaseInterfaceReference(&interface);
std::move(callback).Run(false);
return;
}
// Unregister endpoints from the previously selected alternate setting.
DCHECK(interface.info);
for (const auto& alternate : interface.info->alternates) {
if (alternate->alternate_setting == interface.alternate_setting) {
UnregisterEndpoints(*alternate);
break;
}
}
interface.alternate_setting = alternate_setting;
// Unregister endpoints from the currently selected alternate setting.
for (const auto& alternate : interface.info->alternates) {
if (alternate->alternate_setting == interface.alternate_setting) {
RegisterEndpoints(interface.info, *alternate);
break;
}
}
ReleaseInterfaceReference(&interface);
std::move(callback).Run(true);
}
void UsbDeviceHandleWin::RegisterEndpoints(
const mojom::UsbInterfaceInfo* interface,
const mojom::UsbAlternateInterfaceInfo& alternate) {
for (const auto& endpoint : alternate.endpoints) {
Endpoint& endpoint_info =
endpoints_[ConvertEndpointNumberToAddress(*endpoint)];
endpoint_info.interface = interface;
endpoint_info.type = endpoint->type;
}
}
void UsbDeviceHandleWin::UnregisterEndpoints(
const mojom::UsbAlternateInterfaceInfo& alternate) {
for (const auto& endpoint : alternate.endpoints)
endpoints_.erase(ConvertEndpointNumberToAddress(*endpoint));
}
void UsbDeviceHandleWin::OnClearHalt(int interface_number,
ResultCallback callback,
bool result) {
auto it = interfaces_.find(interface_number);
CHECK(it != interfaces_.end(), base::NotFatalUntil::M130);
ReleaseInterfaceReference(&it->second);
std::move(callback).Run(result);
}
void UsbDeviceHandleWin::OpenInterfaceForControlTransfer(
UsbControlTransferRecipient recipient,
uint16_t index,
OpenInterfaceCallback callback) {
switch (recipient) {
case UsbControlTransferRecipient::DEVICE:
case UsbControlTransferRecipient::OTHER: {
// For control transfers targeting the whole device any interface with
// the WinUSB driver loaded can be used.
for (auto& map_entry : interfaces_) {
const auto& interface = map_entry.second;
if (base::EqualsCaseInsensitiveASCII(interface.function_driver,
kWinUsbDriverName) &&
!interface.function_path.empty()) {
OpenInterfaceHandle(&map_entry.second, std::move(callback));
return;
}
}
if (AllFunctionsEnumerated()) {
std::move(callback).Run(/*interface=*/nullptr);
return;
}
ep0_ready_callbacks_.push_back(
base::BindOnce(&UsbDeviceHandleWin::OnFunctionAvailableForEp0,
weak_factory_.GetWeakPtr(), std::move(callback)));
break;
}
case UsbControlTransferRecipient::ENDPOINT: {
// By convention the lower bits of the wIndex field indicate the target
// endpoint.
auto endpoint_it = endpoints_.find(index & 0xff);
if (endpoint_it == endpoints_.end()) {
std::move(callback).Run(/*interface=*/nullptr);
return;
}
index = endpoint_it->second.interface->interface_number;
[[fallthrough]];
}
case UsbControlTransferRecipient::INTERFACE: {
// By convention the lower bits of the wIndex field indicate the target
// interface.
auto interface_it = interfaces_.find(index & 0xff);
if (interface_it == interfaces_.end()) {
std::move(callback).Run(/*interface=*/nullptr);
return;
}
OpenInterfaceHandle(&interface_it->second, std::move(callback));
break;
}
}
}
void UsbDeviceHandleWin::OnFunctionAvailableForEp0(
OpenInterfaceCallback callback,
Interface* interface) {
OpenInterfaceHandle(interface, std::move(callback));
}
void UsbDeviceHandleWin::OnInterfaceOpenedForControlTransfer(
UsbTransferDirection direction,
UsbControlTransferType request_type,
UsbControlTransferRecipient recipient,
uint8_t request,
uint16_t value,
uint16_t index,
scoped_refptr<base::RefCountedBytes> buffer,
unsigned int timeout,
TransferCallback callback,
Interface* interface) {
if (!interface || interface->function_path.empty()) {
USB_LOG(ERROR) << "Interface handle not available for control transfer.";
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), UsbTransferStatus::TRANSFER_ERROR,
/*buffer=*/nullptr, /*size=*/0));
return;
}
if (!interface->handle.IsValid()) {
// OpenInterfaceHandle() already logged an error.
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), UsbTransferStatus::TRANSFER_ERROR,
/*buffer=*/nullptr, /*size=*/0));
return;
}
WINUSB_SETUP_PACKET setup = {0};
setup.RequestType = BuildRequestFlags(direction, request_type, recipient);
setup.Request = request;
setup.Value = value;
setup.Index = index;
setup.Length = buffer->size();
Request* control_request = MakeRequest(interface);
BOOL result = WinUsb_ControlTransfer(
interface->handle.Get(), setup, buffer->as_vector().data(),
buffer->as_vector().size(),
/*LengthTransferred=*/nullptr, control_request->overlapped());
DWORD last_error = GetLastError();
control_request->MaybeStartWatching(
result, last_error,
base::BindOnce(&UsbDeviceHandleWin::TransferComplete,
weak_factory_.GetWeakPtr(), std::move(callback), buffer));
}
UsbDeviceHandleWin::Request* UsbDeviceHandleWin::MakeRequest(
Interface* interface) {
// The HANDLE used to get the overlapped result must be the
// WINUSB_INTERFACE_HANDLE of the first interface in the function.
//
// https://docs.microsoft.com/en-us/windows/win32/api/winusb/nf-winusb-winusb_getoverlappedresult
interface = GetFirstInterfaceForFunction(interface);
interface->reference_count++;
auto request = std::make_unique<Request>(interface->handle.Get(),
interface->interface_number);
Request* request_ptr = request.get();
requests_.push_back(std::move(request));
return request_ptr;
}
std::unique_ptr<UsbDeviceHandleWin::Request> UsbDeviceHandleWin::UnlinkRequest(
UsbDeviceHandleWin::Request* request_ptr) {
auto it = base::ranges::find(requests_, request_ptr,
&std::unique_ptr<Request>::get);
CHECK(it != requests_.end(), base::NotFatalUntil::M130);
std::unique_ptr<Request> request = std::move(*it);
requests_.erase(it);
return request;
}
void UsbDeviceHandleWin::GotNodeConnectionInformation(
TransferCallback callback,
std::unique_ptr<USB_NODE_CONNECTION_INFORMATION_EX> node_connection_info,
scoped_refptr<base::RefCountedBytes> buffer,
std::pair<DWORD, DWORD> result_and_bytes_transferred) {
if (result_and_bytes_transferred.first != ERROR_SUCCESS) {
SetLastError(result_and_bytes_transferred.first);
USB_PLOG(ERROR) << "Failed to get node connection information";
std::move(callback).Run(UsbTransferStatus::TRANSFER_ERROR, nullptr, 0);
return;
}
DCHECK_EQ(result_and_bytes_transferred.second,
sizeof(USB_NODE_CONNECTION_INFORMATION_EX));
device_->ActiveConfigurationChanged(
node_connection_info->CurrentConfigurationValue);
size_t bytes_transferred =
std::min(sizeof(USB_DEVICE_DESCRIPTOR), buffer->size());
base::span(buffer->as_vector())
.copy_prefix_from(
base::byte_span_from_ref(node_connection_info->DeviceDescriptor)
.first(bytes_transferred));
std::move(callback).Run(UsbTransferStatus::COMPLETED, buffer,
bytes_transferred);
}
void UsbDeviceHandleWin::GotDescriptorFromNodeConnection(
TransferCallback callback,
scoped_refptr<base::RefCountedBytes> request_buffer,
scoped_refptr<base::RefCountedBytes> original_buffer,
std::pair<DWORD, DWORD> result_and_bytes_transferred) {
if (result_and_bytes_transferred.first != ERROR_SUCCESS) {
SetLastError(result_and_bytes_transferred.first);
USB_PLOG(DEBUG) << "Failed to read descriptor from node connection";
std::move(callback).Run(UsbTransferStatus::TRANSFER_ERROR,
/*buffer=*/nullptr, /*length=*/0);
return;
}
if (result_and_bytes_transferred.second < sizeof(USB_DESCRIPTOR_REQUEST)) {
USB_LOG(ERROR) << "Descriptor response too short ("
<< result_and_bytes_transferred.second << " < "
<< sizeof(USB_DESCRIPTOR_REQUEST) << ")";
std::move(callback).Run(UsbTransferStatus::TRANSFER_ERROR,
/*buffer=*/nullptr, /*length=*/0);
return;
}
size_t bytes_transferred =
result_and_bytes_transferred.second - sizeof(USB_DESCRIPTOR_REQUEST);
bytes_transferred = std::min(bytes_transferred, original_buffer->size());
base::span(original_buffer->as_vector())
.copy_prefix_from(
base::span(*request_buffer)
.subspan(sizeof(USB_DESCRIPTOR_REQUEST), bytes_transferred));
std::move(callback).Run(UsbTransferStatus::COMPLETED, original_buffer,
bytes_transferred);
}
void UsbDeviceHandleWin::TransferComplete(
TransferCallback callback,
scoped_refptr<base::RefCountedBytes> buffer,
Request* request_ptr,
DWORD win32_result,
size_t bytes_transferred) {
std::unique_ptr<Request> request = UnlinkRequest(request_ptr);
UsbTransferStatus status = UsbTransferStatus::COMPLETED;
if (win32_result != ERROR_SUCCESS) {
SetLastError(win32_result);
USB_PLOG(ERROR) << "Transfer failed";
buffer = nullptr;
bytes_transferred = 0;
switch (win32_result) {
case ERROR_REQUEST_ABORTED:
status = UsbTransferStatus::CANCELLED;
break;
default:
status = UsbTransferStatus::TRANSFER_ERROR;
}
}
DCHECK_NE(request->interface_number(), -1);
auto it = interfaces_.find(request->interface_number());
CHECK(it != interfaces_.end(), base::NotFatalUntil::M130);
ReleaseInterfaceReference(&it->second);
std::move(callback).Run(status, std::move(buffer), bytes_transferred);
}
void UsbDeviceHandleWin::ReportIsochronousError(
const std::vector<uint32_t>& packet_lengths,
IsochronousTransferCallback callback,
UsbTransferStatus status) {
std::vector<mojom::UsbIsochronousPacketPtr> packets(packet_lengths.size());
for (size_t i = 0; i < packet_lengths.size(); ++i) {
packets[i] = mojom::UsbIsochronousPacket::New();
packets[i]->length = packet_lengths[i];
packets[i]->transferred_length = 0;
packets[i]->status = status;
}
task_runner_->PostTask(FROM_HERE, base::BindOnce(std::move(callback), nullptr,
std::move(packets)));
}
bool UsbDeviceHandleWin::AllFunctionsEnumerated() const {
switch (device_->driver_type()) {
case UsbDeviceWin::DriverType::kUnsupported:
NOTREACHED_IN_MIGRATION();
return false;
case UsbDeviceWin::DriverType::kWinUSB:
return true;
case UsbDeviceWin::DriverType::kComposite:
for (const auto& map_entry : interfaces_) {
const Interface& interface = map_entry.second;
// Iterate over functions, rather than interfaces.
if (interface.first_interface != interface.interface_number)
continue;
if (interface.function_driver.empty())
return false;
}
return true;
}
}
void UsbDeviceHandleWin::ReleaseInterfaceReference(Interface* interface) {
DCHECK_GT(interface->reference_count, 0);
interface->reference_count--;
if (interface->reference_count > 0)
return;
if (interface->handle.IsValid()) {
interface->handle.Close();
interface->alternate_setting = 0;
}
if (interface->function_handle.IsValid())
interface->function_handle.Close();
Interface* first_interface = GetFirstInterfaceForFunction(interface);
if (first_interface != interface)
ReleaseInterfaceReference(first_interface);
}
} // namespace device