blob: 866b947abc3c6c2bf83c406c6ddd49412f0d4216 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/usb/usb_device_impl.h"
#include <fcntl.h>
#include <stddef.h>
#include <algorithm>
#include "base/bind.h"
#include "base/location.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "components/device_event_log/device_event_log.h"
#include "device/usb/usb_context.h"
#include "device/usb/usb_descriptors.h"
#include "device/usb/usb_device_handle_impl.h"
#include "device/usb/usb_error.h"
#include "device/usb/usb_service.h"
#include "third_party/libusb/src/libusb/libusb.h"
namespace device {
UsbDeviceImpl::UsbDeviceImpl(ScopedLibusbDeviceRef platform_device,
const libusb_device_descriptor& descriptor)
: UsbDevice(descriptor.bcdUSB,
descriptor.bDeviceClass,
descriptor.bDeviceSubClass,
descriptor.bDeviceProtocol,
descriptor.idVendor,
descriptor.idProduct,
descriptor.bcdDevice,
base::string16(),
base::string16(),
base::string16(),
libusb_get_bus_number(platform_device.get()),
libusb_get_port_number(platform_device.get())),
platform_device_(std::move(platform_device)) {
CHECK(platform_device_.IsValid()) << "platform_device must be valid";
ReadAllConfigurations();
RefreshActiveConfiguration();
}
UsbDeviceImpl::~UsbDeviceImpl() {
// The destructor must be safe to call from any thread.
}
void UsbDeviceImpl::Open(OpenCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner =
UsbService::CreateBlockingTaskRunner();
blocking_task_runner->PostTask(
FROM_HERE,
base::BindOnce(&UsbDeviceImpl::OpenOnBlockingThread, this,
std::move(callback), base::ThreadTaskRunnerHandle::Get(),
blocking_task_runner));
}
void UsbDeviceImpl::ReadAllConfigurations() {
libusb_device_descriptor device_descriptor;
int rv = libusb_get_device_descriptor(platform_device(), &device_descriptor);
if (rv == LIBUSB_SUCCESS) {
for (uint8_t i = 0; i < device_descriptor.bNumConfigurations; ++i) {
unsigned char* buffer;
rv = libusb_get_raw_config_descriptor(platform_device(), i, &buffer);
if (rv < 0) {
USB_LOG(EVENT) << "Failed to get config descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
continue;
}
if (!descriptor_.Parse(std::vector<uint8_t>(buffer, buffer + rv)))
USB_LOG(EVENT) << "Config descriptor index " << i << " was corrupt.";
free(buffer);
}
} else {
USB_LOG(EVENT) << "Failed to get device descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
}
}
void UsbDeviceImpl::RefreshActiveConfiguration() {
uint8_t config_value;
int rv = libusb_get_active_config_value(platform_device(), &config_value);
if (rv != LIBUSB_SUCCESS) {
USB_LOG(EVENT) << "Failed to get active configuration: "
<< ConvertPlatformUsbErrorToString(rv);
return;
}
ActiveConfigurationChanged(config_value);
}
void UsbDeviceImpl::OpenOnBlockingThread(
OpenCallback callback,
scoped_refptr<base::TaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
libusb_device_handle* handle = nullptr;
const int rv = libusb_open(platform_device(), &handle);
if (LIBUSB_SUCCESS == rv) {
ScopedLibusbDeviceHandle scoped_handle(handle,
platform_device_.GetContext());
task_runner->PostTask(
FROM_HERE,
base::BindOnce(&UsbDeviceImpl::Opened, this, std::move(scoped_handle),
std::move(callback), blocking_task_runner));
} else {
USB_LOG(EVENT) << "Failed to open device: "
<< ConvertPlatformUsbErrorToString(rv);
task_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), nullptr));
}
}
void UsbDeviceImpl::Opened(
ScopedLibusbDeviceHandle platform_handle,
OpenCallback callback,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<UsbDeviceHandle> device_handle = new UsbDeviceHandleImpl(
this, std::move(platform_handle), blocking_task_runner);
handles().push_back(device_handle.get());
std::move(callback).Run(device_handle);
}
} // namespace device