blob: 6defda8ebfc76e8de4efa4d83763b6d0a80e61b8 [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_descriptors.h"
#include <stddef.h>
#include <algorithm>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "device/usb/usb_device_handle.h"
#include "net/base/io_buffer.h"
namespace device {
namespace {
using IndexMap = std::map<uint8_t, base::string16>;
using IndexMapPtr = scoped_ptr<IndexMap>;
// Standard USB requests and descriptor types:
const uint8_t kGetDescriptorRequest = 0x06;
const uint8_t kStringDescriptorType = 0x03;
const int kControlTransferTimeout = 60000; // 1 minute
void StoreStringDescriptor(IndexMap::iterator it,
const base::Closure& callback,
const base::string16& string) {
it->second = string;
callback.Run();
}
void OnReadStringDescriptor(
const base::Callback<void(const base::string16&)>& callback,
UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
size_t length) {
base::string16 string;
if (status == USB_TRANSFER_COMPLETED &&
ParseUsbStringDescriptor(
std::vector<uint8_t>(buffer->data(), buffer->data() + length),
&string)) {
callback.Run(string);
} else {
callback.Run(base::string16());
}
}
void ReadStringDescriptor(
scoped_refptr<UsbDeviceHandle> device_handle,
uint8_t index,
uint16_t language_id,
const base::Callback<void(const base::string16&)>& callback) {
scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize(255);
device_handle->ControlTransfer(
USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
kGetDescriptorRequest, kStringDescriptorType << 8 | index, language_id,
buffer, buffer->size(), kControlTransferTimeout,
base::Bind(&OnReadStringDescriptor, callback));
}
void OnReadLanguageIds(scoped_refptr<UsbDeviceHandle> device_handle,
IndexMapPtr index_map,
const base::Callback<void(IndexMapPtr)>& callback,
const base::string16& languages) {
// Default to English unless the device provides a language and then just pick
// the first one.
uint16_t language_id = languages.empty() ? 0x0409 : languages[0];
std::map<uint8_t, IndexMap::iterator> iterator_map;
for (auto it = index_map->begin(); it != index_map->end(); ++it)
iterator_map[it->first] = it;
base::Closure barrier =
base::BarrierClosure(static_cast<int>(iterator_map.size()),
base::Bind(callback, base::Passed(&index_map)));
for (const auto& map_entry : iterator_map) {
ReadStringDescriptor(
device_handle, map_entry.first, language_id,
base::Bind(&StoreStringDescriptor, map_entry.second, barrier));
}
}
} // namespace
UsbEndpointDescriptor::UsbEndpointDescriptor(
uint8_t address,
UsbEndpointDirection direction,
uint16_t maximum_packet_size,
UsbSynchronizationType synchronization_type,
UsbTransferType transfer_type,
UsbUsageType usage_type,
uint16_t polling_interval)
: address(address),
direction(direction),
maximum_packet_size(maximum_packet_size),
synchronization_type(synchronization_type),
transfer_type(transfer_type),
usage_type(usage_type),
polling_interval(polling_interval) {}
UsbEndpointDescriptor::~UsbEndpointDescriptor() = default;
UsbInterfaceDescriptor::UsbInterfaceDescriptor(uint8_t interface_number,
uint8_t alternate_setting,
uint8_t interface_class,
uint8_t interface_subclass,
uint8_t interface_protocol)
: interface_number(interface_number),
alternate_setting(alternate_setting),
interface_class(interface_class),
interface_subclass(interface_subclass),
interface_protocol(interface_protocol) {}
UsbInterfaceDescriptor::~UsbInterfaceDescriptor() = default;
UsbConfigDescriptor::UsbConfigDescriptor(uint8_t configuration_value,
bool self_powered,
bool remote_wakeup,
uint16_t maximum_power)
: configuration_value(configuration_value),
self_powered(self_powered),
remote_wakeup(remote_wakeup),
maximum_power(maximum_power) {}
UsbConfigDescriptor::~UsbConfigDescriptor() = default;
bool ParseUsbStringDescriptor(const std::vector<uint8_t>& descriptor,
base::string16* output) {
if (descriptor.size() < 2 || descriptor[1] != kStringDescriptorType)
return false;
// Let the device return a buffer larger than the actual string but prefer the
// length reported inside the descriptor.
size_t length = descriptor[0];
length = std::min(length, descriptor.size());
if (length < 2)
return false;
// The string is returned by the device in UTF-16LE.
*output = base::string16(
reinterpret_cast<const base::char16*>(descriptor.data() + 2),
length / 2 - 1);
return true;
}
// For each key in |index_map| this function reads that string descriptor from
// |device_handle| and updates the value in in |index_map|.
void ReadUsbStringDescriptors(
scoped_refptr<UsbDeviceHandle> device_handle,
IndexMapPtr index_map,
const base::Callback<void(IndexMapPtr)>& callback) {
if (index_map->empty()) {
callback.Run(std::move(index_map));
return;
}
ReadStringDescriptor(device_handle, 0, 0,
base::Bind(&OnReadLanguageIds, device_handle,
base::Passed(&index_map), callback));
}
} // namespace device