#!/usr/bin/perl -w
#
################################################################################################
#  Copyright (C) 2009	
#  
#  Mauro Carvalho Chehab <mchehab@redhat.com>
#  Douglas Schilling Landgraf <dougsland@redhat.com> 
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, version 2 of the License.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  Although not very common, on a few devices, the eeprom may be erased, due to a
#  bug on a *few eeprom* chipsets that sometimes considers i2c messages to other
#  devices as being for it.
# 
#  The solution for it is to reprogram the eeprom with their original contents.
#
#  Supported drivers to recover eeprom: em28xx, bttv or saa7134.
#
#
########################################################
# NOTE                                                 #
################################################################################################
# Since the script will try to detect the proper i2c bus address, it will only		       #
# work well if you have just one V4L device connected (in fact, just one em28xx,	       #
# or saa7134 or bttv).									       #
################################################################################################
#
########################################
# What do you need to run this script? #
########################################
#
# * eeprom - A dump file with the older eeprom.
# 
# As example this is a dump from EMPIRE TV DUAL (310U):
#			         ^^^^^^^^^^^^^^ 
# shell> dmesg
#
# [11196.181543] em28xx #0: i2c eeprom 00: 1a eb 67 95 1a eb 10 e3 d0 12 5c 03 6a 22 00 00
# [11196.181559] em28xx #0: i2c eeprom 10: 00 00 04 57 4e 07 00 00 00 00 00 00 00 00 00 00
# [11196.181572] em28xx #0: i2c eeprom 20: 46 00 01 00 f0 10 01 00 00 00 00 00 5b 1e 00 00
# [11196.181585] em28xx #0: i2c eeprom 30: 00 00 20 40 20 80 02 20 01 01 00 00 00 00 00 00
# [11196.181598] em28xx #0: i2c eeprom 40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181610] em28xx #0: i2c eeprom 50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181622] em28xx #0: i2c eeprom 60: 00 00 00 00 00 00 00 00 00 00 22 03 55 00 53 00
# [11196.181635] em28xx #0: i2c eeprom 70: 42 00 20 00 32 00 38 00 38 00 31 00 20 00 44 00
# [11196.181648] em28xx #0: i2c eeprom 80: 65 00 76 00 69 00 63 00 65 00 00 00 00 00 00 00
# [11196.181660] em28xx #0: i2c eeprom 90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181673] em28xx #0: i2c eeprom a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181685] em28xx #0: i2c eeprom b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181698] em28xx #0: i2c eeprom c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181710] em28xx #0: i2c eeprom d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181722] em28xx #0: i2c eeprom e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
# [11196.181735] em28xx #0: i2c eeprom f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
#
#############################################
# Where can I find my original eeprom?      #
#############################################
#
# e.g: Old dmesg output 
#
########################################
# How this script works?	       #
########################################
#
# To use it, you'll need to run the script, passing, as a parameter, the original dmesg with
# your original eeprom (before the corruption). Something like:
#
# shell> perl ./rewrite_eeprom my_old_dmesg_with_right_eeprom.txt > eeprom_script
#
# It will generate the eeprom_script file, with a script capable of recovering
# your eeprom.
#
# After having the proper eeprom_script generated, you'll need to run it, as root:
#
# shell> sh ./eeprom_script
#
#
# After running the script, your original contents should be restored. Try to
# remove it and reinsert to see if it will be properly detected again.
#
#
use Switch;

$argv = $ARGV[0];

$file_eeprom = "/tmp/eeprom-original.txt";
$file_bus = "/tmp/i2c-detect-bus.txt";
$file_addr = "/tmp/i2c-detect-addr.txt";
$file_i2c_tmp = "/tmp/i2ctmp.txt";

$businfo = "";
$addrinfo = "";

$NR_EEPROM_REGS = 0;

my $pos = 0;
my $file = shift or die "Please, specify the file name with the eeprom\n";

sub check_user
{
	if ($>) { 
		die "You must run this program as root\n"; 
	}
	my $st = system("modprobe i2c-dev");
	if ($st < 0) {
		die("Can't load i2c-dev module.");
	}
}
sub check_pkg
{
	my $st = system("i2cset &> /dev/null");
	if ($st < 0) {
		die "Please install i2c-tools package before continue.";
	}
}

sub get_bus_and_addr
{

	$lines = 0;

	system("i2cdetect -l | egrep -e \"em28xx|bttv|saa713\" | cut -b 5 > $file_bus"); 

	# Checking number if lines 
	open(FILE, $file_bus) or die "Can't open `$file_bus': $!";
	while (sysread FILE, $buffer, 1) {
		$lines += ($buffer =~ tr/\n//);
	}
	close FILE;

	switch ($lines) {
		case 0 {
			print "Could not detect i2c bus!\n";
			exit();
		}
		case 1 {
			# Starting script
			print "#!/bin/bash\n";
			print "#\n";
			print "# Note:\n";
			print "# - i2c_dev module should be loaded (check if lsmod)\n";
			print "# - v4l driver should be loaded \n\n";
		}
		else {
			print "humm, I got too many bus, please connect or plug just a hardware peer time!\n";
			print "Read a note inside the script!\n";
			exit();
		}
	}

	# Reading BUS from temporary file
	open (FILE, $file_bus);
	while (<FILE>) {
		chomp;
		$businfo = "$_";
	}
	close FILE;

	system("i2cdetect -y $businfo > $file_i2c_tmp");
	system("awk \'NR==7\' $file_i2c_tmp | cut -d \' \' -f 2 > $file_addr");

	# Reading BUS from temporary file
	open (FILE, $file_addr);
	while (<FILE>) {
		chomp;
		$addrinfo = "$_";
	}
	close FILE;
}

sub print_script 
{
	open (INPUT, "$file_eeprom") or die "Can't open data file: $!";

	@eeprom = "";
	my $eeprom_pos = 0;
	while (!eof(INPUT)) {
		read(INPUT, $fc, 2);

		$eeprom[$eeprom_pos] = "0x$fc";
		seek(INPUT, tell(INPUT) + 1, 0);
		$eeprom_pos++;
		$NR_EEPROM_REGS++;
	}
	close INPUT;

	printf("\necho \"\nStarting recording new eeprom... \n\n[DO NOT REMOVE YOUR DEVICE UNTIL FINISH THE UPDATE]\n\";\n");
	for ($i=0; $i < $NR_EEPROM_REGS; $i++) {
		printf("i2cset -y $businfo 0x$addrinfo 0x%02x $eeprom[$i] b\n", $i);
	}

	printf("\necho \"\nDone! Remove and re-insert your device to see if it will be properly detected again. :-)\n\";\n");
}


# Calling sub routines

&check_user;
&check_pkg;
&get_bus_and_addr;

# Building body of script 

print "if [ ! \"\$UID\" = \"0\" ]; then\n\techo \"You must run this script as root\";\n\texit;\nfi\n\n";

$cmd = "cat $argv | egrep \"eeprom [0-9a-f]\"| sed -e \"s/.*eeprom/eeprom/\" | cut -d ' ' -f 3-22 > $file_eeprom";
system($cmd);

# Printing i2c commands
&print_script;

# Removing old files

system("rm -f $file_bus");
system("rm -f $file_addr");
system("rm -f $file_i2c_tmp");
system("rm -f $file_eeprom");
