root/i2c-tools/trunk/stub/i2c-stub-from-dump

Revision 6013, 5.5 KB (checked in by khali, 2 weeks ago)

i2c-stub-from-dump: Be more tolerant on input dump format

Allow for uppercase hexadecimal digits, "|" instead of ":" as the address
separator, and an optional space before said separator. i2cdump doesn't
use any of these but other dump tools do, so this improves
compatibility.

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl -w
2#
3# Copyright (C) 2007-2008  Jean Delvare <khali@linux-fr.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18# MA  02110-1301, USA
19#
20# This script feeds the i2c-stub driver with dump data from a real
21# I2C or SMBus chip. This can be useful when writing a driver for
22# a device you do not have access to, but of which you have a dump.
23
24use strict;
25use vars qw($bus_nr @addr $err);
26
27# Kernel version detection code by Mark M. Hoffman,
28# copied from sensors-detect.
29use vars qw(@kernel_version);
30
31sub initialize_kernel_version
32{
33        `uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
34        @kernel_version = ($1, $2, $3, $4);
35}
36
37sub kernel_version_at_least
38{
39        my ($vers, $plvl, $slvl) = @_;
40        return 1 if ($kernel_version[0]  > $vers ||
41                      ($kernel_version[0] == $vers && 
42                        ($kernel_version[1]  > $plvl || 
43                          ($kernel_version[1] == $plvl && 
44                            ($kernel_version[2] >= $slvl)))));
45        return 0;
46}
47
48# Find out the i2c bus number of i2c-stub
49sub get_i2c_stub_bus_number
50{
51        my $nr;
52
53        open(FH, "i2cdetect -l |") || die "Can't run i2cdetect";
54        while (<FH>) {
55                next unless m/^i2c-(\d+).*\tSMBus stub/;
56                $nr = $1;
57                last;
58        }
59        close(FH);
60
61        return $nr;
62}
63
64# Unload i2c-stub if we need an address it doesn't offer
65sub check_chip_addr {
66        my $chip_addr_file = shift;
67        my @addr = @{shift()};
68        local $_;
69
70        open(CHIP_ADDR, $chip_addr_file) || return;
71        $_ = <CHIP_ADDR>;
72        chomp;
73        my %stub_addr = map { $_ => 1 } split ',';
74        close(CHIP_ADDR);
75
76        foreach my $addr (@addr) {
77                unless (exists $stub_addr{$addr}) {
78                        print STDERR "Cycling i2c-stub to get the address we need\n";
79                        system("/sbin/rmmod", "i2c-stub");
80                        return;
81                }
82        }
83}
84
85# Load the required kernel drivers if needed
86sub load_kernel_drivers
87{
88        local $_;
89        my @addr = @{shift()};
90        my $nr;
91
92        # i2c-stub may be loaded without the address we want
93        check_chip_addr("/sys/module/i2c_stub/parameters/chip_addr", \@addr);
94
95        # Maybe everything is already loaded
96        $nr = get_i2c_stub_bus_number();
97        return $nr if defined $nr;
98
99        system("/sbin/modprobe", "i2c-dev") == 0 || exit 1;
100        if (kernel_version_at_least(2, 6, 19)) {
101                system("/sbin/modprobe", "i2c-stub",
102                       "chip_addr=".join(',', @addr)) == 0 || exit 1;
103        } else {
104                system("/sbin/modprobe", "i2c-stub") == 0 || exit 1;
105        }
106        # udev may take some time to create the device node
107        if (!(-x "/sbin/udevadm" && system("/sbin/udevadm settle") == 0)
108         && !(-x "/sbin/udevsettle" && system("/sbin/udevsettle") == 0)) {
109                sleep(1);
110        }
111
112        $nr = get_i2c_stub_bus_number();
113        if (!defined($nr)) {
114                print STDERR "Please load i2c-stub first\n";
115                exit 2;
116        }
117
118        return $nr;
119}
120
121sub process_dump
122{
123        my ($addr, $dump) = @_;
124        my $err = 0;
125        my ($bytes, $words);
126
127        open(DUMP, $dump) || die "Can't open $dump: $!\n";
128 OUTER_LOOP:
129        while (<DUMP>) {
130                if (m/^([0-9a-f]0) ?[:|](( [0-9a-fX]{2}){16})/i) {
131                        # Byte dump
132                        my $offset = hex($1);
133                        my @values = split(/ /, $2);
134                        shift(@values);
135                        for (my $i = 0; $i < 16 && (my $val = shift(@values)); $i++) {
136                                next if $val =~ m/X/;
137                                if (system("i2cset", "-y", $bus_nr, $addr,
138                                           sprintf("0x\%02x", $offset+$i),
139                                           "0x$val", "b")) {
140                                        $err = 3;
141                                        last OUTER_LOOP;
142                                }
143                                $bytes++;
144                        }
145                } elsif (m/^([0-9a-f][08]) ?[:|](( [0-9a-fX]{4}){8})/i) {
146                        # Word dump
147                        my $offset = hex($1);
148                        my @values = split(/ /, $2);
149                        shift(@values);
150                        for (my $i = 0; $i < 8 && (my $val = shift(@values)); $i++) {
151                                next if $val =~ m/X/;
152                                if (system("i2cset", "-y", $bus_nr, $addr,
153                                           sprintf("0x\%02x", $offset+$i),
154                                           "0x$val", "w")) {
155                                        $err = 3;
156                                        last OUTER_LOOP;
157                                }
158                                $words++;
159                        }
160                }
161        }
162        close(DUMP);
163
164        if ($bytes) {
165                printf SAVEOUT "$bytes byte values written to \%d-\%04x\n",
166                        $bus_nr, $addr;
167        }
168
169        if ($words) {
170                printf SAVEOUT "$words word values written to \%d-\%04x\n",
171                        $bus_nr, $addr;
172        }
173
174        if (!$err && !$bytes && !$words) {
175                printf SAVEOUT "Only garbage found in dump file $dump\n";
176                $err = 1;
177        }
178
179        return $err;
180}
181
182if ($>) {
183        print "You must be root to use this script\n";
184        exit 1;
185}
186
187if (@ARGV < 2) {
188        print STDERR "Usage: i2c-stub-from-dump <addr>[,<addr>,...] <dump file> [<dump file> ...]\n";
189        exit 1;
190}
191
192# Check the parameters
193@addr = split(/,/, shift @ARGV);
194foreach (@addr) {
195        unless (m/^0x[0-7][0-9a-f]$/i) {
196                print STDERR "Invalid address $_\n";
197                exit 1;
198        }
199        $_ = oct $_;
200}
201
202if (@addr < @ARGV) {
203        print STDERR "Fewer addresses than dumps provided\n";
204        exit 4;
205}
206
207initialize_kernel_version();
208if (@addr > 1 && !kernel_version_at_least(2, 6, 24)) {
209        print STDERR "Multiple addresses not supported by this kernel version\n";
210        exit 5;
211}
212
213$bus_nr = load_kernel_drivers(\@addr);
214
215# We don't want to see the output of 256 i2cset
216open(SAVEOUT, ">&STDOUT");
217open(STDOUT, ">/dev/null");
218foreach (@addr) {
219        if (!@ARGV) {
220                printf STDERR "Skipping \%d-\%04x, no dump file privided\n",
221                       $bus_nr, $_;
222                next;
223        }
224        $err = process_dump($_, shift @ARGV);
225        last if $err;
226}
227close(STDOUT);
228
229exit($err);
Note: See TracBrowser for help on using the browser.