Files
common-raspberrypi/flash.sh
2026-03-28 14:34:44 -03:00

312 lines
8.4 KiB
Bash
Executable File

#!/bin/bash
# Flash Buildroot SD card image to SD card
# Usage: ./flash.sh /dev/sdX
set -e # Exit on error
# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default image path
IMAGE_PATH="output/images/sdcard.img"
# Function to print colored messages
print_error() {
echo -e "${RED}ERROR: $1${NC}" >&2
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}WARNING: $1${NC}"
}
print_info() {
echo -e "$1"
}
# Function to show usage
usage() {
echo "Usage: $0 [-y] <device> [image_path]"
echo ""
echo "Options:"
echo " -y Skip confirmation prompt (automatic yes)"
echo ""
echo "Arguments:"
echo " device Path to SD card device (e.g., /dev/sdb, /dev/mmcblk0)"
echo " image_path Path to image file (default: sdcard.img)"
echo ""
echo "Examples:"
echo " $0 /dev/sdb"
echo " $0 /dev/mmcblk0 output/images/sdcard.img"
echo " $0 -y /dev/sdb sdcard.img"
exit 1
}
# Check if running as root
check_root() {
if [ "$EUID" -ne 0 ]; then
print_error "This script must be run as root or with sudo"
exit 1
fi
}
# Check if device is removable (likely SD card or USB)
is_removable() {
local device=$1
local device_name=$(basename "$device")
# For mmcblk devices, check the device itself
if [[ "$device_name" =~ ^mmcblk[0-9]+p?[0-9]*$ ]]; then
device_name=$(echo "$device_name" | sed 's/p[0-9]*$//')
fi
# For sd devices, remove partition number
if [[ "$device_name" =~ ^sd[a-z][0-9]*$ ]]; then
device_name=$(echo "$device_name" | sed 's/[0-9]*$//')
fi
# Check if removable flag is set
local removable_file="/sys/block/${device_name}/removable"
if [ -f "$removable_file" ]; then
local removable=$(cat "$removable_file")
if [ "$removable" = "1" ]; then
return 0 # Is removable
fi
fi
# Additional check: SD cards often show up in specific subsystems
local device_path="/sys/block/${device_name}/device"
if [ -d "$device_path" ]; then
# Check if it's an MMC/SD device
if readlink -f "$device_path" | grep -q "mmc"; then
return 0 # Is MMC/SD device
fi
# Check if it's a USB device
if readlink -f "$device_path" | grep -q "usb"; then
return 0 # Is USB device (could be USB card reader)
fi
fi
return 1 # Not removable
}
# Get human-readable device info
get_device_info() {
local device=$1
local device_name=$(basename "$device")
# Remove partition number if present
if [[ "$device_name" =~ ^mmcblk[0-9]+p?[0-9]*$ ]]; then
device_name=$(echo "$device_name" | sed 's/p[0-9]*$//')
elif [[ "$device_name" =~ ^sd[a-z][0-9]*$ ]]; then
device_name=$(echo "$device_name" | sed 's/[0-9]*$//')
fi
# Get model and size info
local model_file="/sys/block/${device_name}/device/model"
local size_file="/sys/block/${device_name}/size"
local model="Unknown"
local size_bytes=0
if [ -f "$model_file" ]; then
model=$(cat "$model_file" | tr -d '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
fi
if [ -f "$size_file" ]; then
# Size is in 512-byte sectors
size_bytes=$(($(cat "$size_file") * 512))
fi
# Convert to GB
local size_gb=$(awk "BEGIN {printf \"%.2f\", $size_bytes/1024/1024/1024}")
echo "Model: $model, Size: ${size_gb}GB"
}
# Validate device path
validate_device() {
local device=$1
# Check if device exists
if [ ! -b "$device" ]; then
print_error "Device $device does not exist or is not a block device"
exit 1
fi
# Get device information
local device_info=$(get_device_info "$device")
print_info "Device info: $device_info"
# Check if device is removable (SD card or USB)
if ! is_removable "$device"; then
print_error "Device $device does not appear to be a removable device (SD card or USB)"
print_error "This safety check prevents accidentally flashing internal drives"
print_warning "If you are certain this is correct, you can modify the script to bypass this check"
exit 1
fi
print_success "Device $device appears to be a removable device (SD card/USB)"
}
# Validate image file
validate_image() {
local image=$1
if [ ! -f "$image" ]; then
print_error "Image file $image does not exist"
exit 1
fi
if [ ! -r "$image" ]; then
print_error "Image file $image is not readable"
exit 1
fi
}
# Unmount all partitions of the device
unmount_device() {
local device=$1
print_info "Checking for mounted partitions on $device..."
# Handle both /dev/sdX and /dev/mmcblkX naming schemes
if [[ "$device" == *"mmcblk"* ]] || [[ "$device" == *"loop"* ]]; then
# For mmcblk devices, partitions are named like mmcblk0p1, mmcblk0p2
partitions=$(lsblk -ln -o NAME "$device" | tail -n +2 | sed "s|^|/dev/|")
else
# For sd devices, partitions are named like sdb1, sdb2
partitions=$(lsblk -ln -o NAME "$device" | tail -n +2 | sed "s|^|/dev/|")
fi
# Unmount each partition
for partition in $partitions; do
if mountpoint -q "$partition" 2>/dev/null || grep -qs "$partition" /proc/mounts; then
print_info "Unmounting $partition..."
if ! umount "$partition" 2>/dev/null; then
print_warning "Failed to unmount $partition, trying lazy unmount..."
umount -l "$partition" || true
fi
fi
done
# Give the system a moment to release the device
sleep 1
print_success "All partitions unmounted"
}
# Flash the image
flash_image() {
local device=$1
local image=$2
# Perform the flash operation
print_info "Writing image..."
if command -v pv &> /dev/null; then
# Use pv for progress if available
pv "$image" | dd of="$device" bs=4M conv=fsync status=none
else
# Fall back to dd with progress
dd if="$image" of="$device" bs=4M conv=fsync status=progress
fi
# Ensure all data is written
print_info "Syncing filesystem..."
sync
print_success "Image successfully flashed to $device!"
}
# Main script execution
main() {
local skip_confirm="no"
local device=""
local image=""
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-y)
skip_confirm="yes"
shift
;;
-*)
print_error "Unknown option: $1"
usage
;;
*)
if [ -z "$device" ]; then
device=$1
elif [ -z "$image" ]; then
image=$1
else
print_error "Too many arguments"
usage
fi
shift
;;
esac
done
# Check if device was provided
if [ -z "$device" ]; then
usage
fi
# Set default image if not provided
image=${image:-$IMAGE_PATH}
print_info "================================"
print_info "Buildroot SD Card Flash Script"
print_info "================================"
print_info "Device: $device"
print_info "Image: $image"
print_info ""
# Run checks
check_root
validate_device "$device"
validate_image "$image"
# Get user confirmation before unmounting
# (unmount will happen in flash_image after confirmation)
local image_size=$(stat -c%s "$image")
local image_size_mb=$((image_size / 1024 / 1024))
print_info "Ready to flash $image (${image_size_mb} MB) to $device"
print_warning "This will DESTROY all data on $device!"
if [ "$skip_confirm" != "yes" ]; then
read -p "Continue? [y/N] " -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_info "Aborted by user"
exit 0
fi
else
print_info "Skipping confirmation due to -y flag"
fi
# Now unmount the device after confirmation
unmount_device "$device"
# Flash image
flash_image "$device" "$image"
print_info ""
print_success "✓ Flashing complete!"
print_info "You can now safely remove the SD card"
}
# Run main function
main "$@"