--- /dev/null
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# This validates the user-initiated fw upload mechanism of the firmware
+# loader. It verifies that one or more firmware devices can be created
+# for a device driver. It also verifies the data transfer, the
+# cancellation support, and the error flows.
+set -e
+
+TEST_REQS_FW_UPLOAD="yes"
+TEST_DIR=$(dirname $0)
+
+progress_states="preparing transferring  programming"
+errors="hw-error
+       timeout
+       device-busy
+       invalid-file-size
+       read-write-error
+       flash-wearout"
+error_abort="user-abort"
+fwname1=fw1
+fwname2=fw2
+fwname3=fw3
+
+source $TEST_DIR/fw_lib.sh
+
+check_mods
+check_setup
+verify_reqs
+
+trap "upload_finish" EXIT
+
+upload_finish() {
+       local fwdevs="$fwname1 $fwname2 $fwname3"
+
+       for name in $fwdevs; do
+               if [ -e "$DIR/$name" ]; then
+                       echo -n "$name" > "$DIR"/upload_unregister
+               fi
+       done
+}
+
+upload_fw() {
+       local name="$1"
+       local file="$2"
+
+       echo 1 > "$DIR"/"$name"/loading
+       cat "$file" > "$DIR"/"$name"/data
+       echo 0 > "$DIR"/"$name"/loading
+}
+
+verify_fw() {
+       local name="$1"
+       local file="$2"
+
+       echo -n "$name" > "$DIR"/config_upload_name
+       if ! cmp "$file" "$DIR"/upload_read > /dev/null 2>&1; then
+               echo "$0: firmware compare for $name did not match" >&2
+               exit 1
+       fi
+
+       echo "$0: firmware upload for $name works" >&2
+       return 0
+}
+
+inject_error() {
+       local name="$1"
+       local status="$2"
+       local error="$3"
+
+       echo 1 > "$DIR"/"$name"/loading
+       echo -n "inject":"$status":"$error" > "$DIR"/"$name"/data
+       echo 0 > "$DIR"/"$name"/loading
+}
+
+await_status() {
+       local name="$1"
+       local expected="$2"
+       local status
+       local i
+
+       let i=0
+       while [ $i -lt 50 ]; do
+               status=$(cat "$DIR"/"$name"/status)
+               if [ "$status" = "$expected" ]; then
+                       return 0;
+               fi
+               sleep 1e-03
+               let i=$i+1
+       done
+
+       echo "$0: Invalid status: Expected $expected, Actual $status" >&2
+       return 1;
+}
+
+await_idle() {
+       local name="$1"
+
+       await_status "$name" "idle"
+       return $?
+}
+
+expect_error() {
+       local name="$1"
+       local expected="$2"
+       local error=$(cat "$DIR"/"$name"/error)
+
+       if [ "$error" != "$expected" ]; then
+               echo "Invalid error: Expected $expected, Actual $error" >&2
+               return 1
+       fi
+
+       return 0
+}
+
+random_firmware() {
+       local bs="$1"
+       local count="$2"
+       local file=$(mktemp -p /tmp uploadfwXXX.bin)
+
+       dd if=/dev/urandom of="$file" bs="$bs" count="$count" > /dev/null 2>&1
+       echo "$file"
+}
+
+test_upload_cancel() {
+       local name="$1"
+       local status
+
+       for status in $progress_states; do
+               inject_error $name $status $error_abort
+               if ! await_status $name $status; then
+                       exit 1
+               fi
+
+               echo 1 > "$DIR"/"$name"/cancel
+
+               if ! await_idle $name; then
+                       exit 1
+               fi
+
+               if ! expect_error $name "$status":"$error_abort"; then
+                       exit 1
+               fi
+       done
+
+       echo "$0: firmware upload cancellation works"
+       return 0
+}
+
+test_error_handling() {
+       local name=$1
+       local status
+       local error
+
+       for status in $progress_states; do
+               for error in $errors; do
+                       inject_error $name $status $error
+
+                       if ! await_idle $name; then
+                               exit 1
+                       fi
+
+                       if ! expect_error $name "$status":"$error"; then
+                               exit 1
+                       fi
+
+               done
+       done
+       echo "$0: firmware upload error handling works"
+}
+
+test_fw_too_big() {
+       local name=$1
+       local fw_too_big=`random_firmware 512 5`
+       local expected="preparing:invalid-file-size"
+
+       upload_fw $name $fw_too_big
+       rm -f $fw_too_big
+
+       if ! await_idle $name; then
+               exit 1
+       fi
+
+       if ! expect_error $name $expected; then
+               exit 1
+       fi
+
+       echo "$0: oversized firmware error handling works"
+}
+
+echo -n "$fwname1" > "$DIR"/upload_register
+echo -n "$fwname2" > "$DIR"/upload_register
+echo -n "$fwname3" > "$DIR"/upload_register
+
+test_upload_cancel $fwname1
+test_error_handling $fwname1
+test_fw_too_big $fwname1
+
+fw_file1=`random_firmware 512 4`
+fw_file2=`random_firmware 512 3`
+fw_file3=`random_firmware 512 2`
+
+upload_fw $fwname1 $fw_file1
+upload_fw $fwname2 $fw_file2
+upload_fw $fwname3 $fw_file3
+
+verify_fw ${fwname1} ${fw_file1}
+verify_fw ${fwname2} ${fw_file2}
+verify_fw ${fwname3} ${fw_file3}
+
+echo -n "$fwname1" > "$DIR"/upload_unregister
+echo -n "$fwname2" > "$DIR"/upload_unregister
+echo -n "$fwname3" > "$DIR"/upload_unregister
+
+exit 0