diff options
Diffstat (limited to 'stub')
-rw-r--r-- | stub/Module.mk | 24 | ||||
-rwxr-xr-x | stub/i2c-stub-from-dump | 196 | ||||
-rw-r--r-- | stub/i2c-stub-from-dump.8 | 52 |
3 files changed, 272 insertions, 0 deletions
diff --git a/stub/Module.mk b/stub/Module.mk new file mode 100644 index 0000000..8ebcfcb --- /dev/null +++ b/stub/Module.mk @@ -0,0 +1,24 @@ +# Helper for the Linux i2c-stub bus driver +# +# Copyright (C) 2007-2008 Jean Delvare <khali@linux-fr.org> +# +# Licensed under the GNU General Public License. + +STUB_DIR := stub + +# +# Commands +# + +install-stub: $(STUB_DIR)/i2c-stub-from-dump + $(INSTALL_DIR) $(DESTDIR)$(sbindir) $(DESTDIR)$(man8dir) + $(INSTALL_PROGRAM) $(STUB_DIR)/i2c-stub-from-dump $(DESTDIR)$(sbindir) + $(INSTALL_DATA) $(STUB_DIR)/i2c-stub-from-dump.8 $(DESTDIR)$(man8dir) + +uninstall-stub: + $(RM) $(DESTDIR)$(sbindir)/i2c-stub-from-dump + $(RM) $(DESTDIR)$(man8dir)/i2c-stub-from-dump.8 + +install: install-stub + +uninstall: uninstall-stub 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); diff --git a/stub/i2c-stub-from-dump.8 b/stub/i2c-stub-from-dump.8 new file mode 100644 index 0000000..b594839 --- /dev/null +++ b/stub/i2c-stub-from-dump.8 @@ -0,0 +1,52 @@ +.TH I2C-STUB-FROM-DUMP 8 "April 2008" +.SH NAME +i2c-stub-from-dump \- feed i2c-stub with a dump file + +.SH SYNOPSIS +.B i2c-stub-from-dump +.I address +.I dump-file + +.SH DESCRIPTION +i2c-stub-from-dump is a small helper script for the i2c-stub kernel driver. +It lets you setup a fake I2C chip on the i2c-stub bus based on a dump of +the chip you want to emulate. + +i2c-stub-from-dump requires i2cdetect and i2cset to be installed and +reachable through the user's PATH. The former is used to find out the i2c-stub +bus number, while the latter is used to write to the fake I2C chip. + +.SH EXAMPLE +You have an I2C chip on system A. You would like to do some development on its +driver on system B. Here are the few steps you have to follow. + +On system A, use i2cdump to capture a dump from the chip. Assuming that the +chip in question lives at address 0x4c on I2C bus 0, you would run: + + i2cdump -y 0 0x4c b > chip.dump + +Adjust the bus number and chip address for your case. i2cdetect can help +you find out their values. If the device uses word (16-bit) register +access instead of the traditional byte (8-bit) access, use mode \fBw\fR +instead of \fBb\fR. + +Copy the dump file to system B. + +On system B, run: + + i2c-stub-from-dump 0x4c chip.dump + +This will load the required i2c-dev and i2c-stub kernel drivers if needed, +then write all the register values to the emulated I2C chip at address 0x4c. +Again, adjust the address as needed. + +.SH LIMITATIONS +There are some limitations to the kind of devices that can be handled: +.IP \(bu +Device must not have banks (as most Winbond devices do). + +.SH SEE ALSO +i2cdump(8), i2cdetect(8), i2cset(8) + +.SH AUTHOR +Jean Delvare |