| 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 | |
|---|
| 24 | use strict; |
|---|
| 25 | use vars qw($bus_nr $addr $bytes $words); |
|---|
| 26 | |
|---|
| 27 | # Kernel version detection code by Mark M. Hoffman, |
|---|
| 28 | # copied from sensors-detect. |
|---|
| 29 | use vars qw(@kernel_version); |
|---|
| 30 | |
|---|
| 31 | sub initialize_kernel_version |
|---|
| 32 | { |
|---|
| 33 | `uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/; |
|---|
| 34 | @kernel_version = ($1, $2, $3, $4); |
|---|
| 35 | } |
|---|
| 36 | |
|---|
| 37 | sub 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 |
|---|
| 49 | sub 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 | if (!defined($nr)) { |
|---|
| 62 | print STDERR "Please load i2c-stub first\n"; |
|---|
| 63 | exit 2; |
|---|
| 64 | } |
|---|
| 65 | |
|---|
| 66 | return $nr; |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | sub process_dump |
|---|
| 70 | { |
|---|
| 71 | my $dump = shift; |
|---|
| 72 | |
|---|
| 73 | open(DUMP, $dump) || die "Can't open $dump: $!\n"; |
|---|
| 74 | OUTER_LOOP: |
|---|
| 75 | while (<DUMP>) { |
|---|
| 76 | if (m/^([0-9a-f]0):(( [0-9a-fX]{2}){16})/) { |
|---|
| 77 | # Byte dump |
|---|
| 78 | my $offset = hex($1); |
|---|
| 79 | my @values = split(/ /, $2); |
|---|
| 80 | shift(@values); |
|---|
| 81 | for (my $i = 0; $i < 16 && (my $val = shift(@values)); $i++) { |
|---|
| 82 | next if $val =~ m/X/; |
|---|
| 83 | last OUTER_LOOP if system("i2cset", "-y", |
|---|
| 84 | $bus_nr, $addr, |
|---|
| 85 | sprintf("0x\%02x", $offset+$i), |
|---|
| 86 | "0x$val", "b"); |
|---|
| 87 | $bytes++; |
|---|
| 88 | } |
|---|
| 89 | } elsif (m/^([0-9a-f][08]):(( [0-9a-fX]{4}){8})/) { |
|---|
| 90 | # Word dump |
|---|
| 91 | my $offset = hex($1); |
|---|
| 92 | my @values = split(/ /, $2); |
|---|
| 93 | shift(@values); |
|---|
| 94 | for (my $i = 0; $i < 8 && (my $val = shift(@values)); $i++) { |
|---|
| 95 | next if $val =~ m/X/; |
|---|
| 96 | last OUTER_LOOP if system("i2cset", "-y", |
|---|
| 97 | $bus_nr, $addr, |
|---|
| 98 | sprintf("0x\%02x", $offset+$i), |
|---|
| 99 | "0x$val", "w"); |
|---|
| 100 | $words++; |
|---|
| 101 | } |
|---|
| 102 | } |
|---|
| 103 | } |
|---|
| 104 | close(DUMP); |
|---|
| 105 | } |
|---|
| 106 | |
|---|
| 107 | if ($>) { |
|---|
| 108 | print "You must be root to use this script\n"; |
|---|
| 109 | exit 1; |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | if (@ARGV != 2) { |
|---|
| 113 | print STDERR "Usage: i2c-stub-from-dump <addr> <dump file>\n"; |
|---|
| 114 | exit 1; |
|---|
| 115 | } |
|---|
| 116 | |
|---|
| 117 | # Check the parameters |
|---|
| 118 | $addr = $ARGV[0]; |
|---|
| 119 | if ($addr !~ m/^0x[0-7][0-9a-f]$/i) { |
|---|
| 120 | print STDERR "Invalid address $addr\n"; |
|---|
| 121 | exit 1; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | initialize_kernel_version(); |
|---|
| 125 | |
|---|
| 126 | # Load the required kernel drivers if needed |
|---|
| 127 | system("/sbin/modprobe", "i2c-dev") == 0 || exit 1; |
|---|
| 128 | if (kernel_version_at_least(2, 6, 19)) { |
|---|
| 129 | system("/sbin/modprobe", "i2c-stub", "chip_addr=$addr") == 0 || exit 1; |
|---|
| 130 | } else { |
|---|
| 131 | system("/sbin/modprobe", "i2c-stub") == 0 || exit 1; |
|---|
| 132 | } |
|---|
| 133 | sleep(1); # udev may take some time to create the device node |
|---|
| 134 | |
|---|
| 135 | $bus_nr = get_i2c_stub_bus_number(); |
|---|
| 136 | $bytes = $words = 0; |
|---|
| 137 | |
|---|
| 138 | # We don't want to see the output of 256 i2cset |
|---|
| 139 | open(SAVEOUT, ">&STDOUT"); |
|---|
| 140 | open(STDOUT, ">/dev/null"); |
|---|
| 141 | process_dump($ARGV[1]); |
|---|
| 142 | close(STDOUT); |
|---|
| 143 | |
|---|
| 144 | if ($bytes) { |
|---|
| 145 | printf SAVEOUT "$bytes byte values written to \%d-\%04x\n", |
|---|
| 146 | $bus_nr, oct($addr); |
|---|
| 147 | } |
|---|
| 148 | |
|---|
| 149 | if ($words) { |
|---|
| 150 | printf SAVEOUT "$words word values written to \%d-\%04x\n", |
|---|
| 151 | $bus_nr, oct($addr); |
|---|
| 152 | } |
|---|
| 153 | |
|---|
| 154 | if ($bytes + $words == 0) { |
|---|
| 155 | printf SAVEOUT "Only garbage found in dump file $ARGV[1]\n"; |
|---|
| 156 | exit(1); |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | exit(0); |
|---|