Add flash.sh
This commit is contained in:
311
flash.sh
Executable file
311
flash.sh
Executable file
@@ -0,0 +1,311 @@
|
||||
#!/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 "$@"
|
||||
Reference in New Issue
Block a user