Fuchsia 上的软件通过驱动程序组件公开的服务功能与驱动程序组件进行交互。客户端连接到驱动程序的服务后,会收到表示该驱动程序的 FIDL 协议实例。如需了解详情,请参阅驾驶员通信指南。
在本部分中,您将创建一个新的 eductl
可执行文件,用于发现并与 qemu_edu
驱动程序公开的功能进行交互。
创建新的工具组件
在 Bazel 工作区中为新的二进制工具创建一个新的项目目录:
mkdir -p fuchsia-codelab/qemu_edu/tools
完成本部分后,项目应具有以下目录结构:
//fuchsia-codelab/qemu_edu/tools
|- BUILD.bazel
|- eductl.cc
创建 qemu_edu/tools/BUILD.bazel
文件并添加以下语句,以纳入 Fuchsia SDK 中的必要 build 规则:
qemu_edu/tools/BUILD.bazel
:
load(
"@rules_fuchsia//fuchsia:defs.bzl",
"fuchsia_cc_binary",
"fuchsia_component",
"fuchsia_component_manifest",
"fuchsia_driver_tool",
"fuchsia_package",
)
使用以下代码创建一个新的 qemu_edu/tools/eductl.cc
文件,以设置基本的命令行可执行文件:
qemu_edu/tools/eductl.cc
:
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <getopt.h>
#include <lib/component/incoming/cpp/service_member_watcher.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <filesystem>
int usage(const char* cmd) {
fprintf(stderr,
"\nInteract with the QEMU edu device:\n"
" %s live Performs a card liveness check\n"
" %s fact <n> Computes the factorial of n\n"
" %s help Print this message\n",
cmd, cmd, cmd);
return -1;
}
// Returns "true" if the argument matches the prefix.
// In this case, moves the argument past the prefix.
bool prefix_match(const char** arg, const char* prefix) {
if (!strncmp(*arg, prefix, strlen(prefix))) {
*arg += strlen(prefix);
return true;
}
return false;
}
constexpr long kBadParse = -1;
long parse_positive_long(const char* number) {
char* end;
long result = strtol(number, &end, 10);
if (end == number || *end != '\0' || result < 0) {
return kBadParse;
}
return result;
}
int main(int argc, char* argv[]) {
const char* cmd = basename(argv[0]);
// ...
return usage(cmd);
}
此可执行文件支持两个子命令,用于执行活性检查和阶乘计算。
将以下新规则添加到项目 build 配置的底部,以将此新工具构建到 Fuchsia 软件包中:
qemu_edu/tools/BUILD.bazel
:
fuchsia_cc_binary(
name = "eductl",
srcs = [
"eductl.cc",
],
deps = [
"@fuchsia_sdk//pkg/component_incoming_cpp",
"@fuchsia_sdk//pkg/fdio",
"@fuchsia_sdk//pkg/fidl_cpp_wire",
],
)
fuchsia_driver_tool(
name = "eductl_tool",
binary = ":eductl",
visibility = ["//visibility:public"],
)
fuchsia_package(
name = "pkg",
package_name = "eductl",
tools = [
":eductl_tool",
],
visibility = ["//visibility:public"],
)
实现客户端工具
客户端连接到驱动程序公开的服务以接收 FIDL 协议的实例。将以下代码添加到 eductl
以连接到 edu
设备服务:
qemu_edu/tools/eductl.cc
:
#include <ctype.h>
#include <dirent.h>
#include <fcntl.h>
#include <getopt.h>
#include <lib/component/incoming/cpp/service_member_watcher.h>
#include <libgen.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <filesystem>
#include <fidl/examples.qemuedu/cpp/wire.h>
// Open a FIDL client connection to the examples.qemuedu.Device
zx::result<fidl::WireSyncClient<examples_qemuedu::Device>> ConnectToDevice() {
component::SyncServiceMemberWatcher<examples_qemuedu::Service::Device> watcher;
zx::result<fidl::ClientEnd<examples_qemuedu::Device>> client_end = watcher.GetNextInstance(true);
if (client_end.is_error()) {
fprintf(stderr, "Failed to connect to device service: %s\n", client_end.status_string());
return client_end.take_error();
}
return zx::ok(fidl::WireSyncClient(std::move(*client_end)));
}
// ...
添加 liveness_check()
和 compute_factorial()
函数,以使用从 ConnectToDevice()
返回的 examples.qemuedu/Device
FIDL 协议调用方法。最后,更新工具的 main()
函数,以根据命令行中传递的实参调用相应的设备函数:
qemu_edu/tools/eductl.cc
:
// ...
// Run a liveness check on the QEMU edu device.
// Returns 0 on success.
int liveness_check() {
auto client_result = ConnectToDevice();
if (client_result.is_error()) {
return -1;
}
auto& client = client_result.value();
auto liveness_check_result = client->LivenessCheck();
if (!liveness_check_result.ok()) {
fprintf(stderr, "Error: failed to get liveness check result: %s\n",
zx_status_get_string(liveness_check_result.status()));
return -1;
}
if (liveness_check_result->value()->result) {
printf("Liveness check passed!\n");
return 0;
} else {
printf("Liveness check failed!\n");
return -1;
}
}
// Compute the factorial of n using the QEMU edu device.
// Returns 0 on success.
int compute_factorial(long n) {
auto client_result = ConnectToDevice();
if (client_result.is_error()) {
return -1;
}
auto& client = client_result.value();
if (n >= std::numeric_limits<uint32_t>::max()) {
fprintf(stderr, "N is too large\n");
return -1;
}
uint32_t input = static_cast<uint32_t>(n);
auto compute_factorial_result = client->ComputeFactorial(input);
if (!compute_factorial_result.ok()) {
fprintf(stderr, "Error: failed to call compute factorial result: %s\n",
zx_status_get_string(compute_factorial_result.status()));
return -1;
}
printf("Factorial(%u) = %u\n", input, compute_factorial_result->value()->output);
return 0;
}
int main(int argc, char* argv[]) {
const char* cmd = basename(argv[0]);
// If no arguments passed, bail out after dumping
// usage information.
if (argc < 2) {
return usage(cmd);
}
const char* arg = argv[1];
if (prefix_match(&arg, "live")) {
return liveness_check();
} else if (prefix_match(&arg, "fact")) {
if (argc < 3) {
fprintf(stderr, "Expecting 1 argument\n");
return usage(cmd);
}
long n = parse_positive_long(argv[2]);
return compute_factorial(n);
}
return usage(cmd);
}
更新工具的 build 配置,以依赖于 examples.qemuedu
库的 FIDL 绑定:
qemu_edu/tools/BUILD.bazel
:
fuchsia_cc_binary(
name = "eductl",
srcs = [
"eductl.cc",
],
deps = [
"//fuchsia-codelab/qemu_edu/fidl:examples.qemuedu_cc",
"@fuchsia_sdk//pkg/component_incoming_cpp",
"@fuchsia_sdk//pkg/fdio",
"@fuchsia_sdk//pkg/fidl_cpp_wire",
],
)
重启模拟器
关闭所有现有的模拟器实例:
ffx emu stop --all
启动启用了驱动程序框架的 Fuchsia 模拟器的新实例:
ffx emu start core.x64 --headless
重新加载驱动程序
使用 bazel run
命令构建并执行驱动程序组件目标:
bazel run //fuchsia-codelab/qemu_edu/drivers:pkg.component
运行该工具
使用 bazel run
命令构建并执行该工具,传递实参 fact 12
以计算 12 的阶乘:
bazel run //fuchsia-codelab/qemu_edu/tools:pkg.eductl_tool -- fact 12
bazel run
命令会执行以下步骤:
- 构建可执行文件和软件包。
- 将软件包发布到本地软件包代码库。
- 向目标设备注册软件包代码库。
- 使用
ffx driver run-tool
运行driver_playground
组件内的二进制文件。
该命令会输出类似于以下内容的输出,其中包含计算结果(阶乘):
Factorial(12) = 479001600
恭喜!您已成功从单独的客户端连接到驱动程序公开的服务。