blob: a0609ba1afa77b57f9609948f32daa92ad07c6c5 [file] [log] [blame]
// Copyright 2021 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 "radarutil.h"
#include <fuchsia/hardware/radar/llcpp/fidl.h>
#include <lib/async/time.h>
#include <lib/fidl/llcpp/fidl_allocator.h>
#include <lib/zx/status.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fbl/auto_lock.h>
namespace radarutil {
zx::status<zx::duration> ParseDuration(char* arg) {
char* endptr;
const int64_t duration = strtol(arg, &endptr, 10);
if (endptr == arg || *endptr == '\0' || duration < 0) {
return zx::error(ZX_ERR_INVALID_ARGS);
}
switch (*endptr) {
case 'h':
return zx::ok(zx::hour(duration));
case 'm':
if (strncmp("ms", endptr, 2) == 0) {
return zx::ok(zx::msec(duration));
}
return zx::ok(zx::min(duration));
case 's':
return zx::ok(zx::sec(duration));
case 'u':
return zx::ok(zx::usec(duration));
case 'n':
return zx::ok(zx::nsec(duration));
}
return zx::error(ZX_ERR_INVALID_ARGS);
}
void Usage() {
constexpr char kUsageString[] =
"Usage: radarutil [-h] [-p burst process time] [-t run time] [-v vmos]\n"
" burst process time: Time to sleep after each burst to simulate processing delay."
" Default: 0s\n"
" run time: Total time to read frames."
" Default: 10s\n"
" vmos: Number of VMOs to register for receiving frames."
" Default: 10\n"
"\n"
" For time arguments, add a suffix (h,m,s,ms,us,ns) to indicate units.\n"
" For example: radarutil -p 3ms -t 5m -v 20\n";
fprintf(stderr, "%s", kUsageString);
}
zx_status_t RadarUtil::Run(
int argc, char** argv,
fidl::ClientEnd<fuchsia_hardware_radar::RadarBurstReaderProvider> device) {
RadarUtil radarutil;
zx_status_t status = radarutil.ParseArgs(argc, argv);
if (status != ZX_OK) {
return status;
}
if ((status = radarutil.ConnectToDevice(std::move(device))) != ZX_OK) {
return status;
}
if ((status = radarutil.RegisterVmos()) != ZX_OK) {
return status;
}
if ((status = radarutil.Run()) != ZX_OK) {
return status;
}
return radarutil.UnregisterVmos();
}
zx_status_t RadarUtil::ParseArgs(int argc, char** argv) {
int opt, vmos;
while ((opt = getopt(argc, argv, "hp:t:v:")) != -1) {
switch (opt) {
case 'h':
Usage();
return ZX_OK;
case 'p': {
zx::status<zx::duration> burst_process_time = ParseDuration(optarg);
if (burst_process_time.is_error()) {
Usage();
return burst_process_time.error_value();
}
burst_process_time_ = burst_process_time.value();
break;
}
case 't': {
zx::status<zx::duration> run_time = ParseDuration(optarg);
if (run_time.is_error()) {
Usage();
return run_time.error_value();
}
run_time_ = run_time.value();
break;
}
case 'v':
vmos = atoi(optarg);
if (vmos <= 0) {
Usage();
return ZX_ERR_INVALID_ARGS;
}
vmo_count_ = vmos;
break;
default:
Usage();
return ZX_ERR_INVALID_ARGS;
}
}
return ZX_OK;
}
zx_status_t RadarUtil::ConnectToDevice(fidl::ClientEnd<BurstReaderProvider> device) {
zx_status_t status = loop_.StartThread("radarutil-client-thread");
if (status != ZX_OK) {
fprintf(stderr, "Failed to start client thread: %s\n", zx_status_get_string(status));
return status;
}
fidl::ClientEnd<BurstReader> client_end;
fidl::ServerEnd<BurstReader> server_end;
status = zx::channel::create(0, &client_end.channel(), &server_end.channel());
if (status != ZX_OK) {
fprintf(stderr, "Failed to create channel: %s\n", zx_status_get_string(status));
return status;
}
std::shared_ptr<EventHandler> event_handler(new EventHandler(this));
client_.Bind(std::move(client_end), loop_.dispatcher(), std::move(event_handler));
fidl::WireSyncClient<BurstReaderProvider> provider_client(std::move(device));
auto result = provider_client.Connect(std::move(server_end));
if (!result.ok()) {
fprintf(stderr, "Failed to connect to radar device: %s\n",
zx_status_get_string(result.status()));
return result.status();
}
if (result->result.is_err()) {
fprintf(stderr, "Radar device failed to bind: %u\n", result->result.err());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t RadarUtil::RegisterVmos() {
const auto burst_size = client_->GetBurstSize_Sync();
if (!burst_size.ok()) {
fprintf(stderr, "Failed to get burst size: %s\n", zx_status_get_string(burst_size.status()));
return burst_size.status();
}
fidl::FidlAllocator allocator;
std::vector<zx::vmo> vmos(vmo_count_);
fidl::VectorView<zx::vmo> vmo_dups(allocator, vmo_count_);
fidl::VectorView<uint32_t> vmo_ids(allocator, vmo_count_);
zx_status_t status;
for (uint32_t i = 0; i < vmo_count_; i++) {
if ((status = zx::vmo::create(burst_size->burst_size, 0, &vmos[i])) != ZX_OK) {
fprintf(stderr, "Failed to create VMO: %s\n", zx_status_get_string(status));
return status;
}
if ((status = vmos[i].duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_dups[i])) != ZX_OK) {
fprintf(stderr, "Failed to duplicate VMO: %s\n", zx_status_get_string(status));
return status;
}
vmo_ids[i] = i;
}
const auto result = client_->RegisterVmos_Sync(vmo_ids, vmo_dups);
if (!result.ok()) {
fprintf(stderr, "Failed to register VMOs: %s\n", zx_status_get_string(result.status()));
return result.status();
}
if (result->result.is_err()) {
fprintf(stderr, "Failed to register VMOs: %d\n", result->result.err());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t RadarUtil::UnregisterVmos() {
const auto burst_size = client_->GetBurstSize_Sync();
if (!burst_size.ok()) {
fprintf(stderr, "Failed to get burst size: %s\n", zx_status_get_string(burst_size.status()));
return burst_size.status();
}
fidl::FidlAllocator allocator;
fidl::VectorView<uint32_t> vmo_ids(allocator, vmo_count_);
for (uint32_t i = 0; i < vmo_count_; i++) {
vmo_ids[i] = i;
}
const auto result = client_->UnregisterVmos_Sync(vmo_ids);
if (!result.ok()) {
fprintf(stderr, "Failed to register VMOs: %s\n", zx_status_get_string(result.status()));
return result.status();
}
if (result->result.is_err()) {
fprintf(stderr, "Failed to register VMOs: %d\n", result->result.err());
return ZX_ERR_INTERNAL;
}
return ZX_OK;
}
zx_status_t RadarUtil::Run() {
thrd_t worker_thread;
int thread_status = thrd_create_with_name(
&worker_thread,
[](void* ctx) -> int { return reinterpret_cast<RadarUtil*>(ctx)->WorkerThread(); }, this,
"radarutil-worker-thread");
if (thread_status != thrd_success) {
zx_status_t status = thrd_status_to_zx_status(thread_status);
fprintf(stderr, "Failed to start worker thread: %s\n", zx_status_get_string(status));
return status;
}
{
const auto result = client_->StartBursts();
if (!result.ok()) {
fprintf(stderr, "Failed to start bursts: %s\n", zx_status_get_string(result.status()));
return result.status();
}
}
zx::nanosleep(zx::deadline_after(run_time_));
{
const auto result = client_->StopBursts_Sync();
if (!result.ok()) {
fprintf(stderr, "Failed to stop bursts: %s\n", zx_status_get_string(result.status()));
return result.status();
}
}
run_ = false;
worker_event_.Broadcast();
thrd_join(worker_thread, nullptr);
printf("Received %lu bursts and %lu burst errors\n", bursts_received_, burst_errors_);
return ZX_OK;
}
int RadarUtil::WorkerThread() {
while (run_) {
fbl::AutoLock lock(&lock_);
while (burst_vmo_ids_.empty() && run_) {
worker_event_.Wait(&lock_);
}
while (!burst_vmo_ids_.empty()) {
const uint32_t vmo_id = burst_vmo_ids_.front();
burst_vmo_ids_.pop();
bursts_received_++;
if (burst_process_time_.to_nsecs() > 0) {
zx::nanosleep(zx::deadline_after(burst_process_time_));
}
client_->UnlockVmo(vmo_id);
}
}
return thrd_success;
}
void RadarUtil::OnBurst(fidl::WireResponse<BurstReader::OnBurst>* event) {
if (event->result.is_response()) {
{
fbl::AutoLock lock(&lock_);
burst_vmo_ids_.push(event->result.response().burst.vmo_id);
}
worker_event_.Broadcast();
}
if (event->result.is_err()) {
burst_errors_++;
}
}
} // namespace radarutil