#!/usr/bin/perl
#======================================================
# Vega Serial Firmware Update
#
# This program will listen on a serial port and
# force a firware upgrade on image 1 and 2.
# 
# Author: Nenad Corbic <ncorbic@sangoma.com>
#
#======================================================

use Device::SerialPort;
use strict;
use strict;
use warnings;
use Time::HiRes qw(usleep nanosleep);

use constant {
    TIMEOUT => 15
};

my $port_name="/dev/ttyS0";
my $ftp_ip="10.10.12.14";
my $ftp_user="root";
my $ftp_pass="sangoma";
my $ftp_port=21;
#my $vega_img_bin="/tftpboot/VEGA_R100S003.bin";
#my $vega_img_bin="/tftpboot/VEGA400_R088S030.abs";
my $vega_img_bin="/tftpboot/VEGAEURO_R088S029.abs";
#my $vega_img_name="VEGA_R100S003";
#my $vega_img_name="VEGA400_R088S030";
my $vega_img_name="VEGAEURO_R088S029";

my $port = new Device::SerialPort($port_name) || die "Can't open $port_name: $!\n";
#$port->user_msg('ON'); 
$port->baudrate(115200); 
$port->parity("none"); 
$port->databits(8); 
$port->stopbits(1); 
$port->handshake("none");
$port->write_settings || die "Failed to write port settings";

sleep(2);

my $count = 0;

sub help()
{
  	$port->write("help\r");
    my $help_start=0;
    my $idle=0;
	$port->lookclear; 
    while (1) {
		my $char = $port->lookfor();
		if ($char) {
            if (!$help_start) {
                if ($char =~ m/Commands/) {
                    $help_start=1;
                } else {
                    next;
                }
            }
            if ($help_start) {
		print_tty("$char");
            }
        } else {
            $idle++;
            if ($idle > 2) {
                last; 
            }
            sleep(1);
		}
   }        

   print "Help done\n";
   return 
}

sub msleep
{
	my $val=shift;
    $val=$val*1000;

    usleep($val);
}

sub print_tty
{
 	my $data=shift;

	if ($data =~ m/WARNING:/) {
		return;
    }

	#print "--------------------------\n";
	print "$data\n";
	#print "--------------------------\n";
}

sub lookfor_a_and_b
{
    my $data_a=shift;
    my $data_b=shift;
	my $timeout=shift;
	my $error=shift;
	my $look_for_data_b=0;
	my $cnt=0; 

	while (1) 
    {
		my $char = $port->lookfor();
		if ($char) {
			#print "====================\n";
			print_tty("$char");
			#print "====================\n";
			if ($char =~ m/$data_a/) {
				print "Found $data_a Ok\n";
				$look_for_data_b=1;
				next;
            }

			if ($look_for_data_b) {
				if ($char =~ m/$data_b/) {
					print "Found $data_b Ok\n";
					last;
				}
            }
		} else { 
			$port->write("\r");
 			sleep(1);
			if ($cnt > $timeout) {
				printf "Error: Failed to find $cnt $timeout -$data_a- -$data_b-\n";
				return 1
			}
			$cnt++;
		}
    }

	return 0;
}

sub lookfor_str
{
    my $data=shift;
    my $timeout=shift;
    my $error=shift;
    my $cnt=0;  
    
    while (1)  {	
        #$port->write ("show $data\r");  
	my $char = $port->lookfor();
	if ($char) {
	    #print "====================\n";
	    print_tty("$char");
	    #print "====================\n";
	    if ($char =~ m/$data/) {
	        print "Found $data Ok\n";
	    	return find_admin($timeout);
            }
            if (defined($error)) {
 				if ($char =~ m/$error/) {
				    printf "Error: Found error -$error-\n";
					find_admin($timeout);
					return 2;
				}
            }
		} else { 
			$port->write("\r");
 			sleep(1);
			if ($cnt > $timeout) {
				printf "Error: Failed to find $cnt $timeout -$data-\n";
				return 1
			}
			$cnt++;
		}
    }

	return 0;
}

sub firmware_ftp
{
    my $rc;
    $port->write("set lan.file_transfer_method=FTP\r");

    $rc=lookfor_str("file_transfer_method=FTP", 10);
    if ($rc) {
        return $rc
    } 

    $port->write("set ftp.ip=$ftp_ip\r");

    $rc=lookfor_str("$ftp_ip", 10);
    if ($rc > 0) {
        return $rc
    } 

    if ($ftp_user eq "anonymous") {
	$port->write("set ftp.anonymous_login=1\r");
	find_admin(TIMEOUT);
    }
    else {
	$port->write("set ftp.anonymous_login=0\r");
	find_admin(TIMEOUT);
	$port->write("set ftp.username=$ftp_user\r");
	find_admin(TIMEOUT);
    }
    $port->write("set ftp._password=$ftp_pass\r");
    find_admin(TIMEOUT);
    $port->write("set ftp.port=$ftp_port\r");
    find_admin(TIMEOUT);

    $port->write("apply\r");
    #$rc=lookfor_str("config changes applied", TIMEOUT);
    $rc=lookfor_str("Applying configuration changes", TIMEOUT);
    if ($rc) {
        return $rc
    } 
	$port->write("show ftp\r");
	find_admin(5);

	$port->write("upgrade\r");
	find_admin(5);

	$port->write("download enable\r");
    $rc=lookfor_str("Download enabled", TIMEOUT);
    if ($rc) {
        return $rc
    } 

    $port->write("download firmware $vega_img_bin\r");
    $rc=lookfor_str("All operations completed",360,"operation aborted");
    if ($rc) {
		if ($rc > 1) {
			$port->write("download firmware $vega_img_bin\r");
    		$rc=lookfor_str("All operations completed",360,"operation aborted");
			if ($rc) {
				return $rc;
			}
		} else {
			return $rc;
		}
    } 
    $port->lookclear; 

    find_admin(15);	
	$port->write("reboot system\r");

	printf "Waiting for Reboot....\n";
	read_input(30);
  
	find_login_admin(60);
	

	return 0; 
}

sub firmware_tftp
{
	my $rc;
	$port->write("set lan.file_transfer_method=TFTP\r");
    $rc=lookfor_str("file_transfer_method=TFTP", TIMEOUT);
    if ($rc) {
        return $rc
    } 

	$port->write("set tftp.ip=10.10.12.42\r");
    $rc=lookfor_str("10.10.12.42", TIMEOUT);
    if ($rc) {
        return $rc
    } 

	$port->write("apply\r");
    $rc=lookfor_str("config changes applied", TIMEOUT);
    if ($rc) {
        return $rc
    } 
	$port->write("upgrade\r");
	find_admin(5);

	$port->write("download enable\r");
    $rc=lookfor_str("Download enabled", TIMEOUT);
    if ($rc) {
        return $rc
    } 
    $port->write("download firmware $vega_img_bin\r");
    $rc=lookfor_str("All operations completed",90,"operation aborted");
    if ($rc) {
	if ($rc > 1) {
			$port->write("download firmware $vega_img_bin\r");
    		$rc=lookfor_str("All operations completed",90,"operation aborted");
			if ($rc) {
				return $rc;
			}
		} else {
			return $rc;
		}
    } 
    $port->lookclear; 

    find_admin(10);	
	$port->write("reboot system\r");

	printf "Waiting for Reboot....\n";
	read_input(30);
  
	find_login_admin(60);
	
	$port->write("show version\r");
    $rc=lookfor_str("R100S002",TIMEOUT);
    if ($rc) {
  	    printf "Result: Firmware Update FAIL\n";
		return $rc
    }
    
	printf "Result: Firmware Update PASS\n";

	return 0; 
}

sub read_input
{
	my $timeout=shift;
 	my $cnt=0;
	while (1) {
		# Poll to see if any data is coming in
		my $char = $port->lookfor();

		# If we get data, then print it
		# Send a number to the arduino
		if ($char) {
			#print "====================\n";
			print_tty("$char");
			#print "====================\n";
			next;
		}  

		sleep(1);
		if ($cnt > $timeout) {
			return 0;
		}
		$cnt++;
	}
	return 0;
}

sub find_admin
{
	my $timeout=shift;
 	my $cnt=0;

    if (!defined($timeout)) {
		$timeout=TIMEOUT;
    }

    print "Looking for Admin...\n";
	while (1) {
		# Poll to see if any data is coming in
		my $char = $port->lookfor();
		# If we get data, then print it
		# Send a number to the arduino
		if ($char) {
			print_tty ("$char");
			if ($char =~ m/admin  >?/) {
				 print "Got Admin\n";
                 last;
			} 
			if ($char =~ m/UPGRADE.*\>?/) {
				 print "Got UPGRADE\n";
                 last;
			} 
		} else {
			sleep(1);
			$port->write("\r");
			if ($cnt > $timeout) {
				print "Failed to find Admin\n";
				exit(1);
			}
			$cnt++;
		}
	}
	return 0
}
sub version_print
{
	my $image=shift;

	find_admin(TIMEOUT);
	$port->write("show version\r");
	read_input(2);
	find_admin(TIMEOUT);
    
	return 0;
}

sub version_check
{
	my $image=shift;
	my $rc;

	find_admin(TIMEOUT);
	$port->write("show version\r");
    $rc=lookfor_a_and_b("$image","$vega_img_name",TIMEOUT);
    if ($rc) {
  	    printf "Result: Firmware Update $image FAIL\n";
		return $rc
    }
    
	printf "Result: Firmware Update $image PASS\n";

	return $rc

}

sub find_login_admin
{
	my $timeout=shift;
 	my $cnt=0;
	my $char;
	my $exit_on_empty=0;
        print "find_login_admin is executing\n";
      
	while (1) {
	    #initiate responce from ttyUSB0 interface
	    $port->write("\r");
	    $port->write("\r");
	    
	    # Poll to see if any data is coming in
		$char = $port->lookfor();

		# If we get data, then print it
		# Send a number to the arduino
		if ($char) {
			#print "====================\n";
			print_tty("$char");
			#print "====================\n";
			if ($char =~ m/Console port: Type/) {
				 #print "Got Console help\n";
    			 $port->lookclear; 
				 next;
			} 
			if ($char =~ m/Code/) {
				 #print "Got Code\n";
    			 $port->lookclear; 
				 next;
			} 
			if ($char =~ m/^Username: /) {
				print "Got user\n";
				$port->write("admin\r");
				$port->write("admin\r");
				sleep(1);
			} 

			if (0 && $char =~ m/^Password: ?/) {
				print "Got password\n";
				$port->write("admin\r");
			} 
			if ($char =~ m/UPGRADE.*\>?/) {
				print "Got Upgrade\n";
				$port->write("exit\r");
			} 
			if ($char =~ m/admin  \>/) {
				printf "Got Admin\n";
    			$port->lookclear; 
				return 0;
			} 
		} else {
			if ($exit_on_empty) {
				last;
            }
			$cnt++;
			if ($cnt % 2 == 0) {
				#printf "find_login_admin: got nothing\n";
				$port->write("\r");
			}
			sleep(1);
			if ($cnt > $timeout) {
				die "Failed to find Login & Admin\n";
			}
		} 
	}
	return 0
}


sub writefxo
{
	my $str=shift;
	my $rc;
	#find_admin(TIMEOUT);
	$port->write($str);
    $rc=lookfor_str("SPIWriteData",TIMEOUT);
	return $rc;
}

#my @hybrid=( 'ff', 'f7', 'ff', '4', 'fd', '2', '0', '0');  #2000ft 26awg
#my @hybrid=( '2', 'fa', 'fe', '4', 'fd', '1', '0', '0');    #2000ft 22awg
#my @hybrid=( 'F8',  'E6', '8', '12', 'EA', '2', '1', 'FF'); #EIA 7
my @hybrid=( '0',  '0', '0', '0', '0', '0', '0', '0');     #default
my @hybrid_reg=( '0x2d', '0x2e', '0x2f', '0x30', '0x31', '0x32', '0x33', '0x34');
my $max_ports=4;

sub writefxo_hybrid
{
	my $i;
	my $x;
	find_admin(TIMEOUT);
	#Iterate over ports
	for ($i=1;$i<=$max_ports;$i++) {
		for ($x=0;$x<8;$x++) {
			#Iterate over hybrid values
			writefxo("writefxo $i $hybrid_reg[$x] $hybrid[$x]\r");
		}
    }
	return 0;
}

sub readfxo
{
	find_admin(TIMEOUT);
	$port->write("readfxo all\r");
	read_input(2);
	find_admin(TIMEOUT);
}



print "start execution of upgrade!!!\n";

find_login_admin(30);

writefxo_hybrid();

readfxo();


exit (0);
