本页面介绍了迁移驱动程序接口的最佳实践和示例 从 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 服务器后,您需要将该服务添加到传出数据 目录:
将
ServerBindingGroup
添加到您的驱动程序类中。使用
fidl::ServerBindingGroup
进行 Zircon 传输;fdf::ServerBindingGroup
用于驱动程序传输,例如:class I2cDriver: public DriverBase { ... private: fidl::ServerBindingGroup<fidl_i2c::Device> bindings_; }
将绑定添加到传出目录,例如:
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 协议:
DdkConnectFidlProtocol()
DdkConnectFragmentFidlProtocol()
DdkConnectRuntimeProtocol()
DdkConnectFragmentRuntimeProtocol()
在 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,请执行以下操作:
完成设置兼容型设备服务器 指南。
请按照 转发、添加和解析 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<>()
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 驱动程序指南。