From 093092b9092caf69c47d027be3d18264a06faca7 Mon Sep 17 00:00:00 2001 From: Gabriel Lima Date: Sat, 28 Mar 2026 14:34:44 -0300 Subject: [PATCH] Add flash.sh --- flash.sh | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100755 flash.sh diff --git a/flash.sh b/flash.sh new file mode 100755 index 0000000..68a9d88 --- /dev/null +++ b/flash.sh @@ -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] [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 "$@"