Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
Tazmaniac authored Nov 7, 2022
0 parents commit 9355d4d
Showing 1 changed file with 259 additions and 0 deletions.
259 changes: 259 additions & 0 deletions spt2hex.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
#!/usr/bin/perl
#
# spt to hex converter
# This programm extract firmware from hex2spt gerenated Cypress EZ-USB
# spt script file.
# Copyright (C) 2013 Emmanuel Fusté
#
# 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 3 of the License, or
# 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, see <http://www.gnu.org/licenses/>.
#
#
# spt is a generic USB commands scripting format, but as we know how hex files
# are converted to spt by standard cypress tools or procedures to get back
# an hex file from a hex-converted-to-spt file.
#
#
# each USB transaction is described by a record
# record format: 32 byte header in little endian + data
#
# uint Signature == 0x54505343 or byte[4] == 'CSPT' //4
# uint RecordSize == header size + data size == 32 + data size //8
# ushort HeaderSize == 32 == 0x0020//10
# byte Tag == 0 == 0x00//11
#
# Device on wich the script will be played : selected at runtime,
# 0x0 in the script:
# byte ConfigNum == 0 == 0x00 //12
# byte IntfcNum == 0 == 0x00 //13
# byte AltIntfc == 0 == 0x00 //14
# byte EndPtAddr == 0 = 0x00 //15
#
# byte bReqType == 0x40 in our case (TGT_DEVICE|REQ_VENDOR|DIR_TO_DEVICE) //16
# byte CtlReqCode //17
# byte reserved0 is generaly 0x20//18
#
# ushort wValue //20
# ushort wIndex //22
# byte reserved1 ~ 0x9F //23
# byte reserved2 ~ 0x00 //24

# uint Timeout ~ 0x00000005 //28
# uint DataLen; //32
#
# hex to spt converter do the following:
# 1 CPU put in reset mode (A0 request to write 0x01 at 0xE600 for FX2LP)
# 2 upload of vendor_ax firmware with A0 request
# 3 CPU put in run mode (A0 request to write 0x00 to addresss 0xE600 for FX2LP)
# 4 upload of external RAM code with A3 vendor request implemented by vendor_ax
# 5 CPU put in reset mode
# 6 upload of RAM code with A0 vendor request (all code under 16k on FX2LP)
# 7 CPU put in run mode
#
# spt generated by cypress tools upload inconditionnaly vendor_ax firmware.
# If there is nothing to upload to external ram, step 4 is skipped.
#
# If manualy recorded, step 2,3,4,5 could be omitted if no code need to be
# uploaded to external ram.
#
# all contiguous hex record data are packed in one usb transaction and re-split
# in two if the packed result in payload > 4k
# Cypress tools generate hex file with 16 bytes of data max per record.
# Decoded spt data payload will be re-split in 16 bytes hex record.
#
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;

my $version = "1.0";
my $verbose = '';
my $help = '';

GetOptions("help|h" => \$help, 'verbose|v' => \$verbose);

my $sptfile = $ARGV[0];

if ((!$sptfile ) || $help) { pod2usage(2) };

open my $fh, '<', $sptfile or die "Couldn't open file $sptfile: $!\n";

binmode $fh;

my %allrecords;
my @reset;
my @run;

my $index=0;

while ( read $fh, my $header, 32 ) {
# read $fh, my $header, 32 ;

my ($magic, $rsize, $hsize, $tag, $confnum, $intfcnum, $altintfc,
$endptaddr, $breqtype, $ctlreqcode, $res0, $wvalue, $windex,
$res1, $res2, $timeout, $datalen)
= unpack("a4VvCCCCCCCCvvCCVV" , $header);

if ($verbose) {
printf( "magic : %s\n", $magic);
printf( "record size : 0x%08x\n", $rsize);
printf( "header size : 0x%04x\n", $hsize);
printf( "tag : 0x%02x\n", $tag);
printf( "confnum : 0x%02x\n", $confnum);
printf( "intfcnum : 0x%02x\n", $intfcnum);
printf( "altintfc : 0x%02x\n", $altintfc);
printf( "endptaddr : 0x%02x\n", $endptaddr);
printf( "breqtype : 0x%02x\n", $breqtype);
printf( "ctlreqcode : 0x%02x\n", $ctlreqcode);
printf( "res0 : 0x%02x\n", $res0);
printf( "wvalue : 0x%04x\n", $wvalue);
printf( "windex : 0x%04x\n", $windex);
printf( "res1 : 0x%02x\n", $res1);
printf( "res2 : 0x%02x\n", $res2);
printf( "timeout : 0x%08x\n", $timeout);
printf( "datalen : 0x%08x\n", $datalen);
print "\n";
}

$index++;

# sanity checks
if ( $magic ne "CSPT" ) { die "Bad header magic in record header No $index \n" };
if ( $hsize != 32 ) { die "Bad header size in record header No $index \n" };
if ( $rsize != (32 + $datalen) ) { die "bad record size in record header No $index \n" };
if ( $breqtype != 0x40 ) { die "Record No $index not from a hex2spt file : bad breqtype\n" };
if ( $windex != 0x0000 ) { die "Record No $index not from a hex2spt file : windex not null\n"};

# read the data payload
read $fh, my $rawdata, $datalen or die "Corrupted spt file\n";
my $data = unpack("C", $rawdata);

# is it reset or run command ?
if (($ctlreqcode == 0xa0) && ($wvalue == 0xe600)) {
if (($datalen == 0x00000001) && ($data == 0x01)) {
push @reset, $index;
next;
}
if (($datalen == 0x00000001) && ($data == 0x00)) {
push @run, $index;
next;
}
die "Invalid CPUCS value or length in record $index\n";
}
if ($ctlreqcode == 0xa3) {
die "External ram recovery from spt not implemented\n";
}

$allrecords{$index}{ 'ctlreqcode' } = $ctlreqcode;
$allrecords{$index}{ 'wvalue' } = $wvalue;
$allrecords{$index}{ 'datalen' } = $datalen;
$allrecords{$index}{ 'data' } = $rawdata;

}

my $nbrun = @run;
my $nbreset = @reset;

print "number of records : $index\n";
print "number of reset : $nbreset at record No: @reset\n";
print "number of run : $nbrun at record No: @run\n";

if (($nbreset != $nbrun) || ($nbreset > 2) || ($nbrun > 2)
|| ($nbreset == 0) || ($nbrun == 0)) {
die "Not a hex2spt spt file\n";
}

sub writehex {
my ( $start, $end, $filename) = @_;
my $current;
open my $outfh, '>', $filename or die "Couldn't open file $filename: $!\n";

for ($current = $start; $current <= $end; $current++) {
my $worklen = $allrecords{$current}{'datalen'};
my $offset = $allrecords{$current}{'wvalue'};
my @data = unpack("C*", $allrecords{$current}{'data'});
my $dataoffset = 0;

while ($worklen > 0) {
my $writelen;
my $i;

if ($worklen > 16) {
$writelen = 16;
} else {
$writelen = $worklen;
}

printf($outfh ":%02X%04X00", $writelen, $offset);
my $checksum = $writelen;
$checksum += $offset & 0x000000FF;
$checksum += ($offset & 0x0000FF00) / 256;
for ($i = $dataoffset; $i < ($dataoffset + $writelen); $i++) {
printf($outfh "%02X", $data[$i]);
$checksum += $data[$i];
}
$checksum &= 0x000000FF;
$checksum = 0x100 - $checksum;
$checksum &= 0x000000FF;
printf($outfh "%02X", $checksum);
printf($outfh "\n");
$worklen -= 16;
$offset += 16;
$dataoffset += 16;
}
}

printf($outfh ":00000001FF\n");
close $outfh;
}

my $beginfirstfw;
my $endfirstfw;
my @outfile;
$beginfirstfw = $reset[0] + 1;
$endfirstfw = $run[0] - 1;
@outfile = split(".spt", $sptfile);

if (($nbreset == 2) && ($nbrun == 2)) {
my $beginsecondfw;
my $endsecondfw;
$beginsecondfw = $reset[1] + 1;
$endsecondfw = $run[1] - 1;
print "vend_ax firmware located in record $beginfirstfw to record $endfirstfw \n";
writehex($beginfirstfw, $endfirstfw, "vend_ax.hex");
print "original firmware located in record $beginsecondfw to record $endsecondfw \n";
writehex($beginsecondfw, $endsecondfw, $outfile[0] . ".hex");

} else {
print "original firmware located in record $beginfirstfw to record $endfirstfw \n";
writehex($beginfirstfw, $endfirstfw, $outfile[0] . ".hex");
}

close $fh;

__END__
=head1 NAME
spt2hex - Cypress EZ-USB spt script to hex file converter
=head1 SYNOPSIS
spt2hex [options] file
Options:
-verbose|-v show decoded spt records
-help|-h brief help message
=cut

0 comments on commit 9355d4d

Please sign in to comment.