在 DFv2 驱动程序中提供 Banjo 协议

本指南逐步介绍了如何在 DFv2 驱动程序中提供 Banjo 协议,以及如何从 DFv1 子驱动程序连接到其 Banjo 服务器。

Banjo 协议(主要用于 DFv1 驱动程序)在带有 @transport("Banjo")@banjo_layout("ddk-protocol") 行注解的 FIDL 库中定义,例如:

/// The protocol provides access to functions of the driver.
@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol Misc {
    /// Returns a unique identifier for this device.
    strict GetHardwareId() -> (struct {
        status zx.Status;
        response uint32;
    });

    /// Returns the current device firmware version
    strict GetFirmwareVersion() -> (struct {
        status zx.Status;
        major uint32;
        minor uint32;
    });
};

(来源:gizmo.test.fidl

要使 DFv2 驱动程序能够使用 Banjo 协议,请参阅以下任务:

从 DFv2 驱动程序提供 Banjo 协议

本部分逐步介绍了如何在 DFv2 驱动程序中实现 Banjo 协议,以及如何将该协议提供给 DFv1 子驱动程序。本演示基于 Banjo Transport 示例,该示例在 gizmo.test FIDL 库中实现了 Misc Banjo 协议。

相关步骤如下:

  1. 设置 Banjo 协议
  2. 实现 Banjo 协议
  3. 遵循 Banjo 协议

1. 设置 Banjo 协议

如需在 DFv2 驱动程序中设置 Misc Banjo 协议,请执行以下操作:

  1. BUILD.gn 文件中,将 Banjo 库作为依赖项添加到 fuchsia_driver 目标中,例如:

    fuchsia_driver("parent_driver") {
      output_name = "banjo_transport_parent"
      sources = [ "parent-driver.cc" ]
      deps = [
        "//examples/drivers/bind_library:gizmo.example_cpp",
        "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp",
         ...
      ]
    }
    
  2. 在驱动程序的 C++ 头文件中,添加 Banjo 库的 C++ 头文件,例如:

    #include <fuchsia/examples/gizmo/cpp/banjo.h>
    ...
    
    namespace banjo_transport {
    ...
    

    (来源:parent-driver.h

  3. 如需从 Banjo 协议绑定继承,请使用以下格式更新驱动程序类:

    ddk::<PROTOCOL_NAME>Protocol<<YOUR_DRIVER_CLASS>>
    

    PROTOCOL_NAME 替换为 Banjo 协议的名称,YOUR_DRIVER_CLASS 是驱动程序的类(两者均采用驼峰命名法),例如:

    class ParentBanjoTransportDriver : public fdf::DriverBase,
        public ddk::MiscProtocol<ParentBanjoTransportDriver>  {
        ...
    };
    

    (来源:parent-driver.h

2. 实现 Banjo 协议

在驱动程序类中,定义和实现 Banjo 协议中的每个函数。

例如,以下示例展示了名为 ProtocolName 的 Banjo 协议:

@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol ProtocolName {
   /// Returns a unique identifier for this device.
   strict FunctionName() -> (struct {
       status zx.Status;
       response_1 response_1_type;
       response_2 response_2_type;
   });
};

对于此 ProtocolName Banjo 协议,FunctionName 函数的 C++ 绑定如下所示:

zx_status_t ProtocolNameFunctionName(response_1_type* response_1, response_2_type* response_2);

您可以在 Fuchsia 源代码检出的以下路径中找到现有 Banjo 协议的 C++ 绑定:

<OUT_DIRECTORY>/fidling/gen/<PATH_TO_THE_FIDL_LIBRARY>/banjo

例如,如果您的 out 目录为 out/default 且 FIDL 库位于 examples/drivers/transport 目录中,则 C++ 绑定位于以下目录中:

out/default/fidling/gen/examples/drivers/transport/banjo

请参阅 Banjo Transport 示例中的以下实现:

  • Misc 协议包含以下函数:

    /// Returns a unique identifier for this device.
    strict GetHardwareId() -> (struct {
     status zx.Status;
     response uint32;
    });
    
    /// Returns the current device firmware version
    strict GetFirmwareVersion() -> (struct {
     status zx.Status;
     major uint32;
     minor uint32;
    });
    
  • ParentBanjoTransportDriver 类定义这些函数,如下所示:

    class ParentBanjoTransportDriver : public fdf::DriverBase,
                                      public ddk::MiscProtocol<ParentBanjoTransportDriver> {
    public:
    ...
    
     // MiscProtocol implementation.
     zx_status_t MiscGetHardwareId(uint32_t* out_response);
     zx_status_t MiscGetFirmwareVersion(uint32_t* out_major, uint32_t* out_minor);
    ...
    };
    

    (来源:parent-driver.h

  • 这些函数的实现方式如下:

    zx_status_t ParentBanjoTransportDriver::MiscGetHardwareId(uint32_t* out_response) {
     *out_response = 0x1234ABCD;
     return ZX_OK;
    }
    
    zx_status_t ParentBanjoTransportDriver::MiscGetFirmwareVersion(uint32_t* out_major,
                                                                  uint32_t* out_minor) {
     *out_major = 0x0;
     *out_minor = 0x1;
     return ZX_OK;
    }
    

    (来源:parent-driver.cc

3. 遵循班卓琴的协议

在 DFv2 驱动程序中实现 Banjo 协议后,您需要使用配置了 Banjo 的兼容型设备服务器将该协议提供给 DFv1 子节点。

为此,请完成在 DFv2 驱动程序中设置兼容型设备服务器指南中的以下任务:

  1. 设置 compat 设备服务器
  2. 为后代 DFv1 驱动程序提供 Banjo 服务

从 DFv1 驱动程序连接到 Banjo 服务器

本部分使用 Banjo 传输示例来演示将 DFv1 子驱动程序连接到提供 Banjo 协议的 DFv2 父驱动程序的任务。

相关步骤如下:

  1. 将子驱动程序连接到父驱动程序
  2. 使用 Banjo 协议

1. 将子驱动程序连接到父驱动程序

为了能够将子驱动程序连接到父驱动程序以使用 Banjo 协议,子驱动程序必须与父驱动程序位于同一驱动程序主机中,并且两个驱动程序都需要使用兼容型 banjo_client 库。

如需将子驱动程序连接到父驱动程序,请执行以下操作:

  1. 在子驱动程序的组件清单 (.cml) 中,将 colocate 字段设置为 true,例如:

    {
        include: [ 'syslog/client.shard.cml' ],
        program: {
            runner: 'driver',
            binary: 'driver/banjo_transport_child.so',
            bind: 'meta/bind/child-driver.bindbc',
    
            // Run in the same driver host as the parent driver
            colocate: 'true',
        },
        use: [
            { service: 'fuchsia.driver.compat.Service' },
        ],
    }
    

    (来源:child-driver.cml

  2. 在驱动程序的 BUILD.gn 文件中,将 Banjo 库作为依赖项添加到 fuchsia_driver 目标中,例如:

    fuchsia_driver("child_driver") {
      output_name = "banjo_transport_child"
      sources = [ "child-driver.cc" ]
      deps = [
        "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp",
        "//sdk/lib/driver/component/cpp:cpp",
        "//src/devices/lib/driver:driver_runtime",
      ]
    }
    

    (来源:BUILD.gn

  3. 在驱动程序的 C++ 头文件中,添加 Banjo 库的 C++ 头文件,例如:

    #include <fuchsia/examples/gizmo/cpp/banjo.h>
    ...
    
    namespace banjo_transport {
    ...
    

    (来源:child-driver.h

  4. 在驱动程序的 BUILD.gn 文件中,将 compat banjo_client 库作为依赖项添加到 fuchsia_driver 目标中,例如:

    fuchsia_driver("child_driver") {
      output_name = "banjo_transport_child"
      sources = [ "child-driver.cc" ]
      deps = [
        "//examples/drivers/transport/banjo:fuchsia.examples.gizmo_banjo_cpp",
        "//sdk/lib/driver/compat/cpp",
        "//sdk/lib/driver/component/cpp:cpp",
        "//src/devices/lib/driver:driver_runtime",
      ]
    }
    

    (来源:BUILD.gn

  5. 在驱动程序的 C++ 源文件中,添加 compat banjo_client 库的 C++ 头文件,例如:

    #include "examples/drivers/transport/banjo/v2/child-driver.h"
    
    #include <lib/driver/compat/cpp/compat.h>
    #include <lib/driver/component/cpp/driver_export.h>
    #include <lib/driver/logging/cpp/structured_logger.h>
    
    namespace banjo_transport {
    
    zx::result<> ChildBanjoTransportDriver::Start() {
    ...
    

    (来源:child-driver.cc

  6. 在驱动程序的 C++ 源文件中,使用 compat::ConnectBanjo() 函数设置 Banjo 客户端,例如:

    zx::result<Client> ConnectBanjo(const std::shared_ptr<fdf::Namespace>& incoming,
                                    std::string_view parent_name = "default") {
    

    Banjo Transport 示例中,子级驱动程序会执行以下操作来连接到 Misc 协议:

    zx::result<ddk::MiscProtocolClient> client =
          compat::ConnectBanjo<ddk::MiscProtocolClient>(incoming());
    
    if (client.is_error()) {
        FDF_SLOG(ERROR, "Failed to connect client", KV("status",
             client.status_string()));
        return client.take_error();
    }
    

    (来源:child-driver.cc

2. 使用 Banjo 协议

在子驱动程序中,使用协议客户端调用 Banjo 函数。

例如,以下示例展示了名为 ProtocolName 的 Banjo 协议:

@transport("Banjo")
@banjo_layout("ddk-protocol")
closed protocol ProtocolName {
   /// Returns a unique identifier for this device.
   strict FunctionName() -> (struct {
       status zx.Status;
       response_1 response_1_type;
       response_2 response_2_type;
   });
};

对于此 ProtocolName Banjo 协议,FunctionName 函数的 C++ 绑定如下所示:

zx_status_t ProtocolNameFunctionName(response_1_type* response_1, response_2_type* response_2);

Banjo Transport 示例中,GetHardwareId() 函数的定义如下所示:

/// Returns a unique identifier for this device.
strict GetHardwareId() -> (struct {
 status zx.Status;
 response uint32;
});

(来源:gizmo.test.fidl

将 Banjo 客户端存储在 client_ 对象中并将 hardware_id_ 变量定义为 uint32_t 后,您可以通过以下方式调用 GetHardwareId() 函数:

zx_status_t status = client_.GetHardwareId(&hardware_id_);
if (status != ZX_OK) {
 return status;
}

(来源:child-driver.cc