blob: da518d9a89a6afa7c0471c443b9c2fe0fa7ae7eb [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 "src/devices/usb/drivers/usb-virtual-bus/host.h"
#include <fuchsia/hardware/usb/c/banjo.h>
#include <inttypes.h>
#include <lib/ddk/binding_driver.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/driver.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <unistd.h>
#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <usb/usb-request.h>
#include <usb/usb.h>
namespace virtualbus {
void Device::RequestComplete(usb_request_t* request) {
usb::Request<> req(request, parent_req_size_);
constexpr auto kExpected = 20;
completer_->Reply(req.request()->response.actual == kExpected);
}
Device::~Device() {}
void Device::DdkUnbind(ddk::UnbindTxn txn) {
cancel_thread_ = std::thread([this, unbind_txn = std::move(txn)]() mutable {
usb_client_.CancelAll(bulk_out_addr_);
unbind_txn.Reply();
});
}
void Device::RunShortPacketTest(RunShortPacketTestCompleter::Sync& completer) {
if (completer_.has_value()) {
completer.Close(ZX_ERR_BAD_STATE);
return;
}
usb_request_complete_callback_t complete = {
.callback =
[](void* ctx, usb_request_t* request) {
static_cast<Device*>(ctx)->RequestComplete(request);
},
.ctx = this,
};
completer_ = completer.ToAsync();
std::optional<usb::Request<>> req;
constexpr auto kUsbBufSize = 100;
usb::Request<>::Alloc(&req, kUsbBufSize, bulk_out_addr_, parent_req_size_);
usb_client_.RequestQueue(req->take(), &complete);
}
void Device::DdkRelease() {
cancel_thread_.join();
delete this;
}
zx_status_t Device::Bind() {
zx_status_t status = ZX_OK;
if (!usb_client_.is_valid()) {
return ZX_ERR_NOT_SUPPORTED;
}
// Find our endpoints.
std::optional<usb::InterfaceList> usb_interface_list;
status = usb::InterfaceList::Create(usb_client_, true, &usb_interface_list);
if (status != ZX_OK) {
return status;
}
uint8_t bulk_out_addr = 0;
for (auto& interface : *usb_interface_list) {
for (auto ep_itr : interface.GetEndpointList()) {
if (usb_ep_direction(ep_itr.descriptor()) == USB_ENDPOINT_OUT) {
if (usb_ep_type(ep_itr.descriptor()) == USB_ENDPOINT_BULK) {
bulk_out_addr = ep_itr.descriptor()->b_endpoint_address;
}
}
}
}
if (!bulk_out_addr) {
zxlogf(ERROR, "could not find bulk out endpoint");
return ZX_ERR_NOT_SUPPORTED;
}
parent_req_size_ = usb_client_.GetRequestSize();
status = DdkAdd("virtual-bus-test");
if (status != ZX_OK) {
zxlogf(ERROR, "device_add failed");
return status;
}
bulk_out_addr_ = bulk_out_addr;
return status;
}
zx_status_t Bind(void* ctx, zx_device_t* device) {
fbl::AllocChecker ac;
auto dev = fbl::make_unique_checked<virtualbus::Device>(&ac, device);
if (!ac.check()) {
return ZX_ERR_NO_MEMORY;
}
auto status = dev->Bind();
if (status == ZX_OK) {
// Devmgr is now in charge of the memory for dev.
[[maybe_unused]] auto ptr = dev.release();
}
return status;
}
} // namespace virtualbus
namespace {
static constexpr zx_driver_ops_t virtualbustest_driver_ops = []() {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = virtualbus::Bind;
return ops;
}();
} // namespace
ZIRCON_DRIVER(virtualbustest, virtualbustest_driver_ops, "zircon", "0.1");