本指南提供設定及使用相容裝置的操作說明 以便與 DFv1 驅動程式通訊
Compat 裝置伺服器可協助 DFv2 驅動程式維持與
在遷移過程中的 DFv1 驅動程式。Compat 裝置伺服器提供
fuchsia_driver_compat::Device
介面 (請參閱 compat.fidl
)。
這個介面可讓 DFv2 驅動程式將資源提供給子系
DFv1 驅動程式。
Compat 裝置伺服器的主要特色如下:
資源共用:將 DFv2 驅動程式庫中的資源提供給 子系 DFv1 驅動程式
Banjo 服務:在 DFv2 驅動程式庫中實作的 Offer Banjo 通訊協定 轉介給 DFv1 驅動程式
中繼資料處理:轉送、新增及剖析 DFv1 中繼資料 DFv2 和 DFv1 驅動程式之間的通訊
本指南提供逐步說明和範例,協助您完成 幾項工作:
設定 Compat 裝置伺服器:設定 DFv2 驅動程式庫中的 Compat 裝置伺服器,包括初始化 (同步或非同步) 後再提供給子節點。
為子系 DFv1 驅動程式提供 Banjo 服務: 設定 Banjo 通訊協定,並與子系 DFv1 驅動程式共用。
轉送、新增及剖析 DFv1 中繼資料: 新增、轉寄及擷取中繼資料,確保資訊交換流暢 必須在 DFv2 與 DFv1 驅動程式之間建立連線
設定 Compat 裝置伺服器
本節提供 Compat 裝置伺服器設定操作說明 使用 DFv2 Simple 驅動程式庫範例。
步驟如下:
1. 將 Compat 裝置伺服器指定為依附元件
如要在 DFv2 驅動程式庫中將 Compat 裝置伺服器指定為依附元件, :
將以下依附元件新增至以下項目中的
fuchsia_driver
目標:BUILD.gn
:"//sdk/lib/driver/compat/cpp",
在驅動程式中加入
device_server.h
標頭 來源檔案:#include <lib/driver/compat/cpp/device_server.h>
將
driver_component/driver.shard.cml
資料分割加入驅動程式 元件資訊清單 (.cml
),例如:include: [ "driver_component/driver.shard.cml", "inspect/client.shard.cml", "syslog/client.shard.cml", ],
(資料來源:
simple_driver.cml
)這個資料分割會定義以下項目的
use
、capabilities
和expose
欄位:fuchsia.driver.compat.Service
。
進行這項設定後,您的 DFv2 驅動程式庫現在可以建立 DeviceServer
物件,
子節點每個 DeviceServer
物件都只能有一個目標節點。
2. 以同步或非同步方式初始化 Compat 裝置伺服器
以同步或非同步方式初始化 DeviceServer
物件。根據預設
建議您以非同步方式取代非同步初始化。
不過,您可以考慮使用 非同步初始化 (如果符合其中一個 符合以下條件:
系統不允許目前調度工具的同步或封鎖呼叫。
您的 DFv1 驅動程式庫已經完成非同步程式碼的結構,請 讓效能從非同步行為中獲得提升
同步初始化
如果是同步初始化,請執行下列操作:
在類別中新增
SyncInitializedDeviceServer
物件,例如:class SimpleDriver : public fdf::DriverBase { ... compat::SyncInitializedDeviceServer compat_server_; ... }
在驅動程式庫實作中,呼叫此
DeviceServer
物件的Initialize()
函式:zx::result<> Initialize(const std::shared_ptr<fdf::Namespace>& incoming, const std::shared_ptr<fdf::OutgoingDirectory>& outgoing, const std::optional<std::string>& node_name, std::string_view child_node_name, const ForwardMetadata& forward_metadata = ForwardMetadata::None(), std::optional<DeviceServer::BanjoConfig> banjo_config = std::nullopt, const std::optional<std::string>& child_additional_path = std::nullopt);
請參閱下文,瞭解這個函式的參數:
incoming
、outgoing
和node_name
的值可透過以下方式傳遞: 使用相同的名稱叫用DriverBase
的存取子 (請參閱以下範例)。child_node_name
是為此目標的指定子節點名稱DeviceServer
物件。不過,如果 DFv2 驅動程式庫程式的節點之間有任何中介節點 和目標子節點,您需要將
child_additional_path
設為 節點之間的拓撲路徑,以/
分隔。舉例來說node-a
,然後在目標子節點前方node-b
,然後child_additional_path
需為node-a/node-b/
。forward_metadata
包含待轉送中繼資料的相關資訊 包括父項節點(若需更多資訊,請參閱 轉寄中繼資料)。banjo_config
包含向目標提供 Banjo 通訊協定的資訊 子節點不過,如果裝置伺服器不是提供通訊協定,您也可以 將參數設為std::nullopt
。(若需更多資訊,請參閱 為子系 DFv1 驅動程式提供 Banjo 服務)。
下方範例會初始化同步相容性裝置伺服器物件:
// Initialize our compat server. { zx::result<> result = compat_server_.Initialize( incoming(), outgoing(), node_name(), child_name); if (result.is_error()) { return result.take_error(); } }
非同步初始化
如果是非同步初始化,請執行下列操作:
在類別中新增
AsyncInitializedDeviceServer
物件。 例如:class SimpleDriver : public fdf::DriverBase { ... compat::AsyncInitializedDeviceServer compat_server_; ... }
在驅動程式庫實作中,呼叫此
DeviceServer
物件的Begin()
函式:void Begin(const std::shared_ptr<fdf::Namespace>& incoming, const std::shared_ptr<fdf::OutgoingDirectory>& outgoing, const std::optional<std::string>& node_name, std::string_view child_node_name, fit::callback<void(zx::result<>)> callback, const ForwardMetadata& forward_metadata = ForwardMetadata::None(), std::optional<DeviceServer::BanjoConfig> banjo_config = std::nullopt, const std::optional<std::string>& child_additional_path = std::nullopt);
這些參數與 同步
Initialize()
函式,但callback
欄位。DeviceServer
物件完成時,系統會叫用回呼函式 正在初始化,隨時可供存取建議您使用這個 API 回呼函式來新增子節點。其他參數值是 填入的方式與同步初始化相同。下方範例會初始化非同步相容性裝置伺服器物件:
void SimpleDriver::OnDeviceServerInitialized(zx::result<> device_server_init_result) { // Add the child nodes here async_completer_.value()(zx::ok()); async_completer_.reset(); } void SimpleDriver::Start(fdf::StartCompleter completer) { async_completer_.emplace(std::move(completer)); device_server_.Begin(incoming(), outgoing(), node_name(), child_name, fit::bind_member<&MyDriver::OnDeviceServerInitialized>(this), compat::ForwardMetadata::Some({DEVICE_METADATA_MAC_ADDRESS})); }
3. 為目標子節點提供相容裝置伺服器
Compat 裝置伺服器初始化後,將其優惠傳遞至目標 子節點
不過,如果目標節點不是直接子項,您就必須傳遞優惠
下一個子系中最接近的子項。舉例來說
拓撲會顯示 A -> B -> C -> D
,節點 A 是否為目前的節點,以及節點 D
是目標節點,您需要將優惠傳遞至節點 B
鏈結中最接近的子節點
使用 DeviceServer
物件的 CreateOffers2()
函式設定 offers2
NodeAddArgs
結構中的欄位,例如:
auto properties = std::vector{fdf::MakeProperty(BIND_PROTOCOL, ZX_PROTOCOL_SERIAL_IMPL_ASYNC)};
zx::result child_result = AddChild(child_name, properties, compat_server_.CreateOffers2());
if (child_result.is_error()) {
return child_result.take_error();
}
如有其他優惠,請將其新增至 DeviceServer
物件的
在 NodeAddArgs
結構體中設定 offers2
欄位前,系統會先提供優惠
範例:
auto offers = compat_server_.CreateOffers2();
offers.push_back(fdf::MakeOffer2<fuchsia_hardware_serialimpl::Service>(child_name));
auto properties = std::vector{fdf::MakeProperty(BIND_PROTOCOL, ZX_PROTOCOL_SERIAL_IMPL_ASYNC)};
zx::result child_result = AddChild(child_name, properties, std::move(offers));
if (child_result.is_error()) {
return child_result.take_error();
}
如果您已同步初始化 Compat 裝置伺服器,則需要執行
這項作業會在 SyncInitializedDeviceServer::Initialize()
函式完成
物件。否則,您必須在傳送至
AsyncInitializedDeviceServer::Begin()
呼叫。
為子系 DFv1 驅動程式提供 Banjo 服務
如果您的 DFv2 驅動程式庫實作了 Banjo 通訊協定,並想要將其提供給 目標子節點,您需要將通訊協定新增至相容裝置伺服器。
假設您的驅動程式庫實作 Misc
Banjo 通訊協定,例如:
class ParentBanjoTransportDriver : public fdf::DriverBase,
public ddk::MiscProtocol<ParentBanjoTransportDriver> {
...
(本節中的範例是以 Banjo 傳輸範例)。
如要在 Compat 裝置伺服器中新增 Misc
Banjo 通訊協定,請按照下列步驟操作:
在驅動程式庫的來源檔案中加入下列標頭:
#include <lib/driver/compat/cpp/banjo_server.h>
為 Banjo 通訊協定建立
compat::BanjoServer
物件:compat::BanjoServer banjo_server_{ZX_PROTOCOL_<NAME>, this, &<name>_protocol_ops_};
將上方範本中的
NAME
替換為大寫的通訊協定名稱,並name
。因此,針對Misc
Banjo 通訊協定,物件如下所示:class ParentBanjoTransportDriver : public fdf::DriverBase, public ddk::MiscProtocol<ParentBanjoTransportDriver> { ... private: compat::BanjoServer banjo_server_{ZX_PROTOCOL_MISC, this, &misc_protocol_ops_}; ... }
建立
BanjoConfig
物件,並設定對 Banjo 的通訊協定回呼 伺服器的回呼,例如:compat::DeviceServer::BanjoConfig banjo_config; banjo_config.callbacks[ZX_PROTOCOL_MISC] = banjo_server_.callback();
這項設定會傳遞
BanjoServer
物件新增至 Compat 裝置伺服器。初始化 Compat 裝置伺服器時,請設定
Initialize()
函式的banjo_config
欄位新增至BanjoConfig
物件,例如:// Initialize our compat server. { zx::result<> result = compat_server_.Initialize( incoming(), outgoing(), node_name(), child_name, ForwardMetadata::None(), std::move(banjo_config)); if (result.is_error()) { return result.take_error(); } }
針對向子系提供這個 Banjo 通訊協定的其餘步驟 請參閱 在 DFv2 驅動程式中提供 Banjo 通訊協定 指南。
轉送、新增及剖析 DFv1 中繼資料
許多現有的 DFv1 驅動程式會使用中繼資料,在家長和 他們的孩子。在 DFv2 驅動程式中,您可以使用 Compat 裝置伺服器執行 以下作業:
新增並傳送中繼資料
中繼資料會透過驅動程式庫的相容性裝置伺服器傳遞至子節點。目的地:
新增和傳送中繼資料,驅動程式庫必須建立相容裝置伺服器,然後
呼叫其 AddMetadata()
函式:
zx_status_t AddMetadata(MetadataKey type, const void* data, size_t size);
(資料來源:device_server.h
)
以下範例會使用 AddMetadata()
函式新增中繼資料:
const uint64_t metadata = 0xAABBCCDDEEFF0011;
zx_status_t status = compat_device_server.AddMetadata(
DEVICE_METADATA_PRIVATE, &metadata, sizeof(metadata));
不過,如果中繼資料是 FIDL 類型,您就必須使用
fidl::Persist()
呼叫會先將其新增至 AddMetadata()
函式,
例如:
fuchsia_hardware_i2c_businfo::wire::I2CChannel local_channel(channel);
fit::result metadata = fidl::Persist(local_channel);
if (!metadata.is_ok()) {
FDF_LOG(ERROR, "Failed to fidl-encode channel: %s",
metadata.error_value().FormatDescription().data());
return zx::error(metadata.error_value().status());
}
compat_server->AddMetadata(DEVICE_METADATA_PRIVATE, metadata.value().data(),
metadata.value().size());
轉寄中繼資料
如果您的 DFv2 驅動程式庫收到來自父項節點的中繼資料且必須傳遞 部分或全部中繼資料給其子節點,您可以使用 Compat 裝置伺服器進行檢查
如要轉送中繼資料,請在初始化時設定 forward_metadata
參數
驅動程式庫的 DeviceServer
物件:
如要轉寄所有中繼資料,請將參數設為
ForwardMetadata::All()
,例如:zx::result<> result = compat_server_.Initialize( incoming(), outgoing(), node_name(), child_name, compat::ForwardMetadata::All());
如果您只想轉送部分中繼資料,請建立
ForwardMetadata
物件 具備ForwardMetadata Some(std::unordered_set<MetadataKey> filter)
和票證 將此物件加入參數以下範例僅會透過
DEVICE_METADATA_GPT_INFO
鍵轉送中繼資料:zx::result<> result = compat_server_.Initialize( incoming(), outgoing(), node_name(), child_name, compat::ForwardMetadata::Some({DEVICE_METADATA_GPT_INFO}));
如果不想轉寄中繼資料,請將參數設為
ForwardMetadata::None()
,例如:zx::result<> result = compat_server_.Initialize( incoming(), outgoing(), node_name(), child_name, compat::ForwardMetadata::None());
擷取中繼資料
Compat 裝置伺服器的中繼資料程式庫 (metadata.h
) 提供
用於從驅動程式擷取中繼資料的輔助函式
如要使用這個中繼資料程式庫,請按照下列步驟操作:
將以下依附元件新增至
BUILD.gn
中的fuchsia_driver
目標:"//sdk/lib/driver/compat/cpp",
在驅動程式庫的來源檔案中加入下列標頭:
#include <lib/driver/compat/cpp/metadata.h>
如要從驅動程式庫的 Compat 裝置伺服器擷取中繼資料,請使用
compat::GetMetadata<T>()
方法,並將T
替換為中繼資料類型。以下範例使用
DEVICE_METADATA_VREG
鍵擷取中繼資料 以及fuchsia_hardware_vreg::wire::Metadata
類型:fidl::Arena arena; zx::result<fuchsia_hardware_vreg::wire::Metadata> metadata = compat::GetMetadata<fuchsia_hardware_vreg::wire::Metadata>( incoming(), arena, DEVICE_METADATA_VREG); if (metadata.is_error()) { FDF_LOG(ERROR, "Failed to get metadata %s", metadata.status_string()); return completer(metadata.take_error()); }
如需中繼資料鍵和類型的完整清單,請參閱 這個檔案。
(選用) 如果驅動程式庫為複合資料,您必須傳遞父項名稱 指定中繼資料的來源父項節點,例如:
zx::result<fuchsia_hardware_vreg::wire::Metadata> metadata = compat::GetMetadata<fuchsia_hardware_vreg::wire::Metadata>( incoming(), arena, DEVICE_METADATA_VREG, "pdev");
不過,如果中繼資料類型是動態大小的陣列,請使用
compat::GetMetadataArray<T>()
並將 T
替換為陣列的類型。
假設我們需要擷取
DEVICE_METADATA_GPIO_PINS
是 gpio_pin_t
結構的陣列:
// type: array of gpio_pin_t
#define DEVICE_METADATA_GPIO_PINS 0x4F495047 // GPIO
接著,您需要將 T
替換為 gpio_pin_t
,並擷取中繼資料
如下所示:
zx::result<std::vector<gpio_pin_t>> gpio_pins =
compat::GetMetadataArray<gpio_pin_t>(incoming(), DEVICE_METADATA_GPIO_PINS);
if (gpio_pins.is_error()) {
FDF_LOG(ERROR, "%s: Failed to get gpio pin metadata");
return zx::error(gpio_pins.take_error());
}
同樣地,如果驅動程式庫為複合資料,請傳送父項的名稱以指定 來源節點,例如:
zx::result<std::vector<gpio_pin_t>> gpio_pins =
compat::GetMetadataArray<gpio_pin_t>(incoming(), DEVICE_METADATA_GPIO_PINS, "pdev");