将 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:设备 mixin

ddk::Device<> 是在 ddktl/device.h 中定义的 mixin, 简化了使用 C++ 编写 DDK 驱动程序的流程。

DFv1

以下示例中的 DFv1 驱动程序使用了 ddk::Device<> mixin:

#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 mixin,例如:

#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");
  }
};

析构函数、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::可发送消息的 mixin

在 DFv1 中,ddk::Messageable<> mixin 用于实现 FIDL 服务器。 在 DFv2 中,可以直接继承 ddk:Messageable<> mixin 从 FIDL 服务器复制数据。

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. 完成设置兼容型设备服务器 指南。

  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 中,TRACE_DURATION 宏是从以下 DDK 导入的 库:

#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()

有关说明,请参阅 API 文档中的添加日志部分。 编写最小 DFv2 驱动程序指南。