Skip to content

Instantly share code, notes, and snippets.

@parkerlreed
Created September 27, 2022 03:40
Show Gist options
  • Save parkerlreed/431b3e3947c95aa565c644c0919ce972 to your computer and use it in GitHub Desktop.
Save parkerlreed/431b3e3947c95aa565c644c0919ce972 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -euo pipefail
info() { printf "%s\n" "$*" >&2; }
die() { info "!! $*"; exit 1; }
# D21 Date 14-Sep-2022
CURRENT_D21_FIRMWARE_TIMESTAMP=1663179557
CURRENT_FIRMWARE_FILE_D21=../share/jupiter_controller_fw_updater/D21_APP_REL_63221B25.bin
# D20 Date 14-Sep-2022
CURRENT_D20_FIRMWARE_TIMESTAMP=1663179557
CURRENT_FIRMWARE_FILE_D20=../share/jupiter_controller_fw_updater/D20_APP_REL_63221B25.bin
# RA Date 14-Sep-2022
CURRENT_RA_FIRMWARE_TIMESTAMP=1663179557
CURRENT_FIRMWARE_FILE_RA=..//share/jupiter_controller_fw_updater/RA_APP_REL_63221B25.bin
FIRMWARE_TOOL_TYPE_1=../share/jupiter_controller_fw_updater/d21bootloader16.py
FIRMWARE_TOOL_TYPE_2=../share/jupiter_controller_fw_updater/d20bootloader.py
FIRMWARE_TOOL_TYPE_3=../share/jupiter_controller_fw_updater/d20bootloader.py
# Start with this tool -- it will suffice for getting the HW ID which is required
FIRMWARE_TOOL=$FIRMWARE_TOOL_TYPE_1
JQ=jq
checkmode=""
if [[ $# -eq 1 && ${1-} = "--check" ]]; then
checkmode=1
elif [[ $# -ne 0 ]]; then
die "Usage: $0 [--check]"
fi
###############################################################################################################################
# STAGE 1: Determine device type
###############################################################################################################################
devs=$("$FIRMWARE_TOOL" getdevicesjson) || die "Failed to enumerate devices"
devjq() { $JQ "$@" <<< "$devs"; }
# Is the first device enumerating as a Bootloader?
bootloader=$(devjq '.[0].is_bootloader | select(. != null)')
# If multiple devices, no action if not bootloader (some dev situation w/ multiple controllers?)
# The Type 2 bootloader presents as 2x interfaces, so don't exit if this looks like a bootloader.
count=$(devjq 'length')
if [[ $count -gt 1 && $bootloader = "false" ]]; then
info "Multiple devices found, not performing check/update:"
devjq >&2
exit 1
elif [[ $count -lt 1 ]]; then
die "No compatible devices found"
fi
build_timestamp=$(devjq '.[0].build_timestamp | select(. != null)')
secondary_build_timestamp=$(devjq '.[0].secondary_build_timestamp | select(. != null)')
info "PTS: ${build_timestamp}, STS: ${secondary_build_timestamp}"
# Determine the bootloader type (1,2,3) This is indicated by major release # of USB device (vs. 1 for older, 2 hybrid, 3 for RA).
# This value must be presented properly by both the FW applications and bootloaders to work properly.
release_number=$(devjq '.[0].release_number')
major_release=$((release_number>>8)) # Shift to reserve only major version byte
bootloader_type=1
if [[ $major_release -eq 2 ]]; then
bootloader_type=2
FIRMWARE_TOOL=$FIRMWARE_TOOL_TYPE_2
elif [[ $major_release -eq 3 ]]; then
bootloader_type=3
FIRMWARE_TOOL=$FIRMWARE_TOOL_TYPE_3
fi
###############################################################################################################################
# STAGE 1b: Do not do anything presently w/ Type 3 units: ENABLE this exit for Stable Hotfix
###############################################################################################################################
#if [[ $bootloader_type} -eq 3 ]]; then
# exit 0
#fi
devjq >&2
###############################################################################################################################
# STAGE 2: Now check if we need to update or not.
###############################################################################################################################
needupdate=""
# In OOBE builds, we only want to attempt to touch the firmware if it looks to be in the bootloader.
# Else, leave everything be, and let the subsequent update do the first firmware-touching.
if [[ -e /etc/steamos-oobe-image && $bootloader != "true" ]]; then
info "OOBE Build: Firmware looks valid, not attempting a flash"
exit 0
fi
# If Primary MCU is in bootloader, then always update
if [[ $bootloader = "true" ]]; then
info "Device is in bootloader mode, update required"
needupdate=1
# If either timestamp is 0 in non-Type 3 units (Only check dual MCU Types (1 and 2))
elif [[ $build_timestamp -eq 0 ]] || [[ $secondary_build_timestamp -eq 0 ]] && [[ $bootloader_type -ne 3 ]]; then
info "Device has missing build timestamp, update required"
needupdate=1
# Type 1: Check D21 FW timestamp
elif [[ $bootloader_type -eq 1 ]]; then
if [[ ${build_timestamp} -eq $CURRENT_D21_FIRMWARE_TIMESTAMP ]]; then
info "NO UPDATE: Type 1 device is running latest build $CURRENT_D21_FIRMWARE_TIMESTAMP"
else
needupdate=1
info "UPDATE: Type 1 device is running build '$build_timestamp', updating to build $CURRENT_D21_FIRMWARE_TIMESTAMP"
fi
# Type 2: Check both primary and secondary timestamps. We're not going to determine Type 2 a or b sub-types
elif [[ $bootloader_type -eq 2 ]]; then
if [[ ${build_timestamp} -eq $CURRENT_D21_FIRMWARE_TIMESTAMP ]] && [[ ${secondary_build_timestamp} -eq $CURRENT_D21_FIRMWARE_TIMESTAMP || ${secondary_build_timestamp} -eq $CURRENT_D20_FIRMWARE_TIMESTAMP ]]; then
info "NO UPDATE: Type 1/2 Device is running latest build $CURRENT_D21_FIRMWARE_TIMESTAMP"
else
needupdate=1
info "UPDATE: Type 1/2 Device is running builds ($build_timestamp | $secondary_build_timestamp), updating to build ($CURRENT_D21_FIRMWARE_TIMESTAMP | $CURRENT_D21_FIRMWARE_TIMESTAMP or $CURRENT_D20_FIRMWARE_TIMESTAMP)"
fi
# Type 3: Check the single-MCU's timesamp
elif [[ ${bootloader_type} -eq 3 ]]; then
if [[ ${build_timestamp} -eq $CURRENT_RA_FIRMWARE_TIMESTAMP ]]; then
info "NO UPDATE: Type 3 Device is running latest build $CURRENT_RA_FIRMWARE_TIMESTAMP"
else
needupdate=1
info "UPDATE: Type 3 Device is running build '$build_timestamp', updating to build $CURRENT_RA_FIRMWARE_TIMESTAMP"
fi
else
info "EXIT: Unknown Device Type ${boottloader_type}"
exit 0
fi
# If no update needed, then done.
if [[ -z $needupdate ]]; then
exit 0
fi
###############################################################################################################################
# STAGE 3: Perform the update
###############################################################################################################################
# If Type 2 BL, we need the HW ID to determine if the secondary controller is D21 or D20
# For Type 1 bootloader we have all the info we need. Only the orig. D21 / D21 system has that BL
# Orig D21/D21 system Type 1 BL, Primary HWID = 27 (provided for reference)
# Hybrid D21 / D20 system Type 2 BL, Primary HWID = 30
# Homog D21 / D21 system Type 2 BL, Primary HWID = 31
hybrid=false
if [[ $bootloader_type -eq 2 ]]; then
hwid=$("$FIRMWARE_TOOL" gethwid --clean) || die "Failed to get HW ID"
info "HWID: ${hwid}"
if [[ $hwid -eq 30 ]]; then
hybrid=true
elif [[ $hwid -eq 31 ]]; then
hybrid=false
else
die "Type 2 BL found w/ unknown Primary HWID: ${hwid}"
fi
fi
info "Found candidate device, build timestamp ${build_timestamp:-unknown}, BL $bootloader, BL_Type $major_release, HYB ${hybrid}"
# Done if check mode
if [[ -n $checkmode ]]; then
info " --check specified, not performing update"
if [[ $bootloader = "true" ]]; then
"$FIRMWARE_TOOL" reset
fi
# status code 7 to determine update-needed in check mode, vs general failure
exit 7
fi
# Otherwise, perform the udpate.
# Add a handler to stop the updater if we die while it is running
# (because we can be run as a service, and get SIGTERM'd. The shell does not kill the foreground process in this case.)
unset update_pid
on_error() {
ret=$?
if [[ -n ${update_pid-} ]]; then
info "Interrupted, killing update process $update_pid"
kill $update_pid
wait
info "Update failed, attempting to reset controller..."
"$FIRMWARE_TOOL" reset || true
fi
info "!! Failed to apply firmware update, see above"
exit $ret
}
trap on_error EXIT
run_firmware_tool() {
"$FIRMWARE_TOOL" "$@" &
update_pid=$!
wait $update_pid
}
# The background+wait is so our exit handler above can kill it if the script itself is asked to stop. Because bash.
if [[ $bootloader_type -eq 1 ]]; then
info "Updating Type 1 System"
run_firmware_tool program "$CURRENT_FIRMWARE_FILE_D21"
info "Firmware updated to $CURRENT_D21_FIRMWARE_TIMESTAMP"
elif [[ $bootloader_type -eq 3 ]]; then
info "Updating Type 3 System"
run_firmware_tool program "$CURRENT_FIRMWARE_FILE_RA"
info "Firmware updated to $CURRENT_RA_FIRMWARE_TIMESTAMP"
elif [[ $bootloader_type -eq 2 ]]; then
if [[ $hybrid = "true" ]]; then
info "Updating Hybrid SECONDARY of Type 2 System"
run_firmware_tool program --secondary "$CURRENT_FIRMWARE_FILE_D20"
else
info "Updating Homogeneous SECONDARY of Type 2 System"
run_firmware_tool program --secondary "$CURRENT_FIRMWARE_FILE_D21"
fi
info "Updating PRIMARY of Type 2 System"
run_firmware_tool program --primary "$CURRENT_FIRMWARE_FILE_D21"
info "Firmware updated to $CURRENT_D21_FIRMWARE_TIMESTAMP"
else
info "Unknown System Type " + $bootloader_type
fi
trap - EXIT
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment