公開駕駛功能

驅動程式元件可提供為其他駕駛人提供的功能和服務 非驅動程式庫元件的功能。 如此一來, 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 程式庫並建立新的 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 程式庫:

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 服務。