本頁提供將驅動程式庫介面從 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 掛鉤和 Bind 掛鉤。
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 中,必須在 DriverBase
類別的 PrepareStop()
和 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 中,您可以直接從 FIDL 伺服器替換 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 伺服器後,您必須將服務新增至外送目錄:
將
ServerBindingGroup
加入驅動程式庫類別。針對 Zircon 傳輸和
fdf::ServerBindingGroup
使用fidl::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 中新增複合式節點規格,請參閱複合節點指南中的「驅動程式架構第 2 版 (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();
}
Metadata
在 DFv1 中,驅動程式可以使用 DdkAddMetadata()
和 ddk::GetEncodedMetadata<>()
等函式新增及擷取中繼資料。請參閱下方的程式碼範例。
如要將這些中繼資料函式遷移至 DFv2,請按照下列步驟操作:
完成「設定 compat 裝置伺服器」指南。
請按照轉寄、新增及剖析 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()
。
如需操作說明,請參閱編寫最小的 DFv2 驅動程式指南中的「新增記錄」一節。