驅動程式元件會透過「功能」,將提供的功能和服務提供給其他驅動程式和非驅動程式庫元件。這可讓 Fuchsia 的元件架構在必要時,將這些功能路由至目標元件。詳情請參閱驅動程式通訊指南。
在本節中,您將公開 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(
"@rules_fuchsia//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 檔案定義了 Device
通訊協定 (包含兩種方法),以及 DeviceService
,可供用戶端使用 Device
通訊協定。
在專案建構設定的底部新增下列建構規則,編譯 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 服務後,您需要更新驅動程式庫,實作 Device
通訊協定,並將 DeviceService
提供給其他元件。
完成本節後,專案應具有下列目錄結構:
//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
類別來實作 Device
通訊協定的伺服器端:
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
更新驅動程式庫的建構設定,依附於新 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
驅動程式庫會從傳出目錄提供 fuchsia.examples.qemuedu.DeviceService
,讓其他元件可探索到該元件。為此,驅動程式庫必須:
- 在元件資訊清單中,將這項服務宣告為
capability
。 expose
這項能力,並在元件資訊清單中提供給元件架構。
這樣一來,其他元件 (包括非驅動程式庫元件) 就能依類型要求服務,而不必瞭解驅動程式庫的拓撲路徑。元件架構負責將服務連線從用戶端路由至提供服務的驅動程式庫。
更新驅動程式庫的元件資訊清單,宣告並公開裝置服務做為能力:
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()
方法,開始從元件的傳出目錄提供 fuchsia.examples.qemuedu.DeviceService
:
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();
}
return zx::ok();
}
其他元件現在可以探索 qemu_edu
驅動程式庫的功能。
重新建構驅動程式庫
使用 bazel build
指令,確認驅動程式庫是否已成功建構程式碼變更:
bazel build //fuchsia-codelab/qemu_edu/drivers:pkg
恭喜!您已成功從 Fuchsia 驅動程式公開 FIDL 服務。