blob: c57fe8cc79c359efa0bcd6830a40e1104031d8bb [file] [log] [blame]
// Copyright 2018 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 <fbl/alloc_checker.h>
#include <fbl/auto_call.h>
#include <pretty/hexdump.h>
#include <utility>
#include "debug-logging.h"
#include "usb-audio-descriptors.h"
namespace audio {
namespace usb {
DescriptorListMemory::~DescriptorListMemory() {
// According to docs in the header, data allocated using
// usb_get_descriptor_list should be free'ed using a standard C "free"
if (data_ != nullptr) {
free(data_);
}
}
fbl::RefPtr<DescriptorListMemory> DescriptorListMemory::Create(usb_protocol_t* proto) {
ZX_DEBUG_ASSERT(proto != nullptr);
fbl::AllocChecker ac;
auto ret = fbl::AdoptRef(new (&ac) DescriptorListMemory());
if (!ac.check()) {
GLOBAL_LOG(ERROR, "Failed to allocate descriptor list memory!");
return nullptr;
}
size_t desc_length = usb_get_descriptors_length(proto);
ret->data_ = malloc(desc_length);
if (!ret->data_) {
return nullptr;
}
usb_get_descriptors(proto, ret->data_, desc_length, &ret->size_);
if (zxlog_level_enabled(SPEW)) {
GLOBAL_LOG(SPEW, "Descriptor List is %zu bytes long\n", ret->size_);
hexdump8_ex(ret->data_, ret->size_, 0u);
}
return ret;
}
DescriptorListMemory::Iterator::Iterator(fbl::RefPtr<DescriptorListMemory> mem)
: mem_(std::move(mem)) {
ZX_DEBUG_ASSERT(mem_ != nullptr);
// Make sure our offset is valid, or go ahead and invalidate it right from
// the start.
ValidateOffset();
}
bool DescriptorListMemory::Iterator::Next() {
auto h = hdr();
// Have we already run out of headers?
if (h == nullptr) {
return false;
}
// Advance to the next header, then validate our offset. Note that there is
// no overflow check here. If we overflow a 64 bit size_t, the implication
// is that we are holding a USB descriptor list in RAM whose size is within
// 256 bytes of our entire 64 bit address space. This really should be
// impossible, so we don't bother to check.
offset_ += h->bLength;
return ValidateOffset();
}
bool DescriptorListMemory::Iterator::ValidateOffset() {
ZX_DEBUG_ASSERT(offset_ <= mem_->size());
size_t space = mem_->size() - offset_;
if (!space) {
return false;
}
// If anything goes wrong from here on out, make sure to invalidate our offset.
auto cleanup = fbl::MakeAutoCall([this] { offset_ = mem_->size(); });
if (space < sizeof(usb_descriptor_header_t)) {
GLOBAL_LOG(WARN,
"Insufficient space at offset %zu to contain even the most basic USB descriptor "
"(space needed %zu, space left %zu)\n",
offset_, sizeof(usb_descriptor_header_t), space);
return false;
}
auto tmp = reinterpret_cast<uintptr_t>(mem_->data());
auto h = reinterpret_cast<const usb_descriptor_header_t*>(tmp + offset_);
if (h->bLength > space) {
GLOBAL_LOG(WARN,
"Malformed USB descriptor header (type %u) at offset %zu. "
"Header indicates that it is %u bytes long, but there %zu bytes remaining\n",
h->bDescriptorType,
offset_,
h->bLength,
space);
return false;
}
GLOBAL_LOG(SPEW, "Found Descriptor [type 0x%02x, len 0x%02x] at offset 0x%zx/0x%zx\n",
h->bDescriptorType, h->bLength, offset_, mem_->size());
cleanup.cancel();
return true;
}
} // namespace usb
} // namespace audio