#! /usr/bin/perl -w # Author: Greg Harfst (gch@lcs.mit.edu) # Date: 11/21/00 # File: ipaqflasher # Notes: This is a script aimed at automating the process of # flashing the Compaq iPaq with new kernels and file systems. # # Usage: The script must gather two main pieces of information in order # to flash the iPaq. # 1) what device the iPaq is on. (eg /dev/ttyS0) # 2) what files to flash, along with their type (eg a particular kernel) # # This information can be specified in (a combination of) three ways. # First, it can be placed in a configuration file. This is perhaps # the easiest way to control the script for repeat invocations. Second, # it support command line options. Finally, it can automagically # determine what files to use by being pointed at a directory. # # Before ipflasher performs any transfers, it displays a summary # of the actions it will take and prompts the user for confirmation. # At this point you should probably hit the reset button on the # ipaq, so that when the connection starts up, the ipaq will be # placed into the boot loader mode. # # Examples: # o Using the "directory" option. Suppose you just downloaded the # latest and greatest cramfs filesystem files and kernel, and placed # all four files in "./v18". Simply type # # $ ipaqflasher --dir ./v18 --all # # and everything should work. # # o Continuing the above example, you could type # # $ ipaqflasher --dir ./v18 --usr --kernel # # to only flash with the usr cramfs and kernel. # # o Using a configuration file. Suppose you have a configuration # file 'flasher.config' with the following contents: # # tty /dev/ttyS0 # kernel ./original/zImage-2.4.0-test8-rmk5-np2-hh2 # init ./current_init.cramfs # # root ./current_root.cramfs # # Then running the command # # $ ipaqflasher --config ./flasher.config # # will flash the kernel and init onto the iPaq on /dev/ttyS0. The # root line is commented out. # # o Finally, you can specifiy files from the command line. # # $ ipaqflasher --init ./current_init.cramfs # use Getopt::Long; GetOptions(\%options,"config=s","directory=s","kernel:s","init:s","root:s","usr:s","usrlocal:s","all","tty","help","usage") || Usage(); Usage() if ($options{'help'} || $options{'usage'}); ## Global Vars $IPAQSEND = "ipaqsend2"; $TTY = "/dev/ttyS0"; # the default device $KERNEL = ""; $INIT = ""; $ROOT = ""; $USR = ""; $USRLOCAL = ""; $DIR = ""; ($FLASH_KERNEL,$FLASH_INIT,$FLASH_ROOT,$FLASH_USR,$FLASH_USRLOCAL) = (0,0,0,0,0); ## Main { # if the "all" option is set, we flash everything if ($options{'all'}) { ($FLASH_KERNEL,$FLASH_INIT,$FLASH_ROOT,$FLASH_USR) = (1,1,1,1); } Read_Config_File($options{'config'}) if($options{'config'}); # command line options override config file # set the directory $DIR = $options{'directory'} if($options{'directory'}); $DIR =~ s/\/+$//; # remove trailing slashes if(defined($options{'kernel'})) { $FLASH_KERNEL = 1; if($options{'kernel'} !~ /^\s*$/) { $KERNEL = $options{'kernel'}; } } if(defined($options{'init'})) { $FLASH_INIT = 1; if($options{'init'} !~ /^\s*$/) { $INIT = $options{'init'}; } } if(defined($options{'root'})) { $FLASH_ROOT = 1; if($options{'root'} !~ /^\s*$/) { $ROOT = $options{'root'}; } } if(defined($options{'usr'})) { $FLASH_USR = 1; if($options{'usr'} !~ /^\s*$/) { $USR = $options{'usr'}; } } if(defined($options{'usrlocal'})) { $FLASH_USRLOCAL = 1; if($options{'usrlocal'} !~ /^\s*$/) { $USRLOCAL = $options{'usrlocal'}; } } # if the directory variable is set, we automagically set variables if($DIR) { Set_Vars_From_Dir($DIR); } if ($FLASH_KERNEL || $FLASH_INIT || $FLASH_ROOT || $FLASH_USR || $FLASH_USRLOCAL) { # make sure all files that we're going to flash are okay Verify_Files(); # print out summary and prompt Print_Summary(); # perform the flashing Flash(); } exit; } ######################################### ## Subroutines ######################################### # Sub: Flash # Inputs: none # Outputs: none # Notes: Flashes the ipaq ######################################### sub Flash { my $tmp = "/tmp/disconnect.$$"; # now $IPAQSEND checks the output from the bootldr while it # verifies the checksum, etc. It then returns when # it sees "done." Hence we don't have to sleep too long before # proceeding after the xmodem transfer. my $sleep = 5; # set the tty settings system("stty 115200 -echo -echok -echoe -echoctl -echoke -onlcr -inlcr < $TTY") == 0 or Error("Problem setting the tty on device, '$TTY'"); # open up a connection with the ipaq, using the cu command. # the " system ("echo \" \\n\~.\\n\" | cu -s 115200 -l $TTY") == 0 or Error("Error opening connection using 'cu'"); if ($FLASH_KERNEL) { if(system("$IPAQSEND 'load kernel' $KERNEL $TTY") == 0) { print "Successfully flashed kernel '$KERNEL' on $TTY\n"; } else { Error("Error calling '$IPAQSEND' to flash kernel '$KERNEL' on $TTY"); } print "sleeping for $sleep seconds.\n"; sleep $sleep; } if ($FLASH_INIT) { if(system("$IPAQSEND 'load flash 0x100000' $INIT $TTY") == 0) { print "Successfully flashed init '$INIT' on $TTY\n"; } else { Error("Error calling '$IPAQSEND' to flash init '$INIT' on $TTY"); } print "sleeping for $sleep seconds.\n"; sleep $sleep; } if ($FLASH_ROOT) { if(system("$IPAQSEND 'load flash 0x200000' $ROOT $TTY") == 0) { print "Successfully flashed root '$ROOT' on $TTY\n"; } else { Error("Error calling '$IPAQSEND' to flash root '$ROOT' on $TTY"); } print "sleeping for $sleep seconds.\n"; sleep $sleep; } if ($FLASH_USR) { if(system("$IPAQSEND 'load flash 0x500000' $USR $TTY") == 0) { print "Successfully flashed usr '$USR' on $TTY\n"; } else { Error("Error calling '$IPAQSEND' to flash usr '$USR' on $TTY"); } print "sleeping for $sleep seconds.\n"; sleep $sleep; } if ($FLASH_USRLOCAL) { if(system("$IPAQSEND 'load flash 0xd00000' $USRLOCAL $TTY") == 0) { print "Successfully flashed usr '$USRLOCAL' on $TTY\n"; } else { Error("Error calling '$IPAQSEND' to flash usr '$USRLOCAL' on $TTY"); } print "sleeping for $sleep seconds.\n"; sleep $sleep; } print "booting.\n"; system("echo \"\nboot\n\" > $TTY"); return; } ######################################### # Sub: Print_Summary # Inputs: none # Outputs: none # Notes: Says what we're going to do, and prompts for okay ######################################### sub Print_Summary { my($ans) = ""; print "We will flash the following files to $TTY:\n"; print "\tkernel:\t$KERNEL\n" if ($FLASH_KERNEL); print "\tinit:\t$INIT\n" if ($FLASH_INIT); print "\troot:\t$ROOT\n" if ($FLASH_ROOT); print "\tusr:\t$USR\n" if ($FLASH_USR); print "\tusrlocal:\t$USRLOCAL\n" if ($FLASH_USRLOCAL); print "\n"; while (($ans !~ /n/i) && ($ans !~ /y/i)) { print "Is this okay? (Y/n): "; $ans = ; chomp($ans); $ans = "y" if ($ans eq ""); } if ($ans =~ /n/i) { Error("Not flashing. Please re-run."); } return; } ######################################### # Sub: Verify_Files # Inputs: none # Outputs: none # Notes: Makes sure all the files actually exist, and are readable ######################################### sub Verify_Files { if (($FLASH_KERNEL) && (! -f $KERNEL)) { Error("Kernel file, '$KERNEL', not readable"); } if (($FLASH_INIT) && (! -f $INIT)) { Error("Init cramfs, '$INIT', not readable"); } if (($FLASH_ROOT) && (! -f $ROOT)) { Error("Root cramfs, '$ROOT', not readable"); } if (($FLASH_USR) && (! -f $USR)) { Error("USR cramfs, '$USR', not readable"); } if (($FLASH_USRLOCAL) && (! -f $USRLOCAL)) { Error("USRLOCAL cramfs, '$USRLOCAL', not readable"); } return; } ######################################### # Sub: Set_Vars_From_Dir # Inputs: the directory # Outputs: none -- sets global vars # Notes: Based on what flags are set, looks in the directory # and determines what files will be used. ######################################### sub Set_Vars_From_Dir { my $dir = shift; my $file; opendir DIR, "$dir" || Error("Couldn't open dir '$dir'"); # my @cramfs = grep { /\w+/ } readdir(DIR); my @cramfs = grep { ((/\w+.cramfs$/ || (/Image/)) && !(/md5sum/)) && -f "$dir/$_"} readdir(DIR); closedir DIR; foreach $_ (@cramfs) { case: { $KERNEL = "$dir/$_", last case if ((/Image/) && ($FLASH_KERNEL)); $INIT = "$dir/$_", last case if ((/init/) && ($FLASH_INIT)); $ROOT = "$dir/$_", last case if ((/root/) && ($FLASH_ROOT)); $USR = "$dir/$_", last case if ((/usr/) && ($FLASH_USR)); $USRLOCAL = "$dir/$_", last case if ((/usr/) && ($FLASH_USRLOCAL)); } } return; } ######################################### # Sub: Read_Config_File # Inputs: name of config file # Outputs: none -- sets global vars # Notes: reads the configuration file and sets appropriate global # variables. ######################################### sub Read_Config_File { my ($config) = shift; # make sure file is readable (and exists) Error("File '$config' is not readable.") if (! -f $config); open (CONFIG, "<$config") || Error("Could not open config file '$config'"); while () { chomp; next if (/^\s*$/); # skip blank lines next if (/^\#/); # skip comment lines s/\#.*$//; # remove comments ($var,$val) = split; Assign_Var_Val($var,$val); } close CONFIG; return; } ######################################### # Sub: Assign_Var_Val # Inputs: a var, and a val # Outputs: none # Notes: Might set a global variable ######################################### sub Assign_Var_Val { my($var,$val) = @_; $_ = $var; # to make syntax in switch more concise switch: { ($KERNEL,$FLASH_KERNEL) = ($val,1), last switch if /kernel/i; ($INIT,$FLASH_INIT) = ($val,1), last switch if /init/i; ($ROOT,$FLASH_ROOT) = ($val,1), last switch if /root/i; ($USR,$FLASH_USR) = ($val,1), last switch if /usr/i; ($USRLOCAL,$FLASH_USRLOCAL) = ($val,1), last switch if /usrlocal/i; $TTY = $val, last switch if /tty/i; $DIR = $val, last switch if /dir/i; warn "Variable '$var' not recognized\n"; } return; } ######################################### # Sub: Error # Inputs: error string # Outputs: none -- prints string and exits # Notes: Prints program name, error message, and exits. ######################################### sub Error { my $msg = shift; my $name = $0; $name =~ s/.+\///; # remove all directory information print STDERR "$name: $msg\n"; exit 1; } ######################################### # Sub: Usage # Inputs: none # Outputs: none -- exits # Notes: Prints a usage statement ######################################### sub Usage { my $name = $0; $name =~ s/.+\///; # remove all directory information print "$name: [--config ] [--directory ] [--tty=s]\n\t[--kernel/init/root/usr/usrlocal/all] [--help/usage]\n"; exit 0; }