blob: 2f1ff75e1c7d9e46676e84d78fc3327be4cb39a1 [file] [log] [blame]
// 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]