#!/bin/bash -

# Copyright (C) MOXA Inc. All rights reserved.
# This software is distributed under the terms of the MOXA SOFTWARE NOTICE.
# See the file MOXA-SOFTWARE-NOTICE for details.
#
# Name:
#	MOXA Bootloader Log Utility
#
# Description:
#	Get MOXA Bootloader Log
#
# Copyright (C) Moxa, Inc. All rights reserved.
# Copyright (C) 2022	Henry LC Chen	<HenryLC.Chen@moxa.com>

BASENAME="mx-bootloader-log-tool"

if [ -f "/proc/device-tree/model" ]; then
    device_tree_model_name="$(tr -d '\0' </proc/device-tree/model)"
fi

if echo "${device_tree_model_name}" | grep -q "ioThinx"; then
    DEFAULT_LOG_DEV=/dev/mmcblk2boot1
    DEFAULT_LOG_START_ADDRESS_HEX=0x100000
    DEFAULT_LOG_START_ADDRESS=$(printf "%d" "$DEFAULT_LOG_START_ADDRESS_HEX")
else
    DEFAULT_LOG_PARTITION_LABEL="syslog"
    DEFAULT_LOG_DEV=/dev/$(grep "$DEFAULT_LOG_PARTITION_LABEL" </proc/mtd | awk '{print $1}' | sed 's/[[:punct:]]//g')
    DEFAULT_LOG_START_ADDRESS_HEX=0x0
    DEFAULT_LOG_START_ADDRESS=$(printf "%d" "$DEFAULT_LOG_START_ADDRESS_HEX")
fi

DEFAULT_LOG_SHIFT_ADDRESS_HEX=0x20
DEFAULT_LOG_SHIFT_ADDRESS=$(printf "%d" "$DEFAULT_LOG_SHIFT_ADDRESS_HEX")
DEFAULT_LOG_LENGTH_HEX=0x100000
DEFAULT_LOG_LENGTH=$(printf "%d" "$DEFAULT_LOG_LENGTH_HEX")

# The bootloader logs are stored in a circular buffer. We divide the log device
# into logical blocks (each 0x10 bytes). This function locates the valid log
# entries and displays their content. Each bootloader log is split into 16-byte
# logical blocks. A full log message can span up to 15 blocks, i.e., 240 bytes.
_echo_log_data() {

    local dev=$1
    local dev_addr_offset=$2
    local log_header_offset=$3
    local log_size=$4
    local tmp_file="/run/moxa/mx-bootloader.tmp"

    local block_size=16  # Each logical block = 16 bytes
    local log_block_n=15 # A log message = up to 15 blocks = 240 bytes

    local next_index=0
    local rotate_number=0
    local start_idx=0
    local last_idx=0

    mkdir -p "$(dirname "$tmp_file")"

    # Read the entire section into a temp file
    dd if="$dev" bs="$log_size" count=1 status=none of="$tmp_file"

    # Extract next_index from offset 4 (3 bytes)
    read -r b1 b2 b3 < <(dd if="$tmp_file" bs=1 skip=4 count=3 status=none | od -An -t u1)
    next_index=$(((b3 << 16) + (b2 << 8) + b1))
    # Extract rotate_number from offset 8 (3 bytes)
    read -r b1 b2 b3 < <(dd if="$tmp_file" bs=1 skip=8 count=3 status=none | od -An -t u1)
    rotate_number=$(((b3 << 16) + (b2 << 8) + b1))

    # Calculate indices
    local base_idx=$(((dev_addr_offset + log_header_offset) / block_size))

    if ((rotate_number == 0)); then
        start_idx=$base_idx
        last_idx=$((base_idx + (next_index * block_size)))
    else
        start_idx=$((base_idx + (next_index * block_size)))
        last_idx=$((log_size / block_size))
    fi

    # First loop: Read from start_idx to end
    while ((start_idx < last_idx)); do
        dd if="$tmp_file" bs="$block_size" skip="$start_idx" count="$log_block_n" status=none 2>/dev/null | tr -d '\0'
        start_idx=$((start_idx + block_size))
    done

    # If not rotated, done
    if [ "$rotate_number" -eq 0 ]; then
        rm "$tmp_file"
        return 0
    fi

    # Second loop: Wrap-around portion
    start_idx=$base_idx
    last_idx=$((base_idx + (next_index * block_size)))

    while ((start_idx < last_idx)); do
        dd if="$tmp_file" bs="$block_size" skip="$start_idx" count="$log_block_n" status=none 2>/dev/null | tr -d '\0'
        start_idx=$((start_idx + block_size))
    done
    rm "$tmp_file"
}

print() {
    $LOGGER_ECHO "$BASENAME" "Bootloader log:"
    _echo_log_data "$DEFAULT_LOG_DEV" "$DEFAULT_LOG_START_ADDRESS" "$DEFAULT_LOG_SHIFT_ADDRESS" "$DEFAULT_LOG_LENGTH"
}

parsing_options() {
    while [ -n "$1" ]; do
        case "$1" in
        -p | --print | print)
            action=print
            shift
            ;;
        -h | --help | help)
            $HELPER_MENU "log"
            exit "${?}"
            ;;
        *)
            $HELPER_MENU "wrong" "log"
            exit "${?}"
            ;;
        esac
    done

    if [ -z "$action" ]; then
        $HELPER_MENU "wrong" "log"
        exit "${?}"
    fi

    return 0
}

main() {
    if ! parsing_options "$@"; then
        $HELPER_MENU "wrong" "log"
        exit "${?}"
    fi

    case "$action" in
    print)
        print
        ;;
    *) ;;
    esac

    return 0
}

main "$@"
