summaryrefslogtreecommitdiff
path: root/stub/i2c-stub-from-dump
diff options
context:
space:
mode:
authorGraydon, Tracy <tracy.graydon@intel.com>2013-01-31 18:44:07 -0800
committerGraydon, Tracy <tracy.graydon@intel.com>2013-01-31 18:44:07 -0800
commitb19d79bf9028f58e3e4226bd9410b59f19fe3c8d (patch)
treea84dec919322f3d19c7c0cf3050c8c134a0d4fc6 /stub/i2c-stub-from-dump
downloadi2c-tools-2.0alpha.tar.gz
i2c-tools-2.0alpha.tar.bz2
i2c-tools-2.0alpha.zip
Diffstat (limited to 'stub/i2c-stub-from-dump')
-rwxr-xr-xstub/i2c-stub-from-dump196
1 files changed, 196 insertions, 0 deletions
diff --git a/stub/i2c-stub-from-dump b/stub/i2c-stub-from-dump
new file mode 100755
index 0000000..f91da1c
--- /dev/null
+++ b/stub/i2c-stub-from-dump
@@ -0,0 +1,196 @@
+#!/usr/bin/perl -w
+#
+# Copyright (C) 2007-2008 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 $bytes $words $err);
+
+# Kernel version detection code by Mark M. Hoffman,
+# copied from sensors-detect.
+use vars qw(@kernel_version);
+
+sub initialize_kernel_version
+{
+ `uname -r` =~ /(\d+)\.(\d+)\.(\d+)(.*)/;
+ @kernel_version = ($1, $2, $3, $4);
+}
+
+sub kernel_version_at_least
+{
+ my ($vers, $plvl, $slvl) = @_;
+ return 1 if ($kernel_version[0] > $vers ||
+ ($kernel_version[0] == $vers &&
+ ($kernel_version[1] > $plvl ||
+ ($kernel_version[1] == $plvl &&
+ ($kernel_version[2] >= $slvl)))));
+ return 0;
+}
+
+# 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);
+
+ return $nr;
+}
+
+# Load the required kernel drivers if needed
+sub load_kernel_drivers
+{
+ local $_;
+ my $addr = oct shift;
+ my $nr;
+
+ # Maybe everything is already loaded
+ $nr = get_i2c_stub_bus_number();
+ if (defined $nr) {
+ if (kernel_version_at_least(2, 6, 19)) {
+ # Check if the chip address we need is there
+ open(CHIP_ADDR, "/sys/module/i2c_stub/parameters/chip_addr");
+ $_ = <CHIP_ADDR>;
+ chomp;
+ my @stub_addr = split ',';
+ close(CHIP_ADDR);
+
+ foreach (@stub_addr) {
+ return $nr if $addr == $_;
+ }
+ printf STDERR "i2c-stub already loaded without support for address 0x%02x\n", $addr;
+ exit 2;
+ }
+ return $nr;
+ }
+
+ system("/sbin/modprobe", "i2c-dev") == 0 || exit 1;
+ if (kernel_version_at_least(2, 6, 19)) {
+ system("/sbin/modprobe", "i2c-stub", "chip_addr=$addr") == 0 || exit 1;
+ } else {
+ system("/sbin/modprobe", "i2c-stub") == 0 || exit 1;
+ }
+ sleep(1); # udev may take some time to create the device node
+
+ $nr = get_i2c_stub_bus_number();
+ if (!defined($nr)) {
+ print STDERR "Please load i2c-stub first\n";
+ exit 2;
+ }
+
+ return $nr;
+}
+
+sub process_dump
+{
+ my $dump = shift;
+ my $err = 0;
+
+ open(DUMP, $dump) || die "Can't open $dump: $!\n";
+ OUTER_LOOP:
+ while (<DUMP>) {
+ if (m/^([0-9a-f]0):(( [0-9a-fX]{2}){16})/) {
+ # Byte dump
+ my $offset = hex($1);
+ my @values = split(/ /, $2);
+ shift(@values);
+ for (my $i = 0; $i < 16 && (my $val = shift(@values)); $i++) {
+ next if $val =~ m/X/;
+ if (system("i2cset", "-y", $bus_nr, $addr,
+ sprintf("0x\%02x", $offset+$i),
+ "0x$val", "b")) {
+ $err = 3;
+ last OUTER_LOOP;
+ }
+ $bytes++;
+ }
+ } elsif (m/^([0-9a-f][08]):(( [0-9a-fX]{4}){8})/) {
+ # Word dump
+ my $offset = hex($1);
+ my @values = split(/ /, $2);
+ shift(@values);
+ for (my $i = 0; $i < 8 && (my $val = shift(@values)); $i++) {
+ next if $val =~ m/X/;
+ if (system("i2cset", "-y", $bus_nr, $addr,
+ sprintf("0x\%02x", $offset+$i),
+ "0x$val", "w")) {
+ $err = 3;
+ last OUTER_LOOP;
+ }
+ $words++;
+ }
+ }
+ }
+ close(DUMP);
+
+ return $err;
+}
+
+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;
+}
+
+initialize_kernel_version();
+
+$bus_nr = load_kernel_drivers($addr);
+$bytes = $words = 0;
+
+# We don't want to see the output of 256 i2cset
+open(SAVEOUT, ">&STDOUT");
+open(STDOUT, ">/dev/null");
+$err = process_dump($ARGV[1]);
+close(STDOUT);
+
+if ($bytes) {
+ printf SAVEOUT "$bytes byte values written to \%d-\%04x\n",
+ $bus_nr, oct($addr);
+}
+
+if ($words) {
+ printf SAVEOUT "$words word values written to \%d-\%04x\n",
+ $bus_nr, oct($addr);
+}
+
+if (!$err && ($bytes + $words == 0)) {
+ printf SAVEOUT "Only garbage found in dump file $ARGV[1]\n";
+ exit(1);
+}
+
+exit($err);