在本節中,我們將查看 C++ DDK 範本程式庫 (或稱「DDKTL」)短句 這是一組 C++ 範本類別,可簡化驅動程式庫的編寫工作 提供混合符號,確保類型安全並執行基本功能
如果您不熟悉混合功能,不妨參考下列的維基百科文章: * mixins 和 * CRTP:或尋常的重複範本模式。
我們將討論這種混合配置的定義位於
//src/lib/ddktl/include/ddktl/device.h
。
提供的混合值如下:
混成課 | 函式 | 目的 |
---|---|---|
ddk::GetProtocolable |
DdkGetProtocol() | 擷取通訊協定 |
ddk::Initializable |
DdkInit() | DdkAdd() 之後呼叫,以便安全完成裝置的初始化作業 |
ddk::Unbindable |
DdkUnbind() | 裝置移除時呼叫 |
ddk::Suspendable |
DdkSuspend() | 以便暫停裝置 |
ddk::Resumable |
DdkResume() | 以便重新啟用裝置 |
ddk::PerformanceTunable |
DdkSetPerformanceState() | 轉換成效 |
ddk::AutoSuspendable |
DdkConfigureAutoSuspend() | 設定驅動程式庫能否自動暫停裝置 |
ddk::Rxrpcable |
DdkRxrpc() | 公車裝置的遠端訊息 |
為裝置定義類別時,您必須指定裝置將要執行的函式 加入適當的混合工作 例如 (為說明文件新增的行號):
[01] using DeviceType = ddk::Device<MyDevice,
[02] ddk::Initializable, // safely initialize after **DdkAdd()**
[03] ddk::Unbindable>; // safely clean up before **DdkRelease()**
這會建立 DeviceType
的捷徑。
ddk::Device
範本類別採用一或多個引數,
第一個引數為基本類別 (此處為 MyDevice
)。
額外的範本引數是用於定義
也就是實作的 FDF 裝置成員功能。
定義完成後,我們即可將裝置類別 (MyDevice
) 宣告為繼承
來自 DeviceType
:
[05] class MyDevice : public DeviceType {
[06] public:
[07] explicit MyDevice(zx_device_t* parent)
[08] : DeviceType(parent) {}
[09]
[10] zx_status_t Bind() {
[11] // Any other setup required by MyDevice.
[12] // The device_add_args_t will be filled out by the base class.
[13] return DdkAdd("my-device-name");
[14] }
[15]
[16] // Methods required by the ddk mixins
[17] void DdkInit(ddk::InitTxn txn);
[18] void DdkUnbind(ddk::UnbindTxn txn);
[19] void DdkRelease();
[20] };
因為 DeviceType
類別包含混合 (第 [02
.. 03]
行):
Initializable
和 Unbindable
),我們需要提供個別的
函式實作 (第 [17
.. 18]
行)。
所有 DDKTL 類別都必須提供發布函式 (此處的 [19]
行提供)
DdkRelease()),因此我們會在混合定義中並未指定
DeviceType
。
請注意,回覆
InitTxn
後 (由 DdkInit() 提供) 您「無法」安全地使用裝置執行個體,其他執行緒可能會呼叫 DdkUnbind(),通常呼叫 DdkRelease(),而釋放驅動程式庫 裝置的背景資訊。這樣就會視為「使用釋放後使用」。 對於不支援 DdkInit() 的裝置,此操作會在您呼叫 DdkAdd() 後套用。
回想一下裝置必須向驅動程式管理器註冊的前幾節內容 才能派上用場 整個過程如下:
[26] zx_status_t my_bind(zx_device_t* device,
[27] void** cookie) {
[28] auto dev = std::make_unique<MyDevice>(device);
[29] auto status = dev->Bind();
[30] if (status == ZX_OK) {
[31] // driver manager is now in charge of the memory for dev
[32] dev.release();
[33] }
[34] return status;
[35] }
這裡 my_bind() 會建立 MyDevice
的執行個體並呼叫 Bind() 處理常式,
然後傳回狀態
Bind() (上述 class MyDevice
宣告中的第 [12]
行),執行任何
並設定裝置名稱,然後以裝置名稱呼叫 DdkAdd()。
裝置是 Initializable
,因此驅動程式管理器會呼叫實作項目。
具有 InitTxn
的 DdkInit()。裝置不會顯示,
直到裝置回應 InitTxn
為止。這則回覆可從任何應用程式
執行緒),不一定需要先從 DdkInit() 傳回。
回覆「InitTxn
」後,你的裝置就會顯示在「裝置」中
檔案系統
以 //src/devices/block/drivers/zxcrypt
目錄為例
常見的裝置聲明如下 (device.h
):
[01] class Device;
[02] using DeviceType = ddk::Device<Device,
[03] ddk::GetProtocolable,
[04] ddk::Unbindable>;
...
[05] class Device final : public DeviceType,
[06] public ddk::BlockImplProtocol<Device, ddk::base_protocol>,
[07] public ddk::BlockPartitionProtocol<Device>,
[08] public ddk::BlockVolumeProtocol<Device> {
[09] public:
...
[10] // ddk::Device methods; see ddktl/device.h
[11] zx_status_t DdkGetProtocol(uint32_t proto_id, void* out);
[12] void DdkUnbind(ddk::UnbindTxn txn);
[13] void DdkRelease();
...
行 [01
.. 05]
使用基本類別宣告捷徑 DeviceType
Device
及其混合項目:GetProtocolable
和 Unbindable
。
有意思的是第 [06]
行:我們不僅繼承了 DeviceType
但也來自其他類別的 [07
.. 09]
行 [11
.. 15]
提供三種選用混合的原型以及
必要的 DdkRelease() 成員函式。
以下範例為 zxcrypt
裝置的 DdkGetProtocol
實作 (來自
device.cc
):
zx_status_t Device::DdkGetProtocol(uint32_t proto_id, void* out) {
auto* proto = static_cast<ddk::AnyProtocol*>(out);
proto->ctx = this;
switch (proto_id) {
case ZX_PROTOCOL_BLOCK_IMPL:
proto->ops = &block_impl_protocol_ops_;
return ZX_OK;
case ZX_PROTOCOL_BLOCK_PARTITION:
proto->ops = &block_partition_protocol_ops_;
return ZX_OK;
case ZX_PROTOCOL_BLOCK_VOLUME:
proto->ops = &block_volume_protocol_ops_;
return ZX_OK;
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
根據驅動程式庫狀況所見
我們來看看驅動程式庫如何使用 DDKTL。
我們會使用 USB XHCI 驅動程式庫做為這組程式碼範例;你找得到。
這個頁面://src/devices/usb/drivers/xhci/usb-xhci.cpp
。
驅動程式包含驅動程式庫宣告 (通常位於來源檔案底部),如下所示:
ZIRCON_DRIVER(driver_name, driver_ops, "zircon", "0.1");
ZIRCON_DRIVER() 巨集的第二個參數是 zx_driver_ops_t
結構。
在 C++ 版本中,我們會使用 lambda 函式協助初始化:
namespace usb_xhci {
...
static zx_driver_ops_t driver_ops = [](){
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = UsbXhci::Create;
return ops;
}();
} // namespace usb_xhci
ZIRCON_DRIVER(usb_xhci, usb_xhci::driver_ops, "zircon", "0.1");
這會執行 driver_ops() lambda,以傳回初始化的 zx_driver_ops_t
結構。
為什麼要使用 lambda?C++ 與結構中的部分初始化不同,所以我們先從
空白的 ops
例項、設定感興趣的欄位,然後傳回結構。
UsbXhci::Create() 函式如下所示:
[01] zx_status_t UsbXhci::Create(void* ctx, zx_device_t* parent) {
[02] fbl::AllocChecker ac;
[03] auto dev = std::unique_ptr<UsbXhci>(new (&ac) UsbXhci(parent));
[04] if (!ac.check()) {
[05] return ZX_ERR_NO_MEMORY;
[06] }
[07]
[08] auto status = dev->Init();
[09] if (status != ZX_OK) {
[10] return status;
[11] }
[12]
[13] // driver manager is now in charge of the device.
[14] [[maybe_unused]] auto* unused = dev.release();
[15] return ZX_OK;
[16] }
首先,請留意 dev
的建構函式 (其為 new ... UsbXhci(parent)
呼叫)
第 [03]
行) — 我們稍後會再回頭處理。
建構 dev
後,第 [08]
行會呼叫 dev->Init(),
會呼叫兩個初始化函式的其中一種去多工點:
zx_status_t UsbXhci::Init() {
if (pci_.is_valid()) {
return InitPci();
} else if (pdev_.is_valid()) {
return InitPdev();
} else {
return ZX_ERR_NOT_SUPPORTED;
}
}
父項通訊協定使用方式
讓我們依循 InitPci() 函式的 pci_
成員路徑。
我們將說明裝置如何使用父項通訊協定中的功能。
在 UsbXhci::Create() 中,dev
的建構函式初始化了成員 pci_
來自 parent
引數
以下是類別定義的相關摘錄:
class UsbXhci: ... {
public:
explicit UsbXhci(zx_device_t* parent)
: UsbXhciType(parent), pci_(parent), pdev_(parent) {}
...
private:
ddk::PciProtocolClient pci_;
...
};
pci_
成員的第一次使用 InitPci() 是為了取得
BTI (Bus 交易發起者) 物件:
zx_status_t UsbXhci::InitPci() {
...
zx::bti bti;
status = pci_.GetBti(0, &bti);
if (status != ZX_OK) {
return status;
}
...
這是正常現象。