公開駕駛功能

驅動程式元件會透過capabilities,為其他驅動程式和非驅動程式庫元件提供的功能和服務。這可讓 Fuchsia 的元件架構視需要將這些功能轉送至目標元件。驅動程式也可將其功能匯出至 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 檔案,並加入以下內容,納入 examples.qemuedu 程式庫的 FIDL 繫結,並建立新的 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',
        },
    ],
}

更新驅動程式庫的建構設定,以依附於新 examples.qemuedu 程式庫的 FIDL 繫結:

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 驅動程式庫可讓使用 devfs 探索其他元件的 examples.qemuedu/Device 通訊協定。如要找出系統中可用的驅動程式庫服務,非驅動程式庫元件會查詢裝置檔案系統 (通常掛接在元件命名空間中的 /dev),然後掃描這個檔案系統下的目錄和檔案。

提供與通訊協定 ID 對應的已知裝置類別時,驅動程式管理員可將 devf 中的項目別名為特定的裝置類別項目 (例如 /dev/class/input)。如果非驅動程式庫元件元件不知道 devfs 中驅動程式庫服務的確切路徑,而是特定類型,在本練習中,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 服務。