将其他服务更新到 DFv2

本页提供了与以下内容相关的说明、最佳做法和示例: 更新 DFv1 驱动程序中的各种服务(DDK 接口除外) 到 DFv2

设置兼容型设备服务器

如果您的 DFv1 驱动程序与尚未迁移的其他 DFv1 驱动程序通信 到 DFv2,您需要使用兼容性填充码,让现在的 DFv2 驱动程序 与系统中的其他 DFv1 驱动程序进行通信。如需详细了解如何设置 在 DFv2 驱动程序中使用以下兼容性填充码,请参阅 在 DFv2 驱动程序中设置兼容型设备服务器 指南。

使用 DFv2 服务发现

在进行驱动程序迁移时,您可能会遇到一个或多个 在以下三个场景中,两个驱动程序建立 FIDL 连接(采用 child driver -> parent driver 格式):

  • 场景 1:DFv2 驱动程序 ->DFv2 驱动程序
  • 场景 2:DFv1 驱动程序 ->DFv2 驱动程序
  • 场景 3:DFv2 驱动程序 ->DFv1 驱动程序

情形 1 是针对 DFv2 驱动程序的标准情形(此 示例展示了新的 DFv2 语法)。更新 请参阅 从 DFv2 驱动程序到 DFv2 驱动程序部分。

场景 2 和 3 更加复杂,因为 DFv1 驱动程序 封装在 DFv2 环境中的兼容性填充码中。不过, 区别在于:

  • 场景 2 中,此 Gerrit 更改会显示 一个方法,用于将 DFv2 父级中的服务公开给 DFv1 子级。

  • 场景 3 中,司机连接到 fuchsia_driver_compat::Service::Device 协议提供的 父驱动程序的兼容性 shim,并且驱动程序会调用 ConnectFidl() 方法连接到实际的 协议(有关示例,请参见 Gerrit 更改)。

如需根据场景 2 或 3 更新驾驶员,请参阅 从 DFv1 驱动程序到 DFv2 驱动程序(带有兼容性填充码) 部分。

DFv2 驱动程序到 DFv2 驱动程序

如需让其他 DFv2 驱动程序发现您的驱动程序的服务,请执行以下操作: 执行以下操作:

  1. 更新驱动程序的 .fidl 文件。

    DFv2 中的协议发现需要针对以下内容添加 service 字段: 驱动程序的协议,例如:

    library fuchsia.example;
    
    @discoverable
    @transport("Driver")
    protocol MyProtocol {
        MyMethod() -> (struct {
            ...
        });
    };
    
    service Service {
        my_protocol client_end:MyProtocol;
    };
    
  2. 更新子驱动程序。

    DFv2 驱动程序可以像 FIDL 服务一样连接到协议, 例如:

    incoming()->Connect<fuchsia_example::Service::MyProtocol>();
    

    您还需要更新组件清单 (.cml) 文件才能使用 驱动程序运行时服务,例如:

    use: [
        { service: "fuchsia.example.Service" },
    ]
    
  3. 更新父级驱动程序。

    您的父级驱动程序需要使用 fdf::DriverBaseoutgoing() 函数来获取 fdf::OutgoingDirectory 对象。请注意,您必须使用服务而不是协议。 如果您的司机未使用 fdf::DriverBase,则您必须创建并传送 fdf::OutgoingDirectory

    然后,您需要将运行时服务添加到传出目录。 以下示例是继承自 fdf::DriverBase 的驱动程序 类:

    zx::status<> Start() override {
      auto protocol = [this](
          fdf::ServerEnd<fuchsia_example::MyProtocol> server_end) mutable {
        // bindings_ is a class field with type fdf::ServerBindingGroup<fuchsia_example::MyProtocol>
        bindings_.AddBinding(
          dispatcher()->get(), std::move(server_end), this, fidl::kIgnoreBindingClosure);
      };
    
      fuchsia_example::Service::InstanceHandler handler(
           {.my_protocol = std::move(protocol)});
    
      auto status =
            outgoing()->AddService<fuchsia_wlan_phyimpl::Service>(std::move(handler));
      if (status.is_error()) {
        return status.take_error();
      }
    
      return zx::ok();
    }
    

    更新子节点的 NodeAddArgs 以包含优惠 例如:

    auto offers =
        std::vector{fdf::MakeOffer2<fuchsia_example::Service>(arena, name)};
    
    fidl::WireSyncClient<fuchsia_driver_framework::Node> node(std::move(node()));
      auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
                        .name(arena, “example_node”)
                        .offers2(offers)
                        .Build();
    
      zx::result controller_endpoints =
           fidl::CreateEndpoints<fuchsia_driver_framework::NodeController>();
      ZX_ASSERT(controller_endpoints.is_ok());
    
      auto result = node_->AddChild(
          args, std::move(controller_endpoints->server), {});
    

    同样,将父驱动程序的组件清单 (.cml) 文件更新为 提供运行时服务,例如:

    capabilities: [
        { service: "fuchsia.example.Service" },
    ],
    
    expose: [
        {
            service: "fuchsia.example.Service",
            from: "self",
        },
    ],
    

DFv1 驱动程序到 DFv2 驱动程序(具有兼容性填充码)

如需让其他 DFv1 驱动程序发现您的 DFv2 驱动程序的服务,请执行以下操作: 执行以下操作:

  1. 更新 DFv1 驱动程序。

    您需要更新 DFv1 的组件清单 (.cml) 文件 添加方法,如 从 DFv2 驱动程序到 DFv2 驱动程序部分, 例如:

    • 子女司机:

      {
          include: [
              "//sdk/lib/driver_compat/compat.shard.cml",
              "inspect/client.shard.cml",
              "syslog/client.shard.cml",
          ],
          program: {
              runner: "driver",
              compat: "driver/child-driver-name.so",
              bind: "meta/bind/child-driver-name.bindbc",
              colocate: "true",
          },
          use: [
              { service: "fuchsia.example.Service" },
          ],
      }
      
    • 家长驾驶员:

      {
          include: [
              "//sdk/lib/driver_compat/compat.shard.cml",
              "inspect/client.shard.cml",
              "syslog/client.shard.cml",
          ],
          program: {
              runner: "driver",
              compat: "driver/parent-driver-name.so",
              bind: "meta/bind/parent-driver-name.bindbc",
          },
          capabilities: [
              { service: "fuchsia.example.Service" },
          ],
          expose: [
              {
                  service: "fuchsia.example.Service",
                  from: "self",
              },
          ],
      }
      
  2. 更新 DFv2 驱动程序。

    以下示例展示了从 DFv2 公开 Service 的方法 设置为 DFv1 子级:

      fit::result<fdf::NodeError> AddChild() {
        fidl::Arena arena;
    
        auto offer = fdf::MakeOffer2<ft::Service>(kChildName);
    
        // Set the properties of the node that a driver will bind to.
        auto property =
            fdf::MakeProperty(1 /*BIND_PROTOCOL */, bind_fuchsia_test::BIND_PROTOCOL_COMPAT_CHILD);
    
        auto args = fdf::NodeAddArgs{
          {
            .name = std::string(kChildName),
            .properties = std::vector{std::move(property)},
            .offers2 = std::vector{std::move(offer)},
          }
        };
    
        // Create endpoints of the `NodeController` for the node.
        auto endpoints = fidl::CreateEndpoints<fdf::NodeController>();
        if (endpoints.is_error()) {
          return fit::error(fdf::NodeError::kInternal);
        }
    
        auto add_result = node_.sync()->AddChild(fidl::ToWire(arena, std::move(args)),
                                                 std::move(endpoints->server), {});
    

    (来源:root-driver.cc

更新其他驱动程序的组件清单

若要完成从 DFv1 驱动程序到 DFv2 的迁移,您不仅需要 更新目标驱动程序的组件清单 (.cml) 文件, 但您可能还需要更新某些应用的组件清单文件 与现在采用的 DFv2 驱动程序交互的其他驱动程序。

请执行以下操作:

  1. 更新叶驱动程序的组件清单(即不使用 子驱动程序)进行如下更改:

    • 从以下位置移除 //sdk/lib/driver/compat/compat.shard.cmlinclude 字段。
    • program.compat 字段替换为 program.binary
  2. 更新执行 以下任务:

    • 访问内核 args
    • 创建复合设备。
    • 检测重新启动、关机或重新绑定调用。
    • 使用 Banjo 协议与其他驱动程序交谈。
    • 访问或转发来自家长驱动程序的元数据。
    • 与绑定到您的驱动程序添加的节点的 DFv1 驱动程序对话。

    对于这些驱动程序,请使用相应更改更新其组件清单 如下:

    • 从该 API 中复制部分 use 功能, compat.shard.cml 添加到组件清单中, 例如:

      use: [
          {
              protocol: [
                  "fuchsia.boot.Arguments",
                  "fuchsia.boot.Items",
                  "fuchsia.driver.framework.CompositeNodeManager",
                  "fuchsia.system.state.SystemStateTransition",
              ],
          },
          { service: "fuchsia.driver.compat.Service" },
      ],
      
    • program.runner 字段设置为 driver,例如:

      program: {
          runner: "driver",
          binary: "driver/compat.so",
      },
      

公开 DFv2 驱动程序中的 devfs 节点

如需从 DFv2 驱动程序公开 devfs 节点,您需要将 将 device_args 成员添加到 NodeAddArgs。 具体而言,它要求指定类名称以及 可以简化这一过程,只需使用 Connector 库,例如:

zx::result connector = devfs_connector_.Bind(dispatcher());
if (connector.is_error()) {
  return connector.take_error();
}

auto devfs =
    fuchsia_driver_framework::wire::DevfsAddArgs::Builder(arena).connector(
        std::move(connector.value()));

auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena)
                    .name(arena, name)
                    .devfs_args(devfs.Build())
                    .Build();

(来源:parent-driver.cc

如需了解详情,请参阅 在 API 中公开驱动程序功能 DFv2 驱动程序 Codelab。另请参阅此实现 此 Codelab 中提到的 ExportToDevfs 方法。

如需详细了解 devfs 设置流程, 请参阅在 DFv2 驱动程序中设置 devfs 指南。

使用调度程序

调度程序从 FIDL 客户端-服务器对。默认情况下,此通道中的 FIDL 调用 都是异步的

如需在 DFv2 中向驱动程序引入异步,请参阅以下内容 建议:

  • fdf::Dispatcher::GetCurrent() 方法为您提供了 运行驱动程序的调度程序(请参阅 aml-ethernet 驱动程序示例)。如果可能的话 建议单独使用此默认调度程序。

  • 考虑使用多个调度程序,原因如下: (但不限于):

    • 驱动程序需要并行处理才能提升性能。

    • 驱动程序想要执行阻塞操作(因为 旧版驱动程序或要移植到的非 Fuchsia 驱动程序 Fuchsia),且在屏蔽期间需要处理更多工作。

  • 如果需要多个调度程序,fdf::Dispatcher::Create() 方法可以为驱动程序创建新的调度程序。但是,您必须 在默认调度程序上调用此方法(例如,调用 在 Start() 钩子内使用),以便驱动程序主机知道 属于您的司机的其他调度员。

  • 在 DFv2 中,您无需手动关闭调度程序。他们 将在 PrepareStop()Stop() 调用之间关闭。

如需详细了解如何迁移驱动程序以使用多个调度程序, 请参阅 更新 DFv1 驱动程序以使用非默认调度程序 部分(参见从 Banjo 迁移到 FIDL) 词组)。

使用 DFv2 检查

如需在 DFv2 中设置驱动程序维护的inspect指标,请执行以下操作: 您可以使用 fdf::DriverBase::inspector() 提供的 ComponentInspector

inspect::Node& root = inspector().root();

如果应使用自定义检查器,请调用 fdf::DriverBase::InitInspectorExactlyOnce(inspector) 然后才能使用 inspector() 方法。

DFv2 检查不需要将 inspect::Inspector 的 VMO 传递给驱动程序框架。

DFv2 驱动程序检查将归因于驱动程序(因为它属于“常规”组件)。 不过,DFv2 驱动程序的称呼并不稳定,因此在针对 DFv2 驱动程序编写隐私选择器时, 驱动程序,那么您应使用通配符和名称过滤器来引用特定驱动程序。例如,

bootstrap/*-drivers*:[name=sysmem]root

如需在调试期间访问驱动程序的“Inspect”(检查),您可以使用所有常规工具,例如

ffx inspect show "bootstrap/*-drivers*:[name=sysmem]root"

ffx inspect show --manifest sysmem.cm

(可选)实现您自己的 load_firmware 方法

如果您的 DFv1 驱动程序调用 load_firmware(), 函数,则需要自行实现 因为 DFv2 中没有等效函数。

此函数预计易于实现。您需要 手动从路径中移除后备 VMO。如需查看示例,请查看 Gerrit 更改

(可选)使用 FIDL 服务产品/服务生成的节点属性

DFv2 节点包含由 FIDL 服务生成的节点属性 来自家长的优惠

例如,在父级驱动程序(服务器) 例如,父级驱动程序添加一个名为 "parent" 的节点以及一项服务 fidl.examples.EchoService优惠。在 DFv2 中,一个绑定到此 节点可以具有针对该 FIDL 服务节点属性的绑定规则,例如:

using fidl.examples.echo;

fidl.examples.echo.Echo == fidl.examples.echo.Echo.ZirconTransport;

有关详情,请参阅 FIDL 的生成的绑定库部分 教程页面。

将单元测试更新为 DFv2

mock_ddk 库(在单元测试中使用,用于测试) 驱动程序和设备生命周期)特定于 DFv1。全新的 DFv2 测试 框架(请参阅此 Gerrit 更改)使 通过 TestEnvironment 提供给 DFv2 驱动程序的模拟 FIDL 服务器 类。

以下库可用于对 DFv2 驱动程序进行单元测试:

  • //sdk/lib/driver/testing/cpp

    • TestNode - 此类用于实现 fuchsia_driver_framework::Node 协议,可以将此协议提供给驱动程序以创建子节点。这个 类也用于访问驱动程序的子节点 已创建。

    • TestEnvironment - 用于封装 OutgoingDirectory 对象的封装容器, 充当本地主机的后备 VFS(虚拟文件系统) 受测驱动程序的命名空间内。

    • DriverUnderTest - 此类是 RAII (Resource Acquisition Is Initialization) 封装容器 供受测驾驶员使用

    • DriverRuntime - 此类是托管驱动程序的 RAII 封装容器 运行时线程池

  • //sdk/lib/driver/testing/cpp/driver_runtime.h

    • TestSynchronizedDispatcher - 此类是对 驱动程序调度程序。

以下库可能有助于编写驱动程序单元测试:

最后,以下示例单元测试涵盖不同的配置和 测试用例:

其他资源

以下是一些 DFv2 驱动程序示例:

本部分中提到的所有 Gerrit 更改如下:

本部分中提到的所有源代码文件

本部分中提到的所有文档页面