本文說明如何使用 fuchsia.hardware.usb.endpoint.Endpoint FIDL 通訊協定和 usb::EndpointClient 輔助程式庫,在 Fuchsia 中處理 USB 傳輸要求。
詞彙解釋
- HCI (主機控制器介面):主機控制器介面驅動程式庫負責處理硬體的 USB 要求,以及管理主機模式下連線的裝置。
- DCI (裝置控制器介面):裝置控制器介面驅動程式庫負責在周邊模式下運作時,處理從 USB 主機接收或傳輸至 USB 主機的 USB 要求。
- 端點通訊協定:封裝端點作業、VMO 註冊、要求排隊和完成事件的
fuchsia.hardware.usb.endpoint.EndpointFIDL 通訊協定。 - 端點用戶端:
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_DATA | ZX_CACHE_FLUSH_INVALIDATE的zx_cache_flush),使過時的 CPU 快取失效,然後才能讀取資料。
3. 非同步完成
硬體維修完成後,Endpoint 伺服器會發出向量化 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)