公开驱动程序功能

驱动程序组件能够为其他驱动程序提供自身的功能和服务 通过功能访问非驱动程序组件。 这使得 Fuchsia 的组件框架可以将这些功能路由到 target 组件。驾驶员还可以将自己的功能导出到 devfs:使其他组件能够以文件形式发现它们 该组件的命名空间中装载了多个节点。

在本部分中,您将公开 qemu_edu 驱动程序的阶乘功能 并使用在系统其他位置运行的组件中的请求。

创建新的 FIDL 库

在 Bazel 工作区中,为新的 FIDL 库创建新的项目目录:

mkdir -p fuchsia-codelab/qemu_edu/fidl

完成本部分后,项目应具有以下目录 结构:

//fuchsia-codelab/qemu_edu/fidl
                  |- BUILD.bazel
                  |- qemu_edu.fidl

创建 qemu_edu/fidl/BUILD.bazel 文件并将以下语句添加到 添加来自 Fuchsia SDK 的必要构建规则:

qemu_edu/fidl/BUILD.bazel

load(
    "@fuchsia_sdk//fuchsia:defs.bzl",
    "fuchsia_fidl_library",
    "fuchsia_fidl_llcpp_library",
)

定义驱动程序服务协议

驱动程序使用自定义 FIDL 提供 edu 设备的功能 协议。使用以下命令向项目工作区添加新的 qemu_edu/qemu_edu.fidl 文件: 以下内容:

qemu_edu/fidl/qemu_edu.fidl

library examples.qemuedu;

using zx;

/// The Device protocol provides access to the functions of the QEMU edu hardware.
@discoverable
open protocol Device {
    /// Computes the factorial of 'input' using the edu device and returns the
    /// result.
    flexible ComputeFactorial(struct {
        input uint32;
    }) -> (struct {
        output uint32;
    }) error zx.Status;

    /// Performs a liveness check and return true if the device passes.
    flexible LivenessCheck() -> (struct {
        result bool;
    }) error zx.Status;
};

/// The Service enables the driver framework to offer the Device protocol to
/// other components.
service Service {
    device client_end:Device;
};

此 FIDL 协议提供了两种与阶乘 edu 设备上的计算和活跃性检查硬件寄存器。

将以下构建规则添加到项目构建配置的底部 编译 FIDL 库并生成 C++ 绑定:

  • fuchsia_fidl_library():声明 examples.qemuedu FIDL 库,并描述了它包含的 FIDL 源文件。
  • fuchsia_fidl_llcpp_library():描述生成的 组件的 C++ 绑定 与此 FIDL 库进行交互。

qemu_edu/fidl/BUILD.bazel

fuchsia_fidl_library(
    name = "examples.qemuedu",
    srcs = [
        "qemu_edu.fidl",
    ],
    library = "examples.qemuedu",
    visibility = ["//visibility:public"],
    deps = [
        "@fuchsia_sdk//fidl/zx",
    ],
)

fuchsia_fidl_llcpp_library(
    name = "examples.qemuedu_cc",
    library = ":examples.qemuedu",
    visibility = ["//visibility:public"],
    deps = [
        "@fuchsia_sdk//fidl/zx:zx_llcpp_cc",
        "@fuchsia_sdk//pkg/fidl_cpp_wire",
    ],
)

实现驱动程序服务协议

定义 FIDL 协议后,您需要更新驱动程序,才能实现 并将此协议提供给其他组件。

完成本部分后,项目应具有以下目录 结构:

//fuchsia-codelab/qemu_edu/drivers
                  |- BUILD.bazel
                  |- meta
                  |   |- qemu_edu.cml
                  |- edu_device.cc
                  |- edu_device.h
                  |- edu_server.cc 
                  |- edu_server.h 
                  |- qemu_edu.bind
                  |- qemu_edu.cc
                  |- qemu_edu.h

在项目目录中创建新的 qemu_edu/drivers/edu_server.h 文件 包含以下内容的 FIDL 绑定, examples.qemuedu 库,并创建一个新的 QemuEduServer 类,以 实现 FIDL 协议的服务器端:

qemu_edu/drivers/edu_server.h

#ifndef FUCHSIA_CODELAB_QEMU_EDU_SERVER_H_
#define FUCHSIA_CODELAB_QEMU_EDU_SERVER_H_

#include <fidl/examples.qemuedu/cpp/wire.h>
#include <lib/driver/logging/cpp/structured_logger.h>

#include "edu_device.h"

namespace qemu_edu {

// FIDL server implementation for the `examples.qemuedu/Device` protocol.
class QemuEduServer : public fidl::WireServer<examples_qemuedu::Device> {
 public:
  explicit QemuEduServer(std::weak_ptr<edu_device::QemuEduDevice> device)
      : device_(std::move(device)) {}

  static fidl::ServerBindingRef<examples_qemuedu::Device> BindDeviceClient(
      async_dispatcher_t* dispatcher, std::weak_ptr<edu_device::QemuEduDevice> device,
      fidl::ServerEnd<examples_qemuedu::Device> request) {
    // Bind each connection request to a unique FIDL server instance
    auto server_impl = std::make_unique<QemuEduServer>(device);
    return fidl::BindServer(dispatcher, std::move(request), std::move(server_impl),
                            std::mem_fn(&QemuEduServer::OnUnbound));
  }

  // This method is called when a server connection is torn down.
  void OnUnbound(fidl::UnbindInfo info, fidl::ServerEnd<examples_qemuedu::Device> server_end) {
    if (info.is_peer_closed()) {
      FDF_LOG(DEBUG, "Client disconnected");
    } else if (!info.is_user_initiated()) {
      FDF_LOG(ERROR, "Client connection unbound: %s", info.status_string());
    }
  }

  // fidl::WireServer<examples_qemuedu::Device>

  void ComputeFactorial(ComputeFactorialRequestView request,
                        ComputeFactorialCompleter::Sync& completer) override;
  void LivenessCheck(LivenessCheckCompleter::Sync& completer) override;
  void handle_unknown_method(fidl::UnknownMethodMetadata<examples_qemuedu::Device> metadata,
                             fidl::UnknownMethodCompleter::Sync& completer) override;

 private:
  std::weak_ptr<edu_device::QemuEduDevice> device_;
};

}  // namespace qemu_edu

#endif  // FUCHSIA_CODELAB_QEMU_EDU_SERVER_H_

在项目目录中创建新的 qemu_edu/drivers/edu_server.cc 文件 包含以下内容来实现 examples.qemuedu/Device 协议方法,并将其映射到设备资源方法:

qemu_edu/drivers/edu_server.cc

#include "edu_server.h"

namespace qemu_edu {

// Driver Service: Compute factorial on the edu device
void QemuEduServer::ComputeFactorial(ComputeFactorialRequestView request,
                                     ComputeFactorialCompleter::Sync& completer) {
  auto edu_device = device_.lock();
  if (!edu_device) {
    FDF_LOG(ERROR, "Unable to access device resources.");
    completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  uint32_t input = request->input;

  edu_device->ComputeFactorial(
      input, [completer = completer.ToAsync()](zx::result<uint32_t> result_status) mutable {
        if (result_status.is_error()) {
          completer.ReplyError(result_status.error_value());
          return;
        }
        uint32_t factorial = result_status.value();
        completer.ReplySuccess(factorial);
      });
}

// Driver Service: Complete a liveness check on the edu device
void QemuEduServer::LivenessCheck(LivenessCheckCompleter::Sync& completer) {
  auto edu_device = device_.lock();
  if (!edu_device) {
    FDF_LOG(ERROR, "Unable to access device resources.");
    completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  constexpr uint32_t kChallenge = 0xdeadbeef;
  constexpr uint32_t kExpectedResponse = ~(kChallenge);

  auto status = edu_device->LivenessCheck(kChallenge);
  if (status.is_error()) {
    FDF_LOG(ERROR, "Unable to send liveness check request.");
    completer.ReplyError(status.error_value());
    return;
  }
  const bool alive = (status.value() == kExpectedResponse);

  FDF_SLOG(INFO, "Replying with", KV("result", alive));
  completer.ReplySuccess(alive);
}

}  // namespace qemu_edu

更新驱动程序的组件清单,以声明和公开 FIDL 协议 作为一项功能:

qemu_edu/drivers/meta/qemu_edu.cml

{
    include: [
        "syslog/client.shard.cml",
    ],
    program: {
        runner: 'driver',
        binary: 'driver/libqemu_edu.so',
        bind: 'meta/bind/qemu_edu.bindbc',
        // Identifies the device categories, for compatibility tests. This
        // example driver uses the 'misc' category; real drivers should
        // select a more specific category.
        device_categories: [
          { category: 'misc', subcategory: '' },
        ],
    },
    use: [
        { service: 'fuchsia.hardware.pci.Service' },
    ],
    // Provide the device capability to other components
    capabilities: [
        { service: 'examples.qemuedu.Service' },
    ],
    expose: [
        {
            service: 'examples.qemuedu.Service',
            from: 'self',
        },
    ],
}

更新驱动程序的 build 配置,使其依赖于 新 examples.qemuedu 库:

qemu_edu/drivers/BUILD.bazel

fuchsia_cc_driver(
    name = "qemu_edu",
    srcs = [
        "edu_device.cc",
        "edu_device.h",
        "edu_server.cc",
        "edu_server.h",
        "qemu_edu.cc",
        "qemu_edu.h",
    ],
    deps = [
        "//fuchsia-codelab/qemu_edu/fidl:examples.qemuedu_cc",
        "@fuchsia_sdk//fidl/fuchsia.hardware.pci:fuchsia.hardware.pci_llcpp_cc",
        "@fuchsia_sdk//pkg/driver_component_cpp",
        "@fuchsia_sdk//pkg/driver_devfs_cpp",
        "@fuchsia_sdk//pkg/hwreg",
        "@fuchsia_sdk//pkg/mmio",
    ],
)

导出和传送协议

qemu_edu 驱动程序会执行 examples.qemuedu/Device 协议 供其他组件使用 devfs 检测。发现哪些司机服务 非驱动程序组件会查找相应设备 文件系统(通常装载到组件命名空间中的 /dev)并扫描 该文件系统下的目录和文件。

驱动程序管理器可以将 devfs 中的条目别名设为特定的设备类条目 (例如 /dev/class/input)时,如果匹配的协议 ID 与已知的 提供的设备类别。如果非驱动程序组件不知道确切路径 而不能是特定类型。 在本练习中,edu 设备不符合已知类别,因此您将 请将此条目配置为未分类设备

更新驱动程序组件的清单以请求 fuchsia.devics.fs.Exporter 功能:

qemu_edu/drivers/meta/qemu_edu.cml

{
    include: [
        "syslog/client.shard.cml",
    ],
    program: {
        runner: 'driver',
        binary: 'driver/libqemu_edu.so',
        bind: 'meta/bind/qemu_edu.bindbc',
        // Identifies the device categories, for compatibility tests. This
        // example driver uses the 'misc' category; real drivers should
        // select a more specific category.
        device_categories: [
          { category: 'misc', subcategory: '' },
        ],
    },
    use: [
        { service: 'fuchsia.hardware.pci.Service' },
    ],
    // Provide the device capability to other components
    capabilities: [
        { service: 'examples.qemuedu.Service' },
    ],
    expose: [
        {
            service: 'examples.qemuedu.Service',
            from: 'self',
        },
    ],
}

更新驱动程序的 Start() 方法,以开始提供 examples.qemuedu/Device 协议 添加到与设备节点的拓扑路径匹配的新 devfs 条目:

qemu_edu/drivers/qemu_edu.cc

#include "qemu_edu.h"

#include <lib/driver/component/cpp/driver_export.h>

#include "edu_server.h"

// Initialize this driver instance
zx::result<> QemuEduDriver::Start() {
  // ...

  // Report the version information from the edu device.
  auto version_reg = device_->IdentificationRegister();
  FDF_SLOG(INFO, "edu device version", KV("major", version_reg.major_version()),
           KV("minor", version_reg.minor_version()));

  // Serve the examples.qemuedu/Service capability.
  examples_qemuedu::Service::InstanceHandler handler({
      .device = fit::bind_member<&QemuEduDriver::Serve>(this),
  });

  auto add_result = outgoing()->AddService<examples_qemuedu::Service>(std::move(handler));
  if (add_result.is_error()) {
    FDF_SLOG(ERROR, "Failed to add Device service", KV("status", add_result.status_string()));
    return add_result.take_error();
  }

  if (zx::result result = ExportToDevfs(); result.is_error()) {
    FDF_SLOG(ERROR, "Failed to export to devfs", KV("status", result.status_string()));
    return result.take_error();
  }
  // Create and export a devfs entry for the driver service.

  return zx::ok();
}

现在,其他组件可以发现 qemu_edu 驱动程序的功能。

重新构建驱动程序

使用 bazel build 命令验证驱动程序是否通过 您的代码更改:

bazel build //fuchsia-codelab/qemu_edu/drivers:pkg

恭喜!您已成功通过 Fuchsia 驱动程序公开了 FIDL 服务。