blob: 85f67ee76b66f2d6945d4d4573013ea019f26219 [file] [log] [blame]
// Copyright 2020 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 <fuchsia/factory/camera/cpp/fidl.h>
#include <fuchsia/images/cpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fdio/directory.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <filesystem>
#include <iostream>
#include <fbl/unique_fd.h>
#include "src/lib/fxl/command_line.h"
#include "src/lib/fxl/log_settings_command_line.h"
enum Target {
CONTROLLER,
ISP,
};
enum Command {
START_STREAM,
STOP_STREAM,
CAPTURE_FRAMES,
DISPLAY_TO_SCREEN,
GET_OTP_DATA,
GET_SENSOR_TEMPERATURE,
SET_AWB_MODE,
SET_AE_MODE,
SET_EXPOSURE,
SET_SENSOR_MODE,
SET_TEST_PATTERN_MODE,
SET_BYPASS_MODE,
};
constexpr std::string_view kDeviceDir = "/dev/class/isp";
constexpr std::string_view kFlag = "command";
// Controller Commands
constexpr std::string_view kCommand2 = "CaptureFrames";
constexpr std::string_view kCommand3 = "DisplayToScreen";
// ISP Commands
constexpr std::string_view kCommand4 = "GetOtpData";
constexpr std::string_view kCommand5 = "GetSensorTemperature";
constexpr std::string_view kCommand6 = "SetAWBMode";
constexpr std::string_view kCommand7 = "SetAEMode";
constexpr std::string_view kCommand8 = "SetExposure";
constexpr std::string_view kCommand9 = "SetSensorMode";
constexpr std::string_view kCommand10 = "SetTestPatternMode";
constexpr std::string_view kCommand11 = "SetBypassMode";
fpromise::result<Command, zx_status_t> StrToCommand(const std::string& str) {
// Controller
if (str == kCommand2)
return fpromise::ok(CAPTURE_FRAMES);
if (str == kCommand3)
return fpromise::ok(DISPLAY_TO_SCREEN);
// ISP
if (str == kCommand4)
return fpromise::ok(GET_OTP_DATA);
if (str == kCommand5)
return fpromise::ok(GET_SENSOR_TEMPERATURE);
if (str == kCommand6)
return fpromise::ok(SET_AWB_MODE);
if (str == kCommand7)
return fpromise::ok(SET_AE_MODE);
if (str == kCommand8)
return fpromise::ok(SET_EXPOSURE);
if (str == kCommand9)
return fpromise::ok(SET_SENSOR_MODE);
if (str == kCommand10)
return fpromise::ok(SET_TEST_PATTERN_MODE);
if (str == kCommand11)
return fpromise::ok(SET_BYPASS_MODE);
return fpromise::error(ZX_ERR_INVALID_ARGS);
}
// TODO(https://fxbug.dev/42135937): The varius std::stoi() calls can fail here and cause a crash, be sure to
// sanitize input.
zx_status_t RunCommand(fuchsia::factory::camera::ControllerPtr& controller,
fuchsia::factory::camera::IspPtr& isp, const Command command,
const std::vector<std::string> args,
fit::function<void(const std::string)> exit_on_success_cb,
fit::function<void(zx_status_t, const std::string)> exit_on_failure_cb) {
switch (command) {
case CAPTURE_FRAMES: {
if (args.size() > 1) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts at most one arg";
return ZX_ERR_INVALID_ARGS;
}
controller->CaptureFrames(
args.size() == 0 ? "" : args[0],
[success_cb = std::move(exit_on_success_cb), failure_cb = std::move(exit_on_failure_cb)](
zx_status_t capture_frames_status, fuchsia::images::ImageInfo info) {
if (capture_frames_status != ZX_OK) {
failure_cb(capture_frames_status, std::string{kCommand2});
return;
}
success_cb(std::string{kCommand2});
});
break;
}
case GET_OTP_DATA:
isp->GetOtpData(
[success_cb = std::move(exit_on_success_cb), failure_cb = std::move(exit_on_failure_cb)](
zx_status_t get_otp_status, size_t byte_count, zx::vmo otp_data) {
if (get_otp_status != ZX_OK) {
failure_cb(get_otp_status, std::string{kCommand4});
return;
}
uint8_t buf[byte_count];
otp_data.read(buf, 0, byte_count);
for (auto byte : buf) {
std::cout << byte;
}
success_cb(std::string{kCommand4});
});
break;
case GET_SENSOR_TEMPERATURE:
isp->GetSensorTemperature(
[success_cb = std::move(exit_on_success_cb), failure_cb = std::move(exit_on_failure_cb)](
zx_status_t get_sensor_temperature_status, int32_t temperature) {
if (get_sensor_temperature_status != ZX_OK) {
failure_cb(get_sensor_temperature_status, std::string{kCommand5});
return;
}
success_cb(std::string{kCommand10});
});
break;
case SET_AWB_MODE: {
if (args.size() != 2) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly two args";
return ZX_ERR_INVALID_ARGS;
}
isp->SetAWBMode(static_cast<fuchsia::factory::camera::WhiteBalanceMode>(std::stoi(args[0])),
std::stoi(args[1]), [success_cb = std::move(exit_on_success_cb)]() {
success_cb(std::string{kCommand6});
});
break;
}
case SET_AE_MODE:
if (args.size() != 1) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly one arg";
return ZX_ERR_INVALID_ARGS;
}
isp->SetAEMode(
static_cast<fuchsia::factory::camera::ExposureMode>(std::stoi(args[0])),
[success_cb = std::move(exit_on_success_cb)]() { success_cb(std::string{kCommand7}); });
break;
case SET_EXPOSURE: {
if (args.size() != 3) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly three args";
return ZX_ERR_INVALID_ARGS;
}
isp->SetExposure(
std::stof(args[0]), std::stof(args[1]), std::stof(args[2]),
[success_cb = std::move(exit_on_success_cb)]() { success_cb(std::string{kCommand8}); });
break;
}
case SET_SENSOR_MODE:
if (args.size() != 1) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly one arg";
return ZX_ERR_INVALID_ARGS;
}
isp->SetSensorMode(
static_cast<uint16_t>(std::stoul(args[0])),
[success_cb = std::move(exit_on_success_cb)]() { success_cb(std::string{kCommand9}); });
break;
case SET_TEST_PATTERN_MODE:
if (args.size() != 1) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly one arg";
return ZX_ERR_INVALID_ARGS;
}
isp->SetTestPatternMode(
static_cast<uint16_t>(std::stoul(args[0])),
[success_cb = std::move(exit_on_success_cb)]() { success_cb(std::string{kCommand10}); });
break;
case SET_BYPASS_MODE:
if (args.size() != 1) {
FX_PLOGS(ERROR, ZX_ERR_INVALID_ARGS) << "Accepts exactly one arg";
return ZX_ERR_INVALID_ARGS;
}
isp->SetBypassMode(
static_cast<uint16_t>(std::stoul(args[0])),
[success_cb = std::move(exit_on_success_cb)]() { success_cb(std::string{kCommand11}); });
break;
default:
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
int main(int argc, char* argv[]) {
auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (!fxl::SetLogSettingsFromCommandLine(command_line, {"camera-factory-cli"})) {
FX_LOGS(ERROR);
return EXIT_FAILURE;
}
if (!command_line.HasOption(kFlag)) {
FX_LOGS(ERROR) << "User must specify 'command' flag.";
return EXIT_FAILURE;
}
std::string command;
command_line.GetOptionValue(kFlag, &command);
async::Loop loop(&kAsyncLoopConfigAttachToCurrentThread);
auto exit_on_success_cb = [&loop](const std::string& command) {
FX_PLOGS(INFO, ZX_OK) << "camera-factory-cli ran with command '" << command << "'";
loop.Quit();
};
auto exit_on_failure_cb = [&loop](zx_status_t status, const std::string& command) {
FX_PLOGS(INFO, status) << "camera-factory-cli failed with command '" << command << "'.";
loop.Quit();
};
auto context = sys::ComponentContext::CreateAndServeOutgoingDirectory();
zx_status_t status;
// Parse command.
auto command_result = StrToCommand(command);
if (command_result.is_error()) {
FX_LOGS(ERROR) << "Command not valid for specified target.";
return EXIT_FAILURE;
}
// Connect to the camera-factory Controller.
fuchsia::factory::camera::ControllerHandle controller_handle;
status = context->svc()->Connect(controller_handle.NewRequest());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to request camera-factory Controller service.";
return EXIT_FAILURE;
}
fuchsia::factory::camera::ControllerPtr controller;
status = controller.Bind(std::move(controller_handle), loop.dispatcher());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to get camera-factory Controller pointer.";
return EXIT_FAILURE;
}
// Connect to the ISP.
fuchsia::factory::camera::IspPtr isp;
for (auto const& dir_entry : std::filesystem::directory_iterator{kDeviceDir}) {
const char* path = dir_entry.path().c_str();
status = fdio_service_connect(path, isp.NewRequest(loop.dispatcher()).TakeChannel().release());
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "Failed to get handle for device at " << path;
return EXIT_FAILURE;
}
break;
}
// Error handling
auto shutdown_cb = [&controller, &isp]() {
controller = nullptr;
isp = nullptr;
};
controller.set_error_handler([&loop, &shutdown_cb](zx_status_t status) {
FX_PLOGS(ERROR, status) << "camera-factory-cli Controller channel closing due to error.";
shutdown_cb();
loop.Quit();
});
isp.set_error_handler([&loop, &shutdown_cb](zx_status_t status) {
FX_PLOGS(ERROR, status) << "camera-factory-cli Isp channel closing due to error.";
shutdown_cb();
loop.Quit();
});
// Execute command.
status = RunCommand(controller, isp, command_result.value(), command_line.positional_args(),
exit_on_success_cb, exit_on_failure_cb);
if (status != ZX_OK) {
FX_PLOGS(ERROR, status) << "camera-factory-cli loop never started";
shutdown_cb();
return EXIT_FAILURE;
}
// Wait for exit callback.
FX_LOGS(INFO) << "camera-factory-cli loop started.";
loop.Run();
FX_LOGS(INFO) << "camera-factory-cli loop quit after '" << command << "' ran.";
shutdown_cb();
return EXIT_SUCCESS;
}