#!/bin/bash
# Script to program CMSIS-DAP image onto MCU-Link board
################################################################################
# Copyright 2021-2025 NXP
#
# SPDX-License-Identifier: BSD-3-Clause
#
#################################################################################

usage() {
    echo Usage:
    echo "Connect an MCU-Link probe configured for ISP USB mode."
    echo "This script will program the probe with a CMSIS-DAP image."
    echo
    echo "The following arguments are accepted:"
    echo "$0                     [Programs CMSIS-DAP Image]"
    echo "$0 -s                  [Programs CMSIS-DAP Image silently (non-interactive)]"
    echo "$0 'path to s19 bin'   [Programs the chosen Image]"
    echo "$0 --target=dev_vendor:dev_name:board_vendor:board_name"
    echo "                                    [Configures fixed target for on-board probe"
    echo "                                     Items must match attributes from CMSIS BSP]"
    echo "$0 --no-target         [Removes a previous fixed target configuration]"
    echo "$0 --rtscts            [Enables support for RTS/CTS flow control - ONLY for specific OB boards based on LPC55S69]"
    echo "$0 --no-rtscts         [Disables support for RTS/CTS flow control - ONLY for specific OB boards based on LPC55S69]"
}


run() {

    # Identify image
    if [ -z "$1" ]; then
        MCULinkImageWild=${FwName}_CMSIS-DAP_V*.s19
        for file in "$ImageHome/"$MCULinkImageWild; do MCULinkImage=${file} ; done
    else
        MCULinkImage=$1
    fi

    if [ ! -e "$MCULinkImage" ] ; then
        echo "No Probe image found"
        exit 1
    fi

    ShortMCULinkImage=$(basename "$MCULinkImage")

    if [ $FirstTime -ne 0 ]; then
        # Show min IDE warning
        ImgFWVer=`echo $ShortMCULinkImage | cut -c 21-25`
        ImgFWVerDec=`echo $ImgFWVer | tr -d "_"`
        CrtFWVerDec=`echo $CrtFWVer | tr -d "."`
        if [ $ImgFWVerDec -ge $CrtFWVerDec ]; then
            echo
            echo -e "\033[0;38;5;15;1;41mWARNING:\033[0m"
            echo -e "This firmware version requires MCUXpresso IDE version \033[0;38;5;15;1;41m"$MinIDEVer"\033[0m or later."
            echo
        fi
        if [ $Silent -eq 0 ]; then
            read -s -r -n 1
        fi
        FirstTime=0
    fi

    # Check ready
    RET=1
    until [ ${RET} -eq 0 ]; do
        sleep 1
        $BinHome/rblhost --usb 0x1fc9,$DfuPid get-property 1 2>&1 | grep "Success\." >/dev/null
        RET=$?
        if [ $RET -ne 0 ]; then
            if [ $Silent -eq 0 ]; then
                echo "Configure board for ISP mode and connect via USB then press Space."
                read -s -r -n 1
            else
                echo "No board in ISP mode found!"
                exit 2
            fi
        fi
    done
    echo

    # Apply CMPA fix for MCU-Link Mini (LPC55S16)
    if [[ ${FwName} == *MINI* ]]; then
        $BinHome/rblhost --usb 0x1fc9,$DfuPid read-memory 0x0003E400 0x200 ${TmpFile} 0 >/dev/null 2>&1 >$Log
        [ -s ${TmpFile} ] && cmp ${TmpFile} ${ImageHome}/MCU-MINI_CMSIS-DAP_CMPA.bin >/dev/null 2>&1 >$Log
        RET=$?
        if [ ${RET} -ne 0 ]; then
            echo "Programming CMPA"
            $BinHome/rblhost --usb 0x1fc9,$DfuPid write-memory 0x0003E400 ${ImageHome}/MCU-MINI_CMSIS-DAP_CMPA.bin 0 >/dev/null 2>&1 >$Log
            RET=$?
            if [ ${RET} -ne 0 ]; then
                cat $Log
                echo
                echo "Programming CMPA has failed!"
                exit ${RET}
            fi
        fi
        rm ${TmpFile}
    fi

    # Program part
    echo "Programming "$ShortMCULinkImage

    $BinHome/rblhost --usb 0x1fc9,$DfuPid flash-image "$MCULinkImage" erase 0 >/dev/null 2>&1 >$Log
    RET=$?
    if [ ${RET} -ne 0 ]; then
        cat $Log
        echo
        echo "Programming "$MCULinkImage" has failed!"
        exit 3
    fi

    cat $Log | grep "Success\." >/dev/null
    if [ ${RET} -eq 0 ]; then
        if [ ! -z "${CfgOptions}" ]; then
            # Configure part
            if [ -z "${TgStrings}" ]; then
                echo "Configuring as non-fixed target"
            else
                echo "Configuring as target device ${DeviceName} [${DeviceVendor}] on board ${BoardName} [${BoardVendor}]"
            fi
            $BinHome/rblhost --usb 0x1fc9,$DfuPid read-memory $CfgPgAddr $CfgPgSize ${TmpFile} 0 >/dev/null 2>&1 >$Log
            # treat config page not programmed case (file size as produced by blhost is 0 in this case)
            [ ! -s ${TmpFile} ] && rm ${TmpFile}
            # check valid configuration page - clear if otherwise
            $BinHome/mculink_cfg ${TmpFile} --print >/dev/null 2>&1 >$Log
            RET=$?
            PreOption=
            if [ ${RET} -ne 0 ]; then
                PreOption=--clear
            fi
            $BinHome/mculink_cfg ${TmpFile} $PreOption ${CfgOptions[@]} >/dev/null 2>&1 >$Log
            RET=$?
            if [ ${RET} -eq 0 ]; then
                $BinHome/rblhost --usb 0x1fc9,$DfuPid flash-erase-region $CfgPgAddr $CfgPgSize 0 >/dev/null 2>&1 >$Log
                $BinHome/rblhost --usb 0x1fc9,$DfuPid write-memory $CfgPgAddr ${TmpFile} 0 >/dev/null 2>&1 >$Log
                RET=$?
            fi
            if [ ${RET} -ne 0 ]; then
                cat $Log
                echo
                echo "Configuring has failed!"
                exit ${RET}
            fi
        fi
        echo Programmed successfully.
        echo
        if [ $Silent -eq 0 ]; then
            echo "- To use: remove ISP jumper and reboot."
            echo
            export IFS= 
            read -s -r -p "Connect next board then press Space (or <return> to Quit)" -n 1 fin
            echo
            if [ -z $fin ]; then
                echo
                exit 0
            fi
        else
            echo "Rebooting..."
            $BinHome/rblhost --usb 0x1fc9,$DfuPid reset >/dev/null 2>&1 >$Log
            RET=$?
            if [ ${RET} -ne 0 ]; then
                cat $Log
                echo
                echo "Probe reset has failed!"
                exit 4
            else
                exit 0
            fi
        fi
    else
        cat $Log
        exit 3
    fi
}

detect_target() {

    # Detect target by VID:PID
    $BinHome/rblhost --usb 0x1fc9,0x0022 get-property 1 2>&1 | grep "Success\." >/dev/null
    RET=$?
    if [ $RET -eq 0 ]; then
        FwName=MCU-MINI
        CfgPgAddr=0x3ce00
        DfuPid=0x0022
    else
        FwName=MCU-LINK
        CfgPgAddr=0x9dc00
    fi
}

version="3.172 March 2026"
CrtFWVer="3.172"
MinIDEVer="11.7.1"
ScriptHome="$( cd "$( dirname "$0" )" && pwd )"
BinHome=$ScriptHome/../bin
ImageHome=$ScriptHome/../probe_firmware
Log=/tmp/MCU-LINK_CMSIS-DAP_FW_program.log
TmpFile=/tmp/MCU-LINK_CMSIS-DAP_FW_scratch.bin
DfuPid=0x0021
# will be set in detect_target
FwName=
CfgPgAddr=
CfgPgSize=512
FirstTime=1
Silent=0

clear
for arg in "$@"; do
  case $arg in
   help)
       usage
       exit 1
       ;;
   -s)
       Silent=1
       shift
       ;;
    --target=*)
      TgStrings="${arg#*=}"
      if [ ! -z "${TgStrings}" ]; then
        TgStrings="${TgStrings// /_}"
        IFS=: read -r DeviceVendor DeviceName BoardVendor BoardName <<<"${TgStrings}"
        CfgOptions+=( --device_vendor "${DeviceVendor}" --device_name "${DeviceName}" --board_vendor "${BoardVendor}" --board_name "${BoardName}" )
      fi
      shift
      ;;
    --no-target)
      CfgOptions+=( --no-device_vendor --no-device_name --no-board_vendor --no-board_name )
      shift
      ;;
    --rtscts)
      CfgOptions+=( --rtscts 01 )
      shift
      ;;
    --no-rtscts)
      CfgOptions+=( --no-rtscts )
      shift
      ;;
    -*|--*)
      echo "Unknown option $arg"
      usage
      exit 1
      ;;
    *)
      PathToFirmware="${arg}"
      ;;
  esac
done

detect_target

echo "CMSIS-DAP firmware for MCU-Link programming script V"$version"."
echo
echo "Place the board in ISP USB mode using the appropriate jumper on your MCU-Link."
echo "Refer to the board documentation for more information."
echo "Connect board via USB then press Space."

while true; do run "${PathToFirmware}"; done
