diff --git a/examples/htool.c b/examples/htool.c index ade53de..84fa267 100644 --- a/examples/htool.c +++ b/examples/htool.c @@ -571,11 +571,17 @@ static int command_console(const struct htool_invocation* inv) { return -1; } + libhoth_error err = HOTH_SUCCESS; if (opts.snapshot) { - return htool_console_snapshot(dev, &opts); + err = htool_console_snapshot(dev, &opts); } else { - return htool_console_run(dev, &opts); + err = htool_console_run(dev, &opts); } + if (err != HOTH_SUCCESS) { + htool_report_error("console", err); + return -1; + } + return 0; } static int command_flash_spi_info(const struct htool_invocation* inv) { diff --git a/examples/htool_console.c b/examples/htool_console.c index 3287395..25d1fe9 100644 --- a/examples/htool_console.c +++ b/examples/htool_console.c @@ -61,27 +61,27 @@ void restore_terminal(int fd, const struct termios* old_termios) { tcsetattr(fd, TCSANOW, old_termios); } -int htool_console_run(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts) { +libhoth_error htool_console_run(struct libhoth_device* dev, + const struct libhoth_htool_console_opts* opts) { printf("%sStarting Interactive Console\n", kAnsiRed); struct hoth_channel_uart_config uart_config = {}; - int status = libhoth_get_uart_config(dev, opts, &uart_config); - if (status == LIBHOTH_OK) { + libhoth_error status = libhoth_get_uart_config(dev, opts, &uart_config); + if (status == HOTH_SUCCESS) { if (opts->baud_rate != 0) { uart_config.baud_rate = opts->baud_rate; status = libhoth_set_uart_config(dev, opts, &uart_config); - if (status != LIBHOTH_OK) { - fprintf( - stderr, - "libhoth_set_uart_config() failed: %d; unable to set baud-rate\n", - status); + if (status != HOTH_SUCCESS) { + fprintf(stderr, + "libhoth_set_uart_config() failed: 0x%016llx; unable to set " + "baud-rate\n", + (unsigned long long)status); return status; } status = libhoth_get_uart_config(dev, opts, &uart_config); } } - if (status == LIBHOTH_OK) { + if (status == HOTH_SUCCESS) { printf("Using baud-rate %d\n", uart_config.baud_rate); } printf("[ Use Ctrl+T-Q to quit ]%s\n", kAnsiReset); @@ -90,8 +90,9 @@ int htool_console_run(struct libhoth_device* dev, // will be stored at this offset) uint32_t offset; status = libhoth_get_channel_status(dev, opts, &offset); - if (status != LIBHOTH_OK) { - fprintf(stderr, "libhoth_get_channel_status() failed: %d\n", status); + if (status != HOTH_SUCCESS) { + fprintf(stderr, "libhoth_get_channel_status() failed: 0x%016llx\n", + (unsigned long long)status); return status; } @@ -106,7 +107,7 @@ int htool_console_run(struct libhoth_device* dev, while (!quit) { // Any previous failure should reset all of the USB state before retrying - while (status != LIBHOTH_OK) { + while (status != HOTH_SUCCESS) { // TODO: Read STDIN during this time and buffer it so we can capture // quit events even when the console is disconnected. We will also want // to tune the reconnect time to match. @@ -114,7 +115,7 @@ int htool_console_run(struct libhoth_device* dev, // Make sure we don't end up in a tight retry loop usleep(100 * 1000); } else { - status = LIBHOTH_OK; + status = HOTH_SUCCESS; } } // Give an opportunity for other clients to use the interface. @@ -123,20 +124,21 @@ int htool_console_run(struct libhoth_device* dev, if (libhoth_claim_device(dev, 1000 * 1000 * opts->claim_timeout_secs) != HOTH_SUCCESS) { // If USB is down we might fail claim, just go back and retry - status = LIBHOTH_ERR_FAIL; + status = LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_FAIL); continue; } status = libhoth_read_console(dev, STDOUT_FILENO, false, opts->channel_id, &offset); - if (status != LIBHOTH_OK) { + if (status != HOTH_SUCCESS) { // Device resets cause failures, just loop and allow reconnection continue; } status = libhoth_write_console(dev, opts->channel_id, opts->force_drive_tx, &quit); - if (status != LIBHOTH_OK) { + if (status != HOTH_SUCCESS) { // Device resets cause failures, just loop and allow reconnection continue; } @@ -147,12 +149,14 @@ int htool_console_run(struct libhoth_device* dev, return status; } -int htool_console_snapshot_legacy(struct libhoth_device* dev) { +libhoth_error htool_console_snapshot_legacy(struct libhoth_device* dev) { size_t response_bytes_written; - int status = libhoth_hostcmd_exec(dev, HOTH_CMD_CONSOLE_REQUEST, 0, NULL, 0, - NULL, 0, &response_bytes_written); - if (status != LIBHOTH_OK) { - fprintf(stderr, "HOTH_CMD_CONSOLE_REQUEST status: %d\n", status); + libhoth_error status = + libhoth_hostcmd_exec_v2(dev, HOTH_CMD_CONSOLE_REQUEST, 0, NULL, 0, NULL, + 0, &response_bytes_written); + if (status != HOTH_SUCCESS) { + fprintf(stderr, "HOTH_CMD_CONSOLE_REQUEST status: 0x%016llx\n", + (unsigned long long)status); return status; } @@ -162,11 +166,12 @@ int htool_console_snapshot_legacy(struct libhoth_device* dev) { MAILBOX_SIZE - sizeof(struct hoth_host_response); while (true) { char buf[MAILBOX_SIZE]; - status = libhoth_hostcmd_exec(dev, HOTH_CMD_CONSOLE_READ, 0, &read_request, - sizeof(read_request), buf, max_bytes_per_read, - &response_bytes_written); - if (status != LIBHOTH_OK) { - fprintf(stderr, "HOTH_CMD_CONSOLE_READ status: %d\n", status); + status = libhoth_hostcmd_exec_v2( + dev, HOTH_CMD_CONSOLE_READ, 0, &read_request, sizeof(read_request), buf, + max_bytes_per_read, &response_bytes_written); + if (status != HOTH_SUCCESS) { + fprintf(stderr, "HOTH_CMD_CONSOLE_READ status: 0x%016llx\n", + (unsigned long long)status); return status; } fwrite(buf, strnlen(buf, sizeof(buf)), 1, stdout); @@ -176,8 +181,8 @@ int htool_console_snapshot_legacy(struct libhoth_device* dev) { return status; } -int htool_console_snapshot(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts) { +libhoth_error htool_console_snapshot( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts) { // Legacy host commands for console snapshot. if (!opts->channel_id) { return htool_console_snapshot_legacy(dev); @@ -186,9 +191,10 @@ int htool_console_snapshot(struct libhoth_device* dev, // Starting from current_offset - 0x80000000 so it's guaranteed to be outside // of the Hoth buffer. Repeat read until reaching current offset. uint32_t current_offset; - int status = libhoth_get_channel_status(dev, opts, ¤t_offset); - if (status != LIBHOTH_OK) { - fprintf(stderr, "libhoth_get_channel_status) failed: %d\n", status); + libhoth_error status = libhoth_get_channel_status(dev, opts, ¤t_offset); + if (status != HOTH_SUCCESS) { + fprintf(stderr, "libhoth_get_channel_status failed: 0x%016llx\n", + (unsigned long long)status); return status; } uint32_t offset = current_offset - 0x80000000; @@ -196,7 +202,7 @@ int htool_console_snapshot(struct libhoth_device* dev, while (true) { status = libhoth_read_console(dev, STDOUT_FILENO, false, opts->channel_id, &offset); - if (status != LIBHOTH_OK) { + if (status != HOTH_SUCCESS) { break; } // Extra check in case UINT32_MAX wrap-around. diff --git a/examples/htool_console.h b/examples/htool_console.h index 47a1948..fa9ac78 100644 --- a/examples/htool_console.h +++ b/examples/htool_console.h @@ -27,11 +27,11 @@ extern "C" { struct libhoth_device; -int htool_console_run(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts); +libhoth_error htool_console_run(struct libhoth_device* dev, + const struct libhoth_htool_console_opts* opts); -int htool_console_snapshot(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts); +libhoth_error htool_console_snapshot( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts); #ifdef __cplusplus } diff --git a/protocol/BUILD b/protocol/BUILD index 5094ad2..2b8bb0d 100644 --- a/protocol/BUILD +++ b/protocol/BUILD @@ -542,11 +542,26 @@ cc_library( hdrs = ["console.h"], deps = [ ":host_cmd", + ":libhoth_status", ":util", "//transports:libhoth_device", ], ) +cc_test( + name = "console_test", + srcs = ["console_test.cc"], + deps = [ + ":console", + ":host_cmd", + ":libhoth_status", + "//protocol/test:libhoth_device_mock", + "//transports:libhoth_device", + "@googletest//:gtest", + "@googletest//:gtest_main", + ], +) + cc_library( name = "dfu_hostcmd", srcs = ["dfu_hostcmd.c"], diff --git a/protocol/console.c b/protocol/console.c index 9c6420f..93d7e5c 100644 --- a/protocol/console.c +++ b/protocol/console.c @@ -14,10 +14,12 @@ #include "protocol/console.h" +#include #include #include #include "host_cmd.h" +#include "protocol/status.h" #include "protocol/util.h" void libhoth_print_erot_console(struct libhoth_device* const dev) { @@ -27,9 +29,11 @@ void libhoth_print_erot_console(struct libhoth_device* const dev) { struct libhoth_htool_console_opts opts = {0}; opts.channel_id = EROT_CHANNEL_ID; - int status = libhoth_get_channel_status(dev, &opts, ¤t_offset); - if (status != LIBHOTH_OK) { - fprintf(stderr, "libhoth_get_channel_status() failed: %d\n", status); + libhoth_error status = + libhoth_get_channel_status(dev, &opts, ¤t_offset); + if (status != HOTH_SUCCESS) { + fprintf(stderr, "libhoth_get_channel_status() failed: 0x%016llx\n", + (unsigned long long)status); return; } @@ -40,7 +44,7 @@ void libhoth_print_erot_console(struct libhoth_device* const dev) { while (true) { status = libhoth_read_console(dev, STDOUT_FILENO, true, opts.channel_id, &offset); - if (status != LIBHOTH_OK) { + if (status != HOTH_SUCCESS) { break; } // Extra check in case UINT32_MAX wrap-around. @@ -50,37 +54,48 @@ void libhoth_print_erot_console(struct libhoth_device* const dev) { } } -int libhoth_get_channel_status(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - uint32_t* offset) { +libhoth_error libhoth_get_channel_status( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + uint32_t* offset) { + if (dev == NULL || opts == NULL || offset == NULL) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } struct hoth_channel_status_request req = { .channel_id = opts->channel_id, }; struct hoth_channel_status_response resp; - int status = libhoth_hostcmd_exec( + libhoth_error status = libhoth_hostcmd_exec_v2( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHANNEL_STATUS, /*version=*/0, &req, sizeof(req), &resp, sizeof(resp), NULL); - if (status) { - if (status == HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_INVALID_COMMAND) { - fprintf(stderr, - "This is likely because the running RoT firmware doesn't have " - "support for channels enabled\n"); - } - if (status == HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_INVALID_PARAM) { - fprintf(stderr, - "This is likely because the requested channel doesn't exist.\n"); + if (status != HOTH_SUCCESS) { + if (LIBHOTH_ERR_GET_SPACE(status) == HOTH_HOST_SPACE_EC) { + uint32_t code = LIBHOTH_ERR_GET_CODE(status); + if (code == HOTH_RES_INVALID_COMMAND) { + fprintf(stderr, + "This is likely because the running RoT firmware doesn't have " + "support for channels enabled\n"); + } else if (code == HOTH_RES_INVALID_PARAM) { + fprintf( + stderr, + "This is likely because the requested channel doesn't exist.\n"); + } } return status; } *offset = resp.write_offset; - return 0; + return HOTH_SUCCESS; } -int libhoth_read_console(struct libhoth_device* dev, int fd, - bool prototext_format_enabled, uint32_t channel_id, - uint32_t* offset) { +libhoth_error libhoth_read_console(struct libhoth_device* dev, int fd, + bool prototext_format_enabled, + uint32_t channel_id, uint32_t* offset) { + if (dev == NULL || offset == NULL) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } struct hoth_channel_read_request req = { .channel_id = channel_id, .offset = *offset, @@ -100,10 +115,10 @@ int libhoth_read_console(struct libhoth_device* dev, int fd, "unexpected layout"); size_t response_size = 0; - int status = libhoth_hostcmd_exec( + libhoth_error status = libhoth_hostcmd_exec_v2( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHANNEL_READ, /*version=*/0, &req, sizeof(req), &resp, sizeof(resp), &response_size); - if (status != 0) { + if (status != HOTH_SUCCESS) { return status; } if (req.offset != resp.resp.offset) { @@ -131,13 +146,14 @@ int libhoth_read_console(struct libhoth_device* dev, int fd, else { if (libhoth_force_write(fd, resp.buffer, len) != 0) { perror("Unable to write console output"); - return -1; + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_POSIX, + errno); } } } *offset = resp.resp.offset + len; - return 0; + return HOTH_SUCCESS; } static int unescape(char* buf, int in, struct unescape_flags* flags) { @@ -176,8 +192,13 @@ static int unescape(char* buf, int in, struct unescape_flags* flags) { return out; } -int libhoth_write_console(struct libhoth_device* dev, uint32_t channel_id, - bool force_drive_tx, bool* quit) { +libhoth_error libhoth_write_console(struct libhoth_device* dev, + uint32_t channel_id, bool force_drive_tx, + bool* quit) { + if (dev == NULL || quit == NULL) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } struct { struct hoth_channel_write_request_v1 req; char buffer[64]; @@ -188,12 +209,13 @@ int libhoth_write_console(struct libhoth_device* dev, uint32_t channel_id, // clear non-blocking, as it can affect STDOUT. fcntl(STDIN_FILENO, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); if (numRead <= 0) { - return 0; + return HOTH_SUCCESS; } struct unescape_flags flags = {}; int numWrite = unescape(req.buffer, numRead, &flags); - if ((*quit = flags.quit) || (numWrite == 0 && !flags.uart_break)) return 0; + if ((*quit = flags.quit) || (numWrite == 0 && !flags.uart_break)) + return HOTH_SUCCESS; req.req.channel_id = channel_id; req.req.flags = @@ -201,12 +223,13 @@ int libhoth_write_console(struct libhoth_device* dev, uint32_t channel_id, req.req.flags |= flags.uart_break ? HOTH_CHANNEL_WRITE_REQUEST_FLAG_SEND_BREAK : 0; - int status = libhoth_hostcmd_exec( + libhoth_error status = libhoth_hostcmd_exec_v2( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHANNEL_WRITE, /*version=*/1, &req, sizeof(req.req) + numWrite, NULL, 0, NULL); - if (status != 0) { - if (status == HTOOL_ERROR_HOST_COMMAND_START + HOTH_RES_UNAVAILABLE) { + if (status != HOTH_SUCCESS) { + if (LIBHOTH_ERR_GET_SPACE(status) == HOTH_HOST_SPACE_EC && + LIBHOTH_ERR_GET_CODE(status) == HOTH_RES_UNAVAILABLE) { fprintf(stderr, "This is likely because the RoT was unable to confirm that no " "other device is driving the UART TX net. If you are certain " @@ -215,28 +238,37 @@ int libhoth_write_console(struct libhoth_device* dev, uint32_t channel_id, return status; } - return 0; + return HOTH_SUCCESS; } -int libhoth_get_uart_config(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - struct hoth_channel_uart_config* resp) { +libhoth_error libhoth_get_uart_config( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + struct hoth_channel_uart_config* resp) { + if (dev == NULL || opts == NULL || resp == NULL) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } struct hoth_channel_uart_config_get_req req = { .channel_id = opts->channel_id, }; - return libhoth_hostcmd_exec( + return libhoth_hostcmd_exec_v2( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHANNEL_UART_CONFIG_GET, /*version=*/0, &req, sizeof(req), resp, sizeof(*resp), NULL); } -int libhoth_set_uart_config(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - struct hoth_channel_uart_config* config) { + +libhoth_error libhoth_set_uart_config( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + struct hoth_channel_uart_config* config) { + if (dev == NULL || opts == NULL || config == NULL) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } struct hoth_channel_uart_config_set_req req = { .channel_id = opts->channel_id, .config = *config, }; - return libhoth_hostcmd_exec( + return libhoth_hostcmd_exec_v2( dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HOTH_CHANNEL_UART_CONFIG_SET, /*version=*/0, &req, sizeof(req), NULL, 0, NULL); diff --git a/protocol/console.h b/protocol/console.h index 0192211..7f419c2 100644 --- a/protocol/console.h +++ b/protocol/console.h @@ -23,6 +23,7 @@ extern "C" { #include #include +#include "protocol/status.h" #include "transports/libhoth_device.h" #define EROT_CHANNEL_ID 0x45524F54 // 'EROT' @@ -129,24 +130,25 @@ struct libhoth_htool_console_opts { void libhoth_print_erot_console(struct libhoth_device* const dev); -int libhoth_get_channel_status(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - uint32_t* offset); +libhoth_error libhoth_get_channel_status( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + uint32_t* offset); -int libhoth_read_console(struct libhoth_device* dev, int fd, - bool prototext_format_enabled, uint32_t channel_id, - uint32_t* offset); +libhoth_error libhoth_read_console(struct libhoth_device* dev, int fd, + bool prototext_format_enabled, + uint32_t channel_id, uint32_t* offset); -int libhoth_write_console(struct libhoth_device* dev, uint32_t channel_id, - bool force_drive_tx, bool* quit); +libhoth_error libhoth_write_console(struct libhoth_device* dev, + uint32_t channel_id, bool force_drive_tx, + bool* quit); -int libhoth_get_uart_config(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - struct hoth_channel_uart_config* resp); +libhoth_error libhoth_get_uart_config( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + struct hoth_channel_uart_config* resp); -int libhoth_set_uart_config(struct libhoth_device* dev, - const struct libhoth_htool_console_opts* opts, - struct hoth_channel_uart_config* config); +libhoth_error libhoth_set_uart_config( + struct libhoth_device* dev, const struct libhoth_htool_console_opts* opts, + struct hoth_channel_uart_config* config); #ifdef __cplusplus } diff --git a/protocol/console_test.cc b/protocol/console_test.cc new file mode 100644 index 0000000..0f9e030 --- /dev/null +++ b/protocol/console_test.cc @@ -0,0 +1,222 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "protocol/console.h" + +#include +#include +#include +#include + +#include "protocol/status.h" +#include "test/libhoth_device_mock.h" + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Return; + +TEST_F(LibHothTest, console_get_channel_status_success) { + struct hoth_channel_status_response exp_resp = { + .write_offset = 0x12345678, + }; + + EXPECT_CALL(mock_, send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_HOTH_CHANNEL_STATUS), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(&exp_resp, sizeof(exp_resp)), Return(LIBHOTH_OK))); + + struct libhoth_htool_console_opts opts = { + .channel_id = EROT_CHANNEL_ID, + }; + uint32_t offset = 0; + EXPECT_EQ(libhoth_get_channel_status(&hoth_dev_, &opts, &offset), + HOTH_SUCCESS); + EXPECT_EQ(offset, 0x12345678); +} + +TEST_F(LibHothTest, console_get_channel_status_null_params) { + uint32_t offset = 0; + struct libhoth_htool_console_opts opts = {}; + + libhoth_error err = libhoth_get_channel_status(nullptr, &opts, &offset); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_get_channel_status(&hoth_dev_, nullptr, &offset); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_get_channel_status(&hoth_dev_, &opts, nullptr); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, console_read_success) { + struct { + struct hoth_channel_read_response resp; + char data[16]; + } exp_resp = { + .resp = {.offset = 100}, + .data = "hello world\n", + }; + + EXPECT_CALL(mock_, send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_HOTH_CHANNEL_READ), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(&exp_resp, sizeof(exp_resp)), Return(LIBHOTH_OK))); + + int pipefd[2]; + ASSERT_EQ(pipe(pipefd), 0); + + uint32_t offset = 100; + EXPECT_EQ(libhoth_read_console(&hoth_dev_, pipefd[1], false, EROT_CHANNEL_ID, + &offset), + HOTH_SUCCESS); + EXPECT_EQ(offset, 100 + sizeof(exp_resp.data)); + + char read_buf[32] = {0}; + int n = read(pipefd[0], read_buf, sizeof(read_buf)); + EXPECT_EQ(n, sizeof(exp_resp.data)); + EXPECT_STREQ(read_buf, "hello world\n"); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST_F(LibHothTest, console_read_null_params) { + uint32_t offset = 0; + libhoth_error err = + libhoth_read_console(nullptr, 1, false, EROT_CHANNEL_ID, &offset); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_read_console(&hoth_dev_, 1, false, EROT_CHANNEL_ID, nullptr); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, console_get_uart_config_success) { + struct hoth_channel_uart_config exp_resp = { + .baud_rate = 115200, + .reserved = 0, + }; + + EXPECT_CALL(mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_HOTH_CHANNEL_UART_CONFIG_GET), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce( + DoAll(CopyResp(&exp_resp, sizeof(exp_resp)), Return(LIBHOTH_OK))); + + struct libhoth_htool_console_opts opts = { + .channel_id = EROT_CHANNEL_ID, + }; + struct hoth_channel_uart_config resp = {}; + EXPECT_EQ(libhoth_get_uart_config(&hoth_dev_, &opts, &resp), HOTH_SUCCESS); + EXPECT_EQ(resp.baud_rate, 115200); +} + +TEST_F(LibHothTest, console_get_uart_config_null_params) { + struct libhoth_htool_console_opts opts = {}; + struct hoth_channel_uart_config resp = {}; + + libhoth_error err = libhoth_get_uart_config(nullptr, &opts, &resp); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_get_uart_config(&hoth_dev_, nullptr, &resp); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_get_uart_config(&hoth_dev_, &opts, nullptr); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, console_set_uart_config_success) { + EXPECT_CALL(mock_, + send(_, + UsesCommand(HOTH_CMD_BOARD_SPECIFIC_BASE + + HOTH_PRV_CMD_HOTH_CHANNEL_UART_CONFIG_SET), + _)) + .WillOnce(Return(LIBHOTH_OK)); + + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp("", 0), Return(LIBHOTH_OK))); + + struct libhoth_htool_console_opts opts = { + .channel_id = EROT_CHANNEL_ID, + }; + struct hoth_channel_uart_config config = { + .baud_rate = 57600, + .reserved = 0, + }; + EXPECT_EQ(libhoth_set_uart_config(&hoth_dev_, &opts, &config), HOTH_SUCCESS); +} + +TEST_F(LibHothTest, console_set_uart_config_null_params) { + struct libhoth_htool_console_opts opts = {}; + struct hoth_channel_uart_config config = {}; + + libhoth_error err = libhoth_set_uart_config(nullptr, &opts, &config); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_set_uart_config(&hoth_dev_, nullptr, &config); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_set_uart_config(&hoth_dev_, &opts, nullptr); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +} + +TEST_F(LibHothTest, console_write_null_params) { + bool quit = false; + libhoth_error err = + libhoth_write_console(nullptr, EROT_CHANNEL_ID, false, &quit); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); + + err = libhoth_write_console(&hoth_dev_, EROT_CHANNEL_ID, false, nullptr); + EXPECT_EQ(LIBHOTH_ERR_GET_CTX(err), HOTH_CTX_CMD_EXEC); + EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_LIBHOTH); + EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_INVALID_PARAMETER); +}