summaryrefslogtreecommitdiff
path: root/stub/i2c-stub-from-dump
blob: 4c14faf75411342043d9a308078416a2e82fe368 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/usr/bin/perl -w
#
# Copyright (C) 2007       Jean Delvare <khali@linux-fr.org>
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA  02110-1301, USA
#
# This script feeds the i2c-stub driver with dump data from a real
# I2C or SMBus chip. This can be useful when writing a driver for
# a device you do not have access to, but of which you have a dump.

use strict;
use vars qw($bus_nr $addr $count);

# Find out the i2c bus number of i2c-stub
sub get_i2c_stub_bus_number
{
	my $nr;

	open(FH, "i2cdetect -l |") || die "Can't run i2cdetect";
	while (<FH>) {
		next unless m/^i2c-(\d+).*\tSMBus stub/;
		$nr = $1;
		last;
	}
	close(FH);

	if (!defined($nr)) {
		print STDERR "Please load i2c-stub first\n";
		exit 2;
	}

	return $nr;
}

if ($>) {
	print "You must be root to use this script\n";
	exit 1;
}

if (@ARGV != 2) {
	print STDERR "Usage: i2c-stub-from-dump <addr> <dump file>\n";
	exit 1;
}

# Check the parameters
$addr = $ARGV[0];
if ($addr !~ m/^0x[0-7][0-9a-f]$/i) {
	print STDERR "Invalid address $addr\n";
	exit 1;
}

# Load the required kernel drivers if needed
system("/sbin/modprobe", "i2c-dev") == 0 || exit 1;
system("/sbin/modprobe", "i2c-stub", "chip_addr=$addr") == 0 || exit 1;
sleep(1); # udev may take some time to create the device node

$bus_nr = get_i2c_stub_bus_number();

# We don't want to see the output of 256 i2cset
open(SAVEOUT, ">&STDOUT");
open(STDOUT, ">/dev/null");

$count = 0;
open(DUMP, $ARGV[1]) || die "Can't open $ARGV[1]: $!\n";
OUTER_LOOP:
while (<DUMP>) {
	next unless m/^([0-9a-f]0):(( [0-9a-f]{2}){16})/;
	my $offset = hex($1);
	my @values = split(/ /, $2);
	shift(@values);
	for (my $i = 0; $i < 16 && (my $val = shift(@values)); $i++) {
		last OUTER_LOOP if system("i2cset", "-y",
			$bus_nr, $addr,
			sprintf("0x\%02x", $offset+$i),
			sprintf("0x\%02x", hex($val)), "b");
		$count++;
	}
}
close(DUMP);
close(STDOUT);

if ($count) {
	printf SAVEOUT "$count byte values written to \%d-\%04x\n",
		$bus_nr, oct($addr);
}

exit($count == 0);