| // 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. |
| |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <fidl/fuchsia.hardware.qemuedu/cpp/wire.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> |
| |
| constexpr char kEduDevicePath[] = |
| "/dev/sys/platform/platform-passthrough/PCI0/bus/00:06.0_/qemu-edu"; |
| constexpr char kEduDevicePath2[] = "/dev/sys/platform/platform-passthrough/acpi/acpi-KBLT/qemu-edu"; |
| |
| 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; |
| } |
| |
| fidl::WireSyncClient<fuchsia_hardware_qemuedu::Device> OpenDevice() { |
| int device = open(kEduDevicePath, O_RDWR); |
| |
| if (device < 0) { |
| device = open(kEduDevicePath2, O_RDWR); |
| } |
| if (device < 0) { |
| fprintf(stderr, "Failed to open qemu edu device: %s\n", strerror(errno)); |
| return {}; |
| } |
| |
| fidl::ClientEnd<fuchsia_hardware_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::BindSyncClient(std::move(client_end)); |
| } |
| |
| 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 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->result) { |
| printf("Liveness check passed!\n"); |
| return 0; |
| } else { |
| printf("Liveness check failed!\n"); |
| return -1; |
| } |
| } |
| |
| // Use the qemu edu device to print the factorial of n to stdout. |
| // 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->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); |
| } |