#!/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 <usbfs name> -a <address> -l <length> -r -w [-D <data string>] [-i <hex-file>]
# -d : usbfs device name
# -a : EEPROM address
# -l : data length
# -r : read EEPROM
# -w : write EEPROM
# -D : provides data in <data string> 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 {
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 ($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);
}
}
# #############################################################################
# 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 (@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);
$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");
}
}
# #############################################################################
# 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 ($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;
# pagesize of EEPROM (not relevant for read operation)
my $i2c_pagesize = 1;
open(ZERO_FILE,
"</dev/zero");
$result =
read(ZERO_FILE,
$rd,
$length);
if ($result == $length) {
$result = &USB::usb_control_msg($device, 0xc0, $vendor_codes{'RW_EEPROM'},
$address,
($i2c_wired_address << 4) | $i2c_address_size |
($i2c_pagesize << 8),
$rd, $length, 1000);
if ($result > 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;
}
}
# #############################################################################
# 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 ($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");
}
}
# #############################################################################
# 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 $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) {
}
} 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 $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->&
#91;$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 $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
$address =
hex($
2) *
256 +
hex($
3);
$value = $4;
# precalculate checksum from header data
$record = {};
$record->{'len'} = $len;
$record->{'addr'} = $address;
$record->{'data'} = [];
$data = $record->{'data'};
for ($i = 0; $i < $len; $i++) {
if ($value =~ /^(..)(.*)$/) {
$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");
}
}
}
}
sub print_usage {
Usage: $0 -d <usbfs name> -a <address> -l <length> -r -w [-D <data string>] [-i <hex-file>]
-d : usbfs device name
-a : EEPROM address
-D : provides data in <data string>
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
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)) {
$device_name = $options{'d'};
} else {
print_usage();
}
$address = dec_hex_convert($options{'a'});
}
$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();
}
@data =
split(/ /,
$options{'D'});
print(STDERR "You must specify -a and -l with -D.\n");
print_usage();
}
}
$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 = <STDIN>;
$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();
}
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 ($read) {
$result = read_eeprom($dev_handle, $address, $length);
}
if ($write) {
$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();
}