blob: 8e75cac3ad641fb91b7a2d33ce8ddeadb785177d [file] [log] [blame] [edit]
// Copyright 2022 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.
#ifndef SRC_DEVELOPER_ADB_DRIVERS_USB_ADB_FUNCTION_ADB_FUNCTION_H_
#define SRC_DEVELOPER_ADB_DRIVERS_USB_ADB_FUNCTION_ADB_FUNCTION_H_
#include <endian.h>
#include <fidl/fuchsia.hardware.adb/cpp/fidl.h>
#include <fidl/fuchsia.hardware.usb.function/cpp/fidl.h>
#include <fuchsia/hardware/usb/function/cpp/banjo.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <lib/driver/component/cpp/driver_export.h>
#include <lib/driver/devfs/cpp/connector.h>
#include <lib/sync/cpp/completion.h>
#include <zircon/compiler.h>
#include <mutex>
#include <queue>
#include <usb-endpoint/usb-endpoint-client.h>
#include <usb/descriptors.h>
namespace usb_adb_function {
constexpr uint32_t kBulkTxCount = 16;
constexpr uint32_t kBulkRxCount = 16;
constexpr size_t kVmoDataSize = 2048;
constexpr uint16_t kBulkMaxPacket = 512;
constexpr char kDeviceName[] = "usb-adb-function";
namespace fadb = fuchsia_hardware_adb;
namespace fendpoint = fuchsia_hardware_usb_endpoint;
// The driver's internal state machine. Begins in kAwaitingUsbConnection.
enum class State : uint8_t {
// In kAwaitingUsbConnection, we have called function_.SetInterface(this), and
// are waiting for the function driver to call SetConfigured(true). Calls to
// SetConfigured(false) are ignored.
//
// Once SetConfigured(true) has been called, we:
// - Send USB "receive" requests to the endpoint,
// - Tell any connected UsbAdbImpl clients that the device is online, and
// - Move to kOnline.
//
// If a fadb::Device client requests shutdown by calling StopAdb() or closing
// a UsbAdbImpl connection, we call function_.SetInterface(nullptr), and move
// to kStoppingUsb. Likewise if PrepareStop is called.
kAwaitingUsbConnection,
// In kOnline, the USB connection is live, and we respond to any
// QueueTx/Receive requests from UsbAdbImpl clients.
//
// If any of the following happen:
// - A fadb::Device client calls StopAdb().
// - A UsbAdbImpl client closes their channel.
// - The USB function driver calls SetConfigured(false)
// - PrepareStop get called.
//
// ... we call function_.SetInterface(nullptr) and move to kStoppingUsb. (We
// hold onto the responder for any calls to StopAdb()).
kOnline,
// In kStoppingUsb, we wait for all outstanding USB requests to be completed.
// Once they have been, we:
// - Return OK to any StopAdb() calls that triggered the stoppage (or
// happened while in kStoppingUsb).
// - Tell any connected UsbAdbImpl clients that the device is
// offline,
// - Close all UsbAdbImpl connections.
//
// At that point, if the stoppage was caused by a call to PrepareStop (or
// PrepareStop we called while shutting down), we respond that the driver has
// shutdown successfully. Otherwise, we restart the USB connection by calling
// function_.SetInterface(this), and move back to kAwaitingUsbConnection.
kStoppingUsb,
};
// Implements the USB ADB function driver.
class UsbAdbDevice : public fdf::DriverBase,
public ddk::UsbFunctionInterfaceProtocol<UsbAdbDevice>,
public fidl::WireServer<fadb::Device>,
public fidl::Server<fadb::UsbAdbImpl> {
public:
explicit UsbAdbDevice(fdf::DriverStartArgs start_args,
fdf::UnownedSynchronizedDispatcher driver_dispatcher)
: fdf::DriverBase("usb_adb", std::move(start_args), std::move(driver_dispatcher)),
checker_(dispatcher()) {
// TODO(https://fxrev.dev/333883656): Use SynchronizedDispatcher with
// FDF_DISPATCHER_OPTION_ALLOW_SYNC_CALLS instead.
usb_loop_.StartThread("usb-adb-loop");
usb_dispatcher_ = usb_loop_.dispatcher();
}
// Driver lifecycle methods.
zx::result<> Start() override;
void PrepareStop(fdf::PrepareStopCompleter completer) override;
// UsbFunctionInterface methods.
size_t UsbFunctionInterfaceGetDescriptorsSize();
void UsbFunctionInterfaceGetDescriptors(uint8_t* buffer, size_t buffer_size, size_t* out_actual);
zx_status_t UsbFunctionInterfaceControl(const usb_setup_t* setup, const uint8_t* write_buffer,
size_t write_size, uint8_t* out_read_buffer,
size_t read_size, size_t* out_read_actual);
zx_status_t UsbFunctionInterfaceSetConfigured(bool configured, usb_speed_t speed);
zx_status_t UsbFunctionInterfaceSetInterface(uint8_t interface, uint8_t alt_setting);
// fadb::Device methods.
void StartAdb(StartAdbRequestView request, StartAdbCompleter::Sync& completer) override;
void StopAdb(StopAdbCompleter::Sync& completer) override;
// fadb::UsbAdbImpl methods.
void QueueTx(QueueTxRequest& request, QueueTxCompleter::Sync& completer) override;
void Receive(ReceiveCompleter::Sync& completer) override;
private:
mutable async::sequence_checker checker_;
State state_ __TA_GUARDED(checker_) = State::kAwaitingUsbConnection;
// State transition helpers.
void StartUsb() __TA_REQUIRES(checker_);
void EnableEndpoints() __TA_REQUIRES(checker_);
void ResetOrStopUsb() __TA_REQUIRES(checker_);
void CheckUsbStopComplete() __TA_REQUIRES(checker_);
fidl::ServerBindingGroup<fadb::Device> device_bindings_;
// Structure to store pending transfer requests when there are not enough USB request buffers.
struct txn_req_t {
QueueTxRequest request;
size_t start = 0;
QueueTxCompleter::Async completer;
};
// Helper methods to get free request buffer and queue the request for transmitting.
void SendQueued() __TA_REQUIRES(checker_);
bool SendQueuedOnce() __TA_REQUIRES(checker_);
// Helper methods to get free request buffer and queue the request for receiving.
void ReceiveQueued() __TA_REQUIRES(checker_);
bool ReceiveQueuedOnce() __TA_REQUIRES(checker_);
// USB request completion callback methods - these run on the driver
// dispatcher.
void TxComplete(fendpoint::Completion completion) __TA_REQUIRES(checker_);
void RxComplete(fendpoint::Completion completion) __TA_REQUIRES(checker_);
// USB request completion callback methods - these can run on any thread.
void TxCompleteCallback(fendpoint::Completion completion);
void RxCompleteCallback(fendpoint::Completion completion);
uint8_t bulk_out_addr() const { return descriptors_.bulk_out_ep.b_endpoint_address; }
uint8_t bulk_in_addr() const { return descriptors_.bulk_in_ep.b_endpoint_address; }
ddk::UsbFunctionProtocolClient function_;
// Loop and dispatcher used in the background by the USB endpoint clients.
async::Loop usb_loop_{&kAsyncLoopConfigNeverAttachToThread};
async_dispatcher_t* usb_dispatcher_;
// UsbAdbImpl service binding. ServerEnds passed into StartAdb() end up here.
std::optional<fidl::ServerBinding<fadb::UsbAdbImpl>> adb_binding_ __TA_GUARDED(checker_);
// Callbacks to call when the USB stack has been brought down and it's safe to
// call AdbStart().
std::vector<StopAdbCompleter::Async> stop_completers_ __TA_GUARDED(checker_);
// Holds Stop callback to be invoked once shutdown is complete.
std::optional<fdf::PrepareStopCompleter> shutdown_callback_ __TA_GUARDED(checker_);
// USB ADB interface descriptor.
struct {
usb_interface_descriptor_t adb_intf;
usb_endpoint_descriptor_t bulk_out_ep;
usb_endpoint_descriptor_t bulk_in_ep;
} descriptors_ = {
.adb_intf =
{
.b_length = sizeof(usb_interface_descriptor_t),
.b_descriptor_type = USB_DT_INTERFACE,
.b_interface_number = 0, // set later during AllocInterface
.b_alternate_setting = 0,
.b_num_endpoints = 2,
.b_interface_class = USB_CLASS_VENDOR,
.b_interface_sub_class = USB_SUBCLASS_ADB,
.b_interface_protocol = USB_PROTOCOL_ADB,
.i_interface = 0, // This is set in adb
},
.bulk_out_ep =
{
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0, // set later during AllocEp
.bm_attributes = USB_ENDPOINT_BULK,
.w_max_packet_size = htole16(kBulkMaxPacket),
.b_interval = 0,
},
.bulk_in_ep =
{
.b_length = sizeof(usb_endpoint_descriptor_t),
.b_descriptor_type = USB_DT_ENDPOINT,
.b_endpoint_address = 0, // set later during AllocEp
.bm_attributes = USB_ENDPOINT_BULK,
.w_max_packet_size = htole16(kBulkMaxPacket),
.b_interval = 0,
},
};
zx_status_t InitEndpoint(fidl::ClientEnd<fuchsia_hardware_usb_function::UsbFunction>& client,
uint8_t direction, uint8_t* ep_addrs,
usb::EndpointClient<UsbAdbDevice>& ep, uint32_t req_count)
__TA_REQUIRES(checker_);
// Bulk OUT/RX endpoint
usb::EndpointClient<UsbAdbDevice> bulk_out_ep_{usb::EndpointType::BULK, this,
std::mem_fn(&UsbAdbDevice::RxCompleteCallback)};
// Queue of pending Receive requests from client.
std::queue<ReceiveCompleter::Async> rx_requests_ __TA_GUARDED(checker_);
// pending_replies_ only used for bulk_out_ep_
std::queue<fendpoint::Completion> pending_replies_ __TA_GUARDED(checker_);
// Bulk IN/TX endpoint
usb::EndpointClient<UsbAdbDevice> bulk_in_ep_{usb::EndpointType::BULK, this,
std::mem_fn(&UsbAdbDevice::TxCompleteCallback)};
// Queue of pending transfer requests that need to be transmitted once the BULK IN request buffers
// become available.
std::queue<txn_req_t> tx_pending_reqs_ __TA_GUARDED(checker_);
};
} // namespace usb_adb_function
#endif // SRC_DEVELOPER_ADB_DRIVERS_USB_ADB_FUNCTION_ADB_FUNCTION_H_