#!/bin/bash
#
# Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
#
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES.
# SPDX-License-Identifier: LicenseRef-NVIDIA-Proprietary
#
# devlink tool wrapper — real hardware version
# Each param maps to a different devlink subcommand
source "$(dirname "$0")/_lib.sh"
trace "$@"

OP="$1"; shift
TARGET="$1"; shift

# YANG-enum int -> tool-string decode table.
#
# libdms converts YANG enum string values to their YANG-declared numeric
# ordinal before passing to the backend (see libdms/src/dms.c "Resolve YANG
# enum strings to numeric values" block). Most shell tools (mlxconfig,
# mlxreg, sysfs) accept the int directly; devlink does not. devlink rejects
# numeric input for eswitch-mode and flow_steering_mode with "Unknown
# eswitch mode" / "Bad parameter", and the wrapper would silently no-op the
# SET if it didn't decode the int back to the YANG name here.
#
# Discipline: every entry's value MUST be the exact string the underlying
# tool accepts. For our YANG, the YANG enum names match the devlink-accepted
# strings verbatim (legacy/switchdev, dmfs/smfs/hmfs) — so this table just
# translates the int form back. Pattern mirrors tools/_value_maps.sh.
declare -A YANG_ENUM=(
    # int form (libdms post-conversion)
    ["eswitch-mode:0"]="legacy"
    ["eswitch-mode:1"]="switchdev"
    ["flow-steering-mode:0"]="dmfs"
    ["flow-steering-mode:1"]="smfs"
    ["flow-steering-mode:2"]="hmfs"
    # name form (direct CLI / tests / pass-through)
    ["eswitch-mode:legacy"]="legacy"
    ["eswitch-mode:switchdev"]="switchdev"
    ["flow-steering-mode:dmfs"]="dmfs"
    ["flow-steering-mode:smfs"]="smfs"
    ["flow-steering-mode:hmfs"]="hmfs"
)

# Eswitch configuration requires NIC mode. In DPU mode the kernel returns
# EOPNOTSUPP for devlink eswitch commands, so probe once and bail early.
if ! devlink dev eswitch show "$TARGET" &>/dev/null; then
    echo "Error: devlink eswitch not supported for $TARGET — device may not be in NIC mode" >&2
    exit 1
fi

if [[ "$OP" == "GET" ]]; then
    for PARAM in "$@"; do
        case "$PARAM" in
            eswitch-mode)
                VAL=$(devlink dev eswitch show "$TARGET" 2>/dev/null | grep -oP '(?<=mode )\S+' | head -1)
                echo "mode=${VAL:-legacy}" ;;
            multiport)
                VAL=$(devlink dev param show "$TARGET" name esw_multiport 2>/dev/null | grep "cmode runtime" | grep -oP 'value \K\S+')
                echo "multiport=${VAL:-false}" ;;
            flow-steering-mode)
                VAL=$(devlink dev param show "$TARGET" name flow_steering_mode 2>/dev/null | grep "cmode runtime" | grep -oP 'value \K\S+')
                echo "flow-steering-mode=${VAL:-dmfs}" ;;
            *)
                echo "${PARAM}=unknown" ;;
        esac
    done
elif [[ "$OP" == "SET" ]]; then
    # Error policy: capture devlink stderr, route it to our stderr, exit non-zero
    # on failure. The wrapper used to merge stderr into stdout with `2>&1` and then
    # unconditionally `echo "key=value"`, which made a kernel "Operation not
    # supported" rejection look like a successful SET to the caller. Verify-time
    # surfaced the resulting state mismatch much later than install time.
    rc=0
    for PAIR in "$@"; do
        PARAM="${PAIR%%=*}"
        VALUE="${PAIR#*=}"
        # Decode YANG ordinal -> tool string. Falls through to $VALUE if no
        # entry matches (booleans like multiport=true pass through unchanged).
        DECODED="${YANG_ENUM[$PARAM:$VALUE]:-$VALUE}"
        case "$PARAM" in
            eswitch-mode)
                if ! err=$(devlink dev eswitch set "$TARGET" mode "$DECODED" 2>&1); then
                    echo "$err" >&2
                    echo "mode: $err"
                    rc=1
                    continue
                fi
                echo "mode=${DECODED}" ;;
            multiport)
                if ! err=$(devlink dev param set "$TARGET" name esw_multiport value "$DECODED" cmode runtime 2>&1); then
                    echo "$err" >&2
                    echo "multiport: $err"
                    rc=1
                    continue
                fi
                echo "multiport=${DECODED}" ;;
            flow-steering-mode)
                if ! err=$(devlink dev param set "$TARGET" name flow_steering_mode value "$DECODED" cmode runtime 2>&1); then
                    echo "$err" >&2
                    echo "flow-steering-mode: $err"
                    rc=1
                    continue
                fi
                echo "flow-steering-mode=${DECODED}" ;;
        esac
    done
    exit $rc
fi
