本文档介绍了在 Fuchsia 中使用 fuchsia.hardware.usb.endpoint.Endpoint FIDL 协议和 usb::EndpointClient 辅助库的 USB 传输请求的生命周期。
术语库
- HCI(主机控制器接口):主机控制器接口驱动程序负责处理向硬件发出的 USB 请求,并在主机模式下管理所连接的设备。
- DCI(设备控制器接口):设备控制器接口驱动程序负责在以外围设备模式运行时,处理从 USB 主机接收或传输到 USB 主机的 USB 请求。
- 端点协议:封装端点操作、VMO 注册、请求排队和完成事件的
fuchsia.hardware.usb.endpoint.EndpointFIDL 协议。 - 端点客户端:用于管理端点连接、预注册的 VMO 池、请求分配和完成事件调度的
usb::EndpointClientC++ 辅助类。
请求生命周期概览
+-----------------------------------------------------------------------------------+
| 1. Registration Phase |
| Driver initializes usb::EndpointClient and pre-registers VMO data buffers |
| via AddRequests() / RegisterVmos(). VMOs are mapped locally once. |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| 2. Queueing & Submission Phase |
| Driver gets request from pool (GetRequest()), populates data (CopyTo()), |
| flushes CPU cache (CacheFlush()), and submits via QueueRequests(). |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| 3. Processing Phase |
| Endpoint Server (HCI or DCI driver) processes requests and transfers data |
| over physical hardware. |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| 4. Completion Phase |
| Endpoint Server sends vectorized OnCompletion FIDL event. |
| EndpointClient invalidates cache (CacheFlushInvalidate()) and returns requests |
| back to pool via PutRequest(). |
+-----------------------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------------------+
| 5. Teardown / Cancellation Phase |
| Driver cancels pending requests via CancelAll() and releases endpoint resources |
| via ep_client.Close(). |
+-----------------------------------------------------------------------------------+
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_DATA | ZX_CACHE_FLUSH_INVALIDATE调用zx_cache_flush),以在读取数据之前使过时的 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:如果传输使系统从挂起状态恢复,则为可选的唤醒 eventpair。
使用 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)