summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2016-02-12 17:54:22 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-13 01:56:10 +0000
commitb61e467059e55ad71f1b7ae5e1412beb3d4803e0 (patch)
tree18f93767c81d538f8f61872a45ed4be83e2e1ab7 /device
parent268f1bf33d61c34c9ebcb40947f1d262a31ad0b8 (diff)
downloadchromium_src-b61e467059e55ad71f1b7ae5e1412beb3d4803e0.zip
chromium_src-b61e467059e55ad71f1b7ae5e1412beb3d4803e0.tar.gz
chromium_src-b61e467059e55ad71f1b7ae5e1412beb3d4803e0.tar.bz2
Parse USB interface association descriptors.
USB interface association descriptors are used to combine multiple interfaces into a single functional group. This patch adds support for parsing them out of the |extra_data| field left by libusb's parsing of configuration, interface and endpoint descriptors. The resulting association is then represented by setting the |first_interface| field of each interface in a function to the |interface_number| of the first interface in the function. WebUSB will use these associations to set permissions for an entire function with a single descriptor. BUG=492204 Review URL: https://codereview.chromium.org/1568673002 Cr-Commit-Position: refs/heads/master@{#375324}
Diffstat (limited to 'device')
-rw-r--r--device/usb/usb_descriptors.cc81
-rw-r--r--device/usb/usb_descriptors.h6
-rw-r--r--device/usb/usb_descriptors_unittest.cc75
-rw-r--r--device/usb/usb_device_impl.cc2
4 files changed, 163 insertions, 1 deletions
diff --git a/device/usb/usb_descriptors.cc b/device/usb/usb_descriptors.cc
index 6defda8..22414d7 100644
--- a/device/usb/usb_descriptors.cc
+++ b/device/usb/usb_descriptors.cc
@@ -7,6 +7,7 @@
#include <stddef.h>
#include <algorithm>
+#include <vector>
#include "base/barrier_closure.h"
#include "base/bind.h"
@@ -26,6 +27,42 @@ const uint8_t kStringDescriptorType = 0x03;
const int kControlTransferTimeout = 60000; // 1 minute
+struct UsbInterfaceAssociationDescriptor {
+ UsbInterfaceAssociationDescriptor(uint8_t first_interface,
+ uint8_t interface_count)
+ : first_interface(first_interface), interface_count(interface_count) {}
+
+ bool operator<(const UsbInterfaceAssociationDescriptor& other) const {
+ return first_interface < other.first_interface;
+ }
+
+ uint8_t first_interface;
+ uint8_t interface_count;
+};
+
+void ParseInterfaceAssociationDescriptors(
+ const std::vector<uint8_t>& buffer,
+ std::vector<UsbInterfaceAssociationDescriptor>* functions) {
+ const uint8_t kInterfaceAssociationDescriptorType = 11;
+ const uint8_t kInterfaceAssociationDescriptorLength = 8;
+ std::vector<uint8_t>::const_iterator it = buffer.begin();
+
+ while (it != buffer.end()) {
+ // All descriptors must be at least 2 byte which means the length and type
+ // are safe to read.
+ if (std::distance(it, buffer.end()) < 2)
+ return;
+ uint8_t length = it[0];
+ if (length > std::distance(it, buffer.end()))
+ return;
+ if (it[1] == kInterfaceAssociationDescriptorType &&
+ length == kInterfaceAssociationDescriptorLength) {
+ functions->push_back(UsbInterfaceAssociationDescriptor(it[2], it[3]));
+ }
+ std::advance(it, length);
+ }
+}
+
void StoreStringDescriptor(IndexMap::iterator it,
const base::Closure& callback,
const base::string16& string) {
@@ -113,7 +150,8 @@ UsbInterfaceDescriptor::UsbInterfaceDescriptor(uint8_t interface_number,
alternate_setting(alternate_setting),
interface_class(interface_class),
interface_subclass(interface_subclass),
- interface_protocol(interface_protocol) {}
+ interface_protocol(interface_protocol),
+ first_interface(interface_number) {}
UsbInterfaceDescriptor::~UsbInterfaceDescriptor() = default;
@@ -128,6 +166,47 @@ UsbConfigDescriptor::UsbConfigDescriptor(uint8_t configuration_value,
UsbConfigDescriptor::~UsbConfigDescriptor() = default;
+void UsbConfigDescriptor::AssignFirstInterfaceNumbers() {
+ std::vector<UsbInterfaceAssociationDescriptor> functions;
+ ParseInterfaceAssociationDescriptors(extra_data, &functions);
+ for (const auto& interface : interfaces) {
+ ParseInterfaceAssociationDescriptors(interface.extra_data, &functions);
+ for (const auto& endpoint : interface.endpoints)
+ ParseInterfaceAssociationDescriptors(endpoint.extra_data, &functions);
+ }
+
+ // libusb has collected interface association descriptors in the |extra_data|
+ // fields of other descriptor types. This may have disturbed their order
+ // but sorting by the bFirstInterface should fix it.
+ std::sort(functions.begin(), functions.end());
+
+ uint8_t remaining_interfaces = 0;
+ auto function_it = functions.cbegin();
+ for (auto interface_it = interfaces.begin(); interface_it != interfaces.end();
+ ++interface_it) {
+ if (remaining_interfaces > 0) {
+ // Continuation of a previous function. Tag all alternate interfaces
+ // (which are guaranteed to be contiguous).
+ for (uint8_t interface_number = interface_it->interface_number;
+ interface_it != interfaces.end() &&
+ interface_it->interface_number == interface_number;
+ ++interface_it) {
+ interface_it->first_interface = function_it->first_interface;
+ }
+ if (--remaining_interfaces == 0)
+ ++function_it;
+ } else if (function_it != functions.end() &&
+ interface_it->interface_number == function_it->first_interface) {
+ // Start of a new function.
+ interface_it->first_interface = function_it->first_interface;
+ remaining_interfaces = function_it->interface_count - 1;
+ } else {
+ // Unassociated interfaces already have |first_interface| set to
+ // |interface_number|.
+ }
+ }
+}
+
bool ParseUsbStringDescriptor(const std::vector<uint8_t>& descriptor,
base::string16* output) {
if (descriptor.size() < 2 || descriptor[1] != kStringDescriptorType)
diff --git a/device/usb/usb_descriptors.h b/device/usb/usb_descriptors.h
index c99a6de..a1414154 100644
--- a/device/usb/usb_descriptors.h
+++ b/device/usb/usb_descriptors.h
@@ -85,6 +85,8 @@ struct UsbInterfaceDescriptor {
uint8_t interface_protocol;
std::vector<UsbEndpointDescriptor> endpoints;
std::vector<uint8_t> extra_data;
+ // First interface of the function to which this interface belongs.
+ uint8_t first_interface;
};
struct UsbConfigDescriptor {
@@ -95,6 +97,10 @@ struct UsbConfigDescriptor {
UsbConfigDescriptor() = delete;
~UsbConfigDescriptor();
+ // Scans through |extra_data| for interface association descriptors and
+ // populates |first_interface| for each interface in this configuration.
+ void AssignFirstInterfaceNumbers();
+
uint8_t configuration_value;
bool self_powered;
bool remote_wakeup;
diff --git a/device/usb/usb_descriptors_unittest.cc b/device/usb/usb_descriptors_unittest.cc
index 402304f..5bc2943 100644
--- a/device/usb/usb_descriptors_unittest.cc
+++ b/device/usb/usb_descriptors_unittest.cc
@@ -32,6 +32,81 @@ void ExpectStringDescriptors(
class UsbDescriptorsTest : public ::testing::Test {};
+TEST_F(UsbDescriptorsTest, NoInterfaceAssociations) {
+ UsbConfigDescriptor config(1, false, false, 0);
+ config.interfaces.emplace_back(0, 0, 255, 255, 255);
+ config.interfaces.emplace_back(0, 1, 255, 255, 255);
+ config.interfaces.emplace_back(1, 0, 255, 255, 255);
+ config.AssignFirstInterfaceNumbers();
+
+ EXPECT_EQ(0, config.interfaces[0].first_interface);
+ EXPECT_EQ(0, config.interfaces[1].first_interface);
+ EXPECT_EQ(1, config.interfaces[2].first_interface);
+}
+
+TEST_F(UsbDescriptorsTest, InterfaceAssociations) {
+ // Links interfaces 0 and 1 into a single function.
+ static const uint8_t kIAD1[] = {0x08, 0x0b, 0x00, 0x02,
+ 0xff, 0xff, 0xff, 0x00};
+ // Only references a single interface, 2.
+ static const uint8_t kIAD2[] = {0x08, 0x0b, 0x02, 0x01,
+ 0xff, 0xff, 0xff, 0x00};
+ // Malformed. References interface 3 but bInterfaceCount is 0.
+ static const uint8_t kIAD3[] = {0x08, 0x0b, 0x03, 0x00,
+ 0xff, 0xff, 0xff, 0x00};
+ // Malformed. References an indefined interface.
+ static const uint8_t kIAD4[] = {0x08, 0x0b, 0x07, 0x00,
+ 0xff, 0xff, 0xff, 0x00};
+ // Links interfaces 4 and 5 into a single function.
+ static const uint8_t kIAD5[] = {0x08, 0x0b, 0x04, 0x01,
+ 0xff, 0xff, 0xff, 0x00};
+
+ UsbConfigDescriptor config(1, false, false, 0);
+ config.extra_data.assign(kIAD1, kIAD1 + sizeof(kIAD1));
+ config.extra_data.insert(config.extra_data.end(), kIAD2,
+ kIAD2 + sizeof(kIAD2));
+ config.interfaces.emplace_back(0, 0, 255, 255, 255);
+ config.interfaces.emplace_back(1, 0, 255, 255, 255);
+ UsbInterfaceDescriptor iface1a(1, 1, 255, 255, 255);
+ iface1a.extra_data.assign(kIAD3, kIAD3 + sizeof(kIAD3));
+ config.interfaces.push_back(std::move(iface1a));
+ config.interfaces.emplace_back(2, 0, 255, 255, 255);
+ config.interfaces.emplace_back(3, 0, 255, 255, 255);
+ UsbInterfaceDescriptor iface4(4, 0, 255, 255, 255);
+ iface4.extra_data.assign(kIAD4, kIAD4 + sizeof(kIAD4));
+ iface4.extra_data.insert(iface4.extra_data.end(), kIAD5,
+ kIAD5 + sizeof(kIAD5));
+ config.interfaces.push_back(std::move(iface4));
+ config.interfaces.emplace_back(5, 0, 255, 255, 255);
+ config.AssignFirstInterfaceNumbers();
+
+ EXPECT_EQ(0, config.interfaces[0].first_interface);
+ EXPECT_EQ(0, config.interfaces[1].first_interface);
+ EXPECT_EQ(0, config.interfaces[2].first_interface);
+ EXPECT_EQ(2, config.interfaces[3].first_interface);
+ EXPECT_EQ(3, config.interfaces[4].first_interface);
+ EXPECT_EQ(4, config.interfaces[5].first_interface);
+ EXPECT_EQ(4, config.interfaces[5].first_interface);
+}
+
+TEST_F(UsbDescriptorsTest, CorruptInterfaceAssociations) {
+ {
+ // Descriptor is too short.
+ static const uint8_t kIAD[] = {0x01};
+ UsbConfigDescriptor config(1, false, false, 0);
+ config.extra_data.assign(kIAD, kIAD + sizeof(kIAD));
+ config.AssignFirstInterfaceNumbers();
+ }
+ {
+ // Descriptor is too long.
+ static const uint8_t kIAD[] = {0x09, 0x0b, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00};
+ UsbConfigDescriptor config(1, false, false, 0);
+ config.extra_data.assign(kIAD, kIAD + sizeof(kIAD));
+ config.AssignFirstInterfaceNumbers();
+ }
+}
+
TEST_F(UsbDescriptorsTest, StringDescriptor) {
static const uint8_t kBuffer[] = {0x1a, 0x03, 'H', 0, 'e', 0, 'l', 0, 'l', 0,
'o', 0, ' ', 0, 'w', 0, 'o', 0, 'r', 0,
diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc
index 1a8bedf..118416d 100644
--- a/device/usb/usb_device_impl.cc
+++ b/device/usb/usb_device_impl.cc
@@ -135,6 +135,8 @@ void ConvertConfigDescriptor(const libusb_config_descriptor* platform_config,
configuration->extra_data.assign(
platform_config->extra,
platform_config->extra + platform_config->extra_length);
+
+ configuration->AssignFirstInterfaceNumbers();
}
} // namespace