將 DDK 介面更新至 DFv2

本頁提供遷移驅動程式庫介面的最佳做法和範例 從 DFv1 到 DFv2

DFv1 驅動程式使用 DDK (驅動程式開發套件) 程式庫 (//src/lib/ddk)。 在 DFv2 中,這個程式庫已由驅動程式元件取代 程式庫,內含 DFv2 驅動程式的大多數必要公用程式。

將依附元件從 DDK 更新至 DFv2

移除 DFv1 驅動程式中 DDK 程式庫下的所有套件依附元件,並 改用新的 DFv2 程式庫

來源標頭

替換來源標頭檔案中的 DDK 依附元件:

DFv1

//sdk/lib/driver/component/cpp:cpp

DFv2

#include <lib/driver/component/cpp/driver_base.h>

建構依附元件

替換 BUILD.gn 檔案中 DDK 依附元件:

DFv1

"//src/lib/ddk",
"//src/lib/ddktl",

DFv2

"//sdk/lib/driver/component/cpp:cpp",

將介面從 DDK 更新至 DFv2

ddk:裝置組合

ddk::Device<>ddktl/device.h 中定義的混合值, 簡化了在 C++ 中編寫 DDK 驅動程式的操作。

DFv1

以下 DFv1 驅動程式庫範例使用 ddk::Device<> 混合:

#include <ddktl/device.h>

class SimpleDriver;
using DeviceType = ddk::Device<SimpleDriver, ddk::Initializable>;

class SimpleDriver : public DeviceType {
 public:
  explicit SimpleDriver(zx_device_t* parent);
  ~SimpleDriver();

  static zx_status_t Bind(void* ctx, zx_device_t* dev);

  void DdkInit(ddk::InitTxn txn);
  void DdkRelease();
};

}  // namespace simple

DFv2

在 DFv2 中,DriverBase 類別的用途是寫入驅動程式。 DFv2 驅動程式需要繼承 DriverBase 類別,而非 ddk::Device 混音,例如:

#include <lib/driver/component/cpp/driver_base.h>

class SimpleDriver : public fdf::DriverBase {
 public:
  SimpleDriver(fdf::DriverStartArgs start_args,
               fdf::UnownedSynchronizedDispatcher driver_dispatcher);

  zx::result<> Start() override;
};

建構函式、init 掛鉤和繫結掛鉤

在 DFv1 中,您可以透過建構函式、init 掛鉤初始化驅動程式庫 與繫結掛鉤

init 掛鉤通常會實作為 DdkInit(),例如:

void SimpleDriver::DdkInit(ddk::InitTxn txn) {}

繫結掛鉤是傳遞至 zx_driver_ops_t 的靜態函式 結構體,例如:

zx_status_t SimpleDriver::Bind(void* ctx, zx_device_t* dev) {}

static zx_driver_ops_t simple_driver_ops = [][5] -> zx_driver_ops_t {
  zx_driver_ops_t ops = {};
  ops.version = DRIVER_OPS_VERSION;
  ops.bind = SimpleDriver::Bind;
  return ops;
}();

在 DFv2 中,所有的 DFv1 邏輯都需要進入 DriverBase 類別的 Start() 函式,順序如下:建構函式、Init 掛鉤和 繫結掛鉤。

DFv1

#include <ddktl/device.h>

class SimpleDriver;
using DeviceType = ddk::Device<SimpleDriver, ddk::Initializable>;

class SimpleDriver : public DeviceType {
 public:
  explicit SimpleDriver(zx_device_t* parent) {
     zxlogf(INFO, "SimpleDriver constructor logic");
  }
  ~SimpleDriver() { delete this; }

  static zx_status_t Bind(void* ctx, zx_device_t* dev) {
     zxlogf(INFO, "SimpleDriver bind logic");
  }

  void DdkInit(ddk::InitTxn txn) {
     zxlogf(INFO, "SimpleDriver initialized");
  }
};

}  // namespace simple

static zx_driver_ops_t simple_driver_ops = [][5] -> zx_driver_ops_t {
  zx_driver_ops_t ops = {};
  ops.version = DRIVER_OPS_VERSION;
  ops.bind = SimpleDriver::Bind;
  return ops;
}();

DFv2

class SimpleDriver : public fdf::DriverBase {
 public:
  SimpleDriver(fdf::DriverStartArgs start_args,
               fdf::UnownedSynchronizedDispatcher driver_dispatcher);

  zx::result<> Start() override {
     zxlogf(INFO, "SimpleDriver constructor logic");
     zxlogf(INFO, "SimpleDriver initialized");
     zxlogf(INFO, "SimpleDriver bind logic");
  }
};

Destructor、DdkSuspend()、DdkUnbind() 和 DdkRelease()

在 DFv1 中,解構函式 DdkSuspend()DdkUnbind()DdkRelease() 用於拆除物件和執行緒 (如需進一步瞭解這些掛鉤, 請參閱這個 DFv1 簡易驅動程式庫範例)。

在 DFv2 中,必須在 PrepareStop() 中實作拆解邏輯,並 DriverBase 類別的 Stop() 方法,例如:

class SimpleDriver : public fdf::DriverBase {
 public:
  SimpleDriver(fdf::DriverStartArgs start_args,
               fdf::UnownedSynchronizedDispatcher driver_dispatcher);
  ...

  zx::result<> PrepareStop(fdf::PrepareStopCompleter completer) override{
     // Called before the driver dispatchers are shutdown. Only implement this function
     // if you need to manually clean up objects (ex/ unique_ptrs) in the driver dispatchers");
     completer(zx::ok());
  }

  zx::result<> Stop() {
     // This is called after all driver dispatchers are shutdown.
     // Use this function to perform any remaining teardowns
  }
};

此外,遷移至 DFv2 時,請排除 DdkRelease() 中的自刪除陳述式 方法,例如

void DdkRelease() {
    delete this; // Do not migrate this statement to DFv2
}

ZIRCON_DRIVER() 巨集

在 DFv1 中,是透過 ZIRCON_DRIVER() 巨集宣告驅動程式庫。在 DFv2 中, driver_export 程式庫會取代 ZIRCON_DRIVER() 巨集。 驅動程式匯出程序必須在驅動程式庫實作中宣告,通常 該檔案位於 .cc 檔案中。

DFv1

ZIRCON_DRIVER() 巨集範例:

static zx_driver_ops_t simple_driver_ops = [][5] -> zx_driver_ops_t {
  zx_driver_ops_t ops = {};
  ops.version = DRIVER_OPS_VERSION;
  ops.bind = SimpleDriver::Bind;
  return ops;
}();

ZIRCON_DRIVER(SimpleDriver, simple_driver_ops, "zircon", "0.1");

DFv2

下列 driver_export 範例會遷移 DFv1 ZIRCON_DRIVER() 巨集 範例:

#include <lib/driver/component/cpp/driver_export.h>
...
FUCHSIA_DRIVER_EXPORT(simple::SimpleDriver);

ddk::訊息混合

在 DFv1 中,ddk::Messageable<> 混音是用來實作 FIDL 伺服器。 在 DFv2 中,您可以直接沿用ddk:Messageable<> 網路。

DFv1

using I2cDeviceType = ddk::Device<I2cChild, ddk::Messageable<fuchsia_hardware_i2c::Device>::Mixin>;

class I2cDriver : public I2cDeviceType {
  ...
  // Functions from the fuchsia.hardware.i2c Device protocol
  void Transfer(TransferRequestView request, TransferCompleter::Sync& completer) override;
  void GetName(GetNameCompleter::Sync& completer) override;
}

DFv2

class I2cDriver : public DriverBase, public fidl::WireServer<fuchsia_hardware_i2c::Device> {
  ...
  // Functions from the fuchsia.hardware.i2c Device protocol
  void Transfer(TransferRequestView request, TransferCompleter::Sync& completer) override;
  void GetName(GetNameCompleter::Sync& completer) override;
}

實作 FIDL 伺服器後,您需要將服務新增至傳出 目錄:

  1. ServerBindingGroup 新增至驅動程式庫類別。

  2. 使用 fidl::ServerBindingGroup 傳輸 Zircon,以及 fdf::ServerBindingGroup 用於驅動程式庫運輸,例如:

    class I2cDriver: public DriverBase {
      ...
      private:
        fidl::ServerBindingGroup<fidl_i2c::Device> bindings_;
    }
    
  3. 新增繫結至傳出目錄,例如:

    auto serve_result = outgoing()->AddService<fuchsia_hardware_i2c::Service>(
          fuchsia_hardware_i2c::Service::InstanceHandler({
              .device = bindings_.CreateHandler(this, dispatcher()->async_dispatcher(),
                  fidl::kIgnoreBindingClosure),
          }));
    if (serve_result.is_error()) {
        FDF_LOG(ERROR, "Failed to add Device service %s",
            serve_result.status_string());
        return serve_result.take_error();
    }
    

DdkAdd() 和 device_add()

DdkAdd()device_add() 方法可用來新增子節點 例如:

zx_status_t status = device->DdkAdd(ddk::DeviceAddArgs("i2c");

如要在 DFv2 中新增子節點,請參閱「新增子節點」 請參閱「編寫最小 DFv2 驅動程式」指南的說明。

DdkAddCompositeNodeSpec()

DdkAddCompositeNodeSpec() 方法會將複合節點規格新增至 例如:

auto status = DdkAddCompositeNodeSpec("ft3x27_touch", spec);

如要瞭解如何在 DFv2 中新增複合節點規格, 請參閱驅動程式架構 v2 (DFv2) 一節: 參閱複合節點指南。

device_get_protocol()

device_get_protocol() 方法的用途是擷取並連線至 擁有 Banjo 伺服器在 DFv2 中,系統會將呼叫替換為 compat/cpp/banjo_client 程式庫。

DFv1

misc_protocol_t misc;
zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_MISC, &misc);
if (status != ZX_OK) {
  return status;
}

DFv2

zx::result<ddk::MiscProtocolClient> client =
     compat::ConnectBanjo<ddk::MiscProtocolClient>(incoming());

FIDL 連線函式

在 DFv1 中,DDK 提供下列用於連線至 FIDL 通訊協定的函式:

在 DFv2 中,請使用驅動程式命名空間程式庫進行連線 FIDL 通訊協定

DdkConnectFidlProtocol()

DFv1

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> client_end =
  DdkConnectFidlProtocol<fuchsia_examples_gizmo::Service::Device>();
if (client_end.is_error()) {
  zxlogf(ERROR, "Failed to connect fidl protocol");
  return client_end.status_value();
}

(資料來源:DFv1 驅動程式庫傳輸範例)。

DFv2

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> connect_result =
    incoming()->Connect<fuchsia_examples_gizmo::Service::Device>();
if (connect_result.is_error()) {
  FDF_SLOG(ERROR, "Failed to connect gizmo device protocol.",
           KV("status", connect_result.status_string()));
  return connect_result.take_error();
}

(資料來源:DFv2 驅動程式庫傳輸範例)。

DdkConnectFragmentFidlProtocol()

DFv1

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> client_end =
  DdkConnectFragmentFidlProtocol<fuchsia_examples_gizmo::Service::Device>(parent, "gizmo");
if (codec_client_end.is_error()) {
  zxlogf(ERROR, "Failed to connect to fidl protocol: %s",
      zx_status_get_string(codec_client_end.status_value()));
  return codec_client_end.status_value();
}

DFv2

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> connect_result =
    incoming()->Connect<fuchsia_examples_gizmo::Service::Device>("gizmo");
if (connect_result.is_error()) {
  FDF_LOG(ERROR, "Failed to connect gizmo device protocol: %s",
      connect_result.status_string());
  return connect_result.take_error();
}

DdkConnectRuntimeProtocol()

DFv1

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> client_end =
    DdkConnectRuntimeProtocol<fuchsia_examples_gizmo::Service::Device>();
if (client_end.is_error()) {
  zxlogf(ERROR, "Failed to connect fidl protocol");
  return client_end.status_value();
}

(資料來源:DFv1 驅動程式庫傳輸範例)。

DFv2

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> connect_result =
    incoming()->Connect<fuchsia_examples_gizmo::Service::Device>();
if (connect_result.is_error()) {
  FDF_SLOG(ERROR, "Failed to connect gizmo device protocol.",
           KV("status", connect_result.status_string()));
  return connect_result.take_error();
}

(資料來源:DFv2 驅動程式庫傳輸範例)。

DdkConnectFragmentRuntimeProtocol()

DFv1

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> client_end =
    DdkConnectFragmentRuntimeProtocol<fuchsia_examples_gizmo::Service::Device>("gizmo_parent");
if (client_end.is_error()) {
  zxlogf(ERROR, "Failed to connect fidl protocol");
  return client_end.status_value();
}

DFv2

zx::result<fidl::ClientEnd<fuchsia_examples_gizmo::Device>> connect_result =
    incoming()->Connect<fuchsia_examples_gizmo::Service::Device>("gizmo_parent");
if (connect_result.is_error()) {
  FDF_SLOG(ERROR, "Failed to connect gizmo device protocol.",
           KV("status", connect_result.status_string()));
  return connect_result.take_error();
}

中繼資料

在 DFv1 中,驅動程式可使用下列函式新增及擷取中繼資料: 《DdkAddMetadata()》和《ddk::GetEncodedMetadata<>()》。查看程式碼範例 。

如要將這些中繼資料函式遷移至 DFv2,請按照下列步驟操作:

  1. 完成設定 Compat 裝置伺服器 指南。

  2. 請按照 轉送、新增及剖析 DFv1 中繼資料部分。

DdkAddMetadata()

DFv1

zx_status_t status = dev->DdkAddMetadata(DEVICE_METADATA_I2C_DEVICE,
    metadata.value().data(), sizeof(metadata.value());
if (status != ZX_OK) {
  zxlogf(ERROR, "DdkAddMetadata failed: %s", zx_status_get_string(status));
}

DFv2

compat_server->inner().AddMetadata(DEVICE_METADATA_I2C_DEVICE,
    metadata.value().data(), sizeof(metadata.value()));

ddk::GetEncodedMetadata&lt;&gt;()

DFv1

auto decoded =
    ddk::GetEncodedMetadata<fuchsia_hardware_i2c_businfo::wire::I2CBusMetadata>(
       parent, DEVICE_METADATA_I2C_CHANNELS);

DFv2

fidl::Arena arena;
zx::result i2c_bus_metadata =
    compat::GetMetadata<fuchsia_hardware_i2c_businfo::wire::I2CBusMetadata>(
        incoming(), arena, DEVICE_METADATA_I2C_CHANNELS);

遷移其他介面

TRACE_DURATION

TRACE_DURATION() 方法適用於 DFv1 和 DFv2。

DFv1

在 DFv1 中,從下列 DDK 匯入 TRACE_DURATION 巨集 程式庫:

#include <lib/ddk/trace/event.h>

DFv2

如要在 DFv2 中使用 TRACE_DURATION 巨集,請將 include 行變更為 下列標題:

#include <lib/trace/event.h>

並將下列程式碼加入建構依附元件:

fuchsia_driver("i2c-driver") {
  ...
  deps = [
    ...
    "//zircon/system/ulib/trace",
  ]
}

zxlogf()

zxlogf() 方法不適用於 DFv2,因此必須遷移至 FDF_LOG()FDF_SLOG()

如需操作說明,請參閱「新增記錄」一節 撰寫最低限度的 DFv2 驅動程式指南。