diff --git a/.gitattributes b/.gitattributes index 9c1dc37f88a9af4f91e0ebcab8463dd6ba49c95c..084630caa5d06d64ffa70e079d32f28083d33e12 100644 --- a/.gitattributes +++ b/.gitattributes @@ -316,6 +316,7 @@ MAC/APL/PIC/_RegisterAccess/test/aratest.conf.in -text svneol=native#application MAC/APL/PIC/_RegisterAccess/test/aratest.log_prop.in -text svneol=native#application/octet-stream MAC/APL/PIC/rsuctl/lofar.jpg -text svneol=unset#unset MAC/APL/PIC/rsuctl/ndump.tgz -text svneol=unset#unset +MAC/APL/PIC/rsuctl/rsuctl3.pl -text MAC/APL/RTDBCommon/Makefile.am -text MAC/APL/RTDBCommon/RTDBCommon.spec.in -text MAC/APL/RTDBCommon/bootstrap -text diff --git a/MAC/APL/PIC/rsuctl/rsuctl3.pl b/MAC/APL/PIC/rsuctl/rsuctl3.pl new file mode 100644 index 0000000000000000000000000000000000000000..fad33dfeef95ed185dfe034975dacad9eb52523e --- /dev/null +++ b/MAC/APL/PIC/rsuctl/rsuctl3.pl @@ -0,0 +1,969 @@ +#!/usr/bin/perl +$VERSION = q$Revision: 3.00$; + +# +# rsuctl # Remote System Update - program firmware in FPGA flash +# +# Usage: rsuctl [options] command +# +# options +# [-i ifname] # Ethernet interface name on which to communicate (default is eth1) +# -m mac-address # MAC-address of the target board (Mandatory) +# +# command (specify at least one of the following) +# [-l] # List the images currently stored in all pages of the flash +# +# [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) +# [-x -p page # Start (load & reset) new firmware from the specified page +# [-w -p page -b bp_img[.hex] -a ap_img[.hex] [-F]] # Write bp and ap image into specified page (-F forces write to page 0) +# [-v -p page -b bp_img[.hex] -a ap_img[.hex]] # Compare flash page with bp and ap image +# [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin +# +# [-r] # Reset all FPGA's (load factory image) +# [-c] # Clear all FPGA's (restart current image) +# [-s] # Send SYNC pulse to all FPGA's +# +# [-q] # Prevent checking for running RSPDriver (prevents pause at start) +# [-P] # Ping the board (find board when using broadcast address) +# [-V] # Read RSU status and RSR versions +# [-h] # This help text +# +# + +#use Socket; +use Net::RawIP; +use Getopt::Std; +use File::stat; +use Switch; +use POSIX; # for geteuid() + +$g_debug = 0; # set to 1 to enable debug output +$g_packet = ''; # global packet string +$g_error = 0; # global error status + +# +# Definitions +# +$PID_RSR = 0x01; +$PID_RSU = 0x02; + +$REG_STATUS = 0x00; +$REG_STATUS_SIZE = 200; +$REG_VERSION = 0x01; +$REG_VERSION_SIZE = 2; + +$REG_RW = 0x01; +$REG_ERASE = 0x02; +$REG_RECONFIG = 0x03; +$REG_CLEAR = 0x04; + +$CMD_ERASE = 0x01; + +$CMD_SYNC = 0x01; +$CMD_CLEAR = 0x02; +$CMD_RESET = 0x04; + +$MEPHDRSIZE = 16; + +$TYPE_READ = 0x01; +$TYPE_WRITE = 0x02; +$TYPE_READACK = 0x03; +$TYPE_WRITEACK = 0x04; + +$ADDR_BLP_NONE = 0x00; +$ADDR_RSP = 0x01; + +$FLASHSIZE = 64 * 1024 * 1024; # 64MB +$BLOCKSIZE = 1024; # block = 1024 bytes +$BLOCKS_PER_SECTOR = 128; # sector = 128 blocks +$SECTORS_PER_PAGE = 32; # page = 32 sectors +$BLOCKS_PER_PAGE = $BLOCKS_PER_SECTOR * $SECTORS_PER_PAGE; # 4096 blocks +$PAGESIZE = $SECTORS_PER_PAGE * $BLOCKS_PER_SECTOR * $BLOCKSIZE; # 4MB +$PAGES = 16; + +$PROGRESS_SIZE = 32; + +$RESPONSETIMEOUT = 2; +$TO = 5; + +# +# pack format for the MEP message +# +# C 0x10 Ethernet Type byte 0 +# C 0xFA Ethernet Type byte 1 +# C TYPE +# C STATUS +# S FRAMELENGTH +# C BLPID +# C RSP +# C PID +# C REGID +# S OFFSET +# S PAYLOAD_LENGTH +# S SEQ_NR +# S RESERVED +# +$MEPSENDFORMAT="CCCCSCCCCSSSS"; +$MEPRECVFORMAT="H28CCSCCCCSSSS"; + +# ETHLEN = max length of Ethernet payload +$ETHLEN = 1500; + +# +# Error code +# +%STATUS = ( 0 => "=== OK", + 1 => "=== ERR unknown MEP message type", + 2 => "=== ERR illegal BLP address", + 3 => "=== ERR invalid PID", + 4 => "=== ERR register does not exist", + 5 => "=== ERR offset too large", + 6 => "=== ERR message is too large", + 7 => "=== ERR message corruption during RSP processing", + 8 => "=== ERR message lost during RSP processing" +); + +# +# usage +# +sub usage +{ + print STDERR <<END; + + # Remote System Update - program firmware in FPGA flash + + Usage: rsuctl [options] command + + options + [-i ifname] # Ethernet interface name on which to communicate (default is eth1) + -m mac-address # MAC-address of the target board (Mandatory) + + command (specify at least one of the following, need -m option at least) + [-l] # List the images currently stored in all pages of the flash + + [-e -p page [-F]] # Erase flash page, 0 <= page < 16 (-F forces erase of page 0) + [-x -p page # Start (load & reset) new firmware from the specified page + [-w -p page -b bp_image.hex -a ap_image.hex [-F]] # Write bp and ap image into specified page (-F forces write to page 0) + [-v -p page -b bp_image.hex -a ap_image.hex] # Compare flash page with bp and ap image + [-d -p page -f dumpfile.bin] # Dump flash page to dmp.bin + + [-r] # Reset all FPGA's (load factory image) + [-c] # Clear all FPGA's (restart current image) + [-s] # Send SYNC pulse to all FPGA's + + [-q] # Prevent checking for running RSPDriver (prevents pause at start) + [-P] # Ping the board (find board when using broadcast address) + [-V] # Read RSU status and RSR versions + [-h] # This help text + +END + + exit; +} + +# +# pack_rsu($type, $regid, $size, $offset) +# +# Pack an RSU message +# +sub pack_rsu +{ + my ($type, $regid, $size, $offset) = @_; + + return pack($MEPSENDFORMAT, + 0x10, 0xFA, $type, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSU, $regid, $offset, $size, 0, 0);; +} + +# +# pcapcallback, save packet in $g_packet +# +# savepacket($handle, $hdr, $packet) +# +sub savepacket +{ + my ($handle, $hdr, $packet) = @_; + $g_packet = $packet; +} + +# +# ($payload,$error) = readresponse($pcap) +# +sub readresponse +{ + my ($pcap) = @_; + my ($retval) = 0; + + do { # keep going when we receive invalid messages + $retval = 0; # assume success + + # wait for message to arrive + eval { + use POSIX qw(SIGALRM); + POSIX::sigaction(SIGALRM, + POSIX::SigAction->new(sub { die })) + || die "=== ERR error setting SIGALRM handler: $!\n"; + + alarm $RESPONSETIMEOUT; + (0 == loop ($pcap, 1, \&savepacket, '')) + || die "=== ERR Failed to receive response"; + alarm 0; + }; + if ($@) { + $retval = 1; # fatal (timeout) + return ('', $retval, ''); + } + + ($ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved) = + unpack($MEPRECVFORMAT, $g_packet); + + if ($TYPE_WRITEACK != $type && $TYPE_READACK != $type) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid TYPE\n" if ($g_debug); + } + if ($PID_RSU != $pid && $PID_RSR != $pid) { + $retval = 2; # ignore invalid message + print STDERR "ignoring message with invalid PID\n" if ($g_debug); + } + + printf STDERR "0x%s 0x%02x 0x%02x %5d 0x%02x 0x%02x 0x%02x 0x%02x %5d %5d %5d %5d\n", + $ethdr, $type, $status, $framesize, $blp, $rsp, $pid, $regid, $offset, $size, $seqnr, $reserved if ($g_debug); + + if (0 == $retval && 0 != $status) { + $retval = 1; # fatal + print STDERR "=== FATAL protocol error: status=", $STATUS{$status}, "\n"; + print STDERR "=== Are you using the correct Ethernet interface? Use -i argument to change, e.g. -i eth1\n"; + exit; + } + } until ($retval != 2); + + return ('', $retval, '') if ($retval); + + return (substr($g_packet, 14 + $MEPHDRSIZE), $retval, $ethdr); +} + +# +# flash($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) +# +sub flash +{ + my ($sock, $pcap, $page, $blockinc, $msg, $cmd, $size, $writecb, $readcb, $image) = @_; + + printf STDERR "=== %s page %02d\n", $msg, $page; + print STDERR "=== |" . ("-" x $SECTORS_PER_PAGE) . "|\n=== "; + #print STDERR "Image: $image \n"; + + my ($success) = 1; + # leave last block for directory entry + my ($block); + for ($block = 0; $block < $BLOCKS_PER_PAGE - 1; $block += $blockinc) { + if ($writecb) { + $packet = pack_rsu($TYPE_WRITE, $cmd, $size, ($page * $BLOCKS_PER_PAGE) + $block); + + $packet .= &$writecb($page, $block, $image); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + + return $error if ($error); + } + + if ($readcb) { + $packet = pack_rsu($TYPE_READ, $cmd, $size, + ($page * $BLOCKS_PER_PAGE) + $block); + + $sock->send_eth_frame($packet); + ($payload, $error) = readresponse($pcap); + return $error if ($error); + + $success = &$readcb($page, $block, $payload, $image) && $success; + } + + if (0 == $block % $BLOCKS_PER_SECTOR) { + if ($success) { print STDERR "."; } + else { print STDERR "X"; } + $success = 1; + } + } + print STDERR "\n"; + + return 0; +} + +# +# writedir($sock, $pcap, $page, $entry) +# Write the entry to the last block of page $page +# +sub writedir +{ + my ($sock, $pcap, $page, $entry) = @_; + + die "=== ERR internal error: size of direntry > $BLOCKSIZE" if (length($entry) > $BLOCKSIZE); + + print STDERR "writing dir @", (($page + 1) * $BLOCKS_PER_PAGE) - 1, "\n" if ($g_debug); + + # add zero padding if needed + $entry .= pack("C", 0) x ($BLOCKSIZE - length($entry)) if (length($entry) < $BLOCKSIZE); + + # write last block with dir entry + $packet = pack_rsu($TYPE_WRITE, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) - 1); + $packet .= $entry; + + my $error = 2; + $sock->send_eth_frame($packet); + + ($result, $error) = readresponse($pcap); + + return $error; +} + +# +# readdir($sock, $pcap, $page) +# Read the directory entry from the specified page +# +sub readdirentry +{ + my ($sock, $pcap, $page) = @_; + + my ($packet) = pack_rsu($TYPE_READ, $REG_RW, $BLOCKSIZE, + (($page + 1) * $BLOCKS_PER_PAGE) -1); + + $sock->send_eth_frame($packet); + + my $error = 0; + ($entry, $error) = readresponse($pcap); + return ('', $error) if $error; + + $entry = '' if (substr($entry,0,1) eq pack("C", 0xff)); + + return ($entry, $error); +} + +# +# erasecb($page, $block, $image) +# +sub erasecb +{ + my ($page, $block, $image) = @_; + + print STDERR "erasecb: $page, $block\n" if ($g_debug); + + return pack("C", $CMD_ERASE); +} + +# +# erase($sock, $pcap, $page) +# page = -1 means all pages +# +sub erase +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, $BLOCKS_PER_SECTOR, + "erasing", $REG_ERASE, 1, + \&erasecb, 0, ''); +} + +# +# Verify the data +# +sub verifycb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "verifycb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# verify($sock, $pcap, $page, $image) +# +sub verify +{ + my ($sock, $pcap, $page, $image) = @_; + + return flash($sock, $pcap, $page, 1, + "verifying", $REG_RW, $BLOCKSIZE, + 0, \&verifycb, $image); +} + +# +# Dump the data to file +# +sub dumpcb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "dumpcb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + print DUMPFILE $payload; + + return 1; +} + +# +# dump($sock, $pcap, $page) +# +sub dump($$$) +{ + my ($sock, $pcap, $page) = @_; + + return flash($sock, $pcap, $page, 1, + "dumping", $REG_RW, $BLOCKSIZE, + 0, \&dumpcb, ''); +} + +# +# Write the data +# +sub writepagecb +{ + my ($page, $block, $image) = @_; + + print STDERR "writepagecb: page=$page, block=$block\n" if ($g_debug); + + my ($data) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $data .= pack("C", 0) x ($BLOCKSIZE - length($data)); + + return $data; +} + +# +# Read and verify the data +# +sub readpagecb +{ + my ($page, $block, $payload, $image) = @_; + + print STDERR "readpagecb: page=$page, block=$block, length(payload)=", length($payload), "\n" if ($g_debug); + + my ($refblock) = substr($image, $block * $BLOCKSIZE, $BLOCKSIZE); + + # append zeroes to fill buffer to $BLOCKSIZE + $refblock .= pack("C", 0) x ($BLOCKSIZE - length($refblock)); + + if ($payload ne $refblock) { + print STDERR "=== ERR failed to verify block $block in page $page\n" if ($g_debug); + $g_error = 1; + return 0; + } + + return 1; +} + +# +# Create a direntry string based on filename +# direntry($filename) +# +sub formatentry +{ + my ($bp_filename, $ap_filename) = @_; + + $stat = stat($bp_filename) || die "=== ERR failed to stat file '$bp_filename'"; + $bp_datetime = ctime($stat->mtime); + chop($bp_datetime); + + $stat = stat($ap_filename) || die "=== ERR failed to stat file '$ap_filename'"; + $ap_datetime = ctime($stat->mtime); + chop($ap_datetime); + + # return 'size YYYY mm dd hh:mm bp_filename ; size YYYY mm dd hh:mm ap_filename' + return sprintf("%s( %s ) ; %s( %s )", $bp_filename, $bp_datetime, $ap_filename, $ap_datetime); +} + +# +# writepage($sock, $pcap, $page, $bp_filename, $ap_filename, $image) +# +sub writepage +{ + my ($sock, $pcap, $page, $bp_filename, $ap_filename, $image) = @_; + + $error = flash($sock, $pcap, $page, 1, + "writing and verifying", $REG_RW, $BLOCKSIZE, + \&writepagecb, \&readpagecb, $image); + + if (!$error) { + $error = writedir($sock, $pcap, $page, formatentry($bp_filename, $ap_filename)); + } else { + # clear entry, an error has occured + writedir($sock, $pcap, $page, ''); + } + return $error; +} + + +# +# Read images from the hex files. +# then the files are converted from hex to bin format. +# +# readhexfiles($filename_bp, &filename_ap ) +# +sub readhexfiles +{ + my ($filename_bp, $filename_ap) = @_; + local $/ = undef; # read entire file + + # open input and output files + die "=== ERR no image file specified" if ((!defined($filename_bp)) or (!defined($filename_ap))); + + open(IMAGEFILE, $filename_bp) || die "=== ERR failed to open file '$filename_bp'"; + print STDERR "=== Reading '$filename_bp'\n"; + $data = <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + open(IMAGEFILE, $filename_ap) || die "=== ERR failed to open file '$filename_ap'"; + print STDERR "=== Reading '$filename_ap'\n"; + $data .= <IMAGEFILE>; # read entire file + close(IMAGEFILE); + + # remove \r\n from data file + $data =~ s/\r//; + $data =~ s/\n//; + + print STDERR "=== Converting '$filename_bp' and '$filename_ap' to binary\n"; + print STDERR "=== |" . ("-" x $PROGRESS_SIZE) . "|\n=== "; + + $i = 0; + $interval = length($data) / ($PROGRESS_SIZE * 2); + $interval = 1 if ($interval < .5); + + $bindata = ''; + + for ($byte = 0; $byte < length($data); $byte += 2) { + $bytestr = substr($data, $byte, 2); + + $bindata .= pack("C", hex($bytestr)); + + # print progress bar + $i++; + print STDERR "." if (0 == $i % $interval); + } + + # append FF's to fill buffer to $IMAGESIZE - 1 + $bindata .= pack("C", 255) x (($PAGESIZE - $BLOCKSIZE) - length($bindata)); + + print STDERR "\n"; + + return $bindata; +} + +# +# Read RSU status from RSR status register +# readrsustatus($sock, $pcap) +# +sub readrsustatus +{ + my ($sock, $pcap) = @_; + + $size = $REG_STATUS_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_STATUS, 0, $size, 0, 0); + + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + $rsustatus = unpack("C", substr($result, 164, 1)); + + print STDERR sprintf("\nRSU Status:\n Rdy : %s\n FpgaType : %s\n ImageType: %s\n Trig : %s\n\n", + ('Configuration ongoing', 'Configuration done')[$rsustatus & 1], + ('loaded from flash', 'loaded via JTAG')[($rsustatus >> 2) & 1], + ('Factory image is running', 'User image is running')[($rsustatus >> 3) & 1], + ('Reconfiguration due to button reset' + , 'Unknown' + , 'Reconfiguration due to over temperature' + , 'Unknown' + , 'Reconfiguration due to user reset' + , 'Unknown' + , 'Reconfiguration due to watchdog reset' )[($rsustatus >> 4) & 7]); + + return $error; +} + +# +# Read versions of all FPGA's (handy after reconfig, reset, clear, etc) +# readversions($sock, $pcap) +# +sub readversions +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "RSP version: $id\nBP version: $maj.$min\n"; + + # read AP versions + for ($ap = 0; $ap < 4; $ap++) { + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + 1 << $ap, 0, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + ($result, $error) = readresponse($pcap); + return $error if $error; + + ($id, $version) = unpack("CC", $result); + $maj = $version >> 4; + $min = $version & 15; + print STDERR "AP[$id] version: $maj.$min\n"; + } + + return 0; +} + +# +# Find MAC addresses of active boards +# ping($sock, $pcap) +# +sub ping +{ + my ($sock, $pcap) = @_; + + # read BP version + $size = $REG_VERSION_SIZE; + $packet = pack($MEPSENDFORMAT, + 0x10, 0xFA, $TYPE_READ, 0x00, $MEPHDRSIZE + $size, + $ADDR_BLP_NONE, $ADDR_RSP, $PID_RSR, $REG_VERSION, 0, $size, 0, 0); + $sock->send_eth_frame($packet); + my $error = 0; + + while (!$error) { + ($result, $error, $ethdr) = readresponse($pcap); + $mac = substr($ethdr, 12, 12); + $mac =~ s/(..)(..)(..)(..)(..)(..)/$1:$2:$3:$4:$5:$6/; + print STDERR "Found RSP board on MAC: ", $mac, "\n" if (!$error); + } + + return 0; +} + +# +# Reconfigure. Load new firmware from flash into FPGA's. After loading +# has finished, the FPGA will be reset and restart with new firmware. +# +# reconfig($sock, $pcap, $page) +# +sub reconfig +{ + my ($sock, $pcap, $page) = @_; + + $cmd = $page & 15; # use bit 0..3 + $cmd = $cmd | 128; + $packet = pack_rsu($TYPE_WRITE, $REG_RECONFIG, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + + # there will be no response + # ($response, $error) = readresponse($pcap); + + return 0; +} + +# +# Reset control for all FPGA's +# +# clearctl($sock, $pcap, $cmd) +# valid values for $cmd: $CMD_SYNC, $CMD_CLEAR, $CMD_RESET +# +sub clearctl +{ + my ($sock, $pcap, $cmd) = @_; + + $packet = pack_rsu($TYPE_WRITE, $REG_CLEAR, 1, 0); + $packet .= pack("C", $cmd); + + $sock->send_eth_frame($packet); + ($response, $error) = readresponse($pcap); + + return $error; +} + +# +# Report result +# report($error, $report_ok = false) +# +sub report +{ + my ($error, $report_ok) = @_; + + if ($error || $g_error) { + print STDERR "=== FAILED\n"; + exit 1; + } + else { + print STDERR "=== OK\n" if ($report_ok); + } +} + +# +# Change range of the page parameter +# chkpage($page) +# +sub chkpage +{ + my ($page) = @_; + + die "=== ERR no flash page specified, use -p" if (!defined($page)); + if ($page < 0 || $page >= $PAGES) { + die "=== ERR invalid page index $page, should be >= 0 and < $PAGES"; + } +} + +# +# Get rsu status and versions +# version_cmd($sock, $pcap) +# +sub version_cmd +{ + my ($sock, $pcap) = @_; + + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + return $error; +} + +sub main +{ + my $sock = 0; + + print STDERR "=== rsuctl ($VERSION), for RSP3 boards only\n"; + + Getopt::Std::getopts("i:m:lexwvb:a:dp:f:FrcsPVhq"); + + # check that at least one argument is present + usage() if ($opt_h); + + if (!defined($opt_m)) { + print STDERR "=== ERR Must specify MAC address using -m option"; + usage(); + } + + usage() unless ($opt_l || $opt_e || $opt_x || $opt_w || $opt_v || + $opt_d || $opt_r || $opt_c || $opt_s || $opt_P || $opt_V || $opt_h); + + # assign default values + $opt_i = 'eth1' if !defined($opt_i); + + # Create socket to the boards + die "=== ERR this program needs to run as root to send/recv raw Ethernet frames" if (0 != geteuid()); + + $sock = new Net::RawIP; + $sock->ethnew($opt_i); + $sock->ethset(dest => $opt_m); + + $TIMEOUT=5; + $pcap = $sock->pcapinit($opt_i, "not ether dst $opt_m", $ETHLEN, $TIMEOUT); + + die "=== ERR could not open raw socket to RSP board(s) on $opt_i:$opt_m: $!\n" unless $sock; + + print STDERR "=== connected to RSP board(s) on $opt_i:$opt_m\n"; + + if (!defined($opt_q)) { + print STDERR "=== checking for running RSPDriver\n"; + ($payload, $error) = readresponse($pcap); + die "=== RSPDriver appears to be running, stop it first\n" if ($payload ne ''); + } + + if (defined($opt_l)) { + + # LIST IMAGES IN FLASH + # + # readdirentry for all pages + + print STDERR "=== listing flash contents\n\n"; + for ($page = 0; $page < $PAGES; $page++) { + ($entry, $error) = readdirentry($sock, $pcap, $page); + break if $error; + print STDERR sprintf("%s %2d: %s\n", + (0 == $page ? 'Factory':'Image '), $page, $entry) if ((0 == $page) || $entry ne ''); + } + print STDERR "\n"; + report($error, true); + + return; + } + + if (defined($opt_e)) { + + # ERASE + # + # erase flash page + + die "=== ERR erasing factory image (page 0) prohibited, use -F to force erase of factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + report(erase($sock, $pcap, $opt_p), true); + + return; + } + + if (defined($opt_x)) { + + # RECONFIG + # + # reconfig BP and AP + # wait $TO seconds + # clear + # wait $TO seconds + # read rsustatus & version + + chkpage($opt_p); + report(reconfig($sock, $pcap, $opt_p)); + report(clearctl($sock, $pcap, $CMD_RESET)); + report($error) if $error; + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + $error = readrsustatus($sock, $pcap); + $error = readversions($sock, $pcap) && $error; + report($error, true); + + return; + } + + if (defined($opt_w)) { + + # WRITE + # + # readimage + # erase page + # write & verify page + + die "=== ERR writing to factory image (page 0) prohibited, use -F to force write to factory image" if (!$opt_F && (0 == $opt_p)); + chkpage($opt_p); + + $image = readhexfiles($opt_b, $opt_a); + report(erase($sock, $pcap, $opt_p)); + report(writepage($sock, $pcap, $opt_p, $opt_b, $opt_a, $image), true); + + return; + } + + if (defined($opt_v)) { + + # VERIFY + # + # readimage reference + # verify flash page against reference + + chkpage($opt_p); + $image = readhexfiles($opt_b, $opt_a); + report(verify($sock, $pcap, $opt_p, $image), true); + + return; + } + + if (defined($opt_d)) { + + # DUMP + # + # dump flash page to file + + chkpage($opt_p); + die "=== ERR no image file specified" if (!defined($opt_f)); + + open(DUMPFILE, ">$opt_f") || die "=== ERR failed to open file '$opt_f' for writing\n"; + print STDERR "=== Dumping to file '$opt_f'\n"; + $error = dump($sock, $pcap, $opt_p); + close(DUMPFILE); + report($error, true); + + return; + } + + if (defined($opt_r)) { + + # RESET + # + # send reset (factory image (page 0) will start on BP) + # wait $DTO seconds + # reconfigure AP's with page 8 (factory default for AP's) + # wait $TO second + # read rsustatus and versions + + report(reconfig($sock, $pcap, 0)); # set image 0 (factory image) + report(clearctl($sock, $pcap, $CMD_RESET)); + print STDERR "=== waiting $TO seconds after reset\n"; sleep $TO; + print STDERR "=== reconfiguring BP and AP's with factory image\n"; + + # don't check the error return code because we know + # it indicates failure while the reconfig is actually in progress + + report(version_cmd($sock, $pcap) && $error, true); + + return; + } + + if (defined($opt_c)) { + + # CLEAR + # + # send clear + # wait $TO seconds + # read rsustatus and versions + + report(clearctl($sock, $pcap, $CMD_CLEAR)); + print STDERR "=== waiting $TO seconds after clear\n"; sleep $TO; + report(version_cmd($sock, $pcap), true); + + return; + } + + if (defined($opt_s)) { + + # SYNC + # + # send sync + + report(clearctl($sock, $pcap, $CMD_SYNC), true); + + return; + } + + if (defined($opt_P)) { + report(ping($sock, $pcap), true); + return; + } + + if (defined($opt_V)) { + # versions + # + # read rsustatus and versions + + report(version_cmd($sock, $pcap), true); + + return; + } +} + +main(); \ No newline at end of file