|  | #!/usr/bin/perl -w | 
|  | # | 
|  | # Copyright (C) 2002-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. | 
|  | # | 
|  | # EEPROM data decoding for Sony Vaio laptops. | 
|  | # | 
|  | # The eeprom driver must be loaded. For kernels older than 2.6.0, the | 
|  | # eeprom driver can be found in the lm-sensors package. | 
|  | # | 
|  | # Please note that this is a guess-only work.  Sony support refused to help | 
|  | # me, so if someone can provide information, please contact me. | 
|  | # My knowledge is summarized on this page: | 
|  | # http://khali.linux-fr.org/vaio/eeprom.html | 
|  | # | 
|  | # It seems that if present, the EEPROM is always at 0x57. | 
|  | # | 
|  | # Models tested so far: | 
|  | #   PCG-F403     : No EEPROM | 
|  | #   PCG-F707     : No EEPROM | 
|  | #   PCG-GR114EK  : OK | 
|  | #   PCG-GR114SK  : OK | 
|  | #   PCG-GR214EP  : OK | 
|  | #   PCG-GRT955MP : OK | 
|  | #   PCG-GRX316G  : OK | 
|  | #   PCG-GRX570   : OK | 
|  | #   PCG-GRX600K  : OK | 
|  | #   PCG-U1       : OK | 
|  | #   PCG-Z600LEK  : No EEPROM | 
|  | #   PCG-Z600NE   : No EEPROM | 
|  | #   VGN-S260     : OK | 
|  | #   VGN-S4M/S    : OK | 
|  | #   VGN-TZ11MN/N : OK | 
|  | # | 
|  | # Thanks to Werner Heuser, Carsten Blume, Christian Gennerat, Joe Wreschnig, | 
|  | # Xavier Roche, Sebastien Lefevre, Lars Heer, Steve Dobson, Kent Hunt, | 
|  | # Timo Hoenig and others for their precious help. | 
|  |  | 
|  |  | 
|  | use strict; | 
|  | use Fcntl qw(:DEFAULT :seek); | 
|  | use vars qw($sysfs $found); | 
|  |  | 
|  | use constant VERSION	=> "1.6"; | 
|  | use constant ONLYROOT	=> "Readable only by root"; | 
|  |  | 
|  | sub print_item | 
|  | { | 
|  | my ($label,$value) = @_; | 
|  |  | 
|  | printf("\%16s : \%s\n",$label,$value); | 
|  | } | 
|  |  | 
|  | # Abstract reads so that other functions don't have to care wether | 
|  | # we need to use procfs or sysfs | 
|  | sub read_eeprom_bytes | 
|  | { | 
|  | my ($bus, $addr, $offset, $length) = @_; | 
|  | my $filename; | 
|  |  | 
|  | if ($sysfs) | 
|  | { | 
|  | $filename = "/sys/bus/i2c/devices/$bus-00$addr/eeprom"; | 
|  | sysopen(FH, $filename, O_RDONLY) | 
|  | or die "Can't open $filename"; | 
|  | sysseek(FH, $offset, SEEK_SET) | 
|  | or die "Can't seek in $filename"; | 
|  |  | 
|  | my ($r, $bytes); | 
|  | $bytes = ''; | 
|  | $offset = 0; | 
|  | while($length) | 
|  | { | 
|  | $r = sysread(FH, $bytes, $length, $offset); | 
|  | die "Can't read $filename" | 
|  | unless defined($r); | 
|  | die "Unexpected EOF in $filename" | 
|  | unless $r; | 
|  | $offset += $r; | 
|  | $length -= $r; | 
|  | } | 
|  | close(FH); | 
|  |  | 
|  | return $bytes; | 
|  | } | 
|  | else | 
|  | { | 
|  | my $base = $offset & 0xf0; | 
|  | $offset -= $base; | 
|  | my $values = ''; | 
|  | my $remains = $length + $offset; | 
|  |  | 
|  | # Get all lines in a single string | 
|  | while ($remains > 0) | 
|  | { | 
|  | $filename = "/proc/sys/dev/sensors/eeprom-i2c-$bus-$addr/" | 
|  | . sprintf('%02x', $base); | 
|  | open(FH, $filename) | 
|  | or die "Can't open $filename"; | 
|  | $values .= <FH>; | 
|  | close(FH); | 
|  | $remains -= 16; | 
|  | $base += 16; | 
|  | } | 
|  |  | 
|  | # Store the useful part in an array | 
|  | my @bytes = split(/[ \n]/, $values); | 
|  | @bytes = @bytes[$offset..$offset+$length-1]; | 
|  |  | 
|  | # Back to a binary string | 
|  | return pack('C*', @bytes); | 
|  | } | 
|  | } | 
|  |  | 
|  | sub decode_string | 
|  | { | 
|  | my ($bus, $addr, $offset, $length) = @_; | 
|  |  | 
|  | my $string = read_eeprom_bytes($bus, $addr, $offset, $length); | 
|  | $string =~ s/\x00.*$//; | 
|  |  | 
|  | return($string); | 
|  | } | 
|  |  | 
|  | sub decode_hexa | 
|  | { | 
|  | my ($bus, $addr, $offset, $length) = @_; | 
|  |  | 
|  | my @bytes = unpack('C*', read_eeprom_bytes($bus, $addr, $offset, $length)); | 
|  | my $string=''; | 
|  |  | 
|  | for(my $i=0;$i<$length;$i++) | 
|  | { | 
|  | $string.=sprintf('%02X', shift(@bytes)); | 
|  | } | 
|  |  | 
|  | return($string); | 
|  | } | 
|  |  | 
|  | sub decode_uuid | 
|  | { | 
|  | my ($bus,$addr,$base) = @_; | 
|  |  | 
|  | my @bytes = unpack('C16', read_eeprom_bytes($bus, $addr, $base, 16)); | 
|  | my $string=''; | 
|  |  | 
|  | for(my $i=0;$i<16;$i++) | 
|  | { | 
|  | $string.=sprintf('%02x',shift(@bytes)); | 
|  | if(($i==3)||($i==5)||($i==7)||($i==9)) | 
|  | { | 
|  | $string.='-'; | 
|  | } | 
|  | } | 
|  |  | 
|  | if ($string eq '00000000-0000-0000-0000-000000000000') | 
|  | { | 
|  | return(ONLYROOT); | 
|  | } | 
|  | else | 
|  | { | 
|  | return($string); | 
|  | } | 
|  | } | 
|  |  | 
|  | sub vaio_decode | 
|  | { | 
|  | my ($bus,$addr) = @_; | 
|  |  | 
|  | my $name = decode_string($bus, $addr, 128, 32); | 
|  | # Simple heuristic to skip false positives | 
|  | return 0 unless $name =~ m/^[A-Z-]{4}/; | 
|  |  | 
|  | print_item('Machine Name', $name); | 
|  | my $serial = decode_string($bus, $addr, 192, 32); | 
|  | print_item('Serial Number', $serial ? $serial : ONLYROOT); | 
|  | print_item('UUID', decode_uuid($bus, $addr, 16)); | 
|  | my $revision = decode_string($bus, $addr, 160, 10); | 
|  | print_item(length($revision) > 2 ? 'Service Tag' : 'Revision', | 
|  | $revision); | 
|  | print_item('Asset Tag', decode_string($bus, $addr, 170, 4). | 
|  | decode_hexa($bus, $addr, 174, 12)); | 
|  | print_item('OEM Data', decode_string($bus, $addr, 32, 16)); | 
|  | print_item('Timestamp', decode_string($bus, $addr, 224, 18)); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | BEGIN | 
|  | { | 
|  | print("# Sony Vaio EEPROM Decoder version ".VERSION." by Jean Delvare\n\n"); | 
|  | } | 
|  |  | 
|  | END | 
|  | { | 
|  | print("\n"); | 
|  | } | 
|  |  | 
|  | for (my $i = 0, $found=0; $i <= 4 && !$found; $i++) | 
|  | { | 
|  | if (-r "/sys/bus/i2c/devices/$i-0057/eeprom") | 
|  | { | 
|  | $sysfs = 1; | 
|  | $found += vaio_decode($i, '57'); | 
|  | } | 
|  | elsif (-r "/proc/sys/dev/sensors/eeprom-i2c-$i-57") | 
|  | { | 
|  | if (-r "/proc/sys/dev/sensors/eeprom-i2c-$i-57/data0-15") | 
|  | { | 
|  | print("Deprecated old interface found.  Please upgrade to lm_sensors 2.6.3 or greater."); | 
|  | exit; | 
|  | } | 
|  | else | 
|  | { | 
|  | $sysfs = 0; | 
|  | $found += vaio_decode($i, '57'); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!$found) | 
|  | { | 
|  | print("Vaio EEPROM not found.  Please make sure that the eeprom module is loaded.\n"); | 
|  | } |