#!/usr/bin/perl
# Copyright (c) Mauro Carvalho Chehab <mchehab@infradead.org>
# Released under GPLv2
#
# In order to use, you need to:
# 1) Download the windows driver with something like:
# wget http://www.steventoth.net/linux/xc5000/HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip
# 2) Extract the file hcw85bda.sys from the zip into the current dir:
# unzip -j HVR-12x0-14x0-17x0_1_25_25271_WHQL.zip Driver85/hcw85bda.sys
# 3) run the script:
# ./extract_xc3028.pl
# 4) copy the generated file:
# cp xc3028-v27.fw /lib/firmware
#use strict;
use IO::Handle;
my $debug=0;
sub verify ($$)
{
my ($filename, $hash) = @_;
my ($testhash);
if (system("which md5sum > /dev/null 2>&1")) {
die "This firmware requires the md5sum command - see http://www.gnu.org/software/coreutils/\n";
}
open(CMD, "md5sum ".$filename."|");
$testhash = <CMD>;
$testhash =~ /([a-zA-Z0-9]*)/;
$testhash = $1;
close CMD;
die "Hash of extracted file does not match (found $testhash, expected $hash!\n" if ($testhash ne $hash);
}
sub get_hunk ($$)
{
my ($offset, $length) = @_;
my ($chunklength, $buf, $rcount, $out);
sysseek(INFILE, $offset, SEEK_SET);
while ($length > 0) {
# Calc chunk size
$chunklength = 2048;
$chunklength = $length if ($chunklength > $length);
$rcount = sysread(INFILE, $buf, $chunklength);
die "Ran out of data\n" if ($rcount != $chunklength);
$out .= $buf;
$length -= $rcount;
}
return $out;
}
sub write_le16($)
{
my $val = shift;
my $msb = ($val >> 8) &0xff;
my $lsb = $val & 0xff;
syswrite(OUTFILE, chr($lsb).chr($msb));
}
sub write_le32($)
{
my $val = shift;
my $l3 = ($val >> 24) & 0xff;
my $l2 = ($val >> 16) & 0xff;
my $l1 = ($val >> 8) & 0xff;
my $l0 = $val & 0xff;
syswrite(OUTFILE, chr($l0).chr($l1).chr($l2).chr($l3));
}
sub write_le64($$)
{
my $msb_val = shift;
my $lsb_val = shift;
my $l7 = ($msb_val >> 24) & 0xff;
my $l6 = ($msb_val >> 16) & 0xff;
my $l5 = ($msb_val >> 8) & 0xff;
my $l4 = $msb_val & 0xff;
my $l3 = ($lsb_val >> 24) & 0xff;
my $l2 = ($lsb_val >> 16) & 0xff;
my $l1 = ($lsb_val >> 8) & 0xff;
my $l0 = $lsb_val & 0xff;
syswrite(OUTFILE,
chr($l0).chr($l1).chr($l2).chr($l3).
chr($l4).chr($l5).chr($l6).chr($l7));
}
sub write_hunk($$)
{
my ($offset, $length) = @_;
my $out = get_hunk($offset, $length);
printf "(len %d) ",$length if ($debug);
for (my $i=0;$i<$length;$i++) {
printf "%02x ",ord(substr($out,$i,1)) if ($debug);
}
printf "\n" if ($debug);
syswrite(OUTFILE, $out);
}
sub write_hunk_fix_endian($$)
{
my ($offset, $length) = @_;
my $out = get_hunk($offset, $length);
printf "(len_fix %d) ",$length if ($debug);
for (my $i=0;$i<$length;$i++) {
printf "%02x ",ord(substr($out,$i,1)) if ($debug);
}
printf "\n" if ($debug);
my $i=0;
while ($i<$length) {
my $size = ord(substr($out,$i,1))*256+ord(substr($out,$i+1,1));
syswrite(OUTFILE, substr($out,$i+1,1));
syswrite(OUTFILE, substr($out,$i,1));
$i+=2;
if ($size>0 && $size <0x8000) {
for (my $j=0;$j<$size;