本文档介绍了在 Fuchsia 中使用
fuchsia.hardware.usb.endpoint.Endpoint FIDL 协议和
usb::EndpointClient 帮助程序库的 USB 传输请求的生命周期。
术语库
- HCI(主机控制器接口):主机控制器接口驱动程序 负责处理对硬件的 USB 请求,并在主机模式下管理连接的 设备。
- DCI(设备控制器接口):设备控制器接口驱动程序 负责处理在外围设备模式下从 USB 主机接收或传输到 USB 主机的 USB 请求 。
- 端点协议:
fuchsia.hardware.usb.endpoint.EndpointFIDL 协议,用于封装端点操作、VMO 注册、请求排队和完成事件。 - 端点客户端:
usb::EndpointClientC++ 帮助程序类 ,用于管理端点连接、预注册的 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::EndpointClient 和 usb::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)