與駕駛人互動

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 納入必要的建構規則:

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);
}

這個可執行檔支援兩個子指令,可執行即時檢查和階乘計算。

在專案的建構設定底部新增下列規則,將這項新工具建構至 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);
}

更新工具的建構設定,以依附 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 指令會執行下列步驟:

  1. 建構可執行檔和套件。
  2. 將套件發布至本機套件存放區。
  3. 向目標裝置註冊套件存放區。
  4. 使用 ffx driver run-tooldriver_playground 元件中執行二進位檔。

這個指令會列印類似以下的輸出內容,其中包含階乘的計算結果:

Factorial(12) = 479001600

恭喜!您已從其他用戶端成功連線至驅動程式庫公開的服務。