USB 请求的生命周期

本文档介绍了在 Fuchsia 中使用 fuchsia.hardware.usb.endpoint.Endpoint FIDL 协议和 usb::EndpointClient 帮助程序库的 USB 传输请求的生命周期。

术语库

  • HCI(主机控制器接口):主机控制器接口驱动程序 负责处理对硬件的 USB 请求,并在主机模式下管理连接的 设备。
  • DCI(设备控制器接口):设备控制器接口驱动程序 负责处理在外围设备模式下从 USB 主机接收或传输到 USB 主机的 USB 请求 。
  • 端点协议fuchsia.hardware.usb.endpoint.Endpoint FIDL 协议,用于封装端点操作、VMO 注册、请求排队和完成事件。
  • 端点客户端usb::EndpointClient C++ 帮助程序类 ,用于管理端点连接、预注册的 VMO 池、请求分配和 完成事件调度。

请求生命周期概览


1. 分配和 VMO 注册

Fuchsia USB 驱动程序在端点初始化时预先注册 VMO 缓冲区,而不是为每次传输动态分配请求上下文。

预注册的 VMO 使用 RegisterVmos FIDL 方法与唯一的 VmoId 值相关联。底层端点服务器会保存这些 VMO,而 usb::EndpointClient 会将 VMO 内存映射到客户端进程,并填充线程安全空闲请求池 (usb::FidlRequestPool)。

注意 :预注册的 VMO 和请求绑定到 Endpoint 客户端通道的生命周期。当调用 usb::EndpointClient::Close() 或关闭端点通道时,所有注册的 VMO 都会自动取消注册,并且其映射的虚拟地址会被释放。


2. 提交和缓存管理

使用 QueueRequests FIDL 方法(或通过 usb::EndpointClient::operator->())将请求提交到端点队列。

缓存维护义务

由于 USB 控制器会写入或读取预注册的物理内存区域 (VMO),因此客户端负责维护 CPU 缓存一致性:

  • 在出站 (TX) 写入请求之前: 客户端必须对映射的缓冲区调用 req.CacheFlush(...)(或使用 ZX_CACHE_FLUSH_DATA 调用 zx_cache_flush),以确保写入的 CPU 数据在硬件传输之前刷新到主 RAM。
  • 在入站 (RX) 读取请求之后: 收到完成事件后,客户端必须对映射的req.CacheFlushInvalidate(...) 缓冲区调用zx_cache_flush(或使用ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE),以在读取数据之前使过时的 CPU 缓存失效。

3. 异步完成

硬件服务完成后,端点服务器会通过发出矢量化 FIDL 事件来通知客户端:

strict -> OnCompletion(resource struct {
    completion vector<Completion>:REQUEST_MAX;
});

每个 Completion 表包含: * request:已完成的 fuchsia.hardware.usb.request.Request。 * status: 传输完成状态代码 (zx.Status)。 * transfer_size: 成功传输的总字节数。 * wake_lease:如果传输使系统脱离暂停状态,则为可选唤醒事件对。

使用 usb::EndpointClient 时,传入的完成事件会自动调度到驱动程序的指定成员回调函数。


4. 取消和关停

驱动程序可以通过对端点客户端调用 CancelAll() 来取消未完成的传输请求。 所有待处理的传输都会以 ZX_ERR_CANCELED 状态异步完成,并返回给调用方。

在驱动程序取消绑定或拆解期间,驱动程序必须使用 ep_client.Close() 关闭端点客户端,以安全地取消待处理的传输、取消注册 VMO 并取消绑定 FIDL 事件监听器。


现代 C++ 示例

以下示例演示了如何使用 usb::EndpointClientusb::FidlRequest 实现 USB 客户端驱动程序:

#include <fidl/fuchsia.hardware.usb.endpoint/cpp/fidl.h>
#include <fidl/fuchsia.hardware.usb.function/cpp/fidl.h>
#include <lib/driver/component/cpp/driver_base.h>
#include <usb-endpoint/usb-endpoint-client.h>
#include <usb/request-fidl.h>

class SampleUsbDriver : public fdf::DriverBase {
 public:
  SampleUsbDriver(fdf::DriverStartArgs start_args,
                  fdf::UnownedSynchronizedDispatcher dispatcher)
      : DriverBase("SampleUsbDriver", std::move(start_args), std::move(dispatcher)),
        bulk_in_ep_(usb::EndpointType::BULK, this,
                    std::mem_fn(&SampleUsbDriver::OnBulkInComplete)) {}

  zx::result<> Start() override {
    // 1. Connect and initialize endpoint client channel
    uint8_t ep_addr = 0x81;  // IN bulk endpoint address
    zx_status_t status = bulk_in_ep_.Init(ep_addr, function_client_, dispatcher());
    if (status != ZX_OK) {
      return zx::error(status);
    }

    // 2. Pre-register VMO buffers and populate request pool (e.g. 4 buffers of 512 bytes)
    constexpr size_t kBufferCount = 4;
    constexpr size_t kMaxPacketSize = 512;
    size_t allocated = bulk_in_ep_.AddRequests(
        kBufferCount, kMaxPacketSize, fuchsia_hardware_usb_request::Buffer::Tag::kVmoId);
    if (allocated != kBufferCount) {
      return zx::error(ZX_ERR_NO_MEMORY);
    }

    // 3. Queue initial pre-buffered requests
    QueueBulkInRequests();
    return zx::ok();
  }

  void PrepareStop(fdf::PrepareStopCompleter completer) override {
    // Graceful cancellation and teardown during driver stop
    bulk_in_ep_.Close();
    completer(zx::ok());
  }

 private:
  void QueueBulkInRequests() {
    std::vector<fuchsia_hardware_usb_request::Request> reqs;

    while (!bulk_in_ep_.RequestsEmpty()) {
      auto fidl_req = bulk_in_ep_.GetRequest();
      if (!fidl_req) break;

      // Extract raw request object for FIDL transmission
      reqs.push_back(fidl_req->take_request());
    }

    if (!reqs.empty()) {
      auto result = bulk_in_ep_->QueueRequests(std::move(reqs));
      if (!result.ok()) {
        fdf::error("Failed to queue endpoint requests: {}", result.FormatDescription());
      }
    }
  }

  // 4. Vectorized completion handler
  void OnBulkInComplete(std::vector<fuchsia_hardware_usb_endpoint::Completion> completions) {
    for (auto& completion : completions) {
      zx_status_t status = completion.status().value_or(ZX_ERR_INTERNAL);
      uint64_t bytes_transferred = completion.transfer_size().value_or(0);

      if (status == ZX_OK) {
        usb::FidlRequest fidl_req(std::move(completion.request().value()));

        // Invalidate CPU cache after inbound read transfer
        fidl_req.CacheFlushInvalidate(bulk_in_ep_.GetMapped());

        // Process payload data from mapped VMO
        std::vector<uint8_t> buffer(bytes_transferred);
        fidl_req.CopyFrom(0, buffer.data(), bytes_transferred, bulk_in_ep_.GetMapped());
        ProcessData(buffer);

        // Recycle request back to free pool
        bulk_in_ep_.PutRequest(std::move(fidl_req));
      }
    }

    // Re-queue available requests to keep pipeline filled
    QueueBulkInRequests();
  }

  void ProcessData(const std::vector<uint8_t>& data) {
    // Process received packet...
  }

  fidl::ClientEnd<fuchsia_hardware_usb_function::UsbFunction> function_client_;
  usb::EndpointClient<SampleUsbDriver> bulk_in_ep_;
};

USB 请求堆栈示例

主机堆栈 (HCI)

xHCI Host Controller -> USB Bus -> USB Core Device Driver -> Class Driver (e.g. usb-audio, usb-hid)

外围设备堆栈 (DCI)

DCI Controller (dwc3 / dwc2) -> Peripheral Core Driver -> Function Driver (e.g. usb-cdc-function, ums-function)

另请参阅