#!/usr/bin/perl -w # # ############################################################################# # # $Id: eeprom.pl,v 1.1 2003/10/07 22:46:40 jmuelmen Exp $ # # Copyright (C) 2002, Arnim Laeuger # # 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. See also the file COPYING which # came with this application. # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # ############################################################################# # # Purpose: # ======== # # Talks to the vend_ax.hex firmware on a EZ-USB device to read and write # the onboard I2C EEPROM. # The vend_ax.hex file can be found in Cypress' Development Kit. # # eeprom.pl -d -a
-l -r -w [-D ] [-i ] # -d : usbfs device name # -a : EEPROM address # -l : data length # -r : read EEPROM # -w : write EEPROM # -D : provides data in for write operation # bytes separated by spaces # -i : name of a file in intel hex-format for write operation # # The options -r and -w are mutually exclusive. # So are the options -D and -i. If none is specified, data is expected # from STDIN in ihex-format. # In the case that data is read from a file or STDIN, options -a and -l # are forbidden. # use strict; use USB; use Getopt::Std; my $verbose = 0; my %vendor_codes = ('RW_INTERNAL' => 0xa0, 'RW_EEPROM' => 0xb2, 'RW_MEMORY' => 0xa3, 'GET_EEPROM_SIZE' => 0xa5); # ############################################################################# # dec_hex_convert($number) # # Takes the given number and converts it into the decimal representation if it # is in hexadecimal representation. Decimals are just propagated. # # Input: # $number : a decimal or hexadecimal number # Return value: # the decimal equivalent of $number # # ############################################################################# sub dec_hex_convert { my $number = shift; return($number =~ /^0x/ ? hex($number) : $number); } # ############################################################################# # get_device($bus, $dev) # # Seaches given $bus for device $dev. # # Input: # $bus : bus number of requested device # $dev : device number of requested device # Return value: # the usb_device pointer # # ############################################################################# sub get_device { my $bus = shift; my $dev = shift; my ($usb_bus, $usb_device); my ($bus_name, $dev_name); my %device_desc; my $matching_device = 0; $usb_bus = &USB::get_usb_busses(); SCAN_BUS: { do { $bus_name = &USB::get_usb_busname($usb_bus); printf("Scanning bus name = %s\n", $bus_name) if ($verbose); print("Bus: $bus_name $bus\n") if ($verbose); if ($bus_name == $bus) { if (defined(&USB::get_usb_devices($usb_bus))) { $usb_device = &USB::get_usb_devices($usb_bus); SCAN_DEVICE: { do { $dev_name = &USB::get_usb_devicename($usb_device); print("Device: $dev_name $dev\n") if ($verbose); if ($dev_name == $dev) { %device_desc = %{ USB::get_usb_device_descriptor($usb_device) }; printf("Found device idVendor 0x%04x, idProduct 0x%04x, Class %d\n", $device_desc{'idVendor'}, $device_desc{'idProduct'}, $device_desc{'bDeviceClass'}) if ($verbose); $matching_device = $usb_device; last SCAN_BUS; } if (defined(&USB::get_usb_next_device($usb_device))) { $usb_device = &USB::get_usb_next_device($usb_device); } else { last SCAN_DEVICE; } } while (1 == 1); } } } if (defined(&USB::get_usb_next_bus($usb_bus))) { $usb_bus = &USB::get_usb_next_bus($usb_bus); } else { last SCAN_BUS; } } while (1 == 1); } return($matching_device); } # ############################################################################# # open_device_by_devfs_filename($devfs_filename) # # Opens the usbfs filehandle of the device specified in the parameter. # # Input: # $devfs_filename : filename of the usbfs device # Return value: # returns the opened file handle # # ############################################################################# sub open_device_by_devfs_name { my $devfs_name = shift; my (@path_elems, $num, $bus, $device); my (@this_dev, $result); # test if the file name is really present and writeable if (-w $devfs_name) { @path_elems = split(/\//, $devfs_name); $num = scalar(@path_elems); $device = $path_elems[$num - 1]; $bus = $path_elems[$num - 2]; print("$bus $device\n") if ($verbose); @this_dev = get_device($bus, $device); $result = &USB::usb_open($this_dev[0]); } else { print(STDERR "Can't open $devfs_name for writing!\n"); undef($result); } return($result); } # ############################################################################# # usb_read_eeprom($device, $address, $length, $data) # # Read data at given address from EEPROM. # # Input: # $device : file handle for the usbfs device # $address : address of data in EEPROM # $length : length of data # Output: # $data : this array ref will be filled with the received bytes # Return value: # number of read bytes, -1 on error # # ############################################################################# sub usb_read_eeprom { my $device = shift; my $address = shift; my $length = shift; my $data = shift; my ($rd, $result); local *ZERO_FILE; # 0 = autodetect; 2 = 8 bit address; 3 = 16 bit address my $i2c_address_size = 2; # hardwired address part, not relevant during autodetect my $i2c_wired_address = 1; # pagesize of EEPROM (not relevant for read operation) my $i2c_pagesize = 1; open(ZERO_FILE, " 0) { @{$data} = unpack("C$result", $rd); if ($result != $length) { print(STDERR "Could not read all requested data from the EEPROM.\n"); } } else { print(STDERR "Error while reading the EEPROM.\n"); } } else { print(STDERR "Could not initialize raw data scalar.\n"); $result = -1; } return($result); } # ############################################################################# # usb_write_eeprom($device, $address, $length, $data) # # Write given data at specified address to EEPROM. # # Input: # $device : file handle for the usbfs device # $address : address of data in EEPROM # $length : length of data # $data : reference of array containing the data (bytewise) # Return value: # number of written bytes, -1 on error # # ############################################################################# sub usb_write_eeprom { my $device = shift; my $address = shift; my $length = shift; my $data = shift; my ($rd, $result); # 0 = autodetect; 2 = 8 bit address; 3 = 16 bit address my $i2c_address_size = 2; # hardwired address part, not relevant during autodetect my $i2c_wired_address = 1; # # Some pagesizes taken from their databooks: # # 27xx00 : 1 # 27xx01 : 8 # 27xx16 : 16 # 27xx32 : 32 # 27xx64 : 32 # 27xx128 : 64 # 27xx256 : 64 # 27xx515 : 64 my $i2c_pagesize = 64; $rd = pack("C$length", @{$data}); $result = &USB::usb_control_msg($device, 0x40, $vendor_codes{'RW_EEPROM'}, $address, ($i2c_wired_address << 4) | $i2c_address_size | ($i2c_pagesize << 8), $rd, $length, 1000); if ($result > 0) { if ($result != $length) { print(STDERR "Could not read all requested data from the EEPROM.\n"); } } else { print(STDERR "Error while writing the EEPROM.\n"); } return($result); } # ############################################################################# # read_eeprom($device, $address, $length) # # Wrapper for usb_read_eeprom(). Read requests are split into smaller # packages. This allows debugging the firmware if it seems to have problems # with larger requests. # # Input: # $device : file handle for the usbfs device # $address : address of data in EEPROM # $length : length of data # Return value: # number of read bytes, -1 on error # # ############################################################################# sub read_eeprom { my $device = shift; my $address = shift; my $length = shift; my $max_len = 128; my (@data, $elem); my ($result, $received, $requested); $received = 0; while ($received < $length) { $requested = $length - $received >= $max_len ? $max_len : $length - $received; $result = usb_read_eeprom($device, $address + $received, $requested, \@data); print("Read $result Bytes\n"); if ($result == $requested) { $received += $requested; foreach $elem (@data) { printf("0x%02x ", $elem); } print("\n"); } else { $result = -1; last; } } return($result > 0 ? $received : $result); } # ############################################################################# # usb_write_eeprom($device, $address, $length, $data) # # Wrapper for usb_write_eeprom(). Data is split into packets of 32 bytes. # # Input: # $device : file handle for the usbfs device # $address : address of data in EEPROM # $length : length of data # $data : reference of array containing the data (bytewise) # Return value: # number of written bytes, -1 on error # # ############################################################################# sub write_eeprom { my $device = shift; my $address = shift; my $length = shift; my $data = shift; my $max_len = 128; my ($result, $transmitted, $requested); my (@partial_data, $i); $transmitted = 0; while ($transmitted < $length) { $requested = $length - $transmitted >= $max_len ? $max_len : $length - $transmitted; # copy scheduled data in a loop # could be reduced to one single statement (using [..]) but the dec/hex # conversion is necessary for ($i = $transmitted; $i < $transmitted+$requested; $i++) { push(@partial_data, dec_hex_convert($data->[$i])); } $result = usb_write_eeprom($device, $address + $transmitted, $requested, \@partial_data); if ($result == $requested) { $transmitted += $requested; } else { $result = -1; last; } } return($result > 0 ? $transmitted : $result); } # ############################################################################# # write_eeprom_from_file($device, FILE) # # Reads in all records in FILE and writes them to the EEPROM via # write_eeprom(). # # Input: # $device : file handle for the usbfs device # FILE : descriptor of the opened hex-file # Return value: # 1 on success, 0 on error # # ############################################################################# sub write_eeprom_from_file { my $device = shift; my $file_contents = shift; my ($len, $address, $value, $checksum); my (@records, $record, $data); my ($i, $result, $line); $result = 1; foreach $line (@{$file_contents}) { if ($line =~ m{^:(..) # Record length -> $1 (..) # Load Offset High -> $2 (..) # Load Offset Low -> $3 00 # Record Type (.+) # Data -> $4 (..) # Checksum -> $5 \s*$}x) { print($line); $len = hex($1); $address = hex($2) * 256 + hex($3); $value = $4; # precalculate checksum from header data $checksum = (hex($1) + hex($2) + hex($3) + hex($5)) % 256; $record = {}; $record->{'len'} = $len; $record->{'addr'} = $address; $record->{'data'} = []; push(@records, $record); $data = $record->{'data'}; for ($i = 0; $i < $len; $i++) { if ($value =~ /^(..)(.*)$/) { push(@{$data}, hex('0x'.$1)); $checksum = ($checksum + hex($1)) % 256; $value = $2; } } if ($checksum != 0) { print(STDERR "Checksum error in line:\n $line\n"); $result = -1; last; } } } if ($result > 0) { # transfer the records to the EZ-USB device foreach $record (@records) { $len = $record->{'len'}; $address = $record->{'addr'}; $data = $record->{'data'}; $result = write_eeprom($device, $address, $len, $data); if ($result != $len) { print(STDERR "Could not write $len bytes to EEPROM.\n"); $result = -1; last; } else { print("Writing $len bytes @ $address\n"); } } } return($result); } sub print_usage { print < -a
-l -r -w [-D ] [-i ] -d : usbfs device name -a : EEPROM address -l : data length -r : read EEPROM -w : write EEPROM -D : provides data in for write operation bytes separated by spaces -i : name of a file in intel hex-format for write operation The options -r and -w are mutually exclusive. So are the options -D and -i. If none is specified, data is expected from STDIN in ihex-format. In the case that data is read from a file or STDIN, options -a and -l are forbidden. EOU } # ############################################################################# # Main Program # ############################################################################# my %options; my ($device_name, $dev_handle); my (@data, $elem); my ($address, $length); my ($read, $write); my $ihex_filename = ""; my $read_ihex_file = 0; my $result; my @file_contents; if (getopts('a:d:l:D:rwi:', \%options)) { if (exists($options{'d'})) { $device_name = $options{'d'}; } else { print_usage(); exit(1); } if (exists($options{'a'})) { $address = dec_hex_convert($options{'a'}); } if (exists($options{'l'})) { $length = dec_hex_convert($options{'l'}); } $read = $write = 0; if (exists($options{'r'}) && ($options{'r'} == 1)) { $read = 1; } if (exists($options{'w'}) && ($options{'w'} == 1)) { $write = 1; } if (($read == 1) && ($write == 1)) { print_usage(); exit(1); } if (exists($options{'D'})) { @data = split(/ /, $options{'D'}); if (!(exists($options{'a'}) && exists($options{'l'}))) { print(STDERR "You must specify -a and -l with -D.\n"); print_usage(); exit(1); } } if (exists($options{'i'})) { $ihex_filename = $options{'i'}; # also open the file -r $ihex_filename or die "Can't open $ihex_filename.\n"; @file_contents = `cat $ihex_filename`; $read_ihex_file = 1; } if ($write && !(exists($options{'D'}) || exists($options{'i'}))) { @file_contents = ; $read_ihex_file = 1; } if ($read_ihex_file && (exists($options{'l'}) || exists($options{'a'}))) { print(STDERR "-l or -a must not be specified when reading an ihex file.\n"); print_usage(); exit(1); } die "Can't initialize USB subsystem\n" if (defined(&USB::usb_init())); die "Can't find busses\n" if (&USB::usb_find_busses() < 0); die "Can't find devices\n" if (&USB::usb_find_devices() < 0); $dev_handle = open_device_by_devfs_name($device_name); if (defined($dev_handle)) { if ($read) { $result = read_eeprom($dev_handle, $address, $length); } if ($write) { if (exists($options{'D'})) { $result = write_eeprom($dev_handle, $address, $length, \@data); } else { # print("@file_contents"); $result = write_eeprom_from_file($dev_handle, \@file_contents); } } &USB::usb_close($dev_handle); } else { print(STDERR "Can't open USB device handle\n"); } } else { print_usage(); }