| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // [START imports] |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <lib/fdio/directory.h> |
| #include <libgen.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include <filesystem> |
| // [END imports] |
| |
| // [START fidl_imports] |
| #include <fidl/examples.qemuedu/cpp/wire.h> |
| // [END fidl_imports] |
| |
| // [START device_path] |
| constexpr char kDevfsRootPath[] = "/dev/sys/platform"; |
| constexpr char kEduDevicePath[] = "qemu-edu"; |
| // [END device_path] |
| |
| // [START cli_helpers] |
| 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; |
| } |
| // [END cli_helpers] |
| |
| // [START device_client] |
| // [START_EXCLUDE silent] |
| // TODO(fxbug.dev/114888): Remove this once we can alias a stable path for generic devices. |
| // [END_EXCLUDE] |
| // Search for the device file entry in devfs |
| std::optional<std::string> SearchDevicePath() { |
| for (auto const& dir_entry : std::filesystem::recursive_directory_iterator(kDevfsRootPath)) { |
| if (dir_entry.path().string().find(kEduDevicePath) != std::string::npos) { |
| return {dir_entry.path()}; |
| } |
| } |
| |
| return {}; |
| } |
| |
| // Open a FIDL client connection to the examples.qemuedu.Device |
| fidl::WireSyncClient<examples_qemuedu::Device> OpenDevice() { |
| auto device_path = SearchDevicePath(); |
| if (!device_path.has_value()) { |
| fprintf(stderr, "Unable to locate qemu-edu device path.\n"); |
| return {}; |
| } |
| int device = open(device_path.value().c_str(), O_RDWR); |
| |
| if (device < 0) { |
| fprintf(stderr, "Failed to open qemu edu device: %s\n", strerror(errno)); |
| return {}; |
| } |
| |
| fidl::ClientEnd<examples_qemuedu::Device> client_end; |
| zx_status_t st = fdio_get_service_handle(device, client_end.channel().reset_and_get_address()); |
| if (st != ZX_OK) { |
| fprintf(stderr, "Failed to get service handle: %s\n", zx_status_get_string(st)); |
| return {}; |
| } |
| |
| return fidl::WireSyncClient(std::move(client_end)); |
| } |
| // [END device_client] |
| |
| // [START liveness_check] |
| // Run a liveness check on the QEMU edu device. |
| // Returns 0 on success. |
| int liveness_check() { |
| auto client = OpenDevice(); |
| if (!client.is_valid()) { |
| return -1; |
| } |
| |
| 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; |
| } |
| } |
| // [END liveness_check] |
| |
| // [START compute_factorial] |
| // Compute the factorial of n using the QEMU edu device. |
| // Returns 0 on success. |
| int compute_factorial(long n) { |
| auto client = OpenDevice(); |
| if (!client.is_valid()) { |
| return -1; |
| } |
| |
| 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; |
| } |
| // [END compute_factorial] |
| |
| // [START main] |
| 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); |
| } |
| // [END main] |