驱动程序组件能够为其他驱动程序提供自身的功能和服务 通过功能访问非驱动程序组件。 这使得 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 服务。