blob: 1342e1b05b14ce152362bf55d7e31af888bb6b20 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 <zircon/hw/usb/hid.h>
#include <ddk/protocol/hidbus.h>
#include <usb/usb.h>
#include <zxtest/zxtest.h>
namespace usb {
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdHID;
uint8_t bCountryCode;
uint8_t bNumDescriptors;
} __attribute__((packed)) usb_hid_descriptor_for_test_t;
// The interface configuration corresponding to a HighSpeed device having one alt-interface.
typedef struct {
// clang-format off
usb_interface_descriptor_t interface;
usb_endpoint_descriptor_t ep1;
usb_endpoint_descriptor_t ep2;
usb_hid_descriptor_for_test_t hid_descriptor;
usb_interface_descriptor_t alt_interface;
// clang-format on
} alt_hs_config;
// The interface configuration corresponding to a SuperSpeed device having one alt-interface.
typedef struct {
// clang-format off
usb_interface_descriptor_t interface;
usb_endpoint_descriptor_t ep1;
usb_ss_ep_comp_descriptor_t ss_companion1;
usb_endpoint_descriptor_t ep2;
usb_ss_ep_comp_descriptor_t ss_companion2;
usb_interface_descriptor_t alt_interface;
// clang-format on
} alt_ss_config;
// The binary form of a usb keyboard descriptor. Contains an interface, hid descriptor, and endpoint
// descriptor.
constexpr uint8_t descriptor_binary_array[] = {9, 4, 1, 0, 1, 3, 0, 0, 0, 9, 33, 16, 1,
0, 1, 34, 106, 0, 7, 5, 130, 3, 8, 0, 48};
constexpr alt_hs_config kTestHSInterface = {
.interface =
{
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 8,
.bInterfaceSubClass = 6,
.bInterfaceProtocol = 80,
.iInterface = 0,
},
.ep1 =
{
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = 2,
.wMaxPacketSize = 1024,
.bInterval = 0,
},
.ep2 =
{
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 2,
.bmAttributes = 2,
.wMaxPacketSize = 1024,
.bInterval = 0,
},
.hid_descriptor =
{
.bLength = sizeof(usb_hid_descriptor_for_test_t),
.bDescriptorType = USB_DT_HID,
.bcdHID = 0,
.bCountryCode = 0,
.bNumDescriptors = 0,
},
.alt_interface =
{
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = 8,
.bInterfaceSubClass = 6,
.bInterfaceProtocol = 80,
.iInterface = 0,
},
};
// Taken from a real UMS-class device.
constexpr alt_ss_config kTestSSInterface = {
.interface =
{
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 8,
.bInterfaceSubClass = 6,
.bInterfaceProtocol = 80,
.iInterface = 0,
},
.ep1 =
{
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 0x81,
.bmAttributes = 2,
.wMaxPacketSize = 1024,
.bInterval = 0,
},
.ss_companion1 =
{
.bLength = sizeof(usb_ss_ep_comp_descriptor_t),
.bDescriptorType = USB_DT_SS_EP_COMPANION,
.bMaxBurst = 3,
.bmAttributes = 0,
.wBytesPerInterval = 0,
},
.ep2 =
{
.bLength = sizeof(usb_endpoint_descriptor_t),
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = 2,
.bmAttributes = 2,
.wMaxPacketSize = 1024,
.bInterval = 0,
},
.ss_companion2 =
{
.bLength = sizeof(usb_ss_ep_comp_descriptor_t),
.bDescriptorType = USB_DT_SS_EP_COMPANION,
.bMaxBurst = 3,
.bmAttributes = 0,
.wBytesPerInterval = 0,
},
.alt_interface =
{
.bLength = sizeof(usb_interface_descriptor_t),
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 0,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = 8,
.bInterfaceSubClass = 6,
.bInterfaceProtocol = 80,
.iInterface = 0,
},
};
static void EXPECT_INTERFACE_EQ(usb_interface_descriptor_t a, usb_interface_descriptor_t b) {
EXPECT_EQ(a.bLength, b.bLength);
EXPECT_EQ(a.bDescriptorType, b.bDescriptorType);
EXPECT_EQ(a.bInterfaceNumber, b.bInterfaceNumber);
EXPECT_EQ(a.bAlternateSetting, b.bAlternateSetting);
EXPECT_EQ(a.bNumEndpoints, b.bNumEndpoints);
EXPECT_EQ(a.bInterfaceClass, b.bInterfaceClass);
EXPECT_EQ(a.bInterfaceSubClass, b.bInterfaceSubClass);
EXPECT_EQ(a.bInterfaceProtocol, b.bInterfaceProtocol);
EXPECT_EQ(a.iInterface, b.iInterface);
}
static void EXPECT_ENDPOINT_EQ(usb_endpoint_descriptor_t a, usb_endpoint_descriptor_t b) {
EXPECT_EQ(a.bLength, b.bLength);
EXPECT_EQ(a.bDescriptorType, b.bDescriptorType);
EXPECT_EQ(a.bEndpointAddress, b.bEndpointAddress);
EXPECT_EQ(a.bmAttributes, b.bmAttributes);
EXPECT_EQ(a.wMaxPacketSize, b.wMaxPacketSize);
EXPECT_EQ(a.bInterval, b.bInterval);
}
static void EXPECT_SS_EP_COMP_EQ(usb_ss_ep_comp_descriptor_t a, usb_ss_ep_comp_descriptor_t b) {
EXPECT_EQ(a.bLength, b.bLength);
EXPECT_EQ(a.bDescriptorType, b.bDescriptorType);
EXPECT_EQ(a.bMaxBurst, b.bMaxBurst);
EXPECT_EQ(a.bmAttributes, b.bmAttributes);
EXPECT_EQ(a.wBytesPerInterval, b.wBytesPerInterval);
}
static void EXPECT_DESCRIPTOR_EQ(const usb_descriptor_header_t* a,
const usb_descriptor_header_t* b) {
EXPECT_EQ(a->bDescriptorType, b->bDescriptorType);
EXPECT_EQ(a->bLength, b->bLength);
}
// HighSpeedWrapperTest tests an InterfaceList's ability to process interface descriptors
// corresponding to a HighSpeed device structure (i.e. no SS-COMPANION descriptors).
class HighSpeedWrapperTest : public zxtest::Test {
protected:
void SetUp() {
usb_protocol_t proto{&ops_, this};
ops_.get_descriptors_length = UsbGetDescriptorsLength;
ops_.get_descriptors = UsbGetDescriptors;
usb_ = ddk::UsbProtocolClient(&proto);
}
static void UsbGetDescriptors(void* ctx, void* out_descs_buffer, size_t descs_size,
size_t* out_descs_actual) {
memcpy(out_descs_buffer, &kTestHSInterface, descs_size);
*out_descs_actual = descs_size;
}
static size_t UsbGetDescriptorsLength(void* ctx) { return sizeof(kTestHSInterface); }
usb_protocol_ops_t ops_{};
ddk::UsbProtocolClient usb_;
};
TEST_F(HighSpeedWrapperTest, TestInterfaceRangeIterationSkippingAlt) {
// This tests that for(x : y) syntax produces the correct interface descriptors.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
int count = 0;
const auto interface = ilist->begin();
EXPECT_INTERFACE_EQ(kTestHSInterface.interface, *interface->descriptor());
for (auto& interface : *ilist) {
EXPECT_TRUE(count++ < 1);
EXPECT_INTERFACE_EQ(kTestHSInterface.interface, *interface.descriptor());
};
}
TEST_F(HighSpeedWrapperTest, TestInterfaceRangeIterationNotSkippingAlt) {
// This tests that for(x : y) syntax produces the correct interface descriptors.
const usb_interface_descriptor_t wants[2] = {
kTestHSInterface.interface,
kTestHSInterface.alt_interface,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, false, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
EXPECT_TRUE(count < countof(wants));
EXPECT_INTERFACE_EQ(wants[count++], *interface.descriptor());
}
}
TEST_F(HighSpeedWrapperTest, TestEndpointRangeIteration) {
// This tests that for(x : y) syntax produces the correct endpoint descriptors.
const usb_endpoint_descriptor_t wants[2] = {
kTestHSInterface.ep1,
kTestHSInterface.ep2,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
for (auto ep : interface.GetEndpointList()) {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count++], ep.descriptor);
EXPECT_FALSE(ep.has_companion);
}
}
}
TEST_F(HighSpeedWrapperTest, TestInterfaceAccessOps) {
// This tests the various Interface access ops of a InterfaceList::iterator.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
auto itr = ilist->begin();
int count = 0;
do {
EXPECT_TRUE(count++ < 1);
auto& want = kTestHSInterface.interface;
// operator->()
const usb_interface_descriptor_t* ptr = itr->descriptor();
EXPECT_INTERFACE_EQ(want, *ptr);
// operator*()
ptr = (*itr).descriptor();
EXPECT_INTERFACE_EQ(want, *ptr);
// .get()
ptr = itr.get()->descriptor();
EXPECT_INTERFACE_EQ(want, *ptr);
} while (++itr != ilist->end());
}
TEST_F(HighSpeedWrapperTest, TestEndpointAccessOps) {
// This tests the various endpoint descriptor ops of an Interface::iterator.
const usb_endpoint_descriptor_t wants[2] = {
kTestHSInterface.ep1,
kTestHSInterface.ep2,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep_itr = interface.GetEndpointList().begin();
do {
EXPECT_TRUE(count < countof(wants));
auto& want = wants[count++];
// operator->()
const usb_endpoint_descriptor_t* ptr = &ep_itr->descriptor;
EXPECT_ENDPOINT_EQ(want, *ptr);
// operator*()
ptr = &((*ep_itr).descriptor);
EXPECT_ENDPOINT_EQ(want, *ptr);
// .endpoint()
ptr = &ep_itr.endpoint()->descriptor;
EXPECT_ENDPOINT_EQ(want, *ptr);
} while (++ep_itr != interface.GetEndpointList().end());
}
}
TEST_F(HighSpeedWrapperTest, TestInterfaceIterationSkippingAlt) {
// This tests that the iterator syntax produces the correct interface descriptors.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
auto itr = ilist->begin();
int count = 0;
do {
EXPECT_TRUE(count++ < 1);
EXPECT_INTERFACE_EQ(kTestHSInterface.interface, *itr->descriptor());
} while (++itr != ilist->end());
}
TEST_F(HighSpeedWrapperTest, TestInterfaceIterationNotSkippingAlt) {
// This tests that the iterator syntax produces the correct interface descriptors.
const usb_interface_descriptor_t wants[2] = {
kTestHSInterface.interface,
kTestHSInterface.alt_interface,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, false, &ilist));
auto itr = ilist->begin();
unsigned int count = 0;
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_INTERFACE_EQ(wants[count++], *itr->descriptor());
} while (++itr != ilist->end());
}
TEST_F(HighSpeedWrapperTest, TestEndpointIteration) {
// This tests that the iterator syntax produces the correct endpoint descriptors.
const usb_endpoint_descriptor_t wants[2] = {
kTestHSInterface.ep1,
kTestHSInterface.ep2,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep_itr = interface.GetEndpointList().begin();
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count++], ep_itr->descriptor);
EXPECT_FALSE(ep_itr->has_companion);
} while (++ep_itr != interface.GetEndpointList().end());
}
}
TEST_F(HighSpeedWrapperTest, TestInterfaceConstIterationSkippingAlt) {
// This tests that the const_iterator syntax produces the correct interface descriptors.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
auto itr = ilist->cbegin();
int count = 0;
do {
EXPECT_TRUE(count++ < 1);
EXPECT_INTERFACE_EQ(kTestHSInterface.interface, *itr->descriptor());
} while (++itr != ilist->cend());
}
TEST_F(HighSpeedWrapperTest, TestInterfaceConstIterationNotSkippingAlt) {
// This tests that the const_iterator syntax produces the correct interface descriptors.
const usb_interface_descriptor_t wants[2] = {
kTestHSInterface.interface,
kTestHSInterface.alt_interface,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, false, &ilist));
auto itr = ilist->cbegin();
unsigned int count = 0;
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_INTERFACE_EQ(wants[count++], *itr->descriptor());
} while (++itr != ilist->cend());
}
TEST_F(HighSpeedWrapperTest, TestEndpointConstIteration) {
// This tests that the const_iterator syntax produces the correct endpoint descriptors.
const usb_endpoint_descriptor_t wants[2] = {
kTestHSInterface.ep1,
kTestHSInterface.ep2,
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep_itr = interface.GetEndpointList().cbegin();
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count++], ep_itr->descriptor);
EXPECT_FALSE(ep_itr->has_companion);
} while (++ep_itr != interface.GetEndpointList().cend());
}
}
TEST_F(HighSpeedWrapperTest, TestDescriptorRangeIterationSkippingAlt) {
// This tests that for(x : y) syntax produces the correct descriptors.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
for (auto& interface : *ilist) {
auto descriptor_list_itr = interface.GetDescriptorList().cbegin();
EXPECT_DESCRIPTOR_EQ(reinterpret_cast<const usb_descriptor_header_t*>(&kTestHSInterface.ep1),
descriptor_list_itr.header());
++descriptor_list_itr;
EXPECT_DESCRIPTOR_EQ(reinterpret_cast<const usb_descriptor_header_t*>(&kTestHSInterface.ep2),
descriptor_list_itr.header());
++descriptor_list_itr;
EXPECT_DESCRIPTOR_EQ(
reinterpret_cast<const usb_descriptor_header_t*>(&kTestHSInterface.hid_descriptor),
descriptor_list_itr.header());
++descriptor_list_itr;
EXPECT_EQ(descriptor_list_itr, interface.GetDescriptorList().cend());
}
}
// SuperSpeedWrapperTest tests an InterfaceList's ability to process interface descriptors
// corresponding to a SuperSpeed device structure.
class SuperSpeedWrapperTest : public zxtest::Test {
protected:
void SetUp() {
usb_protocol_t proto{&ops_, this};
ops_.get_descriptors_length = UsbGetDescriptorsLength;
ops_.get_descriptors = UsbGetDescriptors;
usb_ = ddk::UsbProtocolClient(&proto);
}
static void UsbGetDescriptors(void* ctx, void* out_descs_buffer, size_t descs_size,
size_t* out_descs_actual) {
memcpy(out_descs_buffer, &kTestSSInterface, descs_size);
*out_descs_actual = descs_size;
}
static size_t UsbGetDescriptorsLength(void* ctx) { return sizeof(kTestSSInterface); }
usb_protocol_ops_t ops_{};
ddk::UsbProtocolClient usb_;
};
TEST_F(SuperSpeedWrapperTest, TestEndpointRangeIteration) {
// This tests that for(x : y) syntax produces the correct endpoint descriptors.
const usb_iter_endpoint_descriptor_t wants[2] = {
{kTestSSInterface.ep1, kTestSSInterface.ss_companion1, true},
{kTestSSInterface.ep2, kTestSSInterface.ss_companion2, true},
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep = interface.GetEndpointList().cbegin();
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count].descriptor, ep->descriptor);
EXPECT_SS_EP_COMP_EQ(wants[count++].ss_companion, ep->ss_companion);
EXPECT_TRUE(ep->has_companion);
} while (++ep != interface.GetEndpointList().cend());
}
}
TEST_F(SuperSpeedWrapperTest, TestEndpointIteration) {
// This tests that the iterator syntax produces the correct endpoint descriptors.
const usb_iter_endpoint_descriptor_t wants[2] = {
{kTestSSInterface.ep1, kTestSSInterface.ss_companion1, true},
{kTestSSInterface.ep2, kTestSSInterface.ss_companion2, true},
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep_itr = interface.GetEndpointList().cbegin();
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count].descriptor, ep_itr->descriptor);
EXPECT_SS_EP_COMP_EQ(wants[count++].ss_companion, ep_itr->ss_companion);
EXPECT_TRUE(ep_itr->has_companion);
} while (++ep_itr != interface.GetEndpointList().cend());
}
}
TEST_F(SuperSpeedWrapperTest, TestEndpointConstIteration) {
// This tests that the const_iterator syntax produces the correct endpoint descriptors.
const usb_iter_endpoint_descriptor_t wants[2] = {
{kTestSSInterface.ep1, kTestSSInterface.ss_companion1, true},
{kTestSSInterface.ep2, kTestSSInterface.ss_companion2, true},
};
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
unsigned int count = 0;
for (auto& interface : *ilist) {
auto ep_itr = interface.GetEndpointList().cbegin();
do {
EXPECT_TRUE(count < countof(wants));
EXPECT_ENDPOINT_EQ(wants[count].descriptor, ep_itr->descriptor);
EXPECT_SS_EP_COMP_EQ(wants[count++].ss_companion, ep_itr->ss_companion);
EXPECT_TRUE(ep_itr->has_companion);
} while (++ep_itr != interface.GetEndpointList().end());
}
}
// HighSpeedWrapperTest tests an InterfaceList's ability to process interface descriptors
// corresponding to a HighSpeed device structure (i.e. no SS-COMPANION descriptors).
class BinaryHidDescriptorTest : public zxtest::Test {
protected:
void SetUp() {
usb_protocol_t proto{&ops_, this};
ops_.get_descriptors_length = UsbGetDescriptorsLength;
ops_.get_descriptors = UsbGetDescriptors;
usb_ = ddk::UsbProtocolClient(&proto);
}
static void UsbGetDescriptors(void* ctx, void* out_descs_buffer, size_t descs_size,
size_t* out_descs_actual) {
memcpy(out_descs_buffer, &descriptor_binary_array, descs_size);
*out_descs_actual = descs_size;
}
static size_t UsbGetDescriptorsLength(void* ctx) { return sizeof(descriptor_binary_array); }
usb_protocol_ops_t ops_{};
ddk::UsbProtocolClient usb_;
};
TEST_F(BinaryHidDescriptorTest, TestBinaryHidDescriptor) {
// This tests that for(x : y) syntax produces the correct descriptors.
std::optional<InterfaceList> ilist;
ASSERT_OK(InterfaceList::Create(usb_, true, &ilist));
usb_hid_descriptor_t* hid_desc = nullptr;
usb_endpoint_descriptor_t* endpt = nullptr;
int iface_count = 0;
for (auto interface : *ilist) {
if (iface_count) {
break;
}
for (auto& descriptor : interface.GetDescriptorList()) {
if (descriptor.bDescriptorType == USB_DT_HID) {
hid_desc = (usb_hid_descriptor_t*)&descriptor;
if (endpt) {
break;
}
} else if (descriptor.bDescriptorType == USB_DT_ENDPOINT) {
if (usb_ep_direction((usb_endpoint_descriptor_t*)&descriptor) == USB_ENDPOINT_IN &&
usb_ep_type((usb_endpoint_descriptor_t*)&descriptor) == USB_ENDPOINT_INTERRUPT) {
endpt = (usb_endpoint_descriptor_t*)&descriptor;
if (hid_desc) {
break;
}
}
}
}
}
ASSERT_TRUE(hid_desc);
ASSERT_TRUE(endpt);
}
} // namespace usb
int main(int argc, char* argv[]) { return RUN_ALL_TESTS(argc, argv); }