ULi M920x parse

From LinuxTVWiki
Jump to navigation Jump to search

This ULi M920x specific script processes usbsnoop log files (as well as those which have been parsed by mrec's parser.pl utility).

#!/usr/bin/perl

use Getopt::Std;

sub expand_string {
	my @arr = ();
	my ($str) = @_;

	if (length($str) == 8) {
		push(@arr, substr($str, 0, 2));
		push(@arr, substr($str, 2, 2));
		push(@arr, substr($str, 4, 2));
		push(@arr, substr($str, 6, 2));
	}elsif(length($str) == 4) {
		push(@arr, substr($str, 0, 2));
		push(@arr, substr($str, 2, 2));
	}elsif(length($str) == 2) {
		push(@arr, $str);
	}elsif(length($str) == 1) {
		return;
	}
	return @arr;
}

sub expand_string_long {
	my @bytes = ();
	my (@str) = @_;
	
	foreach(@str) {
		#@arr = expand_string($_);
		#foreach(@arr){
		#	push(@bytes, $_);
		#}
		@bytes = ( @bytes, expand_string($_) );
	}
	
	return @bytes;
}

sub print_array_bytes {
	my (@str) = @_;
	
	foreach(expand_string_long(@str)){
		print "$_ ";
	}
}

sub print_bytes {
	my ($str) = @_;
	
	print_array_bytes(split(/ /, $str));
}

sub check {
	my ($cmd, @bytes) = @_;
	my @cmp;
	my $i;
	#print "cmd <$cmd>\n";
	my $fail = 0;
	
	@cmp = split(/ /, $cmd);
	for ($i = 0; $i < scalar(@cmp); $i++) {
		#print "check $bytes[$i] vs $cmp[$i]\n";
		if ($cmp[$i] == "-1") {
			next;
		}
		
		if (not($bytes[$i] =~ m/$cmp[$i]/)) {
			$fail = 1;
			print "($bytes[$i]!=$cmp[$i], $i)";
		}
	}
	if ($fail) {
		print "\n";
		print_array_bytes(@bytes);
		print "\n$cmd\n";
	}
}

sub get_line {
	my ($cmd) = @_; # xxx: could be more flexible 
	my @ret;
	my @cmp;
	my $i;
	
	again:
	while($line = <STDIN>) {
		#001295:  OUT: 000002 ms 135775 ms 40 23 c0 00 80 00 00 00 >>> 
		if($input eq "us" && $line =~ m/\S+:  \S+: \S+ ms \S+ ms ([a-fA-F0-9 ]+)/) {
			@ret = split(/ /, $1); $foo = $1;
			@ret[2,3,4,5,6,7] = @ret[3,2,5,4,7,6];
			last;
		}
		
		if($input eq "um" && $line =~ m/\S+ \S+ \S+ \S+ s ([a-fA-F0-9 ]+)/) {
			@ret = expand_string_long(split(/ /, $1)); $foo = $1;
			last;
		}
	}
	@cmp = split(/ /, $cmd);
	for ($i = 0; $i < scalar(@cmp); $i++) {
		if ($cmp[$i] == "-1") {
			next;
		}
		
		if (not($cmp[$i] eq $ret[$i])) {
			#print "fail\n";
			goto again;
		}
	}
	
	return @ret;
}

sub us_get_write {
	#print "<$line>\n";
	if($input == "us" && $line =~ m/>>>\s+([a-fA-F0-9 ]+)/) {
		return split(/ /, $1);
	}
	if($input == "um") {
		if($line =~ m/\S+ \S+ \S+ \S+ \S+ \S+ \S+ \S+ \S+ \S+ \S+ = ([a-fA-F0-9 ]+)/) {
			#print "read match $line\n";
			return expand_string_long(split(/ /, $1));
		}
	}
}

sub get_read {
	#print "<$line>\n";
	if($input == "us" && $line =~ m/<<<  ([a-fA-F0-9 ]+)/) {
		return split(/ /, $1);
	}
	if($input == "um") {
		while($line = <STDIN>) {
			if($line =~ m/\S+ \S+ \S+ \S+ \S+ \S+ = ([a-fA-F0-9 ]+)/) {
				return expand_string_long(split(/ /, $1));
			}
				
		}
	}
}

sub usage {
	print STDERR << "EOF";

	-i	um (usbmon)
		us (usb snoop)
		sp (snoopy pro)
		
	-m	fw (extract firmware)
		i2c (show i2c traffic)
EOF
exit;
}

getopts("m:i:", \%opt ) or usage();

$mode = $opt{m};
$input = $opt{i};

if ($input != "um" && $input != "us" && $input != "sp") {
	usage();
}

if ($mode != "fw" && $mode != "i2c") {
	usage();
}

if ($mode eq "fw") {
	open(out, ">fw") || die "Can't open fw";
	
	while(@bytes = get_line()) {
		if(scalar(@bytes) <= 1) {
			last;
		}
		
		$len = hex($bytes[6] . $bytes[7]);
		if ($len < 32) {
			next;
		}
		
		@fw_bytes = us_get_write();
		if ($len != scalar(@fw_bytes)) {
			#note: usbmon will not log bulk writes longer than 32 bytes by default
			print "bulk size doesn't match! Check usbmon.\n";
			print $len . " != " . scalar(@fw_bytes) . "\n";
			exit(0);
		}
		print out pack("v", hex($bytes[2] . $bytes[3]));
		print out pack("v", hex($bytes[4] . $bytes[5]));
		print out pack("v", scalar(@fw_bytes));
		
		foreach(@fw_bytes) {
			print out pack("C", hex($_));
		}
	}
	exit(1);
}

while(@bytes = get_line("-1")) {
	if(scalar(@bytes) <= 1) {
		last;
	}
	
	$master_line = $. - 1;
	
	if ($bytes[0] == "40" && $bytes[1] == "23") {
		
		if ($bytes[4] == "80" || $bytes[4] == "00") {
			my $multibyte = 0;
			my $addr;
			
			$addr = $bytes[2];
			
			printf "%06d: ", $master_line;
			print "addr $addr ";
			
			if (hex($addr) & 0x1) {
				print "Invalid address\n";
			}
			
			@bytes = get_line("40 23");
			
			$reg = $bytes[2];
			if ($bytes[4] == "80") {
				$multibyte = 1;
			} else {
				@bytes = get_line("40 23");
			}
			#if ($bytes[4] != "40") {
			#	print "(missing 40)";
			#}
			
			if ($bytes[4] == "80") {
				if ($multibyte == 0) {
					$raddr = sprintf("%02x", hex($addr) | 0x1);
					
					check("40 23 $raddr 00 80 00 00 00", @bytes);
					
					@bytes = get_line("c0 23");
					print "reg $reg = ";
				} else {
					print "$reg = ";
					@bytes = get_line("c0 23");
					while ($bytes[4] == "21") {
						check("c0 23 00 00 21 00 -1 -1", @bytes);
						
						@bytes = get_read();
						print_array_bytes(@bytes);
						
						@bytes = get_line("c0 23");
					}
				}
			
				check("c0 23 -1 00 60 00 -1 -1", @bytes);
				
				@bytes = get_read();
				print_array_bytes(@bytes);
				print "read\n";
				
			} else {
				check("40 23 -1 00 4|00 00 00 00", @bytes);
				print "reg $reg = $bytes[2]";
				
				while ($bytes[4] != "40") {
					@bytes = get_line("40 23");
					check("40 23 -1 00 4|00 00 00 00", @bytes);
					print " $bytes[2]";
				}
				print "\n";
			}
		}
	}
}