將 Banjo 通訊協定轉換為 FIDL 通訊協定

本頁面提供有關下列主題的操作說明、最佳做法及範例: 將 Banjo 通訊協定轉換成 FIDL 通訊協定,做為 DFv1 至 DFv2 的一部分 或用於遷移驅動程式庫

將 DFv1 驅動程式庫從 Banjo 更新為 FIDL

更新驅動程式庫 .fidl 檔案是驅動程式庫很好的起點 但所有項目皆來自 .fidl 檔案。幸運的是 Banjo 和 FIDL 是從相同的 IDL (即 FIDL) 產生程式碼,因此您能 您可能不需要大幅變更現有的 .fidl 檔案。

下方的 .fidl 範例檔案顯示了 Banjo 至 FIDL 遷移作業:

如要將 DFv1 驅動程式庫從 Banjo 更新為 FIDL,請進行下列變更 (主要位於驅動程式庫 .fidl 檔案中):

  1. 在通訊協定定義之前更新屬性
  2. 更新函式定義以使用 FIDL 錯誤語法
  3. 更新 BUILD.gn 中的 FIDL 目標
  4. 將 FIDL 檔案移至 SDK/FIDL 目錄

1. 在通訊協定定義之前更新屬性

如要使用 Banjo,您必須在 .fidl 中使用 @transport("Banjo") 屬性 檔案。不過,對 FIDL 而言,此屬性並非必要,因為 FIDL 是 )。因此,您可以直接將 @transport("Banjo") 屬性從 驅動程式的 .fidl 檔案,例如:

  • 使用 Banjo:

    @transport("Banjo")
    @banjo_layout("ddk-protocol")
    protocol MyExampleProtocol {
     ...
    }
    
  • 如何使用 FIDL:

    @discoverable
    @transport("Driver")
    protocol MyExampleProtocol {
     ...
    }
    

    上述範例中,@discoverable 屬性 是所有 FIDL 通訊協定的必填屬性。這項屬性可讓 用戶端以使用產生的名稱搜尋這個通訊協定。

    不過,@transport("Driver") 屬性 (表示 這是驅動程式傳輸通訊協定) 的選填屬性。 並使用驅動程式執行階段 FIDL 的驅動程式。 而且只有在駕駛人交談時,才需要遷移驅動程式庫執行階段 同一位驅動程式代管程序的其他駕駛。

    如需更多駕駛傳輸通訊協定的範例,請參閱 驅動程式傳輸範例目錄。

2. 更新函式定義,以便使用 FIDL 錯誤語法

如果 .fidl 檔案中某些函式定義包含傳回狀態, 您需要加以更新,以便使用 FIDL 錯誤語法,而非新增狀態 傳回的內容,例如:

  • 使用退貨結構:

    protocol MyExampleProtocol {
       MyExampleFunction() -> (struct {
           Error zx.status;
       });
    };
    

    (資料來源:wlanphy-impl.fidl)

  • 如何傳回 FIDL 錯誤語法:

    protocol MyExampleProtocol {
       BMyExampleFunction() -> () error zx.status;
    };
    

    (資料來源:phyimpl.fidl)

使用 FIDL 錯誤語法有以下優點:

  • 回傳結構現在可以專注於需要傳送的資料 。

  • 由於擷取 不需要讀取傳回結構的狀態。

3. 更新 BUILD.gn 中的 FIDL 目標

編輯這個 .fidl 檔案的 BUILD.gn 檔案,新增下列程式碼 不允許:

contains_drivers = true

以下範例顯示 FIDL 目標中的 contains_drivers = true 行:

import("//build/fidl/fidl.gni")

fidl("fuchsia.wlan.phyimpl") {
 sdk_category = "partner"
 sources = [ "phyimpl.fidl" ]
 public_deps = [
   "//sdk/fidl/fuchsia.wlan.common",
   "//sdk/fidl/fuchsia.wlan.ieee80211",
   "//zircon/vdso/zx",
 ]
 contains_drivers = true
 enable_banjo = true
}

(資料來源:BUILD.gn)

如果沒有 contains_drivers = true 行,則使用 @transport("Driver") 屬性不會產生至 FIDL 程式庫 正確做法。

4. 將 FIDL 檔案移至 SDK/FIDL 目錄

將更新後的 .fidl 檔案從 sdk/banjo 目錄移至 sdk/fidl 目錄內。

sdk/fidl 目錄是儲存所有 及產生 FIDL 代碼不過,如果仍在使用 Banjo 結構或函式 任何其他位置 (也就是驅動程式庫通訊以外),移動這個 .fidl 系統不允許這個檔案。在這種情況下,你可以保留這個「.fidl」的副本 該檔案位於 sdk/banjo 目錄中。

(選用) 更新 DFv1 驅動程式庫以使用驅動程式庫執行階段

本節要求從.fidl 上一節 才能順利產生 FIDL 程式碼這個 FIDL 代碼的用途是 驅動程式之間的驅動程式執行階段 FIDL 通訊。

如要更新 DFv1 驅動程式庫以使用驅動程式庫執行階段,請按照以下步驟操作:

  1. 更新驅動程式庫執行階段的依附元件
  2. 設定驅動程式庫執行階段 FIDL 的用戶端和伺服器物件
  3. 更新驅動程式庫以使用驅動程式庫執行階段 FIDL
  4. 提出 FIDL 要求

1. 更新驅動程式庫執行階段的依附元件

更新伺服器和用戶端,加入新的依附元件,以便使用 驅動程式庫執行階段:

  1. BUILD.gn 檔案中更新依附元件欄位,加入 中的幾行程式碼:

    //sdk/fidl/<YOUR_FIDL_LIB>_cpp_wire
    //sdk/fidl/<YOUR_FIDL_LIB>_cpp_driver
    //src/devices/lib/driver:driver_runtime
    

    YOUR_FIDL_LIB 替換為 FIDL 程式庫的名稱,例如:

    public_deps = [
      ...
      "//sdk/fidl/fuchsia.factory.wlan:fuchsia.factory.wlan_cpp_wire",
      "//sdk/fidl/fuchsia.wlan.fullmac:fuchsia.wlan.fullmac_cpp_driver",
      "//sdk/fidl/fuchsia.wlan.phyimpl:fuchsia.wlan.phyimpl_cpp_driver",
      ...
      "//src/devices/lib/driver:driver_runtime",
      ...
    ]
    

    (資料來源:BUILD.gn)

  2. 在原始碼標頭中更新 include 行,例如:

    #include <fidl/<YOUR_FIDL_LIB>/cpp/driver/wire.h>
    #include <lib/fdf/cpp/arena.h>
    #include <lib/fdf/cpp/channel.h>
    #include <lib/fdf/cpp/channel_read.h>
    #include <lib/fdf/cpp/dispatcher.h>
    #include <lib/fidl/cpp/wire/connect_service.h>
    #include <lib/fidl/cpp/wire/vector_view.h>
    ...
    

    (資料來源:wlanphy-impl-device.h)

2. 設定驅動程式庫執行階段 FIDL 的用戶端和伺服器物件

更新伺服器和用戶端,以使用驅動程式庫執行階段 FIDL。

在用戶端端執行下列操作:

  1. 宣告 FIDL 用戶端物件 (fdf::WireSharedClient<ProtocolName>fdf::WireSyncClient<ProtocolName>)。

    這個 FIDL 用戶端物件可讓您進行 FIDL 呼叫 將要求傳送至伺服器

    以下範例程式碼顯示裝置類別中的 FIDL 用戶端物件:

    class Device : public fidl::WireServer<fuchsia_wlan_device::Phy>,
                   public ::ddk::Device<Device, ::ddk::MessageableManual, ::ddk::Unbindable> {
     ...
     private:
      // Dispatcher for being a FIDL server listening MLME requests.
      async_dispatcher_t* server_dispatcher_;
    
      // The FIDL client to communicate with iwlwifi
      fdf::WireSharedClient<fuchsia_wlan_wlanphyimpl::WlanphyImpl> client_;
     ...
    

    (資料來源:device_dfv2.h)

  2. (僅適用於非同步呼叫) 宣告調度工具物件 裝置類別中 (fdf::Dispatcher)。

    需要調度器來繫結步驟 1 中的 FIDL 用戶端物件。

    以下範例程式碼顯示 FIDL 用戶端和調度工具物件 裝置類別:

    class Device : public fidl::WireServer<fuchsia_wlan_device::Phy>,
                   public ::ddk::Device<Device, ::ddk::MessageableManual, ::ddk::Unbindable> {
     ...
     private:
      // Dispatcher for being a FIDL server listening MLME requests.
      async_dispatcher_t* server_dispatcher_;
    
      // The FIDL client to communicate with iwlwifi
      fdf::WireSharedClient<fuchsia_wlan_wlanphyimpl::WlanphyImpl> client_;
    
      // Dispatcher for being a FIDL client firing requests to WlanphyImpl device.
      fdf::Dispatcher client_dispatcher_;
     ...
    

    (資料來源:device_dfv2.h)

    您可以使用 fdf::Dispatcher::GetCurrent() 方法或建立新的非預設新方法 調度工具 (請參閱 更新 DFv1 驅動程式庫以使用非預設調度工具)。

在伺服器端內執行下列操作:

  1. 繼承 FIDL 伺服器類別 (fdf::WireServer<ProtocolName>)

  2. 宣告 FIDL 伺服器的調度工具物件 (fdf::Dispatcher)

    與用戶端不同,伺服器端一律需要調度工具 來繫結 FIDL 伺服器物件

    下方範例程式碼顯示 FIDL 伺服器和調度工具物件 裝置類別:

    class Device : public fidl::WireServer<fuchsia_wlan_device::Phy>,
                   public ::ddk::Device<Device, ::ddk::MessageableManual, ::ddk::Unbindable> {
     ...
     private:
      // Dispatcher for being a FIDL server listening MLME requests.
      async_dispatcher_t* server_dispatcher_;
     ...
    

    (資料來源:device_dfv2.h)

    您可以使用 fdf::Dispatcher::GetCurrent() 方法或建立新的非預設新方法 調度工具 (請參閱 更新 DFv1 驅動程式庫以使用非預設調度工具)。

.fidl 檔案中執行下列操作:

  • 為以下項目定義駕駛服務通訊協定: 用戶端和伺服器的可用性

    以下範例通訊協定碼顯示的是 .fidl 檔案:

    service Service {
        wlan_phy_impl client_end:WlanPhyImpl;
    };
    

    (資料來源:phyimpl.fidl)

3. 更新驅動程式庫以使用驅動程式庫執行階段 FIDL

透過上一步中的變更, 可以開始更新驅動程式庫實作方式,使用驅動程式庫 執行階段 FIDL。

在用戶端端執行下列操作:

  1. 連線至家長裝置驅動程式新增的通訊協定 其傳出目錄,呼叫 DdkConnectRuntimeProtocol() 函式,例如:

    auto client_end = DdkConnectRuntimeProtocol<fuchsia_wlan_softmac::Service::WlanSoftmac>();
    

    (資料來源:device.cc)

    這個函式會建立一組端點:

    • 這個函式會傳回 fdf::ClientEnd<ProtocolName> 物件 。
    • fdf::ServerEnd<ProtocolName> 物件會在不發出通知的情況下, 家長裝置驅動程式庫。
  2. 呼叫端取得用戶端端物件時,將物件傳送至 fdf::WireSharedClient<ProtocolName>() 的建構函式 (或 fdf::WireSyncClient<ProtocolName>()),例如:

    client_ = fdf::WireSharedClient<fuchsia_wlan_phyimpl::WlanPhyImpl>(std::move(client), client_dispatcher_.get());
    

    (資料來源:device.cc)

在伺服器端內執行下列操作:

  1. 在裝置類別中宣告傳出目錄物件,例如:

    #include <lib/driver/outgoing/cpp/outgoing_directory.h>
    
    class Device : public DeviceType,
                   public fdf::WireServer<fuchsia_wlan_phyimpl::WlanPhyImpl>,
                   public DataPlaneIfc {
    ...
    
       fdf::OutgoingDirectory outgoing_dir_;
    

    (資料來源:device.h)

  2. 呼叫 fdf::OutgoingDirectory::Create() 函式,取得父項 驅動程式庫建立傳出目錄物件,例如:

    #include <lib/driver/outgoing/cpp/outgoing_directory.h>
    
    ...
    
    Device::Device(zx_device_t *parent)
        : DeviceType(parent),
          outgoing_dir_(
              fdf::OutgoingDirectory::Create(
                 fdf::Dispatcher::GetCurrent()->get()))
    

    (資料來源:wlan_interface.cc)

  3. 將服務新增至傳出目錄並提供服務。

    父項驅動程式庫程式的服務通訊協定會提供給這個傳出目錄 讓兒童驅動程式庫連接該裝置

    在父項驅動程式的服務回呼函式 (為函式) 子項節點連線至服務時會呼叫的物件) fdf::ServerEnd<ProtocolName>使用 fdf::BindServer() 函式,例如:

    zx_status_t Device::ServeWlanPhyImplProtocol(
            fidl::ServerEnd<fuchsia_io::Directory> server_end) {
      // This callback will be invoked when this service is being connected.
      auto protocol = [this](
          fdf::ServerEnd<fuchsia_wlan_phyimpl::WlanPhyImpl> server_end) mutable {
        fdf::BindServer(fidl_dispatcher_.get(), std::move(server_end), this);
        protocol_connected_.Signal();
      };
    
      // Register the callback to handler.
      fuchsia_wlan_phyimpl::Service::InstanceHandler handler(
           {.wlan_phy_impl = std::move(protocol)});
    
      // Add this service to the outgoing directory so that the child driver can
      // connect to by calling DdkConnectRuntimeProtocol().
      auto status =
           outgoing_dir_.AddService<fuchsia_wlan_phyimpl::Service>(
                std::move(handler));
      if (status.is_error()) {
        NXPF_ERR("%s(): Failed to add service to outgoing directory: %s\n",
             status.status_string());
        return status.error_value();
      }
    
      // Serve the outgoing directory to the entity that intends to open it, which
      // is DFv1 in this case.
      auto result = outgoing_dir_.Serve(std::move(server_end));
      if (result.is_error()) {
        NXPF_ERR("%s(): Failed to serve outgoing directory: %s\n",
             result.status_string());
        return result.error_value();
      }
    
      return ZX_OK;
    }
    

    (資料來源:device.cc)

    請注意,fdf::BindServer() 函式需要 調度器做為輸入內容。您可以使用預設值 驅動程式代管程序提供的驅動程式庫調度工具 (fdf::Dispatcher::GetCurrent()->get()) 或新建非預設 調度器會分別處理 FIDL 要求 (請參閱 更新 DFv1 驅動程式庫以使用非預設調度工具)。

    此時用戶端和伺服器已準備好互相通訊 來下載該驅動程式庫

4. 提出 FIDL 要求

如要發出 FIDL 呼叫,請使用 在用戶端上建構的 fdf::WireSharedClient<ProtocolName>() 物件 即可。

請參閱以下執行 FIDL 呼叫的語法 (其中 CLIENT_ 是 執行個體):

  • 非同步 FIDL 呼叫:

    CLIENT_.buffer(*std::move(arena))->MyExampleFunction().ThenExactlyOnce([](fdf::WireUnownedResult<FidlFunctionName>& result) mutable {
      // Your result handler.
    });
    

    (資料來源:device.cc)

  • 同步 FIDL 呼叫:

    auto result = CLIENT_.sync().buffer(*std::move(arena))->MyExampleFunction();
    // Your result handler.
    

    (資料來源:device.cc)

以下做法有助您執行 FIDL 呼叫:

  • 使用 FIDL 錯誤語法時 您可以呼叫 result.is_error() 檢查呼叫是否傳回 網域錯誤。同樣地,result.error_value() 呼叫會傳回 確切的錯誤值

  • 進行非同步的雙向用戶端 FIDL 呼叫時 傳遞回呼,您可以使用 .Then(callback).ThenExactlyOnce(callback),指定所需的取消作業 待處理回呼的語意。

  • 同步 FIDL 呼叫會等候伺服器端的回呼。 因此,在 .fidl 檔案。如果沒有,通話只會傳送「射後不理」和 就會立即傳回。

    如需回呼定義,請參閱 .fidl 檔案中的範例:

    protocol MyExampleProtocol {
    // You can only make async calls based on this function.
      FunctionWithoutCallback();
    
      // You can make both sync and async calls based on this function.
      FunctionWithCallback() -> ();
    }
    

    建立回呼定義後,就會有一個函式 ( 同步與非同步回呼範例中的叫用 皆須在伺服器端實作 (請參閱 MyExampleFunction())。

    伺服器端裝置類別繼承 fdf::WireServer<ProtocolName> 並依據通訊協定定義生成虛擬函式 (與 fidl::WireServer<ProtocolName> 相似)。格式如下 這個函式

    void MyExampleFunction(MyExampleFunctionRequestView request, fdf::Arena& arena, MyExampleFunctionCompleter::Sync& completer);
    

    這個函式使用三個參數:

    • MyExampleFunctionRequestView - 由 FIDL 產生,其中包含 以及您要從用戶端傳送至伺服器的請求結構

    • fdf::Arena:這是這個 FIDL 訊息的緩衝區。這個緩衝區為 傳遞作業可做為 會透過 completer 物件傳回訊息或錯誤語法。個人中心 也能重複使用,向下一個等級的驅動程式發出 FIDL 呼叫。

    • MyExampleFunctionCompleter - 由 FIDL 產生,用於叫用 並將這個 FIDL 呼叫的結果傳回用戶端。 如果已定義 FIDL 錯誤語法,您可以使用 completer.ReplySuccess()completer.ReplyError() 傳回訊息和錯誤狀態。 如未定義 FIDL 錯誤語法,則只能使用 completer.Reply() 即可傳回訊息。

  • 你可以將運動場物件移到 FIDL 呼叫中,或直接傳遞 使用 *arena。不過,移動競技物件可能會公開潛在錯誤。 因此,建議您改為傳遞 Area 物件。由於 競技場可重複使用進行下一個層級的 FIDL 呼叫,但 Area 物件不會 已刪除。

  • 如果您的驅動程式庫傳送的訊息格式為 .fidl 檔案,FIDL 會根據 定義然而,在這個情況中,建議你使用接線類型,因為 線型是暫時的, HLCPP 的類型。

  • 如果 FIDL 偵測到端點交換無效,就會關閉其管道 這就是驗證錯誤:

    • 驗證錯誤範例的其中一個例子,就是傳遞 0 (表示非彈性) 列舉) 只會定義 1 到 5 的值。需要進行驗證時 管道將永久關閉,而後續收到的所有訊息 也會失敗這個行為與 Banjo 不同。( 請參閱嚴格與彈性)。

    • 無效值還包括具有 zx_handle_t 值的核心物件 設為 0。例如 zx::vmo(0)

(選用) 更新 DFv1 驅動程式庫以使用非預設調度工具

fdf::Dispatcher::GetCurrent() 方法可讓您 執行驅動程式庫的預設調度器。 建議您盡可能只使用這個預設調度工具。不過 如果您需要建立新的非預設調度工具, 不需要瞭解調度器屬於哪個驅動程式庫。這樣一來, 以便設定適當屬性並關閉驅動程式庫程式 正確設定調度器

以下各節說明分配及管理資源的進階用途 您自己的非預設調度工具:

  1. 分配調度工具
  2. 關閉調度工具

1. 分配調度工具

為了確保調度器在代管執行緒中建立及執行 因為驅動程式庫架構,調度器的分配作業必須在 驅動程式庫程式架構叫用的函式 (例如 DdkInit()):

void Device::DdkInit(ddk::InitTxn txn) {
  bool fw_init_pending = false;
  const zx_status_t status = [&]() -> zx_status_t {
    auto dispatcher = fdf::SynchronizedDispatcher::Create(
        {}, "nxpfmac-sdio-wlanphy",
        [&](fdf_dispatcher_t *) { sync_completion_signal(&fidl_dispatcher_completion_); });
    if (dispatcher.is_error()) {
      NXPF_ERR("Failed to create fdf dispatcher: %s", dispatcher.status_string());
      return dispatcher.status_value();
    }
    fidl_dispatcher_ = std::move(*dispatcher);
  ...

(資料來源:device.cc)

2. 關閉調度工具

同樣地,調度器關閉也需要在函式中進行 驅動程式庫架構。DdkUnbind()device_unbind() 方法非常適合執行此作業。

請注意,調度工具關閉為非同步性質,因此需要處理 才是正確的做法舉例來說,如果調度器在 DdkUnbind() 呼叫,我們需要使用 ddk::UnbindTxn 物件 ( 從之前的 Unbind() 呼叫) 叫用 ddk::UnbindTxn::Reply() 呼叫調度工具的關閉回呼,以確保乾淨關機。

以下程式碼片段範例示範關閉程序 前提:

  1. ddk::UnbindTxn 物件儲存在 DdkUnbind() 中:

    void DdkUnbind(ddk::UnbindTxn txn) {
      // Move the txn here because it’s not copyable.
      unbind_txn_ = std::move(txn);
      ...
    }
    
  2. 做為繫結或 DdkInit() 掛鉤的一部分,建立具有下列權限的調度工具: 叫用 ddk::UnbindTxn::Reply() 的關閉回呼:

      auto dispatcher = fdf::Dispatcher::Create(0, [&](fdf_dispatcher_t*) {
        if (unbind_txn_)
          unbind_txn_->Reply();
        unbind_txn_.reset();
      });
    
  3. DdkUnbind() 結尾的調度工具呼叫 ShutdownAsync()

    void DdkUnbind(ddk::UnbindTxn txn) {
      // Move the txn here because it’s not copyable.
      unbind_txn_ = std::move(txn);
      ...
      dispatcher.ShutDownAsync();
    }
    

但是,如果驅動程式庫中有多名調度員, 由於 ddk::UnbindTxn::Reply() 僅呼叫一次,因此您需要實作 關機作業鏈。舉例來說,假設分配到調度器 A 和 B (可互換),您可以:

  1. 在 A 的關閉回呼中呼叫 B 的 ShutdownAsync()
  2. 在 B 的關閉回呼中呼叫 ddk::UnbindTxn::Reply()

(選用) 更新 DFv1 驅動程式庫以使用雙向通訊

通常只有用戶端上的裝置才需要主動 向伺服器端裝置發出要求但在某些情況下 不必等待整個管道 回應。

如要在 DFv1 驅動程式庫中建立雙向通訊, 下列三種選項:

  • 方法 1:在 FIDL 通訊協定中定義事件 (請參閱「實作 C++ FIDL 伺服器」一節)。

  • 選項 2:反向實作第二個 FIDL 通訊協定 讓兩端的裝置會同時成為伺服器和用戶端

  • 方法 3:使用 FIDL 中的「hanging get」模式 (a FIDL 評分量表建議的流量控制設計模式)。

使用事件的原因 (方法 1) 包括:

  • 簡單 - 一個通訊協定比兩個簡單。

  • 序列化 - 如果您需要兩個通訊協定,事件和回應回覆都是 保證會依照寫入管道的順序進行序列化。

不使用事件的原因 (方法 1) 包括:

  • 您必須在郵件從伺服器傳送到 用戶端。

  • 您需要控制郵件流程。

下列通訊協定實作選項 2,不過兩者的定義是 .fidl 檔案代表兩個不同方向:

在進行 WLAN 驅動程式庫遷移時,團隊選擇了選項 2,因為該選項 將引進其他來自未知網域的 FIDL 語法。但 必須注意的是,WLAN 驅動程式庫是第一個驅動程式庫執行階段 且目前系統不支援驅動程式庫傳輸中的 FIDL 事件 。

視您的需求而定,發送通知通話 (選項 3) 通常最好 選項,因為後者可讓用戶端指明事件已經就緒 處理事件,也提供了流量控制(如有必要 還有其他方法可以為事件新增流量控制,下文將說明 「使用特別銘謝」一節所述的「節制事件」 FIDL 評分量表)。

請更新 DFv1 驅動程式的單元測試以使用 FIDL

如果已有驅動程式的 Banjo API 單元測試,您將需要 來遷移測試,提供模擬 FIDL 伺服器而非 Banjo 伺服器 (或模擬 FIDL 用戶端)。

在 DFv1 中模擬 FIDL 伺服器或用戶端時,您可以使用 MockDevice::FakeRootParent() 方法,例如:

std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent();

(資料來源:ft_device_test.cc)

MockDevice::FakeRootParent() 方法已與 DriverRuntime 測試程式庫 (這是唯一支援的測試程式庫) DFv1)。MockDevice::FakeRootParent() 方法會建立 使用者的 fdf_testing::DriverRuntime 例項。接著執行個體會啟動 並為使用者建立前景驅動程式庫調度工具。 不過,您也可以透過此物件建立背景調度工具。個人中心 可以使用 fdf_testing::DriverRuntime::GetInstance() 擷取執行個體 方法。

如需範例,請參閱模擬的單元測試 DFv1 驅動程式庫的 PWM 和 vreg FIDL 通訊協定

此外,下列程式庫也有助於編寫驅動程式庫單元測試:

其他資源

本節提及的所有「Gerrit 變更」

本節提及的所有原始碼檔案

本節提及的所有說明文件頁面