本指南将帮助您从使用固定标识符来识别驱动程序实例的反模式迁移。
具体而言,本指南适用于执行以下任一操作的非驱动程序组件客户端:
- 使用拓扑路径,例如
/dev/sys/platform/00:00:2e/nand-ctl
- 使用硬编码的类路径(例如/dev/class/camera/000)
- 连接到给定 devfs 条目的
device_controller
,然后调用GetTopologicalPath()
。
在上述每种情况下,客户端都需要从服务/fidl 接口的提供程序是(或似乎是)先验已知且服务始终被假定为存在的情况迁移到汇总服务,在汇总服务中,实例将异步添加和移除,并且实例名称无法用于标识。
选项 1:连接到第一个可用的服务实例
这通常不是首选解决方案,因为它会忽略系统的动态特性,并且如果存在多个服务实例,则会出现错误。不过,对于预计只有一个服务提供方的更简单的工具和测试,这可能已经足够了。以下代码会无限期等待服务实例出现:
C++
SyncServiceMemberWatcher<fidl_example::Service::Device> watcher;
zx::result<ClientEnd<fidl_example::Echo>> result = watcher.GetNextInstance(false);
Rust
use fuchsia_component::client::Service;
let client = Service::open(fuchsia_example::EchoServiceMarker)?
.watch_for_any()
.await?
.connect_to_device()?;
方案 2:从每个服务实例查询信息
客户端应监控其感兴趣的服务,然后查询每个实例以获取标识信息,而不是通过使用唯一标识符连接到实例来识别实例。此类信息可以来自服务提供的现有协议,也可以向服务中添加新协议以协助识别。
使用服务提供的现有 fidl 协议
许多 fidl 协议已指定设备相关信息(例如,fuchsia.camera/Control
协议提供了 GetInfo
调用)。如果当前提供的协议不包含标识信息,则可以修改该协议以添加此类特定于设备的信息。
library fuchsia.hardware.something;
protocol Device {
...
GetDeviceId() -> (struct {
device_id uint32;
}) error zx.Status;
}
service Service {
device client_end:Device;
};
向服务添加其他协议
向服务添加额外的 GetInfo
协议,该协议可以包含设备标识信息。例如:
library fuchsia.hardware.something;
Protocol Device { ... }
Protocol GetInfo {
GetDeviceId() -> (struct {
device_id uint32;
}) error zx.Status;
}
service Service {
device client_end:Device;
info client_end:GetInfo;
};
将识别接口与主通信接口分离有以下几点好处:
- 由于识别接口提供一组静态信息,因此大部分情况下可以由驱动程序库处理。
- 最好让识别接口处理多个同时连接,以便于查询,而大多数驱动程序无法处理与其其他接口的多个同时连接。
- 分离识别逻辑有助于在日后的工作中更轻松地将识别步骤集成到框架中。
如果需要某个节点的拓扑信息,则已经有协议可用于访问该信息,请参阅下一部分。
访问服务实例的拓扑信息。
为了便于从使用 dev-topological
或使用 fuchsia_device::Controller
接口调用 GetTopologicalPath
转换为使用其他接口,我们提供了一个工具来访问 devfs 或服务实例的拓扑路径。此工具旨在作为迁移到使用 fuchsia.driver.token.NodeToken
协议提供总线拓扑结构令牌的服务的步骤。
使用 GetTopologicalPath
工具将客户端转换为服务
使用 fdf_topology::GetTopologicalPath
后,您将无法再直接访问拓扑路径,并且可以将驱动程序转换为服务
将您的类名称添加到许可名单:
const std::list<std::string> kClassesThatAllowTopologicalPath({
"block",
"example_echo",
"devfs_service_test",
});
添加 device_topology 依赖项:
deps = [
"//src/devices/lib/client:device_topology",
],
并使用服务目录和实例名称调用 GetTopologicalPath
:
// Open the service directory
zx::result dir = component::OpenDirectory("/svc/fuchsia.examples.EchoService");
ZX_ASSERT(dir.status_value() == ZX_OK);
// For simplicity in this example, just get the first instance in the service directory:
auto watch_result = device_watcher::WatchDirectoryForItems<std::string>(
*dir, [](std::string_view name) -> std::optional<std::string> { return std::string(name); });
ZX_ASSERT(watch_result.status_value() == ZX_OK);
std::string instance_name = std::move(watch_result.value());
// Now check the topological path of that instance:
zx::result<std::string> path_result = fdf_topology::GetTopologicalPath(*dir, instance_name);
ZX_ASSERT(path_result.is_ok());
if (path_result.value() == kSomePathYouWant) {
//Open the instance
}
执行此迁移步骤应该相对机械,并且可以让您的客户端停止使用 devfs。在执行接下来的两个步骤时,您需要更详细地了解客户端使用拓扑路径的原因。
将 NodeToken
添加到您的协议或服务中
建议您使用 NodeToken
获取有关总线拓扑的信息:
library fuchsia.example;
using fuchsia.driver.token;
Protocol Device {
compose fuchsia.driver.token.NodeToken;
}
service Service {
device client_end:Device;
};
如需了解详情,请参阅总线拓扑。
改用总线拓扑,而不是拓扑路径
此处的实现取决于您需要使用拓扑信息的原因。例如,如果您需要连接到公交车上的特定设备地址,则可以匹配公交车类型和地址编号。
以下示例展示了如何使用 ServiceMemberWatcher 过滤出特定拓扑:
// Define a callback function to be called when the correct device is found
void OnInstanceFound(ClientEnd<fuchsia_examples::Echo> client_end) {}
// Define a filter for choosing the correct device:
void FilterOnBusTopology(ClientEnd<fuchsia_examples::Echo> client_end) {
fidl::WireResult result = fidl::WireCall(client_end)->Get();
ZX_ASSERT(result.ok());
zx::result topo_client = component::Connect<fuchsia.driver.token.NodeBusTopology>();
ZX_ASSERT(topo_client.is_ok());
fidl::WireResult topo_result = fidl::WireCall(topo_client.value())->Get(result.value().value()->token.get());
ZX_ASSERT(topo_result.ok());
// Check topology:
std::vector<fuchsia.driver.framework.BusInfo> bus_info
= topo_result.value().value()->path.get();
if (bus_info[0].bus() == fuchsia_driver_framework::BusType::I2C &&
bus_info[0].address() == 3) {
OnInstanceFound(std::move(client_end));
}
}
// Optionally define an idle function, which will be called when all
// existing instances have been enumerated:
void AllExistingEnumerated() {...}
// Create the ServiceMemberWatcher:
ServiceMemberWatcher<fuchsia_examples::EchoService::MyDevice> watcher;
watcher.Begin(get_default_dispatcher(), &FilterOnBusTopology, &AllExistingEnumerated);
// If you want to stop watching for new service entries:
watcher.Cancel()
您还需要将 fuchsia.driver.token.NodeBusTopology
协议从驱动程序管理器路由到客户端。
如有疑问或需要了解最新状态,请与我们联系: