本指南介绍了如何设置和使用兼容型设备 DFv2 驱动程序中的服务器,以便与 DFv1 驱动程序进行通信。
兼容型设备服务器有助于 DFv2 驱动程序保持与
迁移过程中的 DFv1 驱动程序。兼容型设备服务器提供
fuchsia_driver_compat::Device
接口(请参阅 compat.fidl
)。
此接口允许 DFv2 驱动程序将其资源提供给后代
DFv1 驱动程序。
兼容型设备服务器的主要功能包括:
资源共享:将 DFv2 驱动程序提供的资源提供给 DFv1 后代驱动程序。
Banjo 服务:提供在 DFv2 驱动程序中实现的 Banjo 协议 DFv1 驱动程序
元数据处理:转发、添加和解析 DFv1 元数据, DFv2 和 DFv1 驱动程序之间的通信。
本指南提供了分步说明和示例,可帮助您 以下任务:
设置兼容型设备服务器:设置 DFv2 驱动程序中的兼容型设备服务器,包括初始化 (同步或异步)并将其提供给子节点。
为 DFv1 后代驱动程序提供 Banjo 服务: 配置 Banjo 协议并与 DFv1 后代驱动程序共享。
转发、添加和解析 DFv1 元数据: 添加、转发和检索元数据,确保顺畅的信息交换 DFv2 和 DFv1 驱动程序之间的差异
设置兼容型设备服务器
本部分提供了基于兼容型设备服务器的设置说明, (请参阅 DFv2 Simple 驱动程序示例)。
具体步骤如下:
1. 将兼容型设备服务器指定为依赖项
如需在 DFv2 驱动程序中将兼容型设备服务器指定为依赖项,请执行以下操作: 执行以下操作:
将以下依赖项添加到
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. 同步或异步初始化兼容型设备服务器
同步或异步初始化 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
对象完成时,系统会调用回调函数 可供访问建议您使用此 回调函数来添加子节点。其他参数值包括 填充方式与同步初始化相同。以下示例初始化了一个异步兼容型设备服务器对象:
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. 为目标子节点提供兼容型设备服务器
在兼容型设备服务器初始化后,将其优惠传递给目标 子节点。
但是,如果目标节点不是直接子节点,则需要传递优惠
子节点链中下一个最近的子节点。例如,节点
拓扑会显示 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();
}
如果您的兼容型设备服务器同步初始化,则需要执行
在 SyncInitializedDeviceServer::Initialize()
函数之后,
调用。否则,您需要在传递给
AsyncInitializedDeviceServer::Begin()
通话。
为 DFv1 后代驱动程序提供 Banjo 服务
如果您的 DFv2 驱动程序实现了 Banjo 协议,并希望将其提供给 目标子节点,则需要将该协议添加到兼容型设备服务器。
假设您的驱动程序实现了 Misc
Banjo 协议,例如:
class ParentBanjoTransportDriver : public fdf::DriverBase,
public ddk::MiscProtocol<ParentBanjoTransportDriver> {
...
(本部分中的示例基于 Banjo 交通示例。)
如需将 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
对象。初始化兼容型设备服务器时,将
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 协议的其余步骤 DFv1 驱动程序,请参阅 在 DFv2 驱动程序中提供 Banjo 协议 指南。
转发、添加和解析 DFv1 元数据
许多现有的 DFv1 驱动程序使用元数据在父级和 自己的孩子。在 DFv2 驱动程序中,您可以使用兼容型设备服务器执行 以下操作:
添加和发送元数据
元数据通过驱动程序的兼容型设备服务器传递到子节点。接收者
添加和发送元数据时,驱动程序需要创建一个兼容型设备服务器,
调用其 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 驱动程序从父节点接收元数据,并且需要将 部分或全部元数据传递给其子节点,您可以使用 兼容型设备服务器
如需转发元数据,请在初始化时设置 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());
检索元数据
兼容型设备服务器的元数据库 (metadata.h
) 提供了
用于从驱动程序中检索元数据的辅助函数。
如需使用此元数据库,请执行以下操作:
将以下依赖项添加到
BUILD.gn
中的fuchsia_driver
目标:"//sdk/lib/driver/compat/cpp",
在驱动程序的源文件中添加以下头文件:
#include <lib/driver/compat/cpp/metadata.h>
要从驱动程序的兼容设备服务器检索元数据,请使用
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");