blob: f580683d76a1a67e5911dd394ffdbc119c34e6b7 [file] [log] [blame]
// Copyright 2020 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 <usb/usb.h>
#include <zxtest/zxtest.h>
#include "lib/fit/defer.h"
namespace {
constexpr usb_descriptor_header_t kTestDescriptorHeader = {
.b_length = sizeof(usb_descriptor_header_t),
.b_descriptor_type = 0,
};
constexpr usb_interface_descriptor_t kTestUsbInterfaceDescriptor = {
.b_length = sizeof(usb_interface_descriptor_t),
.b_descriptor_type = USB_DT_INTERFACE,
.b_interface_number = 0,
.b_alternate_setting = 0,
.b_num_endpoints = 2,
.b_interface_class = 8,
.b_interface_sub_class = 6,
.b_interface_protocol = 80,
.i_interface = 0,
};
constexpr usb_endpoint_descriptor_t kTestUsbEndpointDescriptor = {
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0x81,
.bm_attributes = 2,
.w_max_packet_size = 1024,
.b_interval = 0,
};
constexpr usb_ss_ep_comp_descriptor_t kTestUsbSsEpCompDescriptor = {
.b_length = sizeof(usb_ss_ep_comp_descriptor_t),
.b_descriptor_type = USB_DT_SS_EP_COMPANION,
.b_max_burst = 3,
.bm_attributes = 0,
.w_bytes_per_interval = 0,
};
class UsbLibTest : public zxtest::Test {
public:
void SetUp() override {
proto_.ops = &ops_;
proto_.ctx = this;
ops_.get_descriptors_length = UsbGetDescriptorsLength;
ops_.get_descriptors = UsbGetDescriptors;
}
protected:
static void UsbGetDescriptors(void* ctx, uint8_t* out_descs_buffer, size_t descs_size,
size_t* out_descs_actual) {
auto test = reinterpret_cast<UsbLibTest*>(ctx);
size_t descriptors_length = test->GetDescriptorLength();
if (descs_size < descriptors_length) {
descriptors_length = descs_size;
}
memcpy(out_descs_buffer, test->GetDescriptors(), descriptors_length);
*out_descs_actual = descriptors_length;
}
static size_t UsbGetDescriptorsLength(void* ctx) {
auto test = reinterpret_cast<UsbLibTest*>(ctx);
return test->GetDescriptorLength();
}
void SetDescriptors(void* descriptors) { descriptors_ = descriptors; }
void* GetDescriptors() { return descriptors_; }
void SetDescriptorLength(size_t descriptor_length) { descriptor_length_ = descriptor_length; }
size_t GetDescriptorLength() { return descriptor_length_; }
usb_protocol_t* GetUsbProto() { return &proto_; }
usb_protocol_t proto_{};
usb_protocol_ops_t ops_{};
void* descriptors_ = nullptr;
size_t descriptor_length_ = 0;
};
TEST_F(UsbLibTest, TestUsbDescIterPeekNormal) {
usb_desc_iter_t iter;
SetDescriptors((void*)&kTestDescriptorHeader);
SetDescriptorLength(sizeof(kTestDescriptorHeader));
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
auto desc = usb_desc_iter_peek(&iter);
ASSERT_EQ(memcmp(desc, &kTestDescriptorHeader, sizeof(kTestDescriptorHeader)), 0);
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescPeekOverflow) {
usb_desc_iter_t iter;
usb_descriptor_header_t desc = kTestDescriptorHeader;
// Length is invalid and longer than the actual length.
desc.b_length++;
SetDescriptors((void*)&desc);
SetDescriptorLength(sizeof(desc));
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
ASSERT_EQ(nullptr, usb_desc_iter_peek(&iter));
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescIterPeekHeaderTooShort) {
usb_desc_iter_t iter;
SetDescriptors((void*)&kTestDescriptorHeader);
SetDescriptorLength(sizeof(kTestDescriptorHeader) - 1);
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
ASSERT_EQ(nullptr, usb_desc_iter_peek(&iter));
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescClone) {
usb_desc_iter_t src;
SetDescriptors((void*)&kTestDescriptorHeader);
SetDescriptorLength(sizeof(kTestDescriptorHeader));
auto status = usb_desc_iter_init(GetUsbProto(), &src);
ASSERT_OK(status);
usb_desc_iter_t dest;
ASSERT_OK(usb_desc_iter_clone(&src, &dest));
// This should not affect dest.
usb_desc_iter_release(&src);
auto desc = usb_desc_iter_peek(&dest);
ASSERT_EQ(memcmp(desc, &kTestDescriptorHeader, sizeof(kTestDescriptorHeader)), 0);
ASSERT_TRUE(usb_desc_iter_advance(&dest));
ASSERT_EQ(nullptr, usb_desc_iter_peek(&dest));
usb_desc_iter_release(&dest);
}
TEST_F(UsbLibTest, TestUsbDescAdvanceReset) {
usb_desc_iter_t iter;
SetDescriptors((void*)&kTestDescriptorHeader);
SetDescriptorLength(sizeof(kTestDescriptorHeader));
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
ASSERT_TRUE(usb_desc_iter_advance(&iter));
ASSERT_FALSE(usb_desc_iter_advance(&iter));
usb_desc_iter_reset(&iter);
auto desc = usb_desc_iter_peek(&iter);
ASSERT_TRUE(usb_desc_iter_advance(&iter));
ASSERT_EQ(memcmp(desc, &kTestDescriptorHeader, sizeof(kTestDescriptorHeader)), 0);
ASSERT_EQ(nullptr, usb_desc_iter_peek(&iter));
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescGetStructureNormal) {
usb_desc_iter_t iter;
SetDescriptors((void*)&kTestUsbInterfaceDescriptor);
SetDescriptorLength(sizeof(kTestUsbInterfaceDescriptor));
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
auto desc = usb_desc_iter_get_structure(&iter, sizeof(kTestUsbInterfaceDescriptor));
ASSERT_EQ(memcmp(desc, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor)), 0);
ASSERT_TRUE(usb_desc_iter_advance(&iter));
ASSERT_EQ(nullptr, usb_desc_iter_get_structure(&iter, sizeof(kTestUsbInterfaceDescriptor)));
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescGetStructureOverflow) {
usb_desc_iter_t iter;
usb_interface_descriptor_t desc = kTestUsbInterfaceDescriptor;
SetDescriptors((void*)&desc);
SetDescriptorLength(sizeof(desc) - 1);
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
ASSERT_EQ(nullptr, usb_desc_iter_get_structure(&iter, sizeof(kTestUsbInterfaceDescriptor)));
usb_desc_iter_release(&iter);
}
TEST_F(UsbLibTest, TestUsbDescIterNextInterface) {
// Layout is | Intf | Ep | SsEp | Intf | Ep | SsEp |.
size_t desc_length = (sizeof(kTestUsbInterfaceDescriptor) + sizeof(kTestUsbEndpointDescriptor) +
sizeof(kTestUsbSsEpCompDescriptor)) *
2;
void* desc = malloc(desc_length);
auto cleanup = fit::defer([desc]() { free(desc); });
uint8_t* ptr = reinterpret_cast<uint8_t*>(desc);
usb_desc_iter_t iter;
for (size_t i = 0; i < 2; i++) {
memcpy(ptr, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor));
ptr += sizeof(kTestUsbInterfaceDescriptor);
memcpy(ptr, &kTestUsbEndpointDescriptor, sizeof(kTestUsbEndpointDescriptor));
ptr += sizeof(kTestUsbEndpointDescriptor);
memcpy(ptr, &kTestUsbSsEpCompDescriptor, sizeof(kTestUsbSsEpCompDescriptor));
ptr += sizeof(kTestUsbSsEpCompDescriptor);
}
SetDescriptors(desc);
SetDescriptorLength(desc_length);
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
auto iter_cleanup = fit::defer([&iter]() { usb_desc_iter_release(&iter); });
for (size_t i = 0; i < 2; i++) {
usb_interface_descriptor_t* interface = usb_desc_iter_next_interface(&iter, false);
ASSERT_NE(nullptr, interface);
ASSERT_EQ(memcmp(interface, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor)),
0);
}
ASSERT_EQ(nullptr, usb_desc_iter_next_interface(&iter, false));
}
TEST_F(UsbLibTest, TestUsbDescIterNextEndpoint) {
// Layout is | Intf | Ep | Ep | Intf |.
size_t desc_length =
sizeof(kTestUsbInterfaceDescriptor) * 2 + sizeof(kTestUsbEndpointDescriptor) * 2;
void* desc = malloc(desc_length);
auto cleanup = fit::defer([desc]() { free(desc); });
uint8_t* ptr = reinterpret_cast<uint8_t*>(desc);
usb_desc_iter_t iter;
memcpy(ptr, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor));
ptr += sizeof(kTestUsbInterfaceDescriptor);
for (size_t i = 0; i < 2; i++) {
memcpy(ptr, &kTestUsbEndpointDescriptor, sizeof(kTestUsbEndpointDescriptor));
ptr += sizeof(kTestUsbEndpointDescriptor);
}
memcpy(ptr, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor));
ptr += sizeof(kTestUsbInterfaceDescriptor);
SetDescriptors(desc);
SetDescriptorLength(desc_length);
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
auto iter_cleanup = fit::defer([&iter]() { usb_desc_iter_release(&iter); });
ASSERT_NE(nullptr, usb_desc_iter_next_interface(&iter, false));
for (size_t i = 0; i < 2; i++) {
usb_endpoint_descriptor_t* ep = usb_desc_iter_next_endpoint(&iter);
ASSERT_NE(nullptr, ep);
ASSERT_EQ(memcmp(ep, &kTestUsbEndpointDescriptor, sizeof(kTestUsbEndpointDescriptor)), 0);
}
ASSERT_EQ(nullptr, usb_desc_iter_next_endpoint(&iter));
}
TEST_F(UsbLibTest, TestUsbDescIterNextSsEpComp) {
// Layout is | Intf | Ep | SsEp | SsEp | Intf |.
size_t desc_length = sizeof(kTestUsbInterfaceDescriptor) * 2 +
sizeof(kTestUsbEndpointDescriptor) + sizeof(kTestUsbSsEpCompDescriptor) * 2;
void* desc = malloc(desc_length);
auto cleanup = fit::defer([desc]() { free(desc); });
uint8_t* ptr = reinterpret_cast<uint8_t*>(desc);
usb_desc_iter_t iter;
memcpy(ptr, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor));
ptr += sizeof(kTestUsbInterfaceDescriptor);
memcpy(ptr, &kTestUsbEndpointDescriptor, sizeof(kTestUsbEndpointDescriptor));
ptr += sizeof(kTestUsbEndpointDescriptor);
memcpy(ptr, &kTestUsbSsEpCompDescriptor, sizeof(kTestUsbSsEpCompDescriptor));
ptr += sizeof(kTestUsbSsEpCompDescriptor);
memcpy(ptr, &kTestUsbSsEpCompDescriptor, sizeof(kTestUsbSsEpCompDescriptor));
ptr += sizeof(kTestUsbSsEpCompDescriptor);
memcpy(ptr, &kTestUsbInterfaceDescriptor, sizeof(kTestUsbInterfaceDescriptor));
ptr += sizeof(kTestUsbInterfaceDescriptor);
SetDescriptors(desc);
SetDescriptorLength(desc_length);
ASSERT_OK(usb_desc_iter_init(GetUsbProto(), &iter));
auto iter_cleanup = fit::defer([&iter]() { usb_desc_iter_release(&iter); });
ASSERT_NE(nullptr, usb_desc_iter_next_interface(&iter, false));
ASSERT_NE(nullptr, usb_desc_iter_next_endpoint(&iter));
for (size_t i = 0; i < 2; i++) {
usb_ss_ep_comp_descriptor_t* ss_ep = usb_desc_iter_next_ss_ep_comp(&iter);
ASSERT_NE(nullptr, ss_ep);
ASSERT_EQ(memcmp(ss_ep, &kTestUsbSsEpCompDescriptor, sizeof(kTestUsbSsEpCompDescriptor)), 0);
}
ASSERT_EQ(nullptr, usb_desc_iter_next_ss_ep_comp(&iter));
}
} // namespace