Skip to content

Commit

Permalink
extensions: initramfs-usb-gadget-ums: kernel cmdline enables initra…
Browse files Browse the repository at this point in the history
…mfs UMS of all block devices

- this optional extension adds an initramfs script that:
  - enumerates and filters all block devices
  - exposes each device as an UMS (USB Mass Storage) in an USB Gadget
  - loops forever with info (board never boots)
- the idea here is to compensate for UEFI's lack of "ums" or "rockusb" mode that's present in u-boot
- it also allows to expose USB/NVMe devices that might or not be detected by bootloader, if the kernel works
  • Loading branch information
rpardini authored and igorpecovnik committed Nov 19, 2023
1 parent 205dd11 commit 055d871
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 0 deletions.
173 changes: 173 additions & 0 deletions extensions/initramfs-usb-gadget-ums/init-premount/usb-gadget-ums.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#!/bin/sh

# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2023 Ricardo Pardini <[email protected]>
# This file is a part of the Armbian Build Framework https://github.com/armbian/build/

echo ""
echo "Armbian initramfs USB Gadget UMS: ums-initramfs.sh starting..."

# First, check if /proc/cmdline contains "ums=yes", otherwise exit
if ! grep -q "ums=yes" /proc/cmdline; then
echo "Armbian initramfs USB Gadget UMS: ums=yes not found in /proc/cmdline, exiting normally."
exit 0
fi

echo "Armbian initramfs USB Gadget UMS: ums=yes found in /proc/cmdline, continuing..."
sleep 1

deviceinfo_name="Armbian on %%BOARD%%"
deviceinfo_manufacturer="Armbian on %%BOARD%%"
usb_idVendor="0x1d6b" # Linux Foundation
usb_idProduct="0x104" # Multifunction Composite Gadget.
usb_serialnumber="Armbian %%BOARD%%"

echo "Armbian initramfs USB Gadget UMS: found UDC: $(ls /sys/class/udc) for %%BOARD%%"

modprobe g_ffs || echo "Armbian initramfs USB Gadget UMS: Failed to modprobe g_ffs"

mkdir -p /config
mount -t configfs -o nodev,noexec,nosuid configfs /config

CONFIGFS=/config/usb_gadget
GADGET=${CONFIGFS}/g1
CONFIG=${GADGET}/configs/c.1
FUNCTIONS=${GADGET}/functions

if [ -d "${GADGET}" ]; then
echo "Armbian initramfs USB Gadget UMS: Found existing gadget, removing"
echo "" > ${GADGET}/UDC

rm -v ${CONFIG}/mass_storage.usb*

rmdir -v ${CONFIG}/strings/0x409
rmdir -v ${CONFIG}

rmdir -v ${FUNCTIONS}/mass_storage.usb*

rmdir -v ${GADGET}/strings/0x409

rmdir -v "${GADGET}"

echo "Done removing gadget"

if [ -d "${GADGET}" ]; then
echo "Gadget still exists... ${GADGET}"
fi
exit 0
fi

echo " Setting up an USB gadget through configfs"
mkdir ${GADGET} || echo " Couldn't create ${GADGET}"
echo "$usb_idVendor" > "${GADGET}/idVendor"
echo "$usb_idProduct" > "${GADGET}/idProduct"

# Create english (0x409) strings
mkdir ${GADGET}/strings/0x409 || echo " Couldn't create ${GADGET}/strings/0x409"

echo "$deviceinfo_manufacturer" > "${GADGET}/strings/0x409/manufacturer"
echo "$usb_serialnumber" > "${GADGET}/strings/0x409/serialnumber"
echo "$deviceinfo_name" > "${GADGET}/strings/0x409/product"

# Create configuration instance
mkdir ${CONFIG} || echo " Couldn't create ${CONFIG}"

counter=0
all_devices=""

for one_block in /sys/class/block/*; do
partition="${one_block}/partition"
if [ -f "$partition" ]; then
continue # we don't wanna expose partitions
fi
size_file="${one_block}/size"
if [ ! -f "$size_file" ]; then
continue # we don't wanna expose non-block devices
fi
size=$(cat "$size_file")
if [ "$size" -eq 0 ]; then
continue # we don't wanna expose zero-sized devices
fi
# we don't wanna expose devices that smaller than 1Gb (avoids mmcblk0boot0 etc)
if [ "$size" -lt 1953125 ]; then
continue
fi

ro_file="${one_block}/ro"
if [ ! -f "$ro_file" ]; then
continue # we don't wanna expose devices that can't tell if they're read-only
fi
ro=$(cat -v "$ro_file")
if [ "$ro" -ne 0 ]; then
continue # we don't wanna expose read-only devices
fi
phys_block_size_file="${one_block}/queue/physical_block_size"
if [ ! -f "$phys_block_size_file" ]; then
continue # we don't wanna expose devices that can't tell us their physical block size
fi
phys_block_size=$(cat "$phys_block_size_file")
if [ "$phys_block_size" -ne 512 ]; then
continue # we don't wanna expose devices that don't have a 512-byte physical block size
fi

# lets guess the real device name...
basename_device=$(basename "$one_block")
device_in_dash_dev="/dev/${basename_device}"
if [ ! -b "$device_in_dash_dev" ]; then
continue # we don't wanna expose devices that don't have a /dev/ entry
fi

description="Armbian${counter} ${basename_device}"

model_file="${one_block}/device/model"
if [ -f "$model_file" ]; then
model=$(cat "$model_file")
model=$(echo "$model" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')
description="${description} ${model}"
fi

# hack, skip this
#if [ "$basename_device" = "mmcblk0" ]; then
# continue
#fi

echo "one block [${counter}]: $one_block one_block: $one_block size: ${size} ro: ${ro} phys_block_size: ${phys_block_size} basename_device: ${basename_device} device_in_dash_dev: ${device_in_dash_dev} model: '${model}' description: '${description}'"

# add to all_devices
all_devices="${all_devices} '${counter}:${description}' "

# increment counter
counter=$((counter + 1))

echo "Create Mass Storage device ${counter} for ${device_in_dash_dev} desc '${description}'"
MASS_STORAGE_FUNCTION="${FUNCTIONS}/mass_storage.usb${counter}"
mkdir -p "${MASS_STORAGE_FUNCTION}" || echo " Couldn't create ${MASS_STORAGE_FUNCTION}"
echo 1 > "${MASS_STORAGE_FUNCTION}"/stall # allow bulk EPs
echo 0 > "${MASS_STORAGE_FUNCTION}"/lun.0/cdrom # don't emulate CD-ROm
#echo 0 > "${MASS_STORAGE_FUNCTION}"/ro # write access - disabled for now
echo 0 > "${MASS_STORAGE_FUNCTION}"/lun.0/nofua # enable Force Unit Access (FUA)
echo 0 > "${MASS_STORAGE_FUNCTION}"/lun.0/removable
echo "${description}" > "${MASS_STORAGE_FUNCTION}"/lun.0/inquiry_string
echo "${device_in_dash_dev}" > "${MASS_STORAGE_FUNCTION}"/lun.0/file

# Link the function to the config
ln -s "${MASS_STORAGE_FUNCTION}" "${CONFIG}" || echo " Couldn't symlink mass_storage.usb${counter}"

done

echo "Done creating functions and configs, enabling UDC.."

echo "$(ls /sys/class/udc)" > ${GADGET}/UDC || echo " Couldn't write UDC"

#umount /config

echo "Armbian initramfs USB Gadget UMS: done USB Gadget mode."

while true; do
echo "Armbian initramfs USB Gadget UMS: Board: %%BOARD%%"
echo "Armbian initramfs USB Gadget UMS: Machine will hang here forever; connect your USB OTG cable and write to disks!"
echo "Armbian initramfs USB Gadget UMS: UMS devices: ${all_devices}"
echo "Armbian initramfs USB Gadget UMS: UMS UDC: $(ls /sys/class/udc)"
echo "Armbian initramfs USB Gadget UMS: Now: $(date)"
sleep 30
done
31 changes: 31 additions & 0 deletions extensions/initramfs-usb-gadget-ums/initramfs-usb-gadget-ums.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2023 Ricardo Pardini <[email protected]>
# This file is a part of the Armbian Build Framework https://github.com/armbian/build/

# This includes a very early script in the initramfs, which checks the kernel command line
# for the presence of "ums=yes", and if found, sets up a USB gadget for UMS (USB Mass Storage)
# exposing all the block devices found in the system.
# After setting this up, it loops forever, so the initramfs doesn't proceed to boot the system.
# This allows the user to connect the board to a host computer, and use it as a USB storage device,
# to flash the eMMC/SD/NVMe/USB/whatever storage device simply using BalenaEtcher or similar tools.

function extension_prepare_config__check_sanity_usb_gadget_ums() {
display_alert "Checking sanity for" "${EXTENSION} in dir ${EXTENSION_DIR}" "info"
local script_file_src="${EXTENSION_DIR}/init-premount/usb-gadget-ums.sh"
if [[ ! -f "${script_file_src}" ]]; then
exit_with_error "Could not find '${script_file_src}'"
fi
}


# @TODO: maybe include this in the bsp-cli, so it can be updated later
function pre_customize_image__inject_initramfs_usb_gadget_ums() {
display_alert "Enabling" "usb-gadget-ums into initramfs" "info"
local script_file_src="${EXTENSION_DIR}/init-premount/usb-gadget-ums.sh"
local script_file_dst="${SDCARD}/etc/initramfs-tools/scripts/init-premount/usb-gadget-ums.sh"
run_host_command_logged cat "${script_file_src}" "|" sed -e "'s|%%BOARD%%|${BOARD}|g'" ">" "${script_file_dst}"
run_host_command_logged chmod -v +x "${script_file_dst}"
return 0
}

0 comments on commit 055d871

Please sign in to comment.