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);
|