blob: d1df4d3083019fedde9b44526e8296e8f20111ec [file] [log] [blame] [edit]
// 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 <lib/fake_ddk/fake_ddk.h>
#include <ddktl/protocol/usb.h>
#include <usb/request-cpp.h>
#include <zxtest/base/test.h>
#include <zxtest/zxtest.h>
#include "src/devices/usb/testing/descriptor-builder/descriptor-builder.h"
namespace {
using Request = usb::Request<void>;
using UnownedRequest = usb::BorrowedRequest<void>;
using UnownedRequestQueue = usb::BorrowedRequestQueue<void>;
class FakeDevice : public ddk::UsbProtocol<FakeDevice> {
public:
FakeDevice() {
usb::DeviceDescriptorBuilder dev_builder;
usb::ConfigurationBuilder config_builder(0);
usb::InterfaceBuilder interface_builder(0);
usb::EndpointBuilder in_endpoint_builder(0, USB_ENDPOINT_BULK, 0, true);
usb::EndpointBuilder out_endpoint_builder(0, USB_ENDPOINT_BULK, 0, false);
usb::EndpointBuilder int_endpoint_builder(1, USB_ENDPOINT_INTERRUPT, 0, true);
interface_builder.AddEndpoint(in_endpoint_builder);
interface_builder.AddEndpoint(out_endpoint_builder);
interface_builder.AddEndpoint(int_endpoint_builder);
config_builder.AddInterface(interface_builder);
dev_builder.AddConfiguration(config_builder);
descriptor_ = dev_builder.Generate();
}
void Unplug() {
UnownedRequestQueue queue;
{
fbl::AutoLock<fbl::Mutex> lock(&mutex_);
unplugged = true;
queue = std::move(queue_);
}
queue.CompleteAll(ZX_ERR_IO_NOT_PRESENT, 0);
dev_ops_.unbind(dev_context_);
dev_ops_.release(dev_context_);
}
usb_protocol_t proto() const {
usb_protocol_t proto;
proto.ctx = const_cast<FakeDevice*>(this);
proto.ops = const_cast<usb_protocol_ops_t*>(&usb_protocol_ops_);
return proto;
}
zx_status_t UsbControlOut(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index,
int64_t timeout, const void* write_buffer, size_t write_size) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UsbControlIn(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index,
int64_t timeout, void* out_read_buffer, size_t read_size,
size_t* out_read_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
void UsbRequestQueue(usb_request_t* usb_request, const usb_request_complete_t* complete_cb) {
fbl::AutoLock<fbl::Mutex> lock(&mutex_);
if (unplugged) {
lock.release();
usb_request_complete(usb_request, ZX_ERR_IO_NOT_PRESENT, 0, complete_cb);
return;
}
UnownedRequest request(usb_request, *complete_cb, sizeof(usb_request_t));
queue_.push(std::move(request));
}
usb_speed_t UsbGetSpeed() { return USB_SPEED_FULL; }
zx_status_t UsbSetInterface(uint8_t interface_number, uint8_t alt_setting) {
return ZX_ERR_NOT_SUPPORTED;
}
uint8_t UsbGetConfiguration() { return 0; }
zx_status_t UsbSetConfiguration(uint8_t configuration) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t UsbEnableEndpoint(const usb_endpoint_descriptor_t* ep_desc,
const usb_ss_ep_comp_descriptor_t* ss_com_desc, bool enable) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UsbResetEndpoint(uint8_t ep_address) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t UsbResetDevice() { return ZX_ERR_NOT_SUPPORTED; }
size_t UsbGetMaxTransferSize(uint8_t ep_address) { return 0; }
uint32_t UsbGetDeviceId() { return 0; }
void UsbGetDeviceDescriptor(usb_device_descriptor_t* out_desc) {
memcpy(out_desc, descriptor_.data(), sizeof(usb_device_descriptor_t));
}
zx_status_t UsbGetConfigurationDescriptorLength(uint8_t configuration, size_t* out_length) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UsbGetConfigurationDescriptor(uint8_t configuration, void* out_desc_buffer,
size_t desc_size, size_t* out_desc_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
size_t UsbGetDescriptorsLength() { return descriptor_.size(); }
void UsbGetDescriptors(void* out_descs_buffer, size_t descs_size, size_t* out_descs_actual) {
ZX_ASSERT(descs_size == descriptor_.size());
memcpy(out_descs_buffer, descriptor_.data(), descs_size);
}
zx_status_t UsbGetStringDescriptor(uint8_t desc_id, uint16_t lang_id, uint16_t* out_lang_id,
void* out_string_buffer, size_t string_size,
size_t* out_string_actual) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t UsbCancelAll(uint8_t ep_address) { return ZX_ERR_NOT_SUPPORTED; }
uint64_t UsbGetCurrentFrame() { return 0; }
size_t UsbGetRequestSize() { return UnownedRequest::RequestSize(sizeof(usb_request_t)); }
void SetDevOps(void* ctx, zx_protocol_device_t ops) {
dev_context_ = ctx;
dev_ops_ = ops;
}
private:
fbl::Mutex mutex_;
bool unplugged __TA_GUARDED(mutex_) = false;
UnownedRequestQueue queue_ __TA_GUARDED(mutex_);
void* dev_context_;
zx_protocol_device_t dev_ops_;
std::vector<uint8_t> descriptor_;
};
class Binder : public fake_ddk::Bind {
public:
zx_status_t DeviceGetProtocol(const zx_device_t* device, uint32_t proto_id, void* protocol) {
auto context = reinterpret_cast<const FakeDevice*>(device);
if (proto_id == ZX_PROTOCOL_USB) {
*reinterpret_cast<usb_protocol_t*>(protocol) = context->proto();
return ZX_OK;
}
return ZX_ERR_PROTOCOL_NOT_SUPPORTED;
}
zx_status_t DeviceRemove(zx_device_t* device) { return ZX_OK; }
void DeviceInitReply(zx_device_t* device, zx_status_t status,
const device_init_reply_args_t* args) {}
zx_status_t DeviceAdd(zx_driver_t* drv, zx_device_t* parent, device_add_args_t* args,
zx_device_t** out) {
auto context = reinterpret_cast<FakeDevice*>(parent);
context->SetDevOps(args->ctx, *args->ops);
return ZX_OK;
}
};
extern "C" {
zx_status_t hci_bind(void* ctx, zx_device_t* device);
}
class BTHarness : public zxtest::Test {
public:
void SetUp() override {
ctx = std::make_unique<FakeDevice>();
ASSERT_OK(hci_bind(nullptr, reinterpret_cast<zx_device_t*>(ctx.get())));
}
void TearDown() override { ctx->Unplug(); }
std::unique_ptr<FakeDevice> ctx;
private:
Binder bind;
};
TEST_F(BTHarness, DoesNotCrash) {}
} // namespace